Files
jellyfin-packaging/patches/jellyfin-progressive-download-improvements-v10.11.5.patch
mani f517a97ca0 Remove download/transcoding features to patches - keep core fixes
Moved to patches/:
- jellyfin-progressive-download-improvements-v10.11.5.patch (server)
- jellyfin-web-download-dialog-v10.11.5.patch (web UI)

Kept in code:
- Xbox UWP codec detection (AV1/Opus fix)
- FFmpeg log level configuration
- Remote source forced transcoding
- Quality text UI fix (web)

Both jellyfin-server and jellyfin-web reset to v10.11.5 base
2026-01-08 23:37:37 +01:00

110 lines
5.5 KiB
Diff

From: Jellyfin Packaging <noreply@jellyfin.org>
Date: Wed, 8 Jan 2025 00:00:00 +0000
Subject: [PATCH] Progressive download improvements for v10.11.5+
This patch includes all progressive download and transcoding improvements:
- Use /downloads/ folder for full-file progressive downloads
- Fix download transcoding not stopping when client disconnects
- Improve MP4 movflags for better seeking compatibility (mpv/iina/infuse)
- Fix timestamp seeking for progressive downloads
- Use separate transcode paths for HLS vs progressive downloads
---
Jellyfin.Api/Controllers/VideosController.cs | 3 ++-
Jellyfin.Api/Helpers/StreamingHelpers.cs | 15 +++++++++++++++
.../MediaEncoding/EncodingHelper.cs | 22 ++++++++++++++++++++++
3 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index 97f3239bbc..6f9fbadddb 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -369,7 +369,8 @@ public class VideosController : BaseJellyfinApiController
{
var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head;
// CTS lifecycle is managed internally.
- var cancellationTokenSource = new CancellationTokenSource();
+ // Link to HttpContext.RequestAborted so transcode stops when client disconnects
+ var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HttpContext.RequestAborted);
var streamingRequest = new VideoRequestDto
{
Id = itemId,
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index b3f5b9a801..f366fd7a23 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -378,8 +378,23 @@ public static class StreamingHelpers
var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture);
var ext = outputFileExtension.ToLowerInvariant();
+
+ // Use different transcode paths for HLS vs Progressive downloads
+ // HLS segments: use RAM-based transcode path (fast, auto-cleanup)
+ // Progressive downloads: use disk-based path (large files OK, slower cleanup)
var folder = serverConfigurationManager.GetTranscodePath();
+ // Check if this is a full file download (no segments) vs HLS streaming
+ var streamingRequest = state.BaseRequest as StreamingRequestDto;
+
+ if (streamingRequest?.SegmentContainer is null)
+ {
+ // Full file download - use disk-based transcode path
+ var diskTranscodePath = Path.Combine(folder, "downloads");
+ Directory.CreateDirectory(diskTranscodePath);
+ folder = diskTranscodePath;
+ }
+
return Path.Combine(folder, filename + ext);
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index e088cd358d..a31a735bed 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -20,6 +20,7 @@ using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Streaming;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
@@ -7475,8 +7476,20 @@ namespace MediaBrowser.Controller.MediaEncoding
if (Path.GetExtension(outputPath.AsSpan()).Equals(".mp4", StringComparison.OrdinalIgnoreCase)
&& state.BaseRequest.Context == EncodingContext.Streaming)
{
- // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
- format = " -f mp4 -movflags frag_keyframe+empty_moov+delay_moov";
+ // Use fragmented MP4 for adaptive streaming (HLS/DASH with segments)
+ // Use faststart for progressive downloads (better seeking and metadata)
+ var streamingRequest = state.BaseRequest as StreamingRequestDto;
+ if (streamingRequest?.SegmentContainer is not null)
+ {
+ // Fragmented MP4 for HLS/DASH
+ format = " -f mp4 -movflags frag_keyframe+empty_moov+delay_moov";
+ }
+ else
+ {
+ // Progressive download - use faststart for proper seeking and duration
+ // Use frag_keyframe for better seeking compatibility with mpv
+ format = " -f mp4 -movflags frag_keyframe+faststart+default_base_moof";
+ }
}
var threads = GetNumberOfThreads(state, encodingOptions, videoCodec);
@@ -7582,6 +7595,15 @@ namespace MediaBrowser.Controller.MediaEncoding
args += " -start_at_zero";
}
}
+ else if (state.TranscodingType == TranscodingJobType.Progressive && !state.BaseRequest.CopyTimestamps)
+ {
+ // For progressive downloads without copyTimestamps, ensure timestamps start at 0
+ // This fixes seeking issues in strict players like mpv, iina, and infuse
+ args += " -avoid_negative_ts make_zero -start_at_zero";
+ }
var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultPreset);
--
2.43.0