diff --git a/TwitchDownloaderCore/ClipDownloader.cs b/TwitchDownloaderCore/ClipDownloader.cs index 12151782..0ed26659 100644 --- a/TwitchDownloaderCore/ClipDownloader.cs +++ b/TwitchDownloaderCore/ClipDownloader.cs @@ -126,24 +126,76 @@ private async Task GetDownloadUrl() throw new NullReferenceException("Clip has no video qualities, deleted possibly?"); } - string downloadUrl = ""; + var downloadUrl = GetDownloadUrlForQuality(clip, downloadOptions.Quality); - foreach (var quality in clip.videoQualities) + return downloadUrl + "?sig=" + clip.playbackAccessToken.signature + "&token=" + HttpUtility.UrlEncode(clip.playbackAccessToken.value); + } + + private static string GetDownloadUrlForQuality(ClipToken clip, string qualityString) + { + Debug.Assert(clip.videoQualities.OrderBy(x => x, new ClipQualityComparer()).SequenceEqual(clip.videoQualities)); + + if (TryGetKeywordQuality(clip, qualityString, out var downloadUrl)) + { + return downloadUrl; + } + + if (qualityString.Contains('p')) + { + foreach (var quality in clip.videoQualities) + { + var framerate = (int)Math.Round(quality.frameRate); + var framerateString = qualityString.EndsWith('p') && framerate == 30 + ? "" + : framerate.ToString("F0"); + + if ($"{quality.quality}p{framerateString}" == qualityString) + { + return quality.sourceURL; + } + } + } + else { - if (quality.quality + "p" + (Math.Round(quality.frameRate) == 30 ? "" : Math.Round(quality.frameRate).ToString("F0")) == downloadOptions.Quality) + var quality = clip.videoQualities.FirstOrDefault(quality => quality.quality == qualityString); + if (quality is not null) { - downloadUrl = quality.sourceURL; + return quality.sourceURL; } } - if (downloadUrl == "") + return BestQuality(clip).sourceURL; + } + + private static bool TryGetKeywordQuality(ClipToken clip, string qualityString, out string downloadUrl) + { + if (string.IsNullOrWhiteSpace(qualityString)) { - downloadUrl = clip.videoQualities.First().sourceURL; + downloadUrl = BestQuality(clip).sourceURL; + return true; } - return downloadUrl + "?sig=" + clip.playbackAccessToken.signature + "&token=" + HttpUtility.UrlEncode(clip.playbackAccessToken.value); + if (qualityString.Contains("best", StringComparison.OrdinalIgnoreCase) + || qualityString.Contains("source", StringComparison.OrdinalIgnoreCase)) + { + downloadUrl = BestQuality(clip).sourceURL; + return true; + } + + if (qualityString.Contains("worst", StringComparison.OrdinalIgnoreCase)) + { + downloadUrl = WorstQuality(clip).sourceURL; + return true; + } + + downloadUrl = null; + return false; } + private static VideoQuality BestQuality(ClipToken clip) => clip.videoQualities.First(); + + private static VideoQuality WorstQuality(ClipToken clip) => clip.videoQualities.Last(); + private static async Task DownloadFileTaskAsync(string url, FileStream fs, int throttleKib, IProgress progress, CancellationToken cancellationToken) { var request = new HttpRequestMessage(HttpMethod.Get, url);