From d10fdeb7b81c40fad927dea6555865753a77ec24 Mon Sep 17 00:00:00 2001 From: vaindil Date: Sat, 6 Jan 2024 18:47:27 -0500 Subject: [PATCH] Fix VOD download trimming and quality selection (#936) * fix streamOffsetSeconds calculation * handle source quality streams * check GroupId, add tests * use `contains` instead of `equals` Co-authored-by: Scrub <72096833+ScrubN@users.noreply.github.com> --------- Co-authored-by: Scrub <72096833+ScrubN@users.noreply.github.com> --- .../M3U8ExtensionTests.cs | 24 +++++++++++++++++++ .../Extensions/M3U8Extensions.cs | 17 ++++++++++--- TwitchDownloaderCore/VideoDownloader.cs | 2 +- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/TwitchDownloaderCore.Tests/M3U8ExtensionTests.cs b/TwitchDownloaderCore.Tests/M3U8ExtensionTests.cs index be2a7f27..6c73cb37 100644 --- a/TwitchDownloaderCore.Tests/M3U8ExtensionTests.cs +++ b/TwitchDownloaderCore.Tests/M3U8ExtensionTests.cs @@ -99,6 +99,30 @@ public static void CorrectlyFindsStreamOfQualityFromOldM3U8Response(string quali Assert.Equal(expectedPath, selectedQuality.Path); } + [Theory] + [InlineData("1080", "1080p60")] + [InlineData("1080p", "1080p60")] + [InlineData("1080p60", "1080p60")] + [InlineData("720p60", "720p60")] + [InlineData("foo", "1080p60")] + public static void CorrectlyFindsStreamOfQualityFromM3U8ResponseWithoutFramerate(string qualityString, string expectedPath) + { + var m3u8 = new M3U8(new M3U8.Metadata(), new[] + { + new M3U8.Stream( + new M3U8.Stream.ExtMediaInfo(M3U8.Stream.ExtMediaInfo.MediaType.Video, "chunked", "Source", true, true), + new M3U8.Stream.ExtStreamInfo(0, 1, "avc1.4D401F,mp4a.40.2", (1920, 1080), "chunked", 0), + "1080p60"), + new M3U8.Stream( + new M3U8.Stream.ExtMediaInfo(M3U8.Stream.ExtMediaInfo.MediaType.Video, "720p60", "720p60", true, true), + new M3U8.Stream.ExtStreamInfo(0, 1, "avc1.4D401F,mp4a.40.2", (1280, 720), "720p60", 58.644M), + "720p60"), + }); + + var selectedQuality = m3u8.GetStreamOfQuality(qualityString); + Assert.Equal(expectedPath, selectedQuality.Path); + } + [Theory] [InlineData("480p60", "1080p60")] [InlineData("852x480p60", "1080p60")] diff --git a/TwitchDownloaderCore/Extensions/M3U8Extensions.cs b/TwitchDownloaderCore/Extensions/M3U8Extensions.cs index 2a5371aa..d2db63cc 100644 --- a/TwitchDownloaderCore/Extensions/M3U8Extensions.cs +++ b/TwitchDownloaderCore/Extensions/M3U8Extensions.cs @@ -34,7 +34,7 @@ public static M3U8.Stream GetStreamOfQuality(this M3U8 m3u8, string qualityStrin if (qualityString is null) { - return streams.MaxBy(x => x.StreamInfo.Resolution.Width * x.StreamInfo.Resolution.Height * x.StreamInfo.Framerate); + return m3u8.BestQualityStream(); } if (qualityString.Contains("audio", StringComparison.OrdinalIgnoreCase) && @@ -57,7 +57,7 @@ public static M3U8.Stream GetStreamOfQuality(this M3U8 m3u8, string qualityStrin var qualityStringMatch = UserQualityStringRegex.Match(qualityString); if (!qualityStringMatch.Success) { - return streams.MaxBy(x => x.StreamInfo.Resolution.Width * x.StreamInfo.Resolution.Height * x.StreamInfo.Framerate); + return m3u8.BestQualityStream(); } var desiredWidth = qualityStringMatch.Groups["Width"]; @@ -74,7 +74,7 @@ public static M3U8.Stream GetStreamOfQuality(this M3U8 m3u8, string qualityStrin { 1 => filteredStreams[0], 2 when !desiredFramerate.Success => filteredStreams.First(x => Math.Abs(x.StreamInfo.Framerate - 30) <= 2), - _ => streams.MaxBy(x => x.StreamInfo.Resolution.Width * x.StreamInfo.Resolution.Height * x.StreamInfo.Framerate) + _ => m3u8.BestQualityStream() }; } @@ -118,5 +118,16 @@ public static string GetResolutionFramerateString(this M3U8.Stream stream) return $"{frameHeight}p{frameRate}"; } + + /// + /// Returns the best quality stream from the provided M3U8. + /// + public static M3U8.Stream BestQualityStream(this M3U8 m3u8) + { + var source = Array.Find( + m3u8.Streams, x => x.MediaInfo.Name.Contains("source", StringComparison.OrdinalIgnoreCase) || + x.MediaInfo.GroupId.Equals("chunked", StringComparison.OrdinalIgnoreCase)); + return source ?? m3u8.Streams.MaxBy(x => x.StreamInfo.Resolution.Width * x.StreamInfo.Resolution.Height * x.StreamInfo.Framerate); + } } } \ No newline at end of file diff --git a/TwitchDownloaderCore/VideoDownloader.cs b/TwitchDownloaderCore/VideoDownloader.cs index 3d1a5539..49a21527 100644 --- a/TwitchDownloaderCore/VideoDownloader.cs +++ b/TwitchDownloaderCore/VideoDownloader.cs @@ -88,7 +88,7 @@ public async Task DownloadAsync(CancellationToken cancellationToken) .Take(videoListCrop.Start.Value) .Sum(x => x.PartInfo.Duration); - startOffsetSeconds -= downloadOptions.CropBeginningTime; + startOffsetSeconds = downloadOptions.CropBeginningTime - startOffsetSeconds; double seekDuration = Math.Round(downloadOptions.CropEndingTime - downloadOptions.CropBeginningTime); string metadataPath = Path.Combine(downloadFolder, "metadata.txt");