summaryrefslogtreecommitdiff
path: root/player-android/lib/api/player_api_client.dart
AgeCommit message (Collapse)Author
2026-05-22Implement public ShareViewerScreen with Android deep-link intent-filter (9b)Paul Buetow
- New ShareViewerScreen (/share/:token): unauthenticated share-viewer that fetches share metadata via publicApiClientProvider, renders filename, type, duration, thumbnail, and a Play button routing to the video/audio player. - New publicApiClientProvider: bare Dio client (no auth interceptors) for the public share endpoint; shares kPlayerBaseUrl with the authenticated client. - AndroidManifest: http + https deep-link intent-filters for /share/.* so Android routes share URLs directly into the app (App Links / autoVerify). - router.dart: /share/:token bypasses the authentication redirect; guard uses AppRoutes.shareViewerPrefix constant instead of a raw '/share/' string (DIP). - app_routes.dart: shareViewer route constant, shareViewerPrefix, shareViewerPath helper. - error_mappers.dart: shareViewerErrorMessage — 404 invalid/revoked, 410 expired. - player_api_client.dart: baseUrl getter encapsulates rawDio.options.baseUrl so screens never access transport internals directly (ISP, DIP). - Review fixes: OCP icon map in _FallbackThumbnail, LSP explicit baseUrl overrides in test fakes, DIP shareViewerPrefix constant. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement offline progress queue with sqflite and connectivity_plus (3b)Paul Buetow
Introduces ProgressQueue backed by SQLite for offline-capable progress tracking, with ProgressQueueBase (LSP+DIP), databaseFactory injection (DIP), and ProgressSyncClient narrow interface (ISP). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement FolderBrowserScreen with browseSet endpoint and breadcrumb ↵Paul Buetow
navigation (va) Adds a FolderBrowserScreen that calls browseSet on init and pull-to-refresh, renders subfolders first (with cover via setFolderCoverUrl) then media items, and provides a scrollable breadcrumb bar for navigating the folder hierarchy. Key changes: - player-android/lib/screens/folder_browser_screen.dart: new screen with loading/empty/error/refresh states, generation-counter cancellation, and no Dio import in the screen layer (DIP). - player-android/lib/api/player_api_client.dart: add setFolderCoverUrl() so screens never access rawDio directly for URL construction (DIP). - player-android/lib/utils/duration_formatter.dart: extract shared formatDuration() from MediaGridScreen to eliminate the DRY violation. - player-android/lib/screens/media_grid_screen.dart: delegate to formatDuration() from the shared utility. - player-android/lib/utils/error_mappers.dart: add folderErrorMessage(). - player-android/lib/app_routes.dart: add folderBrowser and folderBrowserPath(). - player-android/lib/router.dart: wire /browse/:setId GoRoute. - player-android/test/screens/folder_browser_screen_test.dart: 16 widget tests covering renders, breadcrumbs, folder/media tap navigation, empty, error, retry, and pull-to-refresh. All 298 tests pass; flutter analyze reports no issues. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement all remaining PlayerApiClient stubs and add unit testsPaul Buetow
Implements every previously-unimplemented method in DioPlayerApiClient: shares (listSharesForMedia, listMyShares, revokeShare), public shared-media endpoints (getSharedMediaPage, streamSharedMedia, getSharedThumbnail, downloadSharedMedia), config (getConfig), sets (getSetCover, updateSetCover, uploadToSet), media helpers (regenerateThumbnail, deleteMedia, restoreMedia), progress batch (batchUpdateProgress), podcasts (listPodcasts, listEpisodes, downloadEpisode, toggleEpisodeComplete), admin users/permissions/scanner (listUsers, createUser, deleteUser, listPermissions, grantPermission, revokePermission, triggerRescan, getScanProgress, listTrash), and API tokens (listAPITokens, createAPIToken, revokeAPIToken). Adds listAPITokens/createAPIToken/revokeAPIToken/batchUpdateProgress to the abstract PlayerApiClient. Corrects listPermissions return type from List<Map> to Map<String,dynamic> to match the server's single-object response. Adds 18 new unit tests covering shares (listMyShares success/empty/401, revokeShare success/404/401) and podcasts (listEpisodes success/pagination/ empty/401, toggleEpisodeComplete success/404/401). All 264 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement CreateShareDialog with expiry/max-uses, shareUrl abstraction (7b)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement VideoPlayerScreen with chewie, progress sync, and bearer auth (za)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement MediaDetailScreen with metadata, favorite toggle, player routing (ya)Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Implement MediaGridScreen with grid, loading/empty/error states, and widget ↵Paul Buetow
tests (ua) - Replace placeholder MediaGridScreen with a full implementation: loads media via listMedia(setId:), renders a Material 3 2-column grid of thumbnail cards with filename, type icon, and duration overlay. - Add thumbnailUrl(int mediaId) to PlayerApiClient so screen files construct thumbnail URLs without importing Dio (DIP). - Add mediaErrorMessage() top-level helper to error_mappers.dart (Open-Closed Principle; matches setsErrorMessage pattern). - Update router.dart to forward the set name as a route extra so MediaGridScreen shows the name in the app bar without extra API calls. - Update home_screen.dart _SetCard tap to pass set name as route extra. - Add 10 widget tests covering: loading, grid render, tap navigation to /media/:id, empty state, error state + retry, and pull-to-refresh. - All 109 tests pass; flutter analyze reports no issues. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21Add GET /api/v1/auth/count endpoint and first-run routing in Android appPaul Buetow
Server: expose a public countUsers endpoint (GET /api/v1/auth/count) so mobile clients can detect first-run (count=0) without a session. Android: wire countUsers via DioPlayerApiClient, add firstRunProvider (FutureProvider), update go_router redirect to drive /bootstrap vs /login based on the count, rework LoginScreen to handle loading/error states, and add widget tests for the new login screen and smoke-test updates. Fix review issues: correct FutureProvider cache-lifetime comment in first_run_provider.dart; add TestServer_CountUsers covering zero-users and users-exist cases to handlers_test.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20Add DioClient with auth interceptors and refactor PlayerApiClient (ma)Paul Buetow
Introduces dio_client.dart with _AuthInterceptor (guards Bearer token injection with containsKey so callers can override Authorization) and _UnauthorizedInterceptor (private fields, 401 → login redirect). PlayerApiClient is refactored to accept a pre-configured Dio instance instead of raw credentials; adds dio + flutter_secure_storage deps. Fixes review issues: Auth header comment/behavior corrected, private fields on _UnauthorizedInterceptor, unused import removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20Fix PlayerApiClient const constructor and Media.tags deserialization (d9+e9)Paul Buetow
- Remove const from PlayerApiClient constructor (Uri is not const-constructable) - Replace .cast<String>() with .whereType<String>().toList() in Media.fromJson to silently drop non-string and null tag elements instead of throwing TypeError - Add regression test: tags [1, 'valid', null] deserialized as ['valid'] Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17Add player-android/ Flutter skeletonPaul Buetow