twitch clips support added
This commit is contained in:
@@ -10,8 +10,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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<double> onProgressProcessing = (value) =>
|
||||
{
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageReference Include="SimpleToolkit.MVVM" Version="1.7.2" />
|
||||
<PackageReference Include="SimpleToolkit.UI.Models" Version="1.7.2" />
|
||||
<PackageReference Include="SimpleToolkit.MVVM" Version="1.7.4" />
|
||||
<PackageReference Include="SimpleToolkit.UI.Models" Version="1.7.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageReference Include="SimpleToolkit.UI.WinUI.Behaviors" Version="1.7.2" />
|
||||
<PackageReference Include="SimpleToolkit.UI.WinUI.Controls" Version="1.7.2" />
|
||||
<PackageReference Include="SimpleToolkit.UI.WinUI.Behaviors" Version="1.7.4" />
|
||||
<PackageReference Include="SimpleToolkit.UI.WinUI.Controls" Version="1.7.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace VDownload.Models
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
||||
public abstract Task<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> onProgress, CancellationToken token, TimeSpan trimStart, TimeSpan trimEnd);
|
||||
public abstract Task<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> onProgress, CancellationToken token, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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
|
||||
|
||||
16
VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs
Normal file
16
VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs
Normal file
@@ -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<string, Task<object>> SearchFunction { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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<GetClipTokenVideoQuality> VideoQualities { get; set; }
|
||||
|
||||
[JsonProperty("__typename")]
|
||||
public string Typename { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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> Data { get; set; }
|
||||
|
||||
[JsonProperty("pagination")]
|
||||
public Pagination Pagination { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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<string> AuthValidate(byte[] token);
|
||||
Task<GetVideoTokenResponse> GQLGetVideoToken(string id);
|
||||
Task<GetClipTokenResponse> GQLGetClipToken(string id);
|
||||
Task<GetUsersResponse> HelixGetUser(string login, byte[] token);
|
||||
Task<GetVideosResponse> HelixGetVideo(string id, byte[] token);
|
||||
Task<GetVideosResponse> HelixGetUserVideos(string user_id, byte[] token, int count, string? cursor = null);
|
||||
Task<GetClipsResponse> HelixGetClip(string id, byte[] token);
|
||||
Task<string> UsherGetVideoPlaylist(string id, string videoToken, string videoTokenSignature);
|
||||
}
|
||||
|
||||
@@ -99,6 +106,20 @@ namespace VDownload.Sources.Twitch.Api
|
||||
return await _httpClientService.SendRequestAsync<GetVideosResponse>(request);
|
||||
}
|
||||
|
||||
public async Task<GetClipsResponse> 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<GetClipsResponse>(request);
|
||||
}
|
||||
|
||||
public async Task<GetVideoTokenResponse> GQLGetVideoToken(string id)
|
||||
{
|
||||
GetVideoTokenRequest requestBody = new GetVideoTokenRequest
|
||||
@@ -123,6 +144,36 @@ namespace VDownload.Sources.Twitch.Api
|
||||
return await _httpClientService.SendRequestAsync<GetVideoTokenResponse>(request);
|
||||
}
|
||||
|
||||
public async Task<GetClipTokenResponse> 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<GetClipTokenResponse>(request);
|
||||
}
|
||||
|
||||
public async Task<string> UsherGetVideoPlaylist(string id, string videoToken, string videoTokenSignature)
|
||||
{
|
||||
string url = string.Format(_configurationService.Twitch.Api.Usher.Endpoints.GetVideoPlaylist, id);
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace VDownload.Sources.Twitch.Configuration.Models{
|
||||
{
|
||||
[ConfigurationKeyName("vod")]
|
||||
public DownloadVod Vod { get; set; }
|
||||
|
||||
[ConfigurationKeyName("clip")]
|
||||
public DownloadClip Clip { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> 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
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,10 @@ namespace VDownload.Sources.Twitch.Models
|
||||
{
|
||||
public class TwitchVod : TwitchVideo
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace VDownload.Sources.Twitch.Models
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public async override Task<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> onProgress, CancellationToken token, TimeSpan trimStart, TimeSpan trimEnd)
|
||||
public async override Task<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> 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);
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SimpleToolkit.Extensions" Version="1.7.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" />
|
||||
<ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" />
|
||||
|
||||
@@ -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<Video> ISourceSearchService.SearchVideo(string url) => await SearchVideo(url);
|
||||
public async Task<TwitchVideo> SearchVideo(string url)
|
||||
{
|
||||
foreach (Regex regex in _configurationService.Twitch.Search.Vod.Regexes.Select(x => new Regex(x)))
|
||||
List<SearchRegex> regexes =
|
||||
[
|
||||
.. _searchConfiguration.Vod.Regexes.Select(x => new SearchRegex { Regex = new Regex(x), SearchFunction = async (id) => await GetVod(id) }),
|
||||
.. _searchConfiguration.Clip.Regexes.Select(x => new SearchRegex { Regex = new Regex(x), SearchFunction = async (id) => await GetClip(id) }),
|
||||
];
|
||||
foreach (SearchRegex regex in regexes)
|
||||
{
|
||||
Match match = regex.Match(url);
|
||||
Match match = regex.Regex.Match(url);
|
||||
if (match.Success)
|
||||
{
|
||||
string id = match.Groups[1].Value;
|
||||
return await GetVod(id);
|
||||
return (TwitchVideo)await regex.SearchFunction.Invoke(id);
|
||||
}
|
||||
}
|
||||
throw new MediaSearchException("Invalid url"); // TODO : Change to string resource
|
||||
@@ -72,16 +83,20 @@ namespace VDownload.Sources.Twitch
|
||||
async Task<Playlist> ISourceSearchService.SearchPlaylist(string url, int maxVideoCount) => await SearchPlaylist(url, maxVideoCount);
|
||||
public async Task<TwitchPlaylist> SearchPlaylist(string url, int maxVideoCount)
|
||||
{
|
||||
foreach (Regex regex in _configurationService.Twitch.Search.Channel.Regexes.Select(x => new Regex(x)))
|
||||
List<SearchRegex> regexes =
|
||||
[
|
||||
.. _searchConfiguration.Channel.Regexes.Select(x => new SearchRegex { Regex = new Regex(x), SearchFunction = async (id) => await GetChannel(id, maxVideoCount) }),
|
||||
];
|
||||
foreach (SearchRegex regex in regexes)
|
||||
{
|
||||
Match match = regex.Match(url);
|
||||
Match match = regex.Regex.Match(url);
|
||||
if (match.Success)
|
||||
{
|
||||
string id = match.Groups[1].Value;
|
||||
return await GetChannel(id, maxVideoCount);
|
||||
return (TwitchPlaylist)await regex.SearchFunction.Invoke(id);
|
||||
}
|
||||
}
|
||||
throw new MediaSearchException("Invalid url");
|
||||
throw new MediaSearchException("Invalid url"); // TODO : Change to string resource
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -103,6 +118,19 @@ namespace VDownload.Sources.Twitch
|
||||
return vod;
|
||||
}
|
||||
|
||||
protected async Task<TwitchClip> GetClip(string id)
|
||||
{
|
||||
byte[] token = await GetToken();
|
||||
|
||||
GetClipsResponse info = await _apiService.HelixGetClip(id, token);
|
||||
|
||||
Api.Helix.GetClips.Response.Data clipResponse = info.Data[0];
|
||||
|
||||
TwitchClip clip = await ParseClip(clipResponse);
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
||||
protected async Task<TwitchChannel> GetChannel(string id, int count)
|
||||
{
|
||||
byte[] token = await GetToken();
|
||||
@@ -171,6 +199,31 @@ namespace VDownload.Sources.Twitch
|
||||
return vod;
|
||||
}
|
||||
|
||||
protected async Task<TwitchClip> ParseClip(Api.Helix.GetClips.Response.Data data)
|
||||
{
|
||||
Task<IEnumerable<TwitchClipStream>> streamsTask = GetClipStreams(data.Id);
|
||||
|
||||
TwitchClip clip = new TwitchClip
|
||||
{
|
||||
Title = data.Title,
|
||||
Author = data.BroadcasterName,
|
||||
Creator = data.CreatorName,
|
||||
PublishDate = data.CreatedAt,
|
||||
Duration = TimeSpan.FromSeconds(Math.Round(data.Duration)),
|
||||
Views = data.ViewCount,
|
||||
ThumbnailUrl = new Uri(data.ThumbnailUrl),
|
||||
Url = new Uri(data.Url),
|
||||
};
|
||||
|
||||
await streamsTask;
|
||||
foreach (TwitchClipStream stream in streamsTask.Result)
|
||||
{
|
||||
clip.Streams.Add(stream);
|
||||
}
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
||||
protected async Task<IEnumerable<TwitchVodStream>> GetVodStreams(string id)
|
||||
{
|
||||
GetVideoTokenResponse videoToken = await _apiService.GQLGetVideoToken(id);
|
||||
@@ -195,6 +248,25 @@ namespace VDownload.Sources.Twitch
|
||||
return streams;
|
||||
}
|
||||
|
||||
protected async Task<IEnumerable<TwitchClipStream>> GetClipStreams(string id)
|
||||
{
|
||||
GetClipTokenResponse clipToken = await _apiService.GQLGetClipToken(id);
|
||||
|
||||
List<TwitchClipStream> streams = new List<TwitchClipStream>();
|
||||
foreach (GetClipTokenVideoQuality streamData in clipToken.Data.Clip.VideoQualities)
|
||||
{
|
||||
TwitchClipStream stream = _videoStreamFactoryService.CreateClipStream();
|
||||
stream.Name = $"{streamData.Quality}p{Math.Round(streamData.FrameRate)}";
|
||||
stream.Height = int.Parse(streamData.Quality);
|
||||
stream.FrameRate = streamData.FrameRate;
|
||||
stream.Url = new Uri(streamData.SourceURL);
|
||||
stream.Signature = clipToken.Data.Clip.PlaybackAccessToken.Signature;
|
||||
stream.Token = clipToken.Data.Clip.PlaybackAccessToken.Value;
|
||||
streams.Add(stream);
|
||||
}
|
||||
return streams;
|
||||
}
|
||||
|
||||
protected TimeSpan ParseVodDuration(string duration)
|
||||
{
|
||||
IEnumerable<string> parts = duration.Split(['h', 'm', 's'])[..^1].Reverse();
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace VDownload.Sources.Twitch
|
||||
public interface ITwitchVideoStreamFactoryService
|
||||
{
|
||||
TwitchVodStream CreateVodStream();
|
||||
TwitchClipStream CreateClipStream();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +42,8 @@ namespace VDownload.Sources.Twitch
|
||||
|
||||
public TwitchVodStream CreateVodStream() => new TwitchVodStream(_httpClient, _configurationService, _settingsService);
|
||||
|
||||
public TwitchClipStream CreateClipStream() => new TwitchClipStream(_httpClient, _configurationService, _settingsService);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,9 +153,9 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.0.240109" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="SimpleToolkit.UI.WinUI.Converters" Version="1.7.2" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageReference Include="SimpleToolkit.UI.WinUI.Converters" Version="1.7.4" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -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:(?<duration>\\d+.\\d+),\\n(?<file>.+)",
|
||||
"file_name": "raw.ts"
|
||||
},
|
||||
"clip": {
|
||||
"file_name": "raw.mp4"
|
||||
}
|
||||
},
|
||||
"authentication": {
|
||||
|
||||
Reference in New Issue
Block a user