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

@@ -422,6 +422,31 @@ void main() {
});
});
test('several logins (your server shared): an item goes through its own server\'s login', () async {
await startServer(null);
// Two linked accounts; the default one is on another Jellyfin.
const other = JellyfinCredential(
baseUrl: 'http://127.0.0.1:1',
serverId: 'otherserver',
token: 'OTHER-TOKEN',
client: 'Watch Together',
device: 'host pc',
deviceId: 'dev-1',
version: '0.9.0',
);
await server.share(port: 0, secret: '', jellyfin: [other, cred()]);
expect(server.hub.jellyfinLogins.map((c) => c.serverId), ['otherserver', _serverId]);
final (a, _) = await join('alice');
final state = jsonDecode(await swapToRef(a, _item)) as Map;
expect(state['relay'], true);
final (_, w) = await join('bob');
final r = await relayGet(w.token, _item, range: 'bytes=0-9');
expect(r.statusCode, 206);
expect(await readAll(r), bytes.sublist(0, 10));
expect(jf.auth.single, contains('Token="$_token"'));
expect(jf.auth.single, isNot(contains('OTHER-TOKEN')));
});
test('a credential without serverId looks it up at start', () async {
await startServer(cred(serverId: ''));
expect(server.hub.jellyfin!.serverId, _serverId);