Files
VDownload/VDownload.Core/Services/Sources/Twitch/Clip.cs

176 lines
7.3 KiB
C#
Raw Normal View History

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using VDownload.Core.Enums;
using VDownload.Core.Exceptions;
using VDownload.Core.Interfaces;
2022-03-07 14:59:11 +01:00
using VDownload.Core.Services.Sources.Twitch.Helpers;
using VDownload.Core.Structs;
using Windows.Storage;
namespace VDownload.Core.Services.Sources.Twitch
{
2022-05-05 15:06:10 +02:00
[Serializable]
public class Clip : IVideo
{
#region CONSTRUCTORS
public Clip(string id)
{
ID = id;
2022-05-05 15:06:10 +02:00
Source = VideoSource.TwitchClip;
}
#endregion
#region PROPERTIES
2022-05-05 15:06:10 +02:00
public VideoSource Source { get; private set; }
public string ID { get; private set; }
2022-05-05 15:06:10 +02:00
public Uri Url { get; private set; }
2022-03-07 14:59:11 +01:00
public Metadata Metadata { get; private set; }
public BaseStream[] BaseStreams { get; private set; }
#endregion
#region STANDARD METHODS
// GET CLIP METADATA
2022-02-26 14:32:34 +01:00
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
{
2022-03-02 22:13:28 +01:00
cancellationToken.ThrowIfCancellationRequested();
// Get response
2022-03-07 14:59:11 +01:00
JToken response = null;
using (WebClient client = await Client.Helix())
{
client.QueryString.Add("id", ID);
response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/clips")).GetValue("data");
if (((JArray)response).Count > 0) response = response[0];
else throw new MediaNotFoundException($"Twitch Clip (ID: {ID}) was not found");
2022-03-07 14:59:11 +01:00
}
2022-02-26 14:32:34 +01:00
// Create unified video url
2022-05-05 15:06:10 +02:00
Url = new Uri($"https://clips.twitch.tv/{ID}");
2022-02-26 14:32:34 +01:00
2022-03-07 14:59:11 +01:00
// Set metadata
Metadata = new Metadata()
{
Title = (string)response["title"],
Author = (string)response["broadcaster_name"],
Date = Convert.ToDateTime(response["created_at"]),
Duration = TimeSpan.FromSeconds((double)response["duration"]),
Views = (long)response["view_count"],
Thumbnail = new Uri((string)response["thumbnail_url"]),
};
}
2022-02-26 14:32:34 +01:00
public async Task GetStreamsAsync(CancellationToken cancellationToken = default)
{
2022-03-02 22:13:28 +01:00
cancellationToken.ThrowIfCancellationRequested();
2022-03-07 14:59:11 +01:00
// Get response
JToken[] response;
using (WebClient client = Client.GQL())
{
response = JArray.Parse(await client.UploadStringTaskAsync("https://gql.twitch.tv/gql", "[{\"operationName\":\"VideoAccessToken_Clip\",\"variables\":{\"slug\":\"" + ID + "\"},\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11\"}}}]"))[0]["data"]["clip"]["videoQualities"].ToArray();
}
// Init streams list
2022-03-07 14:59:11 +01:00
List<BaseStream> streams = new List<BaseStream>();
// Parse response
foreach (JToken streamData in response)
{
// Create stream
2022-03-07 14:59:11 +01:00
BaseStream stream = new BaseStream()
{
2022-03-07 14:59:11 +01:00
Url = new Uri((string)streamData["sourceURL"]),
Height = int.Parse((string)streamData["quality"]),
FrameRate = (int)streamData["frameRate"],
};
// Add stream
streams.Add(stream);
}
2022-03-07 14:59:11 +01:00
// Set streams
2022-03-02 22:13:28 +01:00
BaseStreams = streams.ToArray();
}
2022-03-07 14:59:11 +01:00
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default)
{
// Invoke DownloadingStarted event
2022-03-07 14:59:11 +01:00
cancellationToken.ThrowIfCancellationRequested();
DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0));
// Get video GQL access token
2022-03-07 14:59:11 +01:00
JToken videoAccessToken = null;
using (WebClient client = Client.GQL())
{
videoAccessToken = JArray.Parse(await client.UploadStringTaskAsync("https://gql.twitch.tv/gql", "[{\"operationName\":\"VideoAccessToken_Clip\",\"variables\":{\"slug\":\"" + ID + "\"},\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11\"}}}]"))[0]["data"]["clip"]["playbackAccessToken"];
}
2022-03-02 22:13:28 +01:00
// Download
2022-03-02 22:13:28 +01:00
cancellationToken.ThrowIfCancellationRequested();
StorageFile rawFile = await downloadingFolder.CreateFileAsync("raw.mp4");
2022-03-07 14:59:11 +01:00
using (WebClient client = new WebClient())
{
2022-03-07 14:59:11 +01:00
client.DownloadProgressChanged += (s, a) => { DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(a.ProgressPercentage)); };
client.QueryString.Add("sig", (string)videoAccessToken["signature"]);
client.QueryString.Add("token", HttpUtility.UrlEncode((string)videoAccessToken["value"]));
2022-03-02 22:13:28 +01:00
cancellationToken.ThrowIfCancellationRequested();
using (cancellationToken.Register(client.CancelAsync))
{
2022-03-02 22:13:28 +01:00
await client.DownloadFileTaskAsync(baseStream.Url, rawFile.Path);
}
}
2022-03-07 14:59:11 +01:00
DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(100, true));
// Processing
StorageFile outputFile = rawFile;
2022-03-07 14:59:11 +01:00
if (extension != MediaFileExtension.MP4 || mediaType != MediaType.AudioVideo || trimStart != null || trimEnd != null)
{
2022-03-02 22:13:28 +01:00
cancellationToken.ThrowIfCancellationRequested();
outputFile = await downloadingFolder.CreateFileAsync($"transcoded.{extension.ToString().ToLower()}");
2022-03-07 14:59:11 +01:00
MediaProcessor mediaProcessor = new MediaProcessor();
mediaProcessor.ProgressChanged += ProcessingProgressChanged;
Task mediaProcessorTask;
if (trimStart == TimeSpan.Zero && trimEnd == Metadata.Duration) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, cancellationToken: cancellationToken);
else if (trimStart == TimeSpan.Zero) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimStart: trimStart, cancellationToken: cancellationToken);
else if (trimEnd == Metadata.Duration) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimEnd: trimEnd, cancellationToken: cancellationToken);
else mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimStart, trimEnd, cancellationToken);
await mediaProcessorTask;
}
// Return output file
return outputFile;
}
#endregion
#region EVENT HANDLERS
2022-03-07 14:59:11 +01:00
public event EventHandler<EventArgs.ProgressChangedEventArgs> DownloadingProgressChanged;
public event EventHandler<EventArgs.ProgressChangedEventArgs> ProcessingProgressChanged;
#endregion
}
}