Jellyfin in the app: account linking and library browser

Integrations links several Jellyfin servers (Quick Connect or
password, app-obtained tokens only, status/reconnect/sign out). The
Host tab's Jellyfin source is a full browser: continue watching, next
up, latest per library, library grids with sort/filter/infinite
scroll, series pages with smart resume/next-up, details, grouped
search, inspector pane on wide screens, keyboard navigation.
MediaPicker selection is now a sealed PickedMedia. Hosting a Jellyfin
pick is interim (host's stream URL) until per-viewer playback lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-07 02:05:46 +03:00
1 parent 45f4c1bc84
commit 773a04369e
27 files changed
+5169 -228

No files matched your search

+6 -5
View File
@@ -85,12 +85,12 @@ void main() {
expect(c.source, MediaKind.file);
c.pasteText(' https://example.com/a.mkv ');
expect(c.source, MediaKind.link);
expect(c.selection, (mediaPath: '', linkUrl: 'https://example.com/a.mkv'));
expect(c.selection, const LinkMedia('https://example.com/a.mkv'));
c.pasteText('/home/me/b.mkv');
expect(c.source, MediaKind.file);
expect(c.selection, (mediaPath: '/home/me/b.mkv', linkUrl: ''));
expect(c.selection, const FileMedia('/home/me/b.mkv'));
c.source = MediaKind.jellyfin;
expect(c.selection, (mediaPath: '', linkUrl: ''));
expect(c.selection, isNull);
c.dispose();
});
@@ -238,10 +238,11 @@ void main() {
expect(find.text('Paste'), findsOneWidget);
await tester.tap(find.text('Jellyfin'));
await tester.pumpAndSettle();
expect(find.text('Link your Jellyfin in Integrations — coming soon'), findsOneWidget);
// No linked server: sign-in right in the picker.
expect(find.text('Add a Jellyfin server'), findsOneWidget);
await tester.tap(find.text('Open Integrations'));
await tester.pumpAndSettle();
expect(find.text('Link account'), findsOneWidget);
expect(find.text('Add server'), findsOneWidget);
expect(tester.takeException(), isNull);
});