Die Mediathek hörte bei I auf, und jede Folge stand doppelt
Zwei Befunde aus dem laufenden Betrieb. Die Liste holte genau eine Seite. Der Server gibt höchstens 200 Einträge je Zug — das ist richtig so —, nur fragte die Oberfläche nie nach der zweiten. Bei 455 Filmen endete die Mediathek mitten im Alphabet, ohne Hinweis, dass da noch etwas kommt. Jetzt wird beim Scrollen nachgeladen, und die Zahl sagt „200 von 455" statt einer Behauptung, die man beim Scrollen widerlegt. Dabei kam heraus, dass ein IntersectionObserver eine Seite braucht, die auch gezeichnet wird: im Fenster im Hintergrund löst er nicht aus, und beim Prüfen blieb die Liste genau deshalb bei 200 stehen. Daneben steht jetzt eine Prüfung am Scrollereignis, die überall greift. Der zweite Befund: jede Folge zeigte zwei gleich aussehende Knopfreihen. Es sind zwei Dateien — Plex' „Optimized for Mobile"-Kopien. Flaro kannte „optimized", aber nicht „Optimized for Mobile", und sah ohnehin nur den unmittelbaren Elternordner; Plex legt die Fassung aber als `Plex Versions/Optimized for Mobile/<Serie>/<Staffel>/…` ab. Jetzt wird drei Ebenen hoch gesucht, die Schublade `Plex Versions` benennt keine Serie mehr, und an der Folge steht „Mobil" statt einer zweiten, unerklärten Reihe. Nicht um Plex zu bedienen — die Dateien liegen nun einmal so auf der Platte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
960cca3dae
commit
8809b9f46f
@@ -210,9 +210,15 @@ public sealed record MediaStreamResponse(
|
||||
/// App spricht Dateien über ihre Kennung an. Zum Unterscheiden zweier
|
||||
/// Fassungen genügt der Name.
|
||||
/// </remarks>
|
||||
/// <param name="VersionLabel">
|
||||
/// <b>Wie diese Fassung heisst</b> — „Mobil", „4K", „Director's Cut" — oder
|
||||
/// <c>null</c>, wenn es nur eine gibt. Ohne diese Angabe standen zwei Dateien
|
||||
/// derselben Folge als zwei gleich aussehende Knopfreihen untereinander, und
|
||||
/// die Folge wirkte doppelt.
|
||||
/// </param>
|
||||
public sealed record MediaFileResponse(
|
||||
Guid Id, string FileName, long SizeBytes, string? Container, double? DurationSeconds,
|
||||
IReadOnlyList<MediaStreamResponse> Streams);
|
||||
IReadOnlyList<MediaStreamResponse> Streams, string? VersionLabel = null);
|
||||
|
||||
public sealed record MovieResponse(
|
||||
Guid Id, string Title, int? Year, string? Overview,
|
||||
|
||||
@@ -70,8 +70,13 @@ public static class LibraryContentEndpoints
|
||||
|
||||
private static MediaFileResponse Datei(
|
||||
Guid id, string pfad, long bytes, string? schachtel, TimeSpan? dauer,
|
||||
ILookup<Guid, MediaStreamResponse> spuren) =>
|
||||
new(id, System.IO.Path.GetFileName(pfad), bytes, schachtel, dauer?.TotalSeconds, spuren[id].ToList());
|
||||
ILookup<Guid, MediaStreamResponse> spuren, string? fassung = null) =>
|
||||
new(id, System.IO.Path.GetFileName(pfad), bytes, schachtel, dauer?.TotalSeconds,
|
||||
spuren[id].ToList(),
|
||||
// Steht sie nicht in der Datenbank, wird sie aus dem Pfad gelesen:
|
||||
// ein Bestand von vor dieser Änderung soll nicht erst neu gescannt
|
||||
// werden müssen, um „Mobil" zu zeigen.
|
||||
fassung ?? FileNames.Fassung(pfad));
|
||||
|
||||
/// <summary>Die Genres eines Titels, in der Reihenfolge der Quelle.</summary>
|
||||
private static async Task<IReadOnlyList<string>> GenresZu(FlaroContext db, Guid? film, Guid? serie) =>
|
||||
@@ -315,7 +320,7 @@ public static class LibraryContentEndpoints
|
||||
var ids = kennungen.Select(m => m.Id).ToList();
|
||||
var dateien = await db.MediaFiles.AsNoTracking()
|
||||
.Where(f => f.MovieId != null && ids.Contains(f.MovieId!.Value))
|
||||
.Select(f => new { f.Id, f.Path, f.SizeBytes, f.Container, f.Duration, f.MovieId })
|
||||
.Select(f => new { f.Id, f.Path, f.SizeBytes, f.Container, f.Duration, f.MovieId, f.VersionLabel })
|
||||
.ToListAsync();
|
||||
var spuren = await SpurenZu(db, dateien.Select(f => f.Id).ToList());
|
||||
var stände = await ZuständeZu(db, wer, filme: ids);
|
||||
@@ -323,7 +328,7 @@ public static class LibraryContentEndpoints
|
||||
var items = kennungen.Select(m => new MovieResponse(
|
||||
m.Id, m.Title, m.Year, m.Overview, m.PosterImage, m.BackdropImage,
|
||||
dateien.Where(f => f.MovieId == m.Id)
|
||||
.Select(f => Datei(f.Id, f.Path, f.SizeBytes, f.Container, f.Duration, spuren)).ToList(),
|
||||
.Select(f => Datei(f.Id, f.Path, f.SizeBytes, f.Container, f.Duration, spuren, f.VersionLabel)).ToList(),
|
||||
stände.GetValueOrDefault(m.Id),
|
||||
m.LogoImage, m.RuntimeMinutes, m.Rating, null, m.ContentRating,
|
||||
OriginalTitle: m.OriginalTitle)).ToList();
|
||||
@@ -477,7 +482,7 @@ public static class LibraryContentEndpoints
|
||||
|
||||
var dateien = await db.MediaFiles.AsNoTracking()
|
||||
.Where(f => f.MovieId == id)
|
||||
.Select(f => new { f.Id, f.Path, f.SizeBytes, f.Container, f.Duration })
|
||||
.Select(f => new { f.Id, f.Path, f.SizeBytes, f.Container, f.Duration, f.VersionLabel })
|
||||
.ToListAsync();
|
||||
var spuren = await SpurenZu(db, dateien.Select(f => f.Id).ToList());
|
||||
var stände = await ZuständeZu(db, wer, filme: [id]);
|
||||
@@ -489,7 +494,7 @@ public static class LibraryContentEndpoints
|
||||
return Results.Ok(new MovieResponse(
|
||||
film.Id, uebersetzt?.Title ?? film.Title, film.Year,
|
||||
uebersetzt?.Overview ?? film.Overview, film.PosterImage, film.BackdropImage,
|
||||
dateien.Select(f => Datei(f.Id, f.Path, f.SizeBytes, f.Container, f.Duration, spuren)).ToList(),
|
||||
dateien.Select(f => Datei(f.Id, f.Path, f.SizeBytes, f.Container, f.Duration, spuren, f.VersionLabel)).ToList(),
|
||||
stände.GetValueOrDefault(id),
|
||||
film.LogoImage, film.RuntimeMinutes, film.Rating,
|
||||
uebersetzt?.Tagline ?? film.Tagline, film.ContentRating,
|
||||
@@ -553,7 +558,7 @@ public static class LibraryContentEndpoints
|
||||
|
||||
var dateien = await db.MediaFiles.AsNoTracking()
|
||||
.Where(d => d.EpisodeId == naechste.Id)
|
||||
.Select(d => new { d.Id, d.Path, d.SizeBytes, d.Container, d.Duration })
|
||||
.Select(d => new { d.Id, d.Path, d.SizeBytes, d.Container, d.Duration, d.VersionLabel })
|
||||
.ToListAsync();
|
||||
var spuren = await SpurenZu(db, dateien.Select(d => d.Id).ToList());
|
||||
var stände = await ZuständeZu(db, wer, folgen: [naechste.Id]);
|
||||
@@ -569,7 +574,7 @@ public static class LibraryContentEndpoints
|
||||
return Results.Ok(new EpisodeResponse(
|
||||
naechste.Id, naechste.SeasonNumber, naechste.EpisodeNumber,
|
||||
naechste.Title, naechste.Overview, naechste.StillImage ?? serienBild,
|
||||
dateien.Select(d => Datei(d.Id, d.Path, d.SizeBytes, d.Container, d.Duration, spuren)).ToList(),
|
||||
dateien.Select(d => Datei(d.Id, d.Path, d.SizeBytes, d.Container, d.Duration, spuren, d.VersionLabel)).ToList(),
|
||||
stände.GetValueOrDefault(naechste.Id)));
|
||||
}).RequireAuthorization();
|
||||
|
||||
@@ -589,7 +594,7 @@ public static class LibraryContentEndpoints
|
||||
var ids = folgen.Select(f => f.Id).ToList();
|
||||
var dateien = await db.MediaFiles.AsNoTracking()
|
||||
.Where(d => d.EpisodeId != null && ids.Contains(d.EpisodeId!.Value))
|
||||
.Select(d => new { d.Id, d.Path, d.SizeBytes, d.Container, d.Duration, d.EpisodeId })
|
||||
.Select(d => new { d.Id, d.Path, d.SizeBytes, d.Container, d.Duration, d.EpisodeId, d.VersionLabel })
|
||||
.ToListAsync();
|
||||
var spuren = await SpurenZu(db, dateien.Select(d => d.Id).ToList());
|
||||
var stände = await ZuständeZu(db, wer, folgen: ids);
|
||||
@@ -602,7 +607,7 @@ public static class LibraryContentEndpoints
|
||||
f.Id, f.SeasonNumber, f.EpisodeNumber, f.Title, f.Overview,
|
||||
f.StillImage ?? serienBild,
|
||||
dateien.Where(d => d.EpisodeId == f.Id)
|
||||
.Select(d => Datei(d.Id, d.Path, d.SizeBytes, d.Container, d.Duration, spuren)).ToList(),
|
||||
.Select(d => Datei(d.Id, d.Path, d.SizeBytes, d.Container, d.Duration, spuren, d.VersionLabel)).ToList(),
|
||||
stände.GetValueOrDefault(f.Id))).ToList();
|
||||
|
||||
return Results.Ok(new Page<EpisodeResponse>(items, items.Count));
|
||||
|
||||
Reference in New Issue
Block a user