Restructure step 4: local library and media identity
ci / core (push) Successful in 29s
ci / server (push) Successful in 1m8s
ci / updater-windows (push) Failing after 6m2s

- L1 (core, offline): release names with kinds (episode, movie, extra:
  OP/ED/Opening/Ending/NC/PV/OVA…), folder hints (season and name from
  parentheticals, ranges), one show across folders, gaps and next with
  skipped numbers, junk ignored, stable keys.
- L2 (app, device-owned cache): AniList match with synonyms and
  sequel walking, episode mapping across cours (AnimeFlow runs when
  signed in), art and episode titles, "Wrong show?" pins per show or
  folder, episode offsets; lazy, spaced requests, works offline.
- Files tab as a library: poster cards, show pages with banner, gap
  prompt, extras, movies, Wrong show? and Episode numbers.
- media.ref produced for local files, Jellyfin (provider ids) and links,
  carried in swaps and Up next; receivers name media from it.
- Hosting relays each Jellyfin item through its own server's login.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-15 23:34:39 +03:00
1 parent 4d00b7455e
commit bf9fc193b3
44 files changed
+4350 -891

No files matched your search

+18
View File
@@ -1,9 +1,13 @@
import 'package:flutter/widgets.dart';
import '../jellyfin/accounts.dart';
import '../library/library_store.dart';
import '../library/show_matcher.dart';
import '../platform/app_log.dart' show appLogDir;
import '../platform/desktop/desktop_ports.dart';
import '../platform/ports/ports.dart';
import '../schedule/anilist.dart';
import '../schedule/animeflow.dart';
import '../screens/home/health_probe.dart';
import '../session/connection.dart' show Connection;
import '../session/identity.dart' show ClientIdentity, Profile;
@@ -44,6 +48,9 @@ class AppGraph {
/// Home (your server) or a friend's.
final Navigation navigation;
/// The library's matches (L2): local shows to AniList, pins, offsets.
final ShowMatcher library;
AppGraph._({
required this.ports,
required this.sfx,
@@ -53,6 +60,7 @@ class AppGraph {
required this.forms,
required this.yourServer,
required this.navigation,
required this.library,
});
/// The desktop app's graph. Tests pass their own [ports], [identity]
@@ -63,6 +71,7 @@ class AppGraph {
HealthGetter healthGet = defaultHealthGet,
ServerStarter? serverStarter,
JellyfinAccounts? jellyfin,
ShowMatcher? library,
}) {
final p = ports ?? desktopPorts();
final sfx = SynthSfx(
@@ -81,6 +90,14 @@ class AppGraph {
final connection = Connection(notify: notifier.notify, identity: identity ?? ClientIdentity.stored());
final coordinator =
SessionCoordinator(notifier: notifier, connection: connection, ytDlp: p.links, jellyfin: jellyfin);
// Device-owned: the library box; AnimeFlow's runs when signed in.
final matcher = library ??
ShowMatcher(
store: HiveLibraryStore(),
aniList: AniListClient(),
flow: AnimeFlowLookup(AnimeFlowClient.fromAccounts(coordinator.jellyfin)),
);
coordinator.fileRef = matcher.refForPath;
final forms = ConnectStore();
final yourServer = YourServer(
forms: forms,
@@ -119,6 +136,7 @@ class AppGraph {
forms: forms,
yourServer: yourServer,
navigation: navigation,
library: matcher,
);
}