diff --git a/VDownload.Core/VDownload.Core.Strings/VDownload.Core.Strings.csproj b/VDownload.Core/VDownload.Core.Strings/VDownload.Core.Strings.csproj index 7a200cb..0b0aa2a 100644 --- a/VDownload.Core/VDownload.Core.Strings/VDownload.Core.Strings.csproj +++ b/VDownload.Core/VDownload.Core.Strings/VDownload.Core.Strings.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/VDownload.Core/VDownload.Core.Tasks/DownloadTask.cs b/VDownload.Core/VDownload.Core.Tasks/DownloadTask.cs index 15b82f1..05fa933 100644 --- a/VDownload.Core/VDownload.Core.Tasks/DownloadTask.cs +++ b/VDownload.Core/VDownload.Core.Tasks/DownloadTask.cs @@ -157,7 +157,7 @@ namespace VDownload.Core.Tasks UpdateProgressWithDispatcher(value); }); - VideoStreamDownloadResult downloadResult = await DownloadOptions.SelectedStream.Download(tempDirectory, onProgressDownloading, token, DownloadOptions.TrimStart, DownloadOptions.TrimEnd); + VideoStreamDownloadResult downloadResult = await DownloadOptions.SelectedStream.Download(tempDirectory, onProgressDownloading, token, Video.Duration, DownloadOptions.TrimStart, DownloadOptions.TrimEnd); Action onProgressProcessing = (value) => { diff --git a/VDownload.Core/VDownload.Core.ViewModels/VDownload.Core.ViewModels.csproj b/VDownload.Core/VDownload.Core.ViewModels/VDownload.Core.ViewModels.csproj index 2c41f44..b3a0746 100644 --- a/VDownload.Core/VDownload.Core.ViewModels/VDownload.Core.ViewModels.csproj +++ b/VDownload.Core/VDownload.Core.ViewModels/VDownload.Core.ViewModels.csproj @@ -10,11 +10,11 @@ - - + + - - + + diff --git a/VDownload.Core/VDownload.Core.Views/VDownload.Core.Views.csproj b/VDownload.Core/VDownload.Core.Views/VDownload.Core.Views.csproj index e95ff27..c840d2b 100644 --- a/VDownload.Core/VDownload.Core.Views/VDownload.Core.Views.csproj +++ b/VDownload.Core/VDownload.Core.Views/VDownload.Core.Views.csproj @@ -18,11 +18,11 @@ - - + + - - + + diff --git a/VDownload.Models/Video.cs b/VDownload.Models/Video.cs index 00a6003..816e394 100644 --- a/VDownload.Models/Video.cs +++ b/VDownload.Models/Video.cs @@ -11,7 +11,6 @@ namespace VDownload.Models #region PROPERTIES public string Title { get; set; } - public string Description { get; set; } public string Author { get; set; } public DateTime PublishDate { get; set; } public TimeSpan Duration { get; set; } diff --git a/VDownload.Models/VideoStream.cs b/VDownload.Models/VideoStream.cs index a30fe59..045e292 100644 --- a/VDownload.Models/VideoStream.cs +++ b/VDownload.Models/VideoStream.cs @@ -20,7 +20,7 @@ namespace VDownload.Models public override string ToString() => Name; - public abstract Task Download(string taskTemporaryDirectory, IProgress onProgress, CancellationToken token, TimeSpan trimStart, TimeSpan trimEnd); + public abstract Task Download(string taskTemporaryDirectory, IProgress onProgress, CancellationToken token, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd); #endregion } diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Notifications.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Notifications.cs index e9f3c0b..3b3beb9 100644 --- a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Notifications.cs +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Notifications.cs @@ -10,9 +10,9 @@ namespace VDownload.Services.Data.Settings.Models public class Notifications { [JsonProperty("on_successful")] - public bool OnSuccessful { get; set; } = true; + public bool OnSuccessful { get; set; } = false; [JsonProperty("on_unsuccessful")] - public bool OnUnsuccessful { get; set; } = true; + public bool OnUnsuccessful { get; set; } = false; } } diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Temp.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Temp.cs index 03addf7..cb7fd90 100644 --- a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Temp.cs +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Temp.cs @@ -10,7 +10,7 @@ namespace VDownload.Services.Data.Settings.Models public class Temp { [JsonProperty("directory")] - public string Directory { get; set; } = $"{Path.GetTempPath()}\\VDownload"; + public string Directory { get; set; } = $"{Path.GetTempPath()}VDownload"; [JsonProperty("delete_on_error")] public bool DeleteOnError { get; set; } = true; diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/VDownload.Services.UI.Dialogs.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/VDownload.Services.UI.Dialogs.csproj index 5213e9d..aa4e151 100644 --- a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/VDownload.Services.UI.Dialogs.csproj +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/VDownload.Services.UI.Dialogs.csproj @@ -10,7 +10,7 @@ - - + + diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/VDownload.Services.UI.DictionaryResources.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/VDownload.Services.UI.DictionaryResources.csproj index 0404798..86ac367 100644 --- a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/VDownload.Services.UI.DictionaryResources.csproj +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/VDownload.Services.UI.DictionaryResources.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/VDownload.Services.UI.Notifications.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/VDownload.Services.UI.Notifications.csproj index 94c8201..72ceb84 100644 --- a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/VDownload.Services.UI.Notifications.csproj +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/VDownload.Services.UI.Notifications.csproj @@ -10,7 +10,7 @@ - - + + diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/VDownload.Services.UI.StoragePicker.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/VDownload.Services.UI.StoragePicker.csproj index 6ea7798..34ac897 100644 --- a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/VDownload.Services.UI.StoragePicker.csproj +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/VDownload.Services.UI.StoragePicker.csproj @@ -10,7 +10,7 @@ - - + + diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/VDownload.Services.UI.StringResources.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/VDownload.Services.UI.StringResources.csproj index 50e13b6..c15f245 100644 --- a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/VDownload.Services.UI.StringResources.csproj +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/VDownload.Services.UI.StringResources.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/VDownload.Services.UI.WebView.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/VDownload.Services.UI.WebView.csproj index aa294dd..9c8dbdc 100644 --- a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/VDownload.Services.UI.WebView.csproj +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/VDownload.Services.UI.WebView.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegBuilder.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegBuilder.cs index 8149330..fc635da 100644 --- a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegBuilder.cs +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegBuilder.cs @@ -136,7 +136,6 @@ namespace VDownload.Services.Utility.FFmpeg private async Task BuildInputArgumentOptions(FFMpegArgumentOptions options) { options.UsingMultithreading(_settingsService.Data.Common.Processing.UseMultithreading); - options.WithSpeedPreset((Speed)_settingsService.Data.Common.Processing.Speed); if (_settingsService.Data.Common.Processing.UseHardwareAcceleration) { options.WithHardwareAcceleration(HardwareAccelerationDevice.Auto); @@ -183,6 +182,8 @@ namespace VDownload.Services.Utility.FFmpeg { options.WithCustomArgument("-an"); } + + options.WithSpeedPreset((Speed)_settingsService.Data.Common.Processing.Speed); } #endregion diff --git a/VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs b/VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs new file mode 100644 index 0000000..b368dc2 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Sources.Common +{ + public struct SearchRegex + { + public Regex Regex { get; set; } + public Func> SearchFunction { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenExtensions.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenExtensions.cs new file mode 100644 index 0000000..e908c11 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenExtensions.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenExtensions + { + [JsonProperty("persistedQuery")] + public GetClipTokenPersistedQuery PersistedQuery { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenPersistedQuery.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenPersistedQuery.cs new file mode 100644 index 0000000..9774414 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenPersistedQuery.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenPersistedQuery + { + [JsonProperty("version")] + public int Version { get; set; } + + [JsonProperty("sha256Hash")] + public string Sha256Hash { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenRequest.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenRequest.cs new file mode 100644 index 0000000..d3c7af5 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenRequest.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenRequest + { + [JsonProperty("operationName")] + public string OperationName { get; set; } + + [JsonProperty("variables")] + public GetClipTokenVariables Variables { get; set; } + + [JsonProperty("extensions")] + public GetClipTokenExtensions Extensions { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenVariables.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenVariables.cs new file mode 100644 index 0000000..570ac6d --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenVariables.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenVariables + { + [JsonProperty("slug")] + public string Slug { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenClip.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenClip.cs new file mode 100644 index 0000000..c44dac7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenClip.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenClip + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("playbackAccessToken")] + public GetClipTokenPlaybackAccessToken PlaybackAccessToken { get; set; } + + [JsonProperty("videoQualities")] + public List VideoQualities { get; set; } + + [JsonProperty("__typename")] + public string Typename { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenData.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenData.cs new file mode 100644 index 0000000..79ebe8a --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenData.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Configuration.Models; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenData + { + [JsonProperty("clip")] + public GetClipTokenClip Clip { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenExtensions.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenExtensions.cs new file mode 100644 index 0000000..66d294d --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenExtensions.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenExtensions + { + [JsonProperty("durationMilliseconds")] + public int DurationMilliseconds { get; set; } + + [JsonProperty("operationName")] + public string OperationName { get; set; } + + [JsonProperty("requestID")] + public string RequestID { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenPlaybackAccessToken.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenPlaybackAccessToken.cs new file mode 100644 index 0000000..a69a3fd --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenPlaybackAccessToken.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenPlaybackAccessToken + { + [JsonProperty("signature")] + public string Signature { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("__typename")] + public string Typename { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenResponse.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenResponse.cs new file mode 100644 index 0000000..0fca77c --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenResponse + { + [JsonProperty("data")] + public GetClipTokenData Data { get; set; } + + [JsonProperty("extensions")] + public Response.GetClipTokenExtensions Extensions { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenVideoQuality.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenVideoQuality.cs new file mode 100644 index 0000000..758e9dc --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenVideoQuality.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenVideoQuality + { + [JsonProperty("frameRate")] + public double FrameRate { get; set; } + + [JsonProperty("quality")] + public string Quality { get; set; } + + [JsonProperty("sourceURL")] + public string SourceURL { get; set; } + + [JsonProperty("__typename")] + public string Typename { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenData.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenData.cs index 7677c87..1ef98a5 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenData.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenData.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Request; namespace VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response { diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenVideoPlaybackAccessToken.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenVideoPlaybackAccessToken.cs similarity index 87% rename from VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenVideoPlaybackAccessToken.cs rename to VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenVideoPlaybackAccessToken.cs index 8a9505e..c0d7eba 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenVideoPlaybackAccessToken.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenVideoPlaybackAccessToken.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Request +namespace VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response { public class GetVideoTokenVideoPlaybackAccessToken { diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Data.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Data.cs new file mode 100644 index 0000000..62c6edf --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Data.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.Helix.GetClips.Response +{ + public class Data + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("embed_url")] + public string EmbedUrl { get; set; } + + [JsonProperty("broadcaster_id")] + public string BroadcasterId { get; set; } + + [JsonProperty("broadcaster_name")] + public string BroadcasterName { get; set; } + + [JsonProperty("creator_id")] + public string CreatorId { get; set; } + + [JsonProperty("creator_name")] + public string CreatorName { get; set; } + + [JsonProperty("video_id")] + public string VideoId { get; set; } + + [JsonProperty("game_id")] + public string GameId { get; set; } + + [JsonProperty("language")] + public string Language { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("view_count")] + public long ViewCount { get; set; } + + [JsonProperty("created_at")] + public DateTime CreatedAt { get; set; } + + [JsonProperty("thumbnail_url")] + public string ThumbnailUrl { get; set; } + + [JsonProperty("duration")] + public double Duration { get; set; } + + [JsonProperty("vod_offset")] + public int VodOffset { get; set; } + + [JsonProperty("is_featured")] + public bool IsFeatured { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/GetClipsResponse.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/GetClipsResponse.cs new file mode 100644 index 0000000..35de292 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/GetClipsResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Api.Helix.GetVideos.Response; + +namespace VDownload.Sources.Twitch.Api.Helix.GetClips.Response +{ + public class GetClipsResponse + { + [JsonProperty("data")] + public List Data { get; set; } + + [JsonProperty("pagination")] + public Pagination Pagination { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Pagination.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Pagination.cs new file mode 100644 index 0000000..961edb7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Pagination.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.Helix.GetClips.Response +{ + public class Pagination + { + [JsonProperty("cursor")] + public string Cursor { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/TwitchApiService.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/TwitchApiService.cs index 8beeebb..7b4f934 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/TwitchApiService.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/TwitchApiService.cs @@ -1,8 +1,13 @@ -using VDownload.Services.Data.Configuration; +using System.Diagnostics; +using VDownload.Services.Data.Configuration; using VDownload.Services.Utility.HttpClient; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response; using VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response; +using VDownload.Sources.Twitch.Api.Helix.GetClips.Response; using VDownload.Sources.Twitch.Api.Helix.GetUsers.Response; using VDownload.Sources.Twitch.Api.Helix.GetVideos.Response; +using VDownload.Sources.Twitch.Configuration.Models; using VDownload.Sources.Twitch.Search.Models.GetVideoToken.Request; namespace VDownload.Sources.Twitch.Api @@ -11,9 +16,11 @@ namespace VDownload.Sources.Twitch.Api { Task AuthValidate(byte[] token); Task GQLGetVideoToken(string id); + Task GQLGetClipToken(string id); Task HelixGetUser(string login, byte[] token); Task HelixGetVideo(string id, byte[] token); Task HelixGetUserVideos(string user_id, byte[] token, int count, string? cursor = null); + Task HelixGetClip(string id, byte[] token); Task UsherGetVideoPlaylist(string id, string videoToken, string videoTokenSignature); } @@ -99,6 +106,20 @@ namespace VDownload.Sources.Twitch.Api return await _httpClientService.SendRequestAsync(request); } + public async Task HelixGetClip(string id, byte[] token) + { + Token tokenData = new Token(_configurationService.Twitch.Api.Helix.TokenSchema, token); + + HttpRequest request = new HttpRequest(HttpMethodType.GET, _configurationService.Twitch.Api.Helix.Endpoints.GetClips); + + request.Query.Add("id", id); + + request.Headers.Add("Authorization", tokenData.ToString()); + request.Headers.Add("Client-Id", _configurationService.Twitch.Api.Helix.ClientId); + + return await _httpClientService.SendRequestAsync(request); + } + public async Task GQLGetVideoToken(string id) { GetVideoTokenRequest requestBody = new GetVideoTokenRequest @@ -123,6 +144,36 @@ namespace VDownload.Sources.Twitch.Api return await _httpClientService.SendRequestAsync(request); } + public async Task GQLGetClipToken(string id) + { + Gql config = _configurationService.Twitch.Api.Gql; + + GetClipTokenRequest requestBody = new GetClipTokenRequest + { + OperationName = config.Queries.GetClipToken.OperationName, + Variables = new GetClipTokenVariables + { + Slug = id + }, + Extensions = new GQL.GetClipToken.Request.GetClipTokenExtensions + { + PersistedQuery = new GetClipTokenPersistedQuery + { + Version = config.Queries.GetClipToken.PersistedQueryVersion, + Sha256Hash = config.Queries.GetClipToken.PersistedQueryHash, + } + } + }; + + HttpRequest request = new HttpRequest(HttpMethodType.POST, config.Endpoint) + { + Body = requestBody, + }; + request.Headers.Add("Client-Id", config.ClientId); + + return await _httpClientService.SendRequestAsync(request); + } + public async Task UsherGetVideoPlaylist(string id, string videoToken, string videoTokenSignature) { string url = string.Format(_configurationService.Twitch.Api.Usher.Endpoints.GetVideoPlaylist, id); diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Download.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Download.cs index 5e1c31c..c50a01f 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Download.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Download.cs @@ -5,6 +5,9 @@ namespace VDownload.Sources.Twitch.Configuration.Models{ { [ConfigurationKeyName("vod")] public DownloadVod Vod { get; set; } + + [ConfigurationKeyName("clip")] + public DownloadClip Clip { get; set; } } } \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadClip.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadClip.cs new file mode 100644 index 0000000..6d1a39a --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadClip.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class DownloadClip + { + [ConfigurationKeyName("file_name")] + public string FileName { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsHelix.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsHelix.cs index af28011..0748159 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsHelix.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsHelix.cs @@ -7,6 +7,9 @@ namespace VDownload.Sources.Twitch.Configuration.Models [ConfigurationKeyName("get_videos")] public string GetVideos { get; set; } + [ConfigurationKeyName("get_clips")] + public string GetClips { get; set; } + [ConfigurationKeyName("get_users")] public string GetUsers { get; set; } } diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetClipToken.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetClipToken.cs new file mode 100644 index 0000000..e3ec148 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetClipToken.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class GetClipToken + { + [ConfigurationKeyName("operation_name")] + public string OperationName { get; set; } + + [ConfigurationKeyName("persisted_query_version")] + public int PersistedQueryVersion { get; set; } + + [ConfigurationKeyName("persisted_query_hash")] + public string PersistedQueryHash { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Queries.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Queries.cs index 17e9b66..2d8a1d9 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Queries.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Queries.cs @@ -5,6 +5,9 @@ namespace VDownload.Sources.Twitch.Configuration.Models{ { [ConfigurationKeyName("get_video_token")] public GetVideoToken GetVideoToken { get; set; } + + [ConfigurationKeyName("get_clip_token")] + public GetClipToken GetClipToken { get; set; } } } \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClip.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClip.cs new file mode 100644 index 0000000..8cddf43 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClip.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Models +{ + public class TwitchClip : TwitchVideo + { + #region PROPERTIES + + public string Creator { get; set; } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClipStream.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClipStream.cs new file mode 100644 index 0000000..b075bdc --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClipStream.cs @@ -0,0 +1,83 @@ +using SimpleToolkit.Extensions; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using System.Web; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using VDownload.Sources.Twitch.Models.Internal; + +namespace VDownload.Sources.Twitch.Models +{ + public class TwitchClipStream : VideoStream + { + #region SERVICES + + protected readonly HttpClient _httpClient; + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region PROPERTIES + + public int Height { get; set; } + public double FrameRate { get; set; } + public Uri Url { get; set; } + public string Signature { get; set; } + public string Token { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public TwitchClipStream(HttpClient httpClient, IConfigurationService configurationService, ISettingsService settingsService) + { + _httpClient = httpClient; + + _configurationService = configurationService; + _settingsService = settingsService; + } + + #endregion + + + + #region PUBLIC METHODS + + public async override Task Download(string taskTemporaryDirectory, IProgress onProgress, CancellationToken token, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd) + { + token.ThrowIfCancellationRequested(); + + string location = Path.Combine(taskTemporaryDirectory, _configurationService.Twitch.Download.Clip.FileName); + + string url = $"{Url.OriginalString}?sig={Signature}&token={HttpUtility.UrlEncode(Token)}"; + using (FileStream fileStream = File.Create(location)) + { + await _httpClient.DownloadAsync(url, fileStream, token, onProgress); + token.ThrowIfCancellationRequested(); + } + + return new VideoStreamDownloadResult + { + File = location, + NewDuration = duration, + NewTrimEnd = trimEnd, + NewTrimStart = trimStart + }; + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVod.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVod.cs index 374a6ca..97f523f 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVod.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVod.cs @@ -8,5 +8,10 @@ namespace VDownload.Sources.Twitch.Models { public class TwitchVod : TwitchVideo { + #region PROPERTIES + + public string Description { get; set; } + + #endregion } } diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVodStream.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVodStream.cs index c5e97e0..5c00f2f 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVodStream.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVodStream.cs @@ -54,7 +54,7 @@ namespace VDownload.Sources.Twitch.Models #region PUBLIC METHODS - public async override Task Download(string taskTemporaryDirectory, IProgress onProgress, CancellationToken token, TimeSpan trimStart, TimeSpan trimEnd) + public async override Task Download(string taskTemporaryDirectory, IProgress onProgress, CancellationToken token, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd) { token.ThrowIfCancellationRequested(); @@ -92,8 +92,6 @@ namespace VDownload.Sources.Twitch.Models token.ThrowIfCancellationRequested(); - TimeSpan duration = TimeSpan.FromTicks(chunks.Sum(x => x.Duration.Ticks)); - if (_settingsService.Data.Twitch.Vod.PassiveTrimming) { PassiveTrimming(chunks, ref trimStart, ref trimEnd, ref duration); diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/VDownload.Sources.Twitch.Models.csproj b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/VDownload.Sources.Twitch.Models.csproj index a4c545f..6bde5aa 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/VDownload.Sources.Twitch.Models.csproj +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/VDownload.Sources.Twitch.Models.csproj @@ -6,6 +6,10 @@ enable + + + + diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchSearchService.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchSearchService.cs index faee2fe..9d61bde 100644 --- a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchSearchService.cs +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchSearchService.cs @@ -8,7 +8,9 @@ using VDownload.Models; using VDownload.Services.Data.Configuration; using VDownload.Sources.Common; using VDownload.Sources.Twitch.Api; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response; using VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response; +using VDownload.Sources.Twitch.Api.Helix.GetClips.Response; using VDownload.Sources.Twitch.Api.Helix.GetUsers.Response; using VDownload.Sources.Twitch.Api.Helix.GetVideos.Response; using VDownload.Sources.Twitch.Authentication; @@ -34,6 +36,8 @@ namespace VDownload.Sources.Twitch protected readonly ITwitchAuthenticationService _twitchAuthenticationService; protected readonly ITwitchVideoStreamFactoryService _videoStreamFactoryService; + protected readonly Configuration.Models.Search _searchConfiguration; + #endregion @@ -46,6 +50,8 @@ namespace VDownload.Sources.Twitch _apiService = apiService; _twitchAuthenticationService = authenticationService; _videoStreamFactoryService = videoStreamFactoryService; + + _searchConfiguration = _configurationService.Twitch.Search; } #endregion @@ -57,13 +63,18 @@ namespace VDownload.Sources.Twitch async Task diff --git a/VDownload/configuration.json b/VDownload/configuration.json index c22e3a6..83f88b7 100644 --- a/VDownload/configuration.json +++ b/VDownload/configuration.json @@ -117,6 +117,7 @@ "client_id": "yukkqkwp61wsv3u1pya17crpyaa98y", "endpoints": { "get_videos": "https://api.twitch.tv/helix/videos", + "get_clips": "https://api.twitch.tv/helix/clips", "get_users": "https://api.twitch.tv/helix/users" } }, @@ -127,6 +128,11 @@ "get_video_token": { "operation_name": "PlaybackAccessToken_Template", "query": "query PlaybackAccessToken_Template($login: String!, $isLive: Boolean!, $vodID: ID!, $isVod: Boolean!, $playerType: String!) { streamPlaybackAccessToken(channelName: $login, params: {platform: \"web\", playerBackend: \"mediaplayer\", playerType: $playerType}) @include(if: $isLive) { value signature __typename } videoPlaybackAccessToken(id: $vodID, params: {platform: \"web\", playerBackend: \"mediaplayer\", playerType: $playerType}) @include(if: $isVod) { value signature __typename }}" + }, + "get_clip_token": { + "operation_name": "VideoAccessToken_Clip", + "persisted_query_version": 1, + "persisted_query_hash": "36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11" } } }, @@ -168,6 +174,9 @@ "vod": { "chunk_regex": "#EXTINF:(?\\d+.\\d+),\\n(?.+)", "file_name": "raw.ts" + }, + "clip": { + "file_name": "raw.mp4" } }, "authentication": {