diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index f12659b3..206cc32f 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -876,12 +876,14 @@ class VideoPlayerScreenState extends State with WidgetsBindin // Check whether any thumbnails exist by requesting the first one if (_currentMediaInfo?.partId != null && !widget.isOffline) { - final url = _buildThumbnailUrl(context, Duration.zero); - if (url != null) { - HttpClient().getUrl(Uri.parse(url)).then((req) => req.close()).then((res) { - if (mounted) setState(() => _hasThumbnails = res.statusCode == 200); - }).catchError((_) {}); - } + final partId = _currentMediaInfo!.partId!; + final client = _getClientForMetadata(context); + client.checkThumbnailsAvailable(partId).then((available) { + // Guard against media having changed while the probe was in flight + if (mounted && _currentMediaInfo?.partId == partId) { + setState(() => _hasThumbnails = available); + } + }); } // Initialize video PIP and filter manager with player and available versions @@ -1888,7 +1890,9 @@ class VideoPlayerScreenState extends State with WidgetsBindin controlsVisible: _controlsVisible, shaderService: _shaderService, onShaderChanged: () => setState(() {}), - thumbnailUrlBuilder: _hasThumbnails ? (Duration time) => _buildThumbnailUrl(context, time)! : null, + thumbnailUrlBuilder: _hasThumbnails && _currentMediaInfo?.partId != null + ? (Duration time) => _buildThumbnailUrl(context, time)! + : null, ), ); }, diff --git a/lib/services/plex_client.dart b/lib/services/plex_client.dart index a307fdbf..40b4bdb4 100644 --- a/lib/services/plex_client.dart +++ b/lib/services/plex_client.dart @@ -754,6 +754,20 @@ class PlexClient { return '${config.baseUrl}/$path'.withPlexToken(config.token); } + /// Check whether thumbnail previews are available for a given part. + /// Returns true if the server responds with 200 to the first thumbnail. + Future checkThumbnailsAvailable(int partId) async { + try { + final response = await _dio.get( + '/library/parts/$partId/indexes/sd/0', + options: Options(responseType: ResponseType.bytes, receiveTimeout: const Duration(seconds: 5)), + ); + return response.statusCode == 200; + } catch (_) { + return false; + } + } + /// Get chapters for a media item Future> getChapters(String ratingKey) async { final response = await _dio.get('/library/metadata/$ratingKey', queryParameters: {'includeChapters': 1});