diff --git a/TwitchDownloaderCore/ClipDownloader.cs b/TwitchDownloaderCore/ClipDownloader.cs index 403deb49..c7e6fcf5 100644 --- a/TwitchDownloaderCore/ClipDownloader.cs +++ b/TwitchDownloaderCore/ClipDownloader.cs @@ -86,22 +86,23 @@ void DownloadProgressHandler(StreamCopyProgress streamProgress) private async Task GetDownloadUrl() { var listLinks = await TwitchHelper.GetClipLinks(downloadOptions.Id); + var clip = listLinks[0].data.clip; - if (listLinks[0].data.clip.playbackAccessToken is null) + if (clip.playbackAccessToken is null) { throw new NullReferenceException("Invalid Clip, deleted possibly?"); } - if (listLinks[0].data.clip.videoQualities is null || listLinks[0].data.clip.videoQualities.Count == 0) + if (clip.videoQualities is null || clip.videoQualities.Length == 0) { throw new NullReferenceException("Clip has no video qualities, deleted possibly?"); } string downloadUrl = ""; - foreach (var quality in listLinks[0].data.clip.videoQualities) + foreach (var quality in clip.videoQualities) { - if (quality.quality + "p" + (quality.frameRate.ToString() == "30" ? "" : quality.frameRate.ToString()) == downloadOptions.Quality) + if (quality.quality + "p" + (Math.Round(quality.frameRate) == 30 ? "" : Math.Round(quality.frameRate).ToString("F0")) == downloadOptions.Quality) { downloadUrl = quality.sourceURL; } @@ -109,10 +110,10 @@ private async Task GetDownloadUrl() if (downloadUrl == "") { - downloadUrl = listLinks[0].data.clip.videoQualities.First().sourceURL; + downloadUrl = clip.videoQualities.First().sourceURL; } - return downloadUrl + "?sig=" + listLinks[0].data.clip.playbackAccessToken.signature + "&token=" + HttpUtility.UrlEncode(listLinks[0].data.clip.playbackAccessToken.value); + return downloadUrl + "?sig=" + clip.playbackAccessToken.signature + "&token=" + HttpUtility.UrlEncode(clip.playbackAccessToken.value); } private static async Task DownloadFileTaskAsync(string url, string destinationFile, int throttleKib, IProgress progress, CancellationToken cancellationToken) diff --git a/TwitchDownloaderCore/Tools/ClipQualityComparer.cs b/TwitchDownloaderCore/Tools/ClipQualityComparer.cs new file mode 100644 index 00000000..8d8dd876 --- /dev/null +++ b/TwitchDownloaderCore/Tools/ClipQualityComparer.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using TwitchDownloaderCore.TwitchObjects.Gql; + +namespace TwitchDownloaderCore.Tools +{ + public class ClipQualityComparer : IComparer + { + public int Compare(VideoQuality x, VideoQuality y) + { + if (x is null) + { + if (y is null) return 0; + return -1; + } + + if (y is null) return 1; + + if (int.TryParse(x.quality, out var xQuality) | int.TryParse(y.quality, out var yQuality)) + { + if (xQuality < yQuality) return 1; + if (xQuality > yQuality) return -1; + + if (x.frameRate < y.frameRate) return 1; + if (x.frameRate > y.frameRate) return -1; + return 0; + } + + return Math.Clamp(string.Compare(x.quality, y.quality, StringComparison.Ordinal), -1, 1) * -1; + } + } +} \ No newline at end of file diff --git a/TwitchDownloaderCore/Tools/CommentTools.cs b/TwitchDownloaderCore/Tools/CommentTools.cs index cb3d769f..56d8e41f 100644 --- a/TwitchDownloaderCore/Tools/CommentTools.cs +++ b/TwitchDownloaderCore/Tools/CommentTools.cs @@ -15,7 +15,7 @@ public int Compare(Comment x, Comment y) return -1; } - if (y is null) return -1; + if (y is null) return 1; // In the off chance that it causes problems with old chats, we will first compare offsets before comparing creation dates. var xOffset = x.content_offset_seconds; diff --git a/TwitchDownloaderCore/TwitchHelper.cs b/TwitchDownloaderCore/TwitchHelper.cs index 06fc4934..d7494e91 100644 --- a/TwitchDownloaderCore/TwitchHelper.cs +++ b/TwitchDownloaderCore/TwitchHelper.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using TwitchDownloaderCore.Chat; +using TwitchDownloaderCore.Tools; using TwitchDownloaderCore.TwitchObjects; using TwitchDownloaderCore.TwitchObjects.Api; using TwitchDownloaderCore.TwitchObjects.Gql; @@ -80,7 +81,7 @@ public static async Task GetClipInfo(object clipId) return await response.Content.ReadFromJsonAsync(); } - public static async Task> GetClipLinks(string clipId) + public static async Task GetClipLinks(string clipId) { var request = new HttpRequestMessage() { @@ -91,7 +92,10 @@ public static async Task> GetClipLinks(string clipId) request.Headers.Add("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko"); using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); - return await response.Content.ReadFromJsonAsync>(); + + var gqlClipTokenResponses = await response.Content.ReadFromJsonAsync(); + Array.Sort(gqlClipTokenResponses[0].data.clip.videoQualities, new ClipQualityComparer()); + return gqlClipTokenResponses; } public static async Task GetGqlVideos(string channelName, string cursor = "", int limit = 50) diff --git a/TwitchDownloaderCore/TwitchObjects/Gql/GqlClipTokenResponse.cs b/TwitchDownloaderCore/TwitchObjects/Gql/GqlClipTokenResponse.cs index 8c386137..1e909c1e 100644 --- a/TwitchDownloaderCore/TwitchObjects/Gql/GqlClipTokenResponse.cs +++ b/TwitchDownloaderCore/TwitchObjects/Gql/GqlClipTokenResponse.cs @@ -1,12 +1,10 @@ -using System.Collections.Generic; - -namespace TwitchDownloaderCore.TwitchObjects.Gql +namespace TwitchDownloaderCore.TwitchObjects.Gql { public class ClipToken { public string id { get; set; } public PlaybackAccessToken playbackAccessToken { get; set; } - public List videoQualities { get; set; } + public VideoQuality[] videoQualities { get; set; } public string __typename { get; set; } } diff --git a/TwitchDownloaderWPF/PageClipDownload.xaml.cs b/TwitchDownloaderWPF/PageClipDownload.xaml.cs index 9c0a7fbe..92c91e45 100644 --- a/TwitchDownloaderWPF/PageClipDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageClipDownload.xaml.cs @@ -55,7 +55,7 @@ private async Task GetClipInfo() btnGetInfo.IsEnabled = false; comboQuality.Items.Clear(); Task taskClipInfo = TwitchHelper.GetClipInfo(clipId); - Task> taskLinks = TwitchHelper.GetClipLinks(clipId); + Task taskLinks = TwitchHelper.GetClipLinks(clipId); await Task.WhenAll(taskClipInfo, taskLinks); GqlClipResponse clipData = taskClipInfo.Result;