1.0-dev10 (Code cleaning)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
namespace VDownload.Core.Enums
|
||||
{
|
||||
public enum VideoStatus
|
||||
public enum TaskStatus
|
||||
{
|
||||
Idle,
|
||||
Waiting,
|
||||
8
VDownload.Core/EventArgs/PlaylistSearchEventArgs.cs
Normal file
8
VDownload.Core/EventArgs/PlaylistSearchEventArgs.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class PlaylistSearchEventArgs : System.EventArgs
|
||||
{
|
||||
public string Phrase { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Objects;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace VDownload.Core.EventArgsObjects
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class VideoAddEventArgs : EventArgs
|
||||
public class VideoAddEventArgs : System.EventArgs
|
||||
{
|
||||
public IVideoService VideoService { get; set; }
|
||||
public MediaType MediaType { get; set; }
|
||||
public Stream Stream { get; set; }
|
||||
public IBaseStream Stream { get; set; }
|
||||
public TimeSpan TrimStart { get; set; }
|
||||
public TimeSpan TrimEnd { get; set; }
|
||||
public string Filename { get; set; }
|
||||
7
VDownload.Core/EventArgs/VideoSearchEventArgs.cs
Normal file
7
VDownload.Core/EventArgs/VideoSearchEventArgs.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class VideoSearchEventArgs : System.EventArgs
|
||||
{
|
||||
public string Phrase { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace VDownload.Core.EventArgsObjects
|
||||
{
|
||||
public class PlaylistSearchEventArgs : EventArgs
|
||||
{
|
||||
public string Phrase { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace VDownload.Core.EventArgsObjects
|
||||
{
|
||||
public class VideoSearchEventArgs : EventArgs
|
||||
{
|
||||
public string Phrase { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using VDownload.Core.Enums;
|
||||
|
||||
namespace VDownload.Core.Interfaces
|
||||
{
|
||||
public interface IAStream
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
Uri Url { get; }
|
||||
bool IsChunked { get; }
|
||||
StreamType StreamType { get; }
|
||||
int AudioBitrate { get; }
|
||||
string AudioCodec { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
|
||||
namespace VDownload.Core.Interfaces
|
||||
{
|
||||
public interface IVStream
|
||||
public interface IBaseStream
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
Uri Url { get; }
|
||||
bool IsChunked { get; }
|
||||
StreamType StreamType { get; }
|
||||
int Width { get; }
|
||||
int Height { get; }
|
||||
int FrameRate { get; }
|
||||
string VideoCodec { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace VDownload.Core.Interfaces
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
// PLAYLIST PROPERTIES
|
||||
string ID { get; }
|
||||
string Name { get; }
|
||||
|
||||
@@ -16,8 +17,10 @@ namespace VDownload.Core.Interfaces
|
||||
|
||||
#region METHODS
|
||||
|
||||
// GET PLAYLIST METADATA
|
||||
Task GetMetadataAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
// GET VIDEOS FROM PLAYLIST
|
||||
Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Objects;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace VDownload.Core.Interfaces
|
||||
@@ -21,7 +20,7 @@ namespace VDownload.Core.Interfaces
|
||||
TimeSpan Duration { get; }
|
||||
long Views { get; }
|
||||
Uri Thumbnail { get; }
|
||||
Stream[] Streams { get; }
|
||||
IBaseStream[] BaseStreams { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -36,14 +35,7 @@ namespace VDownload.Core.Interfaces
|
||||
Task GetStreamsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
// DOWNLOAD VIDEO
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IBaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using VDownload.Core.Interfaces;
|
||||
|
||||
namespace VDownload.Core.Objects
|
||||
{
|
||||
public class Stream : IVStream, IAStream
|
||||
public class Stream : IBaseStream
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using VDownload.Core.Enums;
|
||||
using Windows.Media.Editing;
|
||||
using Windows.Storage;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace VDownload.Core.Services
|
||||
{
|
||||
public class MediaProcessor
|
||||
{
|
||||
#region CONSTRUCTOR
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public MediaProcessor(StorageFile outputFile, TimeSpan trimStart, TimeSpan trimEnd)
|
||||
{
|
||||
@@ -40,10 +40,11 @@ namespace VDownload.Core.Services
|
||||
|
||||
#region STANDARD METHODS
|
||||
|
||||
// SINGLE AUDIO & VIDEO FILE PROCESSING
|
||||
public async Task Run(StorageFile audioVideoInputFile, MediaFileExtension extension, MediaType mediaType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Invoke ProcessingStarted event
|
||||
ProcessingStarted?.Invoke(this, EventArgs.Empty);
|
||||
ProcessingStarted?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
// Init transcoder
|
||||
MediaTranscoder mediaTranscoder = new MediaTranscoder
|
||||
@@ -55,31 +56,31 @@ namespace VDownload.Core.Services
|
||||
};
|
||||
|
||||
// Start transcoding operation
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (IRandomAccessStream outputFileOpened = await OutputFile.OpenAsync(FileAccessMode.ReadWrite))
|
||||
{
|
||||
PrepareTranscodeResult transcodingPreparated = await mediaTranscoder.PrepareStreamTranscodeAsync(await audioVideoInputFile.OpenAsync(FileAccessMode.Read), outputFileOpened, await GetMediaEncodingProfile(audioVideoInputFile, extension, mediaType));
|
||||
IAsyncActionWithProgress<double> transcodingTask = transcodingPreparated.TranscodeAsync();
|
||||
try
|
||||
{
|
||||
await transcodingTask.AsTask(cancellationToken, new Progress<double>((percent) => { ProcessingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(percent), null)); }));
|
||||
await outputFileOpened.FlushAsync();
|
||||
}
|
||||
catch (TaskCanceledException) { }
|
||||
transcodingTask.Close();
|
||||
}
|
||||
|
||||
// Invoke ProcessingCompleted event
|
||||
ProcessingCompleted?.Invoke(this, EventArgs.Empty);
|
||||
ProcessingCompleted?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
// SEPARATE AUDIO & VIDEO FILES PROCESSING
|
||||
public async Task Run(StorageFile audioFile, StorageFile videoFile, VideoFileExtension extension, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Invoke ProcessingStarted event
|
||||
ProcessingStarted?.Invoke(this, EventArgs.Empty);
|
||||
ProcessingStarted?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
// Init editor
|
||||
MediaComposition mediaEditor = new MediaComposition();
|
||||
|
||||
// Add media files
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Task<MediaClip> getVideoFileTask = MediaClip.CreateFromFileAsync(videoFile).AsTask();
|
||||
Task<BackgroundAudioTrack> getAudioFileTask = BackgroundAudioTrack.CreateFromFileAsync(audioFile).AsTask();
|
||||
await Task.WhenAll(getVideoFileTask, getAudioFileTask);
|
||||
@@ -97,11 +98,14 @@ namespace VDownload.Core.Services
|
||||
// Start rendering operation
|
||||
var renderOperation = mediaEditor.RenderToFileAsync(OutputFile, (MediaTrimmingPreference)Config.GetValue("media_editing_algorithm"), await GetMediaEncodingProfile(videoFile, audioFile, (MediaFileExtension)extension, MediaType.AudioVideo));
|
||||
renderOperation.Progress += (info, progress) => { ProcessingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(progress), null)); };
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await renderOperation.AsTask(cancellationToken);
|
||||
|
||||
// Invoke ProcessingCompleted event
|
||||
ProcessingCompleted?.Invoke(this, EventArgs.Empty);
|
||||
ProcessingCompleted?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
// SINGLE AUDIO OR VIDEO FILES PROCESSING
|
||||
public async Task Run(StorageFile audioFile, AudioFileExtension extension, CancellationToken cancellationToken = default) { await Run(audioFile, (MediaFileExtension)extension, MediaType.OnlyAudio, cancellationToken); }
|
||||
public async Task Run(StorageFile videoFile, VideoFileExtension extension, CancellationToken cancellationToken = default) { await Run(videoFile, (MediaFileExtension)extension, MediaType.OnlyVideo, cancellationToken); }
|
||||
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.RegularExpressions;
|
||||
using VDownload.Core.Enums;
|
||||
|
||||
namespace VDownload.Core.Services
|
||||
@@ -12,6 +7,7 @@ namespace VDownload.Core.Services
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
// VIDEO SOURCES REGULAR EXPRESSIONS
|
||||
private static readonly (Regex Regex, VideoSource Type)[] VideoSources = new (Regex Regex, VideoSource Type)[]
|
||||
{
|
||||
(new Regex(@"^https://www.twitch.tv/videos/(?<id>\d+)"), VideoSource.TwitchVod),
|
||||
@@ -19,6 +15,7 @@ namespace VDownload.Core.Services
|
||||
(new Regex(@"^https://clips.twitch.tv/(?<id>[^?]+)"), VideoSource.TwitchClip),
|
||||
};
|
||||
|
||||
// PLAYLIST SOURCES REGULAR EXPRESSIONS
|
||||
private static readonly (Regex Regex, PlaylistSource Type)[] PlaylistSources = new (Regex Regex, PlaylistSource Type)[]
|
||||
{
|
||||
(new Regex(@"^https://www.twitch.tv/(?<id>[^?]+)"), PlaylistSource.TwitchChannel),
|
||||
@@ -30,6 +27,7 @@ namespace VDownload.Core.Services
|
||||
|
||||
#region METHODS
|
||||
|
||||
// GET VIDEO SOURCE
|
||||
public static (VideoSource Type, string ID) GetVideoSource(string url)
|
||||
{
|
||||
foreach ((Regex Regex, VideoSource Type) Source in VideoSources)
|
||||
@@ -40,6 +38,7 @@ namespace VDownload.Core.Services
|
||||
return (VideoSource.Null, null);
|
||||
}
|
||||
|
||||
// GET PLAYLIST SOURCE
|
||||
public static (PlaylistSource Type, string ID) GetPlaylistSource(string url)
|
||||
{
|
||||
foreach ((Regex Regex, PlaylistSource Type) Source in PlaylistSources)
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
StorageFile authDataFile = await authDataFolder.CreateFileAsync("Twitch.auth", CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
// Save data
|
||||
FileIO.WriteTextAsync(authDataFile, accessToken);
|
||||
await FileIO.WriteTextAsync(authDataFile, accessToken);
|
||||
}
|
||||
|
||||
// DELETE ACCESS TOKEN
|
||||
|
||||
@@ -40,14 +40,13 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
// GET CHANNEL METADATA
|
||||
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Set cancellation token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string accessToken = await Auth.ReadAccessTokenAsync();
|
||||
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
|
||||
|
||||
// Check access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var twitchAccessTokenValidation = await Auth.ValidateAccessTokenAsync(accessToken);
|
||||
if (!twitchAccessTokenValidation.IsValid) throw new TwitchAccessTokenNotValidException();
|
||||
|
||||
@@ -58,6 +57,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
// Get response
|
||||
client.QueryString.Add("login", ID);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
JToken response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/users"))["data"][0];
|
||||
|
||||
// Set parameters
|
||||
@@ -68,10 +68,8 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
// GET CHANNEL VIDEOS
|
||||
public async Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Set cancellation token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string accessToken = await Auth.ReadAccessTokenAsync();
|
||||
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
|
||||
|
||||
@@ -88,6 +86,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
do
|
||||
{
|
||||
// Check access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var twitchAccessTokenValidation = await Auth.ValidateAccessTokenAsync(accessToken);
|
||||
if (!twitchAccessTokenValidation.IsValid) throw new TwitchAccessTokenNotValidException();
|
||||
|
||||
@@ -104,6 +103,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
client.QueryString.Add("first", count.ToString());
|
||||
client.QueryString.Add("after", pagination);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
JToken response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/videos"));
|
||||
|
||||
pagination = (string)response["pagination"]["cursor"];
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
public TimeSpan Duration { get; private set; }
|
||||
public long Views { get; private set; }
|
||||
public Uri Thumbnail { get; private set; }
|
||||
public Stream[] Streams { get; private set; }
|
||||
public IBaseStream[] BaseStreams { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -59,14 +59,13 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
// GET CLIP METADATA
|
||||
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Set cancellation token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string accessToken = await Auth.ReadAccessTokenAsync();
|
||||
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
|
||||
|
||||
// Check access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var twitchAccessTokenValidation = await Auth.ValidateAccessTokenAsync(accessToken);
|
||||
if (!twitchAccessTokenValidation.IsValid) throw new TwitchAccessTokenNotValidException();
|
||||
|
||||
@@ -93,14 +92,12 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
public async Task GetStreamsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Set cancellation token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create client
|
||||
WebClient client = new WebClient { Encoding = Encoding.UTF8 };
|
||||
client.Headers.Add("Client-ID", Auth.GQLApiClientID);
|
||||
|
||||
// Get video streams
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
JToken[] 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
|
||||
@@ -126,60 +123,55 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
}
|
||||
|
||||
// Set Streams parameter
|
||||
Streams = streams.ToArray();
|
||||
BaseStreams = streams.ToArray();
|
||||
}
|
||||
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default)
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IBaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Set cancellation token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Invoke DownloadingStarted event
|
||||
DownloadingStarted?.Invoke(this, EventArgs.Empty);
|
||||
DownloadingStarted?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
// Create client
|
||||
WebClient client = new WebClient();
|
||||
client.Headers.Add("Client-Id", Auth.GQLApiClientID);
|
||||
|
||||
// Get video GQL access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
JToken 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"];
|
||||
|
||||
// Download
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
StorageFile rawFile = await downloadingFolder.CreateFileAsync("raw.mp4");
|
||||
using (client = new WebClient())
|
||||
{
|
||||
client.DownloadProgressChanged += (s, a) => { DownloadingProgressChanged(this, new ProgressChangedEventArgs(a.ProgressPercentage, null)); };
|
||||
client.QueryString.Add("sig", (string)videoAccessToken["signature"]);
|
||||
client.QueryString.Add("token", HttpUtility.UrlEncode((string)videoAccessToken["value"]));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (cancellationToken.Register(client.CancelAsync))
|
||||
{
|
||||
await client.DownloadFileTaskAsync(audioVideoStream.Url, rawFile.Path);
|
||||
await client.DownloadFileTaskAsync(baseStream.Url, rawFile.Path);
|
||||
}
|
||||
}
|
||||
DownloadingCompleted?.Invoke(this, EventArgs.Empty);
|
||||
DownloadingCompleted?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
// Processing
|
||||
StorageFile outputFile = rawFile;
|
||||
if (extension != MediaFileExtension.MP4 || mediaType != MediaType.AudioVideo || trimStart > new TimeSpan(0) || trimEnd < Duration)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
outputFile = await downloadingFolder.CreateFileAsync($"transcoded.{extension.ToString().ToLower()}");
|
||||
MediaProcessor mediaProcessor = new MediaProcessor(outputFile, trimStart, trimEnd);
|
||||
mediaProcessor.ProcessingStarted += ProcessingStarted;
|
||||
mediaProcessor.ProcessingProgressChanged += ProcessingProgressChanged;
|
||||
mediaProcessor.ProcessingCompleted += ProcessingCompleted;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await mediaProcessor.Run(rawFile, extension, mediaType, cancellationToken);
|
||||
}
|
||||
|
||||
// Return output file
|
||||
return outputFile;
|
||||
}
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioVideoStream, extension, mediaType, new TimeSpan(0), Duration, cancellationToken); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) { throw new NotImplementedException("Twitch Clip download service doesn't support separate video and audio streams"); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioStream, videoStream, extension, new TimeSpan(0), Duration, cancellationToken); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) { throw new NotImplementedException("Twitch Clip download service doesn't support separate video and audio streams"); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioStream, extension, new TimeSpan(0), Duration, cancellationToken); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) { throw new NotImplementedException("Twitch Clip download service doesn't support separate video and audio streams"); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, videoStream, extension, new TimeSpan(0), Duration, cancellationToken); }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -51,7 +50,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
public TimeSpan Duration { get; private set; }
|
||||
public long Views { get; private set; }
|
||||
public Uri Thumbnail { get; private set; }
|
||||
public Stream[] Streams { get; private set; }
|
||||
public IBaseStream[] BaseStreams { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -62,14 +61,13 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
// GET VOD METADATA
|
||||
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Set cancellation token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string accessToken = await Auth.ReadAccessTokenAsync();
|
||||
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
|
||||
|
||||
// Check access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var twitchAccessTokenValidation = await Auth.ValidateAccessTokenAsync(accessToken);
|
||||
if (!twitchAccessTokenValidation.IsValid) throw new TwitchAccessTokenNotValidException();
|
||||
|
||||
@@ -80,6 +78,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
// Get response
|
||||
client.QueryString.Add("id", ID);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
JToken response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/videos")).GetValue("data")[0];
|
||||
|
||||
// Set parameters
|
||||
@@ -102,17 +101,16 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
// GET VOD STREAMS
|
||||
public async Task GetStreamsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Set cancellation token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create client
|
||||
WebClient client = new WebClient();
|
||||
client.Headers.Add("Client-Id", Auth.GQLApiClientID);
|
||||
|
||||
// Get video GQL access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
JToken videoAccessToken = JObject.Parse(await client.UploadStringTaskAsync("https://gql.twitch.tv/gql", "{\"operationName\":\"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 }}\",\"variables\":{\"isLive\":false,\"login\":\"\",\"isVod\":true,\"vodID\":\"" + ID + "\",\"playerType\":\"embed\"}}"))["data"]["videoPlaybackAccessToken"];
|
||||
|
||||
// Get video streams
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string[] response = (await client.DownloadStringTaskAsync($"http://usher.twitch.tv/vod/{ID}?nauth={videoAccessToken["value"]}&nauthsig={videoAccessToken["signature"]}&allow_source=true&player=twitchweb")).Split("\n");
|
||||
|
||||
// Init streams list
|
||||
@@ -147,74 +145,64 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
}
|
||||
|
||||
// Set Streams parameter
|
||||
Streams = streams.ToArray();
|
||||
BaseStreams = streams.ToArray();
|
||||
}
|
||||
|
||||
// DOWNLOAD AND TRANSCODE VOD
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default)
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IBaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Invoke DownloadingStarted event
|
||||
if (!cancellationToken.IsCancellationRequested) DownloadingStarted?.Invoke(this, EventArgs.Empty);
|
||||
DownloadingStarted?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
// Get video chunks
|
||||
List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList = null;
|
||||
if (!cancellationToken.IsCancellationRequested) chunksList = await ExtractChunksFromM3U8Async(audioVideoStream.Url);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList = await ExtractChunksFromM3U8Async(baseStream.Url, cancellationToken);
|
||||
|
||||
// Passive trim
|
||||
if ((bool)Config.GetValue("twitch_vod_passive_trim")) (trimStart, trimEnd) = PassiveVideoTrim(chunksList, trimStart, trimEnd, Duration);
|
||||
|
||||
// Download
|
||||
StorageFile rawFile = null;
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
rawFile = await downloadingFolder.CreateFileAsync("raw.ts");
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
StorageFile rawFile = await downloadingFolder.CreateFileAsync("raw.ts");
|
||||
|
||||
float chunksDownloaded = 0;
|
||||
|
||||
Task<byte[]> downloadTask;
|
||||
Task writeTask;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
downloadTask = DownloadChunkAsync(chunksList[0].ChunkUrl);
|
||||
await downloadTask;
|
||||
for (int i = 1; i < chunksList.Count && !cancellationToken.IsCancellationRequested; i++)
|
||||
for (int i = 1; i < chunksList.Count; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
writeTask = WriteChunkToFileAsync(rawFile, downloadTask.Result);
|
||||
downloadTask = DownloadChunkAsync(chunksList[i].ChunkUrl);
|
||||
await Task.WhenAll(writeTask, downloadTask);
|
||||
DownloadingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(++chunksDownloaded * 100 / chunksList.Count), null));
|
||||
}
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await WriteChunkToFileAsync(rawFile, downloadTask.Result);
|
||||
DownloadingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(++chunksDownloaded * 100 / chunksList.Count), null));
|
||||
|
||||
DownloadingCompleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
DownloadingCompleted?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
|
||||
// Processing
|
||||
StorageFile outputFile = null;
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
outputFile = await downloadingFolder.CreateFileAsync($"transcoded.{extension.ToString().ToLower()}");
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
StorageFile outputFile = await downloadingFolder.CreateFileAsync($"transcoded.{extension.ToString().ToLower()}");
|
||||
|
||||
MediaProcessor mediaProcessor = new MediaProcessor(outputFile, trimStart, trimEnd);
|
||||
mediaProcessor.ProcessingStarted += ProcessingStarted;
|
||||
mediaProcessor.ProcessingProgressChanged += ProcessingProgressChanged;
|
||||
mediaProcessor.ProcessingCompleted += ProcessingCompleted;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await mediaProcessor.Run(rawFile, extension, mediaType, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
// Return output file
|
||||
return outputFile;
|
||||
}
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioVideoStream, extension, mediaType, new TimeSpan(0), Duration, cancellationToken); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) { throw new NotImplementedException("Twitch VOD download service doesn't support separate video and audio streams"); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioStream, videoStream, extension, new TimeSpan(0), Duration, cancellationToken); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) { throw new NotImplementedException("Twitch VOD download service doesn't support separate video and audio streams"); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioStream, extension, new TimeSpan(0), Duration, cancellationToken); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) { throw new NotImplementedException("Twitch VOD download service doesn't support separate video and audio streams"); }
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, videoStream, extension, new TimeSpan(0), Duration, cancellationToken); }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -223,14 +211,16 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
#region LOCAL METHODS
|
||||
|
||||
// GET CHUNKS DATA FROM M3U8 PLAYLIST
|
||||
private static async Task<List<(Uri ChunkUrl, TimeSpan ChunkDuration)>> ExtractChunksFromM3U8Async(Uri streamUrl)
|
||||
private static async Task<List<(Uri ChunkUrl, TimeSpan ChunkDuration)>> ExtractChunksFromM3U8Async(Uri streamUrl, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Create client
|
||||
WebClient client = new WebClient();
|
||||
client.Headers.Add("Client-Id", Auth.GQLApiClientID);
|
||||
|
||||
// Get playlist
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string response = await client.DownloadStringTaskAsync(streamUrl);
|
||||
|
||||
// Create dictionary
|
||||
List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunks = new List<(Uri ChunkUrl, TimeSpan ChunkDuration)>();
|
||||
|
||||
@@ -273,11 +263,12 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
}
|
||||
|
||||
// DOWNLOAD CHUNK
|
||||
private static async Task<byte[]> DownloadChunkAsync(Uri chunkUrl)
|
||||
private static async Task<byte[]> DownloadChunkAsync(Uri chunkUrl, CancellationToken cancellationToken = default)
|
||||
{
|
||||
int retriesCount = 0;
|
||||
while ((bool)Config.GetValue("twitch_vod_downloading_chunk_retry_after_error") && retriesCount < (int)Config.GetValue("twitch_vod_downloading_chunk_max_retries"))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
try
|
||||
{
|
||||
using (WebClient client = new WebClient())
|
||||
|
||||
@@ -1,38 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VDownload.Core.Services
|
||||
{
|
||||
public class TaskId
|
||||
{
|
||||
// VARIABLES
|
||||
#region CONSTANTS
|
||||
|
||||
// RANDOM
|
||||
private static readonly Random Random = new Random();
|
||||
private static readonly char[] CharsID = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
|
||||
private static readonly int LengthID = 10;
|
||||
|
||||
// ID SETTINGS
|
||||
private static readonly char[] IDChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
|
||||
private static readonly int IDLength = 10;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// USED IDS LIST
|
||||
private static readonly List<string> UsedIDs = new List<string>();
|
||||
|
||||
// METHOD
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region METHODS
|
||||
|
||||
// GET TASK ID
|
||||
public static string Get()
|
||||
{
|
||||
string id;
|
||||
do
|
||||
{
|
||||
id = "";
|
||||
while (id.Length < LengthID)
|
||||
while (id.Length < IDLength)
|
||||
{
|
||||
id += CharsID[Random.Next(0, CharsID.Length)];
|
||||
id += IDChars[Random.Next(0, IDChars.Length)];
|
||||
}
|
||||
} while (UsedIDs.Contains(id));
|
||||
UsedIDs.Add(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
// DISPOSE TASK ID
|
||||
public static void Dispose(string id)
|
||||
{
|
||||
UsedIDs.Remove(id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,16 +127,15 @@
|
||||
<Compile Include="Enums\StreamType.cs" />
|
||||
<Compile Include="Enums\VideoFileExtension.cs" />
|
||||
<Compile Include="Enums\VideoSource.cs" />
|
||||
<Compile Include="Enums\VideoStatus.cs" />
|
||||
<Compile Include="EventArgsObjects\VideoAddEventArgs.cs" />
|
||||
<Compile Include="EventArgsObjects\VideoSearchEventArgs.cs" />
|
||||
<Compile Include="EventArgsObjects\PlaylistSearchEventArgs.cs" />
|
||||
<Compile Include="Enums\TaskStatus.cs" />
|
||||
<Compile Include="EventArgs\VideoAddEventArgs.cs" />
|
||||
<Compile Include="EventArgs\VideoSearchEventArgs.cs" />
|
||||
<Compile Include="EventArgs\PlaylistSearchEventArgs.cs" />
|
||||
<Compile Include="Exceptions\TwitchAccessTokenNotFoundException.cs" />
|
||||
<Compile Include="Exceptions\TwitchAccessTokenNotValidException.cs" />
|
||||
<Compile Include="Interfaces\IAStream.cs" />
|
||||
<Compile Include="Interfaces\IBaseStream.cs" />
|
||||
<Compile Include="Interfaces\IPlaylistService.cs" />
|
||||
<Compile Include="Interfaces\IVideoService.cs" />
|
||||
<Compile Include="Interfaces\IVStream.cs" />
|
||||
<Compile Include="Objects\Stream.cs" />
|
||||
<Compile Include="Services\Config.cs" />
|
||||
<Compile Include="Services\MediaProcessor.cs" />
|
||||
|
||||
@@ -11,18 +11,27 @@ using Windows.Storage;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using System.Diagnostics;
|
||||
using Windows.Storage.AccessCache;
|
||||
|
||||
namespace VDownload
|
||||
{
|
||||
sealed partial class App : Application
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
Suspending += OnSuspending;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// ON LAUNCHED
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs e)
|
||||
{
|
||||
// Rebuild configuration file
|
||||
@@ -31,7 +40,12 @@ namespace VDownload
|
||||
// Delete temp on start
|
||||
if ((bool)Config.GetValue("delete_temp_on_start"))
|
||||
{
|
||||
IReadOnlyList<IStorageItem> tempItems = await ApplicationData.Current.TemporaryFolder.GetItemsAsync();
|
||||
IReadOnlyList<IStorageItem> tempItems;
|
||||
if ((bool)Config.GetValue("custom_temp_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_temp_location"))
|
||||
tempItems = await (await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_temp_location")).GetItemsAsync();
|
||||
else
|
||||
tempItems = await ApplicationData.Current.TemporaryFolder.GetItemsAsync();
|
||||
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (IStorageItem item in tempItems) tasks.Add(item.DeleteAsync().AsTask());
|
||||
await Task.WhenAll(tasks);
|
||||
@@ -64,16 +78,20 @@ namespace VDownload
|
||||
}
|
||||
}
|
||||
|
||||
// ON NAVIGATION FAILED
|
||||
private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
}
|
||||
|
||||
// ON SUSPENDING
|
||||
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
//TODO: Save application state and stop any background activity
|
||||
deferral.Complete();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 154 KiB |
BIN
VDownload/Assets/Sources/Twitch.png
Normal file
BIN
VDownload/Assets/Sources/Twitch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 267 KiB |
5
VDownload/Resources/Images.xaml
Normal file
5
VDownload/Resources/Images.xaml
Normal file
@@ -0,0 +1,5 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<BitmapImage x:Name="UnknownThumbnailImage" UriSource="ms-appx:///Assets/Images/UnknownThumbnail.png"/>
|
||||
</ResourceDictionary>
|
||||
@@ -196,6 +196,27 @@ The number in the numberbox indicades how many videos will be got from playlist.
|
||||
<data name="HomeOptionsBarVideoSearchingTwitchAccessTokenNotValidErrorDialogDescription" xml:space="preserve">
|
||||
<value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextCancelled" xml:space="preserve">
|
||||
<value>Cancelled</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextDone" xml:space="preserve">
|
||||
<value>Done</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextDownloading" xml:space="preserve">
|
||||
<value>Downloading</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextFinalizing" xml:space="preserve">
|
||||
<value>Finalizing</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextIdle" xml:space="preserve">
|
||||
<value>Idle</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextProcessing" xml:space="preserve">
|
||||
<value>Processing</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextWaiting" xml:space="preserve">
|
||||
<value>Queued</value>
|
||||
</data>
|
||||
<data name="HomeVideoAddingDownloadingOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Downloading options</value>
|
||||
</data>
|
||||
@@ -220,27 +241,6 @@ The number in the numberbox indicades how many videos will be got from playlist.
|
||||
<data name="HomeVideoAddingTrimSettingControl.Title" xml:space="preserve">
|
||||
<value>Trim</value>
|
||||
</data>
|
||||
<data name="HomeVideoPanelStateTextCancelled" xml:space="preserve">
|
||||
<value>Cancelled</value>
|
||||
</data>
|
||||
<data name="HomeVideoPanelStateTextDone" xml:space="preserve">
|
||||
<value>Done</value>
|
||||
</data>
|
||||
<data name="HomeVideoPanelStateTextDownloading" xml:space="preserve">
|
||||
<value>Downloading</value>
|
||||
</data>
|
||||
<data name="HomeVideoPanelStateTextFinalizing" xml:space="preserve">
|
||||
<value>Finalizing</value>
|
||||
</data>
|
||||
<data name="HomeVideoPanelStateTextIdle" xml:space="preserve">
|
||||
<value>Idle</value>
|
||||
</data>
|
||||
<data name="HomeVideoPanelStateTextProcessing" xml:space="preserve">
|
||||
<value>Processing</value>
|
||||
</data>
|
||||
<data name="HomeVideoPanelStateTextWaiting" xml:space="preserve">
|
||||
<value>Queued</value>
|
||||
</data>
|
||||
<data name="MainPageNavigationPanelHomeItem.Content" xml:space="preserve">
|
||||
<value>Home</value>
|
||||
</data>
|
||||
|
||||
@@ -140,10 +140,9 @@
|
||||
<Compile Include="Views\Home\HomeVideoAddingPanel.xaml.cs">
|
||||
<DependentUpon>HomeVideoAddingPanel.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\HomeVideoPanel.xaml.cs">
|
||||
<DependentUpon>HomeVideoPanel.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\HomeTaskPanel.xaml.cs">
|
||||
<DependentUpon>HomeTaskPanel.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\HomeVideosList.cs" />
|
||||
<Compile Include="Views\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -250,7 +249,8 @@
|
||||
<Content Include="Assets\Logo\Wide310x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Logo\Wide310x150Logo.scale-400.png" />
|
||||
<Content Include="Assets\Icons\Twitch.png" />
|
||||
<Content Include="Assets\UnknownThumbnail.png" />
|
||||
<Content Include="Assets\Images\UnknownThumbnail.png" />
|
||||
<Content Include="Assets\Sources\Twitch.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
@@ -269,6 +269,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Resources\Images.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\About\AboutMain.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -293,7 +297,7 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\HomeVideoPanel.xaml">
|
||||
<Page Include="Views\Home\HomeTaskPanel.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
|
||||
@@ -54,13 +54,13 @@
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="HomeVideosListRow" Height="1*"/>
|
||||
<RowDefinition x:Name="HomeTasksListRow" Height="1*"/>
|
||||
<RowDefinition x:Name="HomeOptionBarAndAddingPanelRow" Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- VIDEOS LIST -->
|
||||
<ScrollViewer Margin="0,0,0,10" CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<StackPanel x:Name="HomeVideosList" Spacing="10"/>
|
||||
<StackPanel x:Name="HomeTasksList" Spacing="10"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- OPTIONS BAR AND ADDING PANEL -->
|
||||
|
||||
@@ -6,12 +6,11 @@ using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgsObjects;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
@@ -42,8 +41,8 @@ namespace VDownload.Views.Home
|
||||
private CancellationTokenSource SearchingCancellationToken = new CancellationTokenSource();
|
||||
|
||||
// HOME VIDEOS LIST
|
||||
private static StackPanel HomeVideosListOldPanel = null;
|
||||
public static List<HomeVideoPanel> VideoPanelsList = new List<HomeVideoPanel>();
|
||||
private static StackPanel HomeTasksListParent = null;
|
||||
public static List<HomeTaskPanel> TaskPanelsList = new List<HomeTaskPanel>();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -54,9 +53,9 @@ namespace VDownload.Views.Home
|
||||
// ON NAVIGATED TO
|
||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
if (HomeVideosListOldPanel != null) HomeVideosListOldPanel.Children.Clear();
|
||||
HomeVideosListOldPanel = HomeVideosList;
|
||||
foreach (HomeVideoPanel homeVideoPanel in VideoPanelsList) HomeVideosList.Children.Add(homeVideoPanel);
|
||||
if (HomeTasksListParent != null) HomeTasksListParent.Children.Clear();
|
||||
HomeTasksListParent = HomeTasksList;
|
||||
foreach (HomeTaskPanel homeVideoPanel in TaskPanelsList) HomeTasksList.Children.Add(homeVideoPanel);
|
||||
}
|
||||
|
||||
// ADD VIDEO BUTTON CHECKED
|
||||
@@ -158,7 +157,7 @@ namespace VDownload.Views.Home
|
||||
|
||||
|
||||
HomeOptionBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
HomeVideosListRow.Height = new GridLength(0);
|
||||
HomeTasksListRow.Height = new GridLength(0);
|
||||
HomeVideoAddingPanel addingPanel = new HomeVideoAddingPanel(videoService);
|
||||
addingPanel.VideoAddRequest += HomeVideoAddingPanel_VideoAddRequest;
|
||||
HomeAddingPanel.Content = addingPanel;
|
||||
@@ -170,19 +169,19 @@ namespace VDownload.Views.Home
|
||||
// Uncheck video button
|
||||
HomeOptionsBarAddVideoButton.IsChecked = false;
|
||||
|
||||
// Create video panel/task
|
||||
HomeVideoPanel videoPanel = new HomeVideoPanel(e.VideoService, e.MediaType, e.Stream, e.TrimStart, e.TrimEnd, e.Filename, e.Extension, e.Location);
|
||||
// Create video task
|
||||
HomeTaskPanel taskPanel = new HomeTaskPanel(e.VideoService, e.MediaType, e.Stream, e.TrimStart, e.TrimEnd, e.Filename, e.Extension, e.Location);
|
||||
|
||||
videoPanel.VideoRemovingRequested += (s, a) =>
|
||||
taskPanel.TaskRemovingRequested += (s, a) =>
|
||||
{
|
||||
// Remove video panel/task from videos list
|
||||
VideoPanelsList.Remove(videoPanel);
|
||||
HomeVideosList.Children.Remove(videoPanel);
|
||||
// Remove task from tasks lists
|
||||
TaskPanelsList.Remove(taskPanel);
|
||||
HomeTasksList.Children.Remove(taskPanel);
|
||||
};
|
||||
|
||||
// Add video panel/task to videos list
|
||||
HomeVideosList.Children.Add(videoPanel);
|
||||
VideoPanelsList.Add(videoPanel);
|
||||
// Add task to tasks lists
|
||||
HomeTasksList.Children.Add(taskPanel);
|
||||
TaskPanelsList.Add(taskPanel);
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +286,7 @@ namespace VDownload.Views.Home
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
|
||||
HomeOptionBarAndAddingPanelRow.Height = GridLength.Auto;
|
||||
HomeVideosListRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
HomeTasksListRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
|
||||
HomeAddingPanel.Content = null;
|
||||
HomeOptionsBarAddingControl.Content = null;
|
||||
@@ -298,7 +297,7 @@ namespace VDownload.Views.Home
|
||||
// DOWNLOAD ALL BUTTON CLICKED
|
||||
private async void HomeOptionsBarDownloadAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (HomeVideoPanel videoPanel in HomeVideosList.Children.Where(video => ((HomeVideoPanel)video).VideoStatus == VideoStatus.Idle))
|
||||
foreach (HomeTaskPanel videoPanel in HomeTasksList.Children.Where((object video) => ((HomeTaskPanel)video).TaskStatus == Core.Enums.TaskStatus.Idle))
|
||||
{
|
||||
await Task.Delay(50);
|
||||
videoPanel.Start();
|
||||
@@ -314,7 +313,7 @@ namespace VDownload.Views.Home
|
||||
// WAIT IN QUEUE
|
||||
public static async Task WaitInQueue(CancellationToken token)
|
||||
{
|
||||
while (VideoPanelsList.Where(video => video.VideoStatus == VideoStatus.InProgress).Count() >= (int)Config.GetValue("max_active_video_task") && !token.IsCancellationRequested)
|
||||
while (TaskPanelsList.Where((HomeTaskPanel video) => video.TaskStatus == Core.Enums.TaskStatus.InProgress).Count() >= (int)Config.GetValue("max_active_video_task") && !token.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using VDownload.Core.EventArgsObjects;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Services;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
@@ -24,9 +24,6 @@ namespace VDownload.Views.Home
|
||||
// MAX VIDEOS NUMBERBOX DEFAULT VALUE
|
||||
public int DefaultMaxPlaylistVideos = (int)Config.GetValue("default_max_playlist_videos");
|
||||
|
||||
// SEARCH BUTTON EVENT HANDLER
|
||||
public event EventHandler<PlaylistSearchEventArgs> SearchButtonClicked;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -56,5 +53,13 @@ namespace VDownload.Views.Home
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<PlaylistSearchEventArgs> SearchButtonClicked;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using VDownload.Core.EventArgsObjects;
|
||||
using VDownload.Core.EventArgs;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
@@ -18,16 +18,7 @@ namespace VDownload.Views.Home
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// SEARCH BUTTON EVENT HANDLER
|
||||
public event EventHandler<VideoSearchEventArgs> SearchButtonClicked;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// SEARCH BUTTON CLICKED
|
||||
private void HomeOptionsBarAddVideoControlSearchButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -51,5 +42,13 @@ namespace VDownload.Views.Home
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<VideoSearchEventArgs> SearchButtonClicked;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.HomeVideoPanel"
|
||||
x:Class="VDownload.Views.Home.HomeTaskPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||
d:DesignHeight="150"
|
||||
@@ -52,12 +53,12 @@
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Duration}"/>
|
||||
<Image Grid.Row="2" Grid.Column="0" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource FileIcon}"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind File}"/>
|
||||
<Image x:Name="HomeVideoPanelStateIcon" Grid.Row="3" Grid.Column="0" Width="{StaticResource MetadataIconSize}"/>
|
||||
<TextBlock x:Name="HomeVideoPanelStateText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}"/>
|
||||
<muxc:ProgressBar x:Name="HomeVideoPanelStateProgressBar" Grid.Row="3" Grid.Column="2" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
||||
<Image x:Name="HomeTaskPanelStateIcon" Grid.Row="3" Grid.Column="0" Width="{StaticResource MetadataIconSize}"/>
|
||||
<TextBlock x:Name="HomeTaskPanelStateText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}"/>
|
||||
<muxc:ProgressBar x:Name="HomeTaskPanelStateProgressBar" Grid.Row="3" Grid.Column="2" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
<AppBarButton Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="{x:Bind SourceImage}" Click="HomeVideoPanelSourceButton_Click"/>
|
||||
<AppBarButton x:Name="HomeVideoPanelStartStopButton" Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Download" Click="HomeVideoPanelStartStopButton_Click"/>
|
||||
<AppBarButton Grid.Row="2" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Cancel" Click="HomeVideoPanelRemoveButton_Click"/>
|
||||
<AppBarButton Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="{x:Bind SourceImage}" Click="HomeTaskPanelSourceButton_Click"/>
|
||||
<AppBarButton x:Name="HomeTaskPanelStartStopButton" Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Download" Click="HomeTaskPanelStartStopButton_Click"/>
|
||||
<AppBarButton Grid.Row="2" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Cancel" Click="HomeTaskPanelRemoveButton_Click"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -5,7 +5,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Objects;
|
||||
using VDownload.Core.Services;
|
||||
using Windows.ApplicationModel.ExtendedExecution;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
@@ -16,15 +15,14 @@ using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class HomeVideoPanel : UserControl
|
||||
public sealed partial class HomeTaskPanel : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
ResourceDictionary IconsRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") };
|
||||
ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -32,12 +30,12 @@ namespace VDownload.Views.Home
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeVideoPanel(IVideoService videoService, MediaType mediaType, Stream stream, TimeSpan trimStart, TimeSpan trimEnd, string filename, MediaFileExtension extension, StorageFolder location)
|
||||
public HomeTaskPanel(IVideoService videoService, MediaType mediaType, IBaseStream stream, TimeSpan trimStart, TimeSpan trimEnd, string filename, MediaFileExtension extension, StorageFolder location)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set video status
|
||||
VideoStatus = VideoStatus.Idle;
|
||||
TaskStatus = Core.Enums.TaskStatus.Idle;
|
||||
|
||||
// Set video service object
|
||||
VideoService = videoService;
|
||||
@@ -55,8 +53,8 @@ namespace VDownload.Views.Home
|
||||
Location = location;
|
||||
|
||||
// Set metadata
|
||||
ThumbnailImage = new BitmapImage { UriSource = VideoService.Thumbnail ?? new Uri("ms-appx:///Assets/UnknownThumbnail.png") };
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Icons/{VideoService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
ThumbnailImage = (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{VideoService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Title = VideoService.Title;
|
||||
Author = VideoService.Author;
|
||||
TimeSpan newDuration = TrimEnd.Subtract(TrimStart);
|
||||
@@ -67,9 +65,9 @@ namespace VDownload.Views.Home
|
||||
File += $@"{(Location != null ? Location.Path : $@"{UserDataPaths.GetDefault().Downloads}\VDownload")}\{Filename}.{Extension.ToString().ToLower()}";
|
||||
|
||||
// Set state controls
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateIdleIcon"];
|
||||
HomeVideoPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextIdle");
|
||||
HomeVideoPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateIdleIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextIdle");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -79,9 +77,9 @@ namespace VDownload.Views.Home
|
||||
#region PROPERTIES
|
||||
|
||||
// VIDEO STATUS
|
||||
public VideoStatus VideoStatus { get; set; }
|
||||
public Core.Enums.TaskStatus TaskStatus { get; set; }
|
||||
|
||||
// VIDEO CANECELLATION TOKEN
|
||||
// TASK CANCELLATION TOKEN
|
||||
public CancellationTokenSource CancellationTokenSource { get; set; }
|
||||
|
||||
// VIDEO SERVICE
|
||||
@@ -89,7 +87,7 @@ namespace VDownload.Views.Home
|
||||
|
||||
// VIDEO OPTIONS
|
||||
private MediaType MediaType { get; set; }
|
||||
private Stream Stream { get; set; }
|
||||
private IBaseStream Stream { get; set; }
|
||||
private TimeSpan TrimStart { get; set; }
|
||||
private TimeSpan TrimEnd { get; set; }
|
||||
private string Filename { get; set; }
|
||||
@@ -114,26 +112,26 @@ namespace VDownload.Views.Home
|
||||
public async Task Start()
|
||||
{
|
||||
// Change icon
|
||||
HomeVideoPanelStartStopButton.Icon = new SymbolIcon(Symbol.Stop);
|
||||
HomeTaskPanelStartStopButton.Icon = new SymbolIcon(Symbol.Stop);
|
||||
|
||||
// Create cancellation token
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Set video status
|
||||
VideoStatus = VideoStatus.Waiting;
|
||||
// Set task status
|
||||
TaskStatus = Core.Enums.TaskStatus.Waiting;
|
||||
|
||||
// Set state controls
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateWaitingIcon"];
|
||||
HomeVideoPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextWaiting");
|
||||
HomeVideoPanelStateProgressBar.Visibility = Visibility.Visible;
|
||||
HomeVideoPanelStateProgressBar.IsIndeterminate = true;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateWaitingIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextWaiting");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Visible;
|
||||
HomeTaskPanelStateProgressBar.IsIndeterminate = true;
|
||||
|
||||
// Wait in queue
|
||||
await HomeMain.WaitInQueue(CancellationTokenSource.Token);
|
||||
if (!CancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
// Set video status
|
||||
VideoStatus = VideoStatus.InProgress;
|
||||
// Set task status
|
||||
TaskStatus = Core.Enums.TaskStatus.InProgress;
|
||||
|
||||
// Get task unique ID
|
||||
string uniqueID = TaskId.Get();
|
||||
@@ -148,7 +146,7 @@ namespace VDownload.Views.Home
|
||||
|
||||
try
|
||||
{
|
||||
// Set cancellation token to throw exception on request
|
||||
// Throw exception if cancellation requested
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
// Start stopwatch
|
||||
@@ -157,28 +155,28 @@ namespace VDownload.Views.Home
|
||||
// Set progress event handlers
|
||||
VideoService.DownloadingStarted += (s, a) =>
|
||||
{
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDownloadingIcon"];
|
||||
HomeVideoPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextDownloading")} (0%)";
|
||||
HomeVideoPanelStateProgressBar.IsIndeterminate = false;
|
||||
HomeVideoPanelStateProgressBar.Value = 0;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDownloadingIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextDownloading")} (0%)";
|
||||
HomeTaskPanelStateProgressBar.IsIndeterminate = false;
|
||||
HomeTaskPanelStateProgressBar.Value = 0;
|
||||
};
|
||||
VideoService.DownloadingProgressChanged += (s, a) =>
|
||||
{
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDownloadingIcon"];
|
||||
HomeVideoPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextDownloading")} ({a.ProgressPercentage}%)";
|
||||
HomeVideoPanelStateProgressBar.Value = a.ProgressPercentage;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDownloadingIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextDownloading")} ({a.ProgressPercentage}%)";
|
||||
HomeTaskPanelStateProgressBar.Value = a.ProgressPercentage;
|
||||
};
|
||||
VideoService.ProcessingStarted += (s, a) =>
|
||||
{
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateProcessingIcon"];
|
||||
HomeVideoPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextProcessing")} (0%)";
|
||||
HomeVideoPanelStateProgressBar.Value = 0;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateProcessingIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextProcessing")} (0%)";
|
||||
HomeTaskPanelStateProgressBar.Value = 0;
|
||||
};
|
||||
VideoService.ProcessingProgressChanged += (s, a) =>
|
||||
{
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateProcessingIcon"];
|
||||
HomeVideoPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextProcessing")} ({a.ProgressPercentage}%)";
|
||||
HomeVideoPanelStateProgressBar.Value = a.ProgressPercentage;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateProcessingIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextProcessing")} ({a.ProgressPercentage}%)";
|
||||
HomeTaskPanelStateProgressBar.Value = a.ProgressPercentage;
|
||||
};
|
||||
|
||||
// Request extended session
|
||||
@@ -186,7 +184,8 @@ namespace VDownload.Views.Home
|
||||
await session.RequestExtensionAsync();
|
||||
|
||||
// Start task
|
||||
StorageFile tempOutputFile = await VideoService.DownloadAndTranscodeAsync(tempFolder, Stream, Extension, MediaType, CancellationTokenSource.Token);
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
StorageFile tempOutputFile = await VideoService.DownloadAndTranscodeAsync(tempFolder, Stream, Extension, MediaType, TrimStart, TrimEnd, CancellationTokenSource.Token);
|
||||
|
||||
// Dispose session
|
||||
session.Dispose();
|
||||
@@ -195,13 +194,12 @@ namespace VDownload.Views.Home
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
// Set state controls
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateFinalizingIcon"];
|
||||
HomeVideoPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextFinalizing");
|
||||
HomeVideoPanelStateProgressBar.IsIndeterminate = true;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateFinalizingIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextFinalizing");
|
||||
HomeTaskPanelStateProgressBar.IsIndeterminate = true;
|
||||
|
||||
// Move to output location
|
||||
StorageFile outputFile;
|
||||
Debug.WriteLine($"{Filename}.{Extension.ToString().ToLower()}");
|
||||
if (Location != null) outputFile = await Location.CreateFileAsync($"{Filename}.{Extension.ToString().ToLower()}", (bool)Config.GetValue("replace_output_file_if_exists") ? CreationCollisionOption.ReplaceExisting : CreationCollisionOption.GenerateUniqueName);
|
||||
else outputFile = await DownloadsFolder.CreateFileAsync($"{Filename}.{Extension.ToString().ToLower()}", (bool)Config.GetValue("replace_output_file_if_exists") ? CreationCollisionOption.ReplaceExisting : CreationCollisionOption.GenerateUniqueName);
|
||||
await tempOutputFile.MoveAndReplaceAsync(outputFile);
|
||||
@@ -210,25 +208,25 @@ namespace VDownload.Views.Home
|
||||
taskStopwatch.Stop();
|
||||
|
||||
// Set state controls
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDoneIcon"];
|
||||
HomeVideoPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextDone")} ({(Math.Floor(taskStopwatch.Elapsed.TotalHours) > 0 ? $"{ Math.Floor(taskStopwatch.Elapsed.TotalHours):0}:" : "")}{taskStopwatch.Elapsed.Minutes:00}:{taskStopwatch.Elapsed.Seconds:00})";
|
||||
HomeVideoPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDoneIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextDone")} ({(Math.Floor(taskStopwatch.Elapsed.TotalHours) > 0 ? $"{ Math.Floor(taskStopwatch.Elapsed.TotalHours):0}:" : "")}{taskStopwatch.Elapsed.Minutes:00}:{taskStopwatch.Elapsed.Seconds:00})";
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Set state controls
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"];
|
||||
HomeVideoPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextCancelled");
|
||||
HomeVideoPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextCancelled");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Change icon
|
||||
HomeVideoPanelStartStopButton.Icon = new SymbolIcon(Symbol.Download);
|
||||
HomeTaskPanelStartStopButton.Icon = new SymbolIcon(Symbol.Download);
|
||||
|
||||
// Set video status
|
||||
VideoStatus = VideoStatus.Idle;
|
||||
TaskStatus = Core.Enums.TaskStatus.Idle;
|
||||
|
||||
// Delete temporary files
|
||||
await tempFolder.DeleteAsync();
|
||||
@@ -240,9 +238,9 @@ namespace VDownload.Views.Home
|
||||
else
|
||||
{
|
||||
// Set state controls
|
||||
HomeVideoPanelStateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"];
|
||||
HomeVideoPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeVideoPanelStateTextCancelled");
|
||||
HomeVideoPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextCancelled");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,24 +251,24 @@ namespace VDownload.Views.Home
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// SOURCE BUTTON CLICKED
|
||||
private async void HomeVideoPanelSourceButton_Click(object sender, RoutedEventArgs e)
|
||||
private async void HomeTaskPanelSourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Launch the website
|
||||
await Windows.System.Launcher.LaunchUriAsync(VideoService.VideoUrl);
|
||||
}
|
||||
|
||||
// START STOP BUTTON CLICKED
|
||||
private async void HomeVideoPanelStartStopButton_Click(object sender, RoutedEventArgs e)
|
||||
private async void HomeTaskPanelStartStopButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (VideoStatus == VideoStatus.InProgress || VideoStatus == VideoStatus.Waiting) CancellationTokenSource.Cancel();
|
||||
if (TaskStatus == Core.Enums.TaskStatus.InProgress || TaskStatus == Core.Enums.TaskStatus.Waiting) CancellationTokenSource.Cancel();
|
||||
else await Start();
|
||||
}
|
||||
|
||||
// REMOVE BUTTON CLICKED
|
||||
private void HomeVideoPanelRemoveButton_Click(object sender, RoutedEventArgs e)
|
||||
private void HomeTaskPanelRemoveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (VideoStatus == VideoStatus.InProgress || VideoStatus == VideoStatus.Waiting) CancellationTokenSource.Cancel();
|
||||
VideoRemovingRequested?.Invoke(sender, EventArgs.Empty);
|
||||
if (TaskStatus == Core.Enums.TaskStatus.InProgress || TaskStatus == Core.Enums.TaskStatus.Waiting) CancellationTokenSource.Cancel();
|
||||
TaskRemovingRequested?.Invoke(sender, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -279,7 +277,7 @@ namespace VDownload.Views.Home
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler VideoRemovingRequested;
|
||||
public event EventHandler TaskRemovingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgsObjects;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Objects;
|
||||
using VDownload.Core.Services;
|
||||
@@ -20,12 +18,18 @@ using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class HomeVideoAddingPanel : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeVideoAddingPanel(IVideoService videoService)
|
||||
@@ -36,8 +40,8 @@ namespace VDownload.Views.Home
|
||||
VideoService = videoService;
|
||||
|
||||
// Set metadata
|
||||
ThumbnailImage = new BitmapImage { UriSource = VideoService.Thumbnail ?? new Uri("ms-appx:///Assets/UnknownThumbnail.png") };
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Icons/{VideoService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
ThumbnailImage = VideoService.Thumbnail != null ? new BitmapImage { UriSource = VideoService.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{VideoService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Title = VideoService.Title;
|
||||
Author = VideoService.Author;
|
||||
Views = VideoService.Views.ToString();
|
||||
@@ -53,7 +57,7 @@ namespace VDownload.Views.Home
|
||||
HomeVideoAddingMediaTypeSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_media_type");
|
||||
|
||||
// Set quality
|
||||
foreach (Stream stream in VideoService.Streams)
|
||||
foreach (IBaseStream stream in VideoService.BaseStreams)
|
||||
{
|
||||
HomeVideoAddingQualitySettingControlComboBox.Items.Add($"{stream.Height}p{(stream.FrameRate > 0 ? stream.FrameRate.ToString() : "N/A")}");
|
||||
}
|
||||
@@ -130,7 +134,7 @@ namespace VDownload.Views.Home
|
||||
|
||||
// VIDEO OPTIONS
|
||||
private MediaType MediaType { get; set; }
|
||||
private Stream Stream { get; set; }
|
||||
private IBaseStream Stream { get; set; }
|
||||
private TimeSpan TrimStart { get; set; }
|
||||
private TimeSpan TrimEnd { get; set; }
|
||||
private string Filename { get; set; }
|
||||
@@ -150,7 +154,7 @@ namespace VDownload.Views.Home
|
||||
if (HomeVideoAddingMediaTypeSettingControlComboBox.SelectedIndex == (int)MediaType.OnlyAudio)
|
||||
{
|
||||
HomeVideoAddingQualitySettingControl.Visibility = Visibility.Collapsed;
|
||||
HomeVideoAddingQualitySettingControlComboBox.SelectedIndex = VideoService.Streams.Count() - 1;
|
||||
HomeVideoAddingQualitySettingControlComboBox.SelectedIndex = VideoService.BaseStreams.Count() - 1;
|
||||
|
||||
HomeVideoAddingExtensionComboBox.Items.Clear();
|
||||
foreach (AudioFileExtension extension in Enum.GetValues(typeof(AudioFileExtension)))
|
||||
@@ -176,7 +180,7 @@ namespace VDownload.Views.Home
|
||||
// QUALITY COMBOBOX SELECTION CHANGED
|
||||
private void HomeVideoAddingQualitySettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
Stream = VideoService.Streams[HomeVideoAddingQualitySettingControlComboBox.SelectedIndex];
|
||||
Stream = VideoService.BaseStreams[HomeVideoAddingQualitySettingControlComboBox.SelectedIndex];
|
||||
}
|
||||
|
||||
// TRIM START TEXTBOX TEXT CHANGED
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public class HomeVideosList
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,14 @@
|
||||
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Thickness x:Key="NavigationViewContentMargin">0,48,0,0</Thickness>
|
||||
<Thickness x:Key="NavigationViewContentGridBorderThickness">0</Thickness>
|
||||
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent"></SolidColorBrush>
|
||||
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent"/>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
|
||||
Reference in New Issue
Block a user