This commit is contained in:
Akhil Kumar Achanta 2026-01-21 19:54:34 +00:00 committed by GitHub
commit 6bacffb14b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 51 additions and 4 deletions

View file

@ -1399,6 +1399,18 @@ public class DynamicHlsController : BaseJellyfinApiController
TranscodingJobType,
cancellationTokenSource.Token)
.ConfigureAwait(false);
// Calculate the starting segment index from current transcoding state
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
var segmentExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer);
var startSegmentIndex = 0;
var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
if (currentTranscodingIndex.HasValue)
{
startSegmentIndex = currentTranscodingIndex.Value;
}
var mediaSourceId = state.BaseRequest.MediaSourceId;
var request = new CreateMainPlaylistRequest(
mediaSourceId is null ? null : Guid.Parse(mediaSourceId),
@ -1408,7 +1420,8 @@ public class DynamicHlsController : BaseJellyfinApiController
state.Request.SegmentContainer ?? string.Empty,
"hls1/main/",
Request.QueryString.ToString(),
EncodingHelper.IsCopyCodec(state.OutputVideoCodec));
EncodingHelper.IsCopyCodec(state.OutputVideoCodec),
startSegmentIndex);
var playlist = _dynamicHlsPlaylistGenerator.CreateMainPlaylist(request);
return new FileContentResult(Encoding.UTF8.GetBytes(playlist), MimeTypes.GetMimeType("playlist.m3u8"));

View file

@ -18,7 +18,8 @@ public class CreateMainPlaylistRequest
/// <param name="endpointPrefix">The URI prefix for the relative URL in the playlist.</param>
/// <param name="queryString">The desired query string to append (must start with ?).</param>
/// <param name="isRemuxingVideo">Whether the video is being remuxed.</param>
public CreateMainPlaylistRequest(Guid? mediaSourceId, string filePath, int desiredSegmentLengthMs, long totalRuntimeTicks, string segmentContainer, string endpointPrefix, string queryString, bool isRemuxingVideo)
/// <param name="startSegmentIndex">The starting segment index for the playlist.</param>
public CreateMainPlaylistRequest(Guid? mediaSourceId, string filePath, int desiredSegmentLengthMs, long totalRuntimeTicks, string segmentContainer, string endpointPrefix, string queryString, bool isRemuxingVideo, int startSegmentIndex = 0)
{
MediaSourceId = mediaSourceId;
FilePath = filePath;
@ -28,6 +29,7 @@ public class CreateMainPlaylistRequest
EndpointPrefix = endpointPrefix;
QueryString = queryString;
IsRemuxingVideo = isRemuxingVideo;
StartSegmentIndex = startSegmentIndex;
}
/// <summary>
@ -69,4 +71,9 @@ public class CreateMainPlaylistRequest
/// Gets a value indicating whether the video is being remuxed.
/// </summary>
public bool IsRemuxingVideo { get; }
/// <summary>
/// Gets the starting segment index for the playlist.
/// </summary>
public int StartSegmentIndex { get; }
}

View file

@ -62,9 +62,9 @@ public class DynamicHlsPlaylistGenerator : IDynamicHlsPlaylistGenerator
.Append("#EXT-X-TARGETDURATION:")
.Append(Math.Ceiling(segments.Count > 0 ? segments.Max() : request.DesiredSegmentLengthMs))
.AppendLine()
.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
.AppendLine(CultureInfo.InvariantCulture, $"#EXT-X-MEDIA-SEQUENCE:{request.StartSegmentIndex}");
var index = 0;
var index = request.StartSegmentIndex;
if (isHlsInFmp4)
{

View file

@ -11,6 +11,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Moq" />
</ItemGroup>
<ItemGroup>

View file

@ -1,6 +1,9 @@
using System;
using Jellyfin.MediaEncoding.Hls.Extractors;
using Jellyfin.MediaEncoding.Hls.Playlist;
using Jellyfin.MediaEncoding.Keyframes;
using MediaBrowser.Controller.Configuration;
using Moq;
using Xunit;
namespace Jellyfin.MediaEncoding.Hls.Tests.Playlist
@ -96,6 +99,29 @@ namespace Jellyfin.MediaEncoding.Hls.Tests.Playlist
return data;
}
[Fact]
public void CreateMainPlaylist_WithStartSegmentIndex_GeneratesCorrectSequence()
{
var configManager = new Mock<IServerConfigurationManager>();
var generator = new DynamicHlsPlaylistGenerator(configManager.Object, Array.Empty<IKeyframeExtractor>());
var request = new CreateMainPlaylistRequest(
null,
"/path/to/file.mp4",
6000,
600000000000,
"mp4",
"hls1/main/",
"?params=1",
false,
startSegmentIndex: 1187);
var playlist = generator.CreateMainPlaylist(request);
Assert.Contains("#EXT-X-MEDIA-SEQUENCE:1187", playlist, StringComparison.Ordinal);
Assert.Contains("hls1/main/1187.mp4", playlist, StringComparison.Ordinal);
Assert.Contains("hls1/main/1188.mp4", playlist, StringComparison.Ordinal);
}
private static long MsToTicks(int value) => TimeSpan.FromMilliseconds(value).Ticks;
}
}