From c5ab200846a25631689f61a09b09de18f748f76c Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sat, 10 Oct 2026 13:55:24 +0200 Subject: [PATCH] =?UTF-8?q?Die=20Startseite=20holte=20ihre=20Reihen=20im?= =?UTF-8?q?=20G=C3=A4nsemarsch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Weboberfläche holt alles, was auf der Startseite steht, in einem einzigen `Promise.all` — und sie holt sogar mehr davon: Vorschlag und saisonale Reihe kennt die App gar nicht. Trotzdem war sie spürbar langsamer. Der Grund stand in einer Schleife: for mediathek in mediatheken { ... await client.send(...) } Ein `await` im Schleifenrumpf heisst: Rundreise, warten, nächste Rundreise. Bei vier Mediatheken viermal hintereinander, nachdem „Fortsetzen" und „Als Nächstes" längst da waren. Dieselbe Schleife stand in beiden Hintergründen, für Flaro wie für Jellyfin. Jetzt laufen sie nebeneinander, in einer `TaskGroup`. Die Reihenfolge der Mediatheken bleibt erhalten — nach den Antworten zu ordnen hiesse, dass die Reihen nach jedem Start woanders stünden. Ein Actor steht dem nicht im Weg: jede Anfrage gibt ihn beim Warten frei, genau wie die beiden `async let` darüber es schon tun. Nebenbei: ein Fehler bei einer Mediathek nimmt die anderen nicht mehr mit. Eine Startseite mit drei von vier Reihen ist besser als keine. Co-Authored-By: Claude Opus 5 --- .../Sources/FlaroServerKit/FlaroBackend.swift | 81 +++++++++++++------ .../Sources/JellyfinKit/JellyfinBackend.swift | 37 ++++++--- 2 files changed, 81 insertions(+), 37 deletions(-) diff --git a/Packages/FlaroKit/Sources/FlaroServerKit/FlaroBackend.swift b/Packages/FlaroKit/Sources/FlaroServerKit/FlaroBackend.swift index 852a432..010787f 100644 --- a/Packages/FlaroKit/Sources/FlaroServerKit/FlaroBackend.swift +++ b/Packages/FlaroKit/Sources/FlaroServerKit/FlaroBackend.swift @@ -86,38 +86,67 @@ public actor FlaroBackend: MediaBackend { // Einstellungen — „Gesehenes in Neuzugängen ausblenden" und // „Aufmacher nur aus Neuzugängen" —, und beide taten an einem // Flaro-Server deshalb nichts. - for mediathek in mediatheken { - let pfad = mediathek.kind == .show ? "/api/v1/series" : "/api/v1/movies" - let abfrage = [ - URLQueryItem(name: "library", value: mediathek.id), - URLQueryItem(name: "sort", value: "added"), - URLQueryItem(name: "take", value: "24"), - ] - let titel: [MediaItem] - if mediathek.kind == .show { - guard let seite = try? await client.send( - FlaroRequest(path: pfad, query: abfrage), - as: Wire.Page.self) else { continue } - titel = seite.items.map { $0.neutral() } - } else { - guard let seite = try? await client.send( - FlaroRequest(path: pfad, query: abfrage), - as: Wire.Page.self) else { continue } - titel = seite.items.map { $0.neutral() } + // **Nebeneinander, nicht nacheinander.** + // + // Hier stand eine Schleife mit `await` darin: bei vier Mediatheken + // vier Rundreisen hintereinander, jede erst nach der vorigen. Die + // Weboberfläche holt dieselben Reihen in einem `Promise.all` — und + // sie holt sogar mehr davon. Genau das war der Unterschied, den man + // der App ansah. + // + // Ein Actor steht dem nicht im Weg: jede Anfrage gibt ihn beim + // Warten wieder frei, so wie die beiden darüber es schon tun. + let neuzugaenge = await withTaskGroup(of: (Int, MediaRowGroup?).self) { gruppe in + for (platz, mediathek) in mediatheken.enumerated() { + gruppe.addTask { (platz, await self.neuzugang(in: mediathek)) } } - guard !titel.isEmpty else { continue } - reihen.append(MediaRowGroup( - id: "latest.\(mediathek.id)", - title: String( - localized: "flaro.row.latestIn \(mediathek.title)", - bundle: .module), - libraryID: mediathek.id, - items: titel)) + + // **Die Reihenfolge der Mediatheken, nicht die der Antworten.** + // Sonst stünden die Reihen nach jedem Start woanders — je + // nachdem, welche Abfrage zuerst zurückkam. + var gesammelt: [(Int, MediaRowGroup?)] = [] + for await ergebnis in gruppe { gesammelt.append(ergebnis) } + return gesammelt.sorted { $0.0 < $1.0 }.compactMap(\.1) } + reihen.append(contentsOf: neuzugaenge) return reihen } + /// Die Reihe „Zuletzt hinzugefügt" einer Mediathek — oder nichts. + /// + /// **Ein Fehler bei einer Mediathek nimmt nicht die anderen mit.** Die + /// Startseite mit drei von vier Reihen ist besser als gar keine. + private func neuzugang(in mediathek: MediaLibrary) async -> MediaRowGroup? { + let pfad = mediathek.kind == .show ? "/api/v1/series" : "/api/v1/movies" + let abfrage = [ + URLQueryItem(name: "library", value: mediathek.id), + URLQueryItem(name: "sort", value: "added"), + URLQueryItem(name: "take", value: "24"), + ] + + let titel: [MediaItem] + if mediathek.kind == .show { + guard let seite = try? await client.send( + FlaroRequest(path: pfad, query: abfrage), + as: Wire.Page.self) else { return nil } + titel = seite.items.map { $0.neutral() } + } else { + guard let seite = try? await client.send( + FlaroRequest(path: pfad, query: abfrage), + as: Wire.Page.self) else { return nil } + titel = seite.items.map { $0.neutral() } + } + + guard !titel.isEmpty else { return nil } + + return MediaRowGroup( + id: "latest.\(mediathek.id)", + title: String(localized: "flaro.row.latestIn \(mediathek.title)", bundle: .module), + libraryID: mediathek.id, + items: titel) + } + public func items( inLibrary library: MediaLibrary, query: MediaQuery, diff --git a/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend.swift index 1b2b99a..c8f3c0d 100644 --- a/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend.swift +++ b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend.swift @@ -130,19 +130,34 @@ public func isInLocalNetwork() async -> Bool { // sind zwei verschiedene Fragen — in einer gemeinsamen Reihe steht am // Ende beides durcheinander, und wer nach einem Film sucht, blättert // an Folgen vorbei. - for library in libraries { - let items = try? await latestItems(limit: 20, inLibrary: library.id) - rows.append(MediaRowGroup( - id: "latest.\(library.id)", - title: String( - localized: "row.latestIn \(library.title)", - bundle: .module - ), - libraryID: library.id, - items: items ?? [] - )) + // + // **Nebeneinander, nicht nacheinander.** Hier stand ein `await` in + // der Schleife: bei vier Mediatheken vier Rundreisen hintereinander. + // Die Reihenfolge der Mediatheken bleibt trotzdem erhalten — sonst + // stünden die Reihen nach jedem Start woanders. + let latest = await withTaskGroup(of: (Int, MediaRowGroup).self) { group in + for (place, library) in libraries.enumerated() { + group.addTask { + let items = try? await self.latestItems(limit: 20, inLibrary: library.id) + return (place, MediaRowGroup( + id: "latest.\(library.id)", + title: String( + localized: "row.latestIn \(library.title)", + bundle: .module + ), + libraryID: library.id, + items: items ?? [] + )) + } + } + + var collected: [(Int, MediaRowGroup)] = [] + for await result in group { collected.append(result) } + return collected.sorted { $0.0 < $1.0 }.map(\.1) } + rows.append(contentsOf: latest) + rows.append(MediaRowGroup( id: "favourites", title: String(localized: "row.favourites", bundle: .module),