Eine Sekunde heisst nicht „Weiterschauen"

Die Schwelle aus den Einstellungen wirkt serverseitig, also sofort überall:
`/home/continue` liest die gespeicherte Spalte. Eine Stelle blieb trotzdem
widersprüchlich — die **Beschriftung**.

Web und Apps entschieden „Weiterschauen" gegen „Abspielen" selbst, allein
daran, ob eine Stelle über null steht. Nach einer Sekunde stand der Titel
damit nicht mehr in der Reihe, hiess aber weiter „Weiterschauen", trug
einen Fortschrittsbalken unter der Kachel und bot „Aus Weiterschauen
entfernen" an.

Der Server hatte diese Frage längst beantwortet. `ItemStateResponse` bringt
sie jetzt mit; `MediaItem.isResumable` und `.resumeProgress` sind die
einzigen Stellen, die sie noch stellen. Im Simulator nachgeprüft: nach
einer Sekunde heisst der Knopf „Abspielen", „Von vorn" fehlt, und der
Balken unter der Kachel ist weg.

Kennt ein Server das Feld nicht — Jellyfin, oder ein älterer Flaro —, gilt
wie früher die blosse Stelle.

**Und das Profilbild steht in den Apps.** `FlaroAccount` führt es mit,
`/auth/me` bringt es bei jedem Start frisch, und der Buchstabenkreis tritt
zurück, sobald eines da ist. Jellyfin bleibt beim Buchstaben — dort gibt es
kein Profilbild, und das ist keine Lücke, sondern der Stand der Dinge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-06 17:30:38 +02:00
co-authored by Claude Opus 5
parent 579a49b73b
commit e30158b84c
25 changed files with 155 additions and 39 deletions
@@ -443,17 +443,26 @@ public struct FlaroAccount: Sendable, Equatable {
public let userName: String
public let isAdministrator: Bool
/// Das eigene Bild, als Schlüssel in die Bildablage — `nil`: der Buchstabe.
///
/// **Mit Vorgabewert, damit Jellyfin unberührt bleibt.** Dort gibt es
/// kein Profilbild, und ein Pflichtfeld hier zwänge jeden Aufrufer, sich
/// dazu zu äussern.
public let avatarImage: String?
public init(
baseURL: URL,
serverName: String,
userID: String,
userName: String,
isAdministrator: Bool
isAdministrator: Bool,
avatarImage: String? = nil
) {
self.baseURL = baseURL
self.serverName = serverName
self.userID = userID
self.userName = userName
self.isAdministrator = isAdministrator
self.avatarImage = avatarImage
}
}
@@ -38,6 +38,8 @@ enum Wire {
let id: String
let userName: String
let isAdministrator: Bool
/// Das Profilbild, als Schlüssel in die Bildablage des Servers.
let avatarImage: String?
}
struct Library: Decodable, Sendable {
@@ -78,6 +80,12 @@ enum Wire {
let rating: Int?
let dismissed: Bool
let lastPlayedAt: Date?
/// Ob der Server den Titel in „Weiterschauen" führt.
///
/// **Nicht dasselbe wie „Stelle über null".** Seit es eine
/// einstellbare Untergrenze gibt, sind das zwei Fragen: eine Sekunde
/// wird gemerkt, zählt aber nicht als angefangen.
let inProgress: Bool?
}
struct Movie: Decodable, Sendable {
@@ -20,6 +20,7 @@ extension Wire.Movie {
resumeMilliseconds: state.map { Int($0.positionSeconds * 1000) },
lastPlayedAt: state?.lastPlayedAt,
isFavourite: state?.favourite ?? false,
isInProgress: state?.inProgress,
availableFrom: publishAt,
versions: [Wire.fassung(id: id, files: files)]
)
@@ -69,6 +70,7 @@ extension Wire.Episode {
resumeMilliseconds: state.map { Int($0.positionSeconds * 1000) },
lastPlayedAt: state?.lastPlayedAt,
isFavourite: state?.favourite ?? false,
isInProgress: state?.inProgress,
availableFrom: publishAt,
versions: [Wire.fassung(id: id, files: files)]
)
@@ -104,6 +106,7 @@ extension Wire.Started {
resumeMilliseconds: Int(state.positionSeconds * 1000),
lastPlayedAt: state.lastPlayedAt,
isFavourite: state.favourite,
isInProgress: state.inProgress,
availableFrom: publishAt
)
}
@@ -84,7 +84,8 @@ public enum FlaroSignIn {
serverName: gruss.serverName,
userID: antwort.user.id,
userName: antwort.user.userName,
isAdministrator: antwort.user.isAdministrator), client)
isAdministrator: antwort.user.isAdministrator,
avatarImage: antwort.user.avatarImage), client)
}
/// Nimmt ein gespeichertes Token wieder auf.
@@ -111,7 +112,8 @@ public enum FlaroSignIn {
serverName: gruss?.serverName ?? baseURL.host() ?? "Flaro",
userID: wer.id,
userName: wer.userName,
isAdministrator: wer.isAdministrator), client)
isAdministrator: wer.isAdministrator,
avatarImage: wer.avatarImage), client)
}
// MARK: - Der Weg über einen Code
@@ -179,7 +181,8 @@ public enum FlaroSignIn {
serverName: gruss?.serverName ?? baseURL.host() ?? "Flaro",
userID: anmeldung.user.id,
userName: anmeldung.user.userName,
isAdministrator: anmeldung.user.isAdministrator)), client)
isAdministrator: anmeldung.user.isAdministrator,
avatarImage: anmeldung.user.avatarImage)), client)
case "expired":
return (.expired, nil)
default: