Fix BIF scrub preview aspect ratio and raise desktop size limit

This commit is contained in:
Micah Morrison
2026-05-28 15:46:08 -04:00
parent 4217da1a8f
commit 9cc32e125b
5 changed files with 17 additions and 7 deletions
+4
View File
@@ -25,6 +25,9 @@ class MediaSourceInfo {
/// null here and uses [partId] + the BIF service instead.
final Map<int, TrickplayInfo>? trickplayByWidth;
/// Display aspect ratio of the video stream (width / height).
final double? videoAspectRatio;
MediaSourceInfo({
required this.videoUrl,
required this.audioTracks,
@@ -36,6 +39,7 @@ class MediaSourceInfo {
this.defaultAudioStreamIndex,
this.defaultSubtitleStreamIndex,
this.trickplayByWidth,
this.videoAspectRatio,
});
int? getPartId() => partId;
}
+10 -3
View File
@@ -4,6 +4,7 @@ import 'dart:typed_data';
import 'plex_client.dart';
import 'scrub_preview_source.dart';
import '../utils/app_logger.dart';
import '../utils/platform_detector.dart';
/// A single BIF thumbnail entry: timestamp in milliseconds + JPEG bytes.
typedef BifEntry = ({int timestampMs, Uint8List imageBytes});
@@ -66,14 +67,20 @@ List<BifEntry> _parseBifBytes(Uint8List bytes) {
class BifThumbnailService implements ScrubPreviewSource {
List<BifEntry>? _entries;
double? _aspectRatio;
/// Download and parse the BIF file for [partId].
/// Returns silently on failure (thumbnails simply won't be available).
Future<void> load(PlexClient client, int partId) async {
Future<void> load(PlexClient client, int partId, {double? aspectRatio}) async {
_aspectRatio = aspectRatio;
_entries = null;
try {
final bytes = await client.downloadBifFile(partId);
if (bytes == null || bytes.isEmpty) return;
if (bytes.length > 50 * 1024 * 1024) {
const fiftyMb = 50 * 1024 * 1024;
const twoHundredMb = 200 * 1024 * 1024;
final maxBytes = PlatformDetector.isDesktopOS() ? twoHundredMb : fiftyMb;
if (bytes.length > maxBytes) {
appLogger.w('BIF file too large (${bytes.length} bytes), skipping');
return;
}
@@ -91,7 +98,7 @@ class BifThumbnailService implements ScrubPreviewSource {
ScrubFrame? getFrame(Duration time) {
final bytes = getThumbnail(time);
if (bytes == null) return null;
return BytesScrubFrame(bytes);
return BytesScrubFrame(bytes, aspectRatio: _aspectRatio ?? 16 / 9);
}
/// Return the JPEG bytes for the thumbnail nearest to [time].
+1 -1
View File
@@ -3505,7 +3505,7 @@ class PlexClient
if (partId == null) return null;
final service = BifThumbnailService();
try {
await service.load(this, partId);
await service.load(this, partId, aspectRatio: mediaSource.videoAspectRatio);
return service;
} catch (e, st) {
appLogger.w('BIF thumbnail load failed for part $partId', error: e, stackTrace: st);
+1
View File
@@ -59,6 +59,7 @@ PlexVideoPlaybackData parsePlexVideoPlaybackDataFromJson(
chapters: chapters,
partId: part['id'] as int?,
displayCriteria: PlexMappers.displayCriteriaFromJson(media as Map<String, dynamic>?, streams.videoStream),
videoAspectRatio: ((media as Map?)?['aspectRatio'] as num?)?.toDouble(),
);
}
}
+1 -3
View File
@@ -17,9 +17,7 @@ sealed class ScrubFrame {
double get aspectRatio;
}
/// Plex BIF: standalone JPEG bytes ready for [Image.memory]. Plex doesn't
/// expose per-BIF dimensions out-of-band; servers virtually always use
/// 16:9, which matches the default tooltip box.
/// Plex BIF: standalone JPEG bytes ready for [Image.memory].
class BytesScrubFrame extends ScrubFrame {
final Uint8List bytes;
@override