diff --git a/flutter_client/lib/player/audio_handler.dart b/flutter_client/lib/player/audio_handler.dart index 657e5b4c..13886545 100644 --- a/flutter_client/lib/player/audio_handler.dart +++ b/flutter_client/lib/player/audio_handler.dart @@ -1,4 +1,5 @@ import 'package:audio_service/audio_service.dart'; +import 'package:flutter/foundation.dart'; import 'package:just_audio/just_audio.dart'; import '../models/track.dart'; @@ -15,6 +16,8 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl void configure({required String baseUrl, required String? token}) { _baseUrl = baseUrl; _token = token; + debugPrint('audio_handler.configure: baseUrl="$baseUrl" ' + 'tokenPresent=${token != null && token.isNotEmpty}'); } Future setQueueFromTracks(List tracks, {int initialIndex = 0}) async { @@ -25,8 +28,12 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl } final headers = _token == null ? null : {'Authorization': 'Bearer $_token'}; + debugPrint('audio_handler.setQueueFromTracks: ' + '_baseUrl="$_baseUrl" trackCount=${tracks.length}'); final sources = tracks.map((t) { final url = _resolveStreamUrl(t); + debugPrint('audio_handler: track.id=${t.id} ' + 'track.streamUrl="${t.streamUrl}" → resolved="$url"'); // Defensive: a scheme-less URL handed to ExoPlayer crashes with // a confusing "Cleartext HTTP traffic to 127.0.0.1 not permitted" // error because Android's URL parser falls back to localhost. diff --git a/flutter_client/lib/shared/widgets/server_image.dart b/flutter_client/lib/shared/widgets/server_image.dart index 6559ac9d..2b8ca417 100644 --- a/flutter_client/lib/shared/widgets/server_image.dart +++ b/flutter_client/lib/shared/widgets/server_image.dart @@ -31,7 +31,15 @@ class ServerImage extends ConsumerWidget { final base = ref.watch(serverUrlProvider).value; final resolved = _resolve(base, url); if (resolved == null) return empty; - return Image.network(resolved, fit: fit); + // Cover endpoints are gated by RequireUser server-side. Image.network + // doesn't carry cookies/Bearer tokens automatically the way the + // browser's tag does, so we forward the session token as a + // header. Without this, the server returns 401 and the image fails. + final token = ref.watch(sessionTokenProvider).value; + final headers = (token != null && token.isNotEmpty) + ? {'Authorization': 'Bearer $token'} + : null; + return Image.network(resolved, fit: fit, headers: headers); } static String? _resolve(String? baseUrl, String url) {