feat(plex): migrate video transcoding to HLS

This commit is contained in:
edde746
2026-07-14 18:11:04 +02:00
parent 9d812fa3cc
commit abf1027b77
17 changed files with 201 additions and 550 deletions
+6 -7
View File
@@ -43,9 +43,9 @@ class CodecUtils {
};
}
/// Image-based (bitmap) subtitle codecs. These can't be converted to text;
/// during an HTTP/MKV transcode Plex copies the stream into the container and
/// the player renders it natively.
/// Image-based (bitmap) subtitle codecs. Plex burns these into the video
/// when the selected output transport cannot carry a bitmap subtitle
/// rendition.
static bool isImageSubtitleCodec(String? codec) {
if (codec == null) return false;
return switch (codec.toLowerCase()) {
@@ -61,10 +61,9 @@ class CodecUtils {
};
}
/// Subtitle codecs that can be carried inside the HTTP/MKV transcode stream
/// (`subtitles=embedded`): text codecs plus the image codecs the MKV target
/// supports. Keyed sidecars are delivered separately and are not covered here.
static bool isEmbeddableSubtitleCodec(String? codec) {
/// Subtitle codecs Plex can deliver in a transcode. Text codecs can become
/// segmented HLS WebVTT; image codecs can be burned into the video.
static bool isTranscodableSubtitleCodec(String? codec) {
return isTextSubtitleCodec(codec) || isImageSubtitleCodec(codec);
}
-42
View File
@@ -1,11 +1,6 @@
import '../mpv/mpv.dart';
const restartBeforePreviousItemThreshold = Duration(seconds: 3);
const plexTranscodeSeekRangeStartTolerance = Duration(milliseconds: 500);
const plexTranscodeSeekRangeEndGuard = Duration(milliseconds: 500);
const plexTranscodeSeekNoopTolerance = Duration(seconds: 1);
enum PlexTranscodeSeekAction { nativeSeek, restartTranscode }
bool shouldRestartBeforePreviousItem(Duration position) {
return position > restartBeforePreviousItemThreshold;
@@ -17,40 +12,3 @@ Duration clampSeekPosition(Player player, Duration position) {
if (duration > Duration.zero && position > duration) return duration;
return position;
}
/// Plex MKV-over-HTTP transcodes are only native-seeked inside ranges the
/// player reports as locally seekable. Anything outside those ranges needs a
/// server-offset transcode restart.
PlexTranscodeSeekAction resolvePlexTranscodeSeekAction({
required Duration currentPosition,
required Duration target,
required List<BufferRange> bufferRanges,
bool allowBufferedNativeSeek = true,
Duration rangeStartTolerance = plexTranscodeSeekRangeStartTolerance,
Duration rangeEndGuard = plexTranscodeSeekRangeEndGuard,
Duration noopTolerance = plexTranscodeSeekNoopTolerance,
}) {
final validRanges = bufferRanges.where((range) => range.end >= range.start).toList();
if (allowBufferedNativeSeek &&
_isInAnyBufferedSeekRange(target, validRanges, startTolerance: rangeStartTolerance, endGuard: rangeEndGuard)) {
return PlexTranscodeSeekAction.nativeSeek;
}
if ((target - currentPosition).abs() <= noopTolerance) {
return PlexTranscodeSeekAction.nativeSeek;
}
return PlexTranscodeSeekAction.restartTranscode;
}
bool _isInAnyBufferedSeekRange(
Duration target,
List<BufferRange> ranges, {
required Duration startTolerance,
required Duration endGuard,
}) {
for (final range in ranges) {
if (target >= range.start - startTolerance && target <= range.end - endGuard) return true;
}
return false;
}