| Age | Commit message (Collapse) | Author |
|
The audio/video players spawn a localhost proxy (just_audio) or call
ExoPlayer directly (video_player) using their own HTTP stack, which does
not share Dio's cookie jar. Without the session cookie those requests
hit the stream endpoint anonymously and fail with 401.
Expose the Dio CookieJar via a Riverpod provider (cookieJarProvider) and
attach a Cookie header (alongside the existing Authorization: Bearer) to
both AudioSource.uri and VideoPlayerController.networkUrl.
Also enable android:usesCleartextTraffic="true" on the Application —
just_audio's headers-injection proxy listens on 127.0.0.1 and Android
28+ blocks cleartext to it without the explicit opt-in.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Existing databases created before the finished column was added to the
base schema still satisfied CREATE TABLE IF NOT EXISTS and never got the
column, causing GET /api/v1/media/{id} to fail with
"SQL logic error: no such column: finished" and break media detail view.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- 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>
|
|
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>
|
|
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>
|
|
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>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
MediaDetailScreen (5b)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
MediaGridScreen (4b)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
(ab)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
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>
|
|
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>
|
|
Replace UnimplementedError stubs with concrete Dio calls for bootstrap, login,
logout, listSets, browseSet, listMedia, getMedia, streamMedia, downloadMedia,
getThumbnail, healthz, and readyz. Wire DioPlayerApiClient into the Riverpod
provider. Add http_mock_adapter dev dependency and 25 unit tests covering
success + error paths for all implemented methods.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
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>
|
|
- 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>
|
|
|