Fix BIF scrub preview aspect ratio and raise desktop size limit
This commit is contained in:
@@ -25,6 +25,9 @@ class MediaSourceInfo {
|
|||||||
/// null here and uses [partId] + the BIF service instead.
|
/// null here and uses [partId] + the BIF service instead.
|
||||||
final Map<int, TrickplayInfo>? trickplayByWidth;
|
final Map<int, TrickplayInfo>? trickplayByWidth;
|
||||||
|
|
||||||
|
/// Display aspect ratio of the video stream (width / height).
|
||||||
|
final double? videoAspectRatio;
|
||||||
|
|
||||||
MediaSourceInfo({
|
MediaSourceInfo({
|
||||||
required this.videoUrl,
|
required this.videoUrl,
|
||||||
required this.audioTracks,
|
required this.audioTracks,
|
||||||
@@ -36,6 +39,7 @@ class MediaSourceInfo {
|
|||||||
this.defaultAudioStreamIndex,
|
this.defaultAudioStreamIndex,
|
||||||
this.defaultSubtitleStreamIndex,
|
this.defaultSubtitleStreamIndex,
|
||||||
this.trickplayByWidth,
|
this.trickplayByWidth,
|
||||||
|
this.videoAspectRatio,
|
||||||
});
|
});
|
||||||
int? getPartId() => partId;
|
int? getPartId() => partId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'dart:typed_data';
|
|||||||
import 'plex_client.dart';
|
import 'plex_client.dart';
|
||||||
import 'scrub_preview_source.dart';
|
import 'scrub_preview_source.dart';
|
||||||
import '../utils/app_logger.dart';
|
import '../utils/app_logger.dart';
|
||||||
|
import '../utils/platform_detector.dart';
|
||||||
|
|
||||||
/// A single BIF thumbnail entry: timestamp in milliseconds + JPEG bytes.
|
/// A single BIF thumbnail entry: timestamp in milliseconds + JPEG bytes.
|
||||||
typedef BifEntry = ({int timestampMs, Uint8List imageBytes});
|
typedef BifEntry = ({int timestampMs, Uint8List imageBytes});
|
||||||
@@ -66,14 +67,20 @@ List<BifEntry> _parseBifBytes(Uint8List bytes) {
|
|||||||
class BifThumbnailService implements ScrubPreviewSource {
|
class BifThumbnailService implements ScrubPreviewSource {
|
||||||
List<BifEntry>? _entries;
|
List<BifEntry>? _entries;
|
||||||
|
|
||||||
|
double? _aspectRatio;
|
||||||
|
|
||||||
/// Download and parse the BIF file for [partId].
|
/// Download and parse the BIF file for [partId].
|
||||||
/// Returns silently on failure (thumbnails simply won't be available).
|
/// 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;
|
_entries = null;
|
||||||
try {
|
try {
|
||||||
final bytes = await client.downloadBifFile(partId);
|
final bytes = await client.downloadBifFile(partId);
|
||||||
if (bytes == null || bytes.isEmpty) return;
|
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');
|
appLogger.w('BIF file too large (${bytes.length} bytes), skipping');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -91,7 +98,7 @@ class BifThumbnailService implements ScrubPreviewSource {
|
|||||||
ScrubFrame? getFrame(Duration time) {
|
ScrubFrame? getFrame(Duration time) {
|
||||||
final bytes = getThumbnail(time);
|
final bytes = getThumbnail(time);
|
||||||
if (bytes == null) return null;
|
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].
|
/// Return the JPEG bytes for the thumbnail nearest to [time].
|
||||||
|
|||||||
@@ -3505,7 +3505,7 @@ class PlexClient
|
|||||||
if (partId == null) return null;
|
if (partId == null) return null;
|
||||||
final service = BifThumbnailService();
|
final service = BifThumbnailService();
|
||||||
try {
|
try {
|
||||||
await service.load(this, partId);
|
await service.load(this, partId, aspectRatio: mediaSource.videoAspectRatio);
|
||||||
return service;
|
return service;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
appLogger.w('BIF thumbnail load failed for part $partId', error: e, stackTrace: st);
|
appLogger.w('BIF thumbnail load failed for part $partId', error: e, stackTrace: st);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ PlexVideoPlaybackData parsePlexVideoPlaybackDataFromJson(
|
|||||||
chapters: chapters,
|
chapters: chapters,
|
||||||
partId: part['id'] as int?,
|
partId: part['id'] as int?,
|
||||||
displayCriteria: PlexMappers.displayCriteriaFromJson(media as Map<String, dynamic>?, streams.videoStream),
|
displayCriteria: PlexMappers.displayCriteriaFromJson(media as Map<String, dynamic>?, streams.videoStream),
|
||||||
|
videoAspectRatio: ((media as Map?)?['aspectRatio'] as num?)?.toDouble(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ sealed class ScrubFrame {
|
|||||||
double get aspectRatio;
|
double get aspectRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Plex BIF: standalone JPEG bytes ready for [Image.memory]. Plex doesn't
|
/// Plex BIF: standalone JPEG bytes ready for [Image.memory].
|
||||||
/// expose per-BIF dimensions out-of-band; servers virtually always use
|
|
||||||
/// 16:9, which matches the default tooltip box.
|
|
||||||
class BytesScrubFrame extends ScrubFrame {
|
class BytesScrubFrame extends ScrubFrame {
|
||||||
final Uint8List bytes;
|
final Uint8List bytes;
|
||||||
@override
|
@override
|
||||||
|
|||||||
Reference in New Issue
Block a user