From d8eb103dcc75d44da3d244c2564eb6f81c0a45d3 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Wed, 2 Mar 2022 22:13:28 +0100 Subject: [PATCH] 1.0-dev10 (Code cleaning) --- .../Enums/{VideoStatus.cs => TaskStatus.cs} | 2 +- .../EventArgs/PlaylistSearchEventArgs.cs | 8 ++ .../VideoAddEventArgs.cs | 6 +- .../EventArgs/VideoSearchEventArgs.cs | 7 ++ .../PlaylistSearchEventArgs.cs | 10 -- .../EventArgsObjects/VideoSearchEventArgs.cs | 9 -- VDownload.Core/Interfaces/IAStream.cs | 18 --- .../{IVStream.cs => IBaseStream.cs} | 8 +- VDownload.Core/Interfaces/IPlaylistService.cs | 3 + VDownload.Core/Interfaces/IVideoService.cs | 12 +- VDownload.Core/Objects/Stream.cs | 2 +- VDownload.Core/Services/Config.cs | 3 +- VDownload.Core/Services/MediaProcessor.cs | 26 ++-- VDownload.Core/Services/Source.cs | 11 +- .../Services/Sources/Twitch/Auth.cs | 2 +- .../Services/Sources/Twitch/Channel.cs | 12 +- .../Services/Sources/Twitch/Clip.cs | 38 +++--- VDownload.Core/Services/Sources/Twitch/Vod.cs | 103 +++++++-------- VDownload.Core/Services/TaskId.cs | 36 ++++-- VDownload.Core/VDownload.Core.csproj | 11 +- VDownload/App.xaml.cs | 22 +++- .../Assets/{ => Images}/UnknownThumbnail.png | Bin VDownload/Assets/Sources/Twitch.png | Bin 0 -> 273790 bytes VDownload/Resources/Images.xaml | 5 + VDownload/Strings/en-US/Resources.resw | 42 +++---- VDownload/VDownload.csproj | 14 ++- VDownload/Views/Home/HomeMain.xaml | 4 +- VDownload/Views/Home/HomeMain.xaml.cs | 39 +++--- .../HomeOptionsBarAddPlaylistControl.xaml.cs | 13 +- .../HomeOptionsBarAddVideoControl.xaml.cs | 21 ++-- ...HomeVideoPanel.xaml => HomeTaskPanel.xaml} | 17 +-- ...deoPanel.xaml.cs => HomeTaskPanel.xaml.cs} | 118 +++++++++--------- .../Views/Home/HomeVideoAddingPanel.xaml.cs | 28 +++-- VDownload/Views/Home/HomeVideosList.cs | 12 -- VDownload/Views/MainPage.xaml | 11 +- VDownload/Views/Sources/SourcesMain.xaml.cs | 1 - 36 files changed, 338 insertions(+), 336 deletions(-) rename VDownload.Core/Enums/{VideoStatus.cs => TaskStatus.cs} (78%) create mode 100644 VDownload.Core/EventArgs/PlaylistSearchEventArgs.cs rename VDownload.Core/{EventArgsObjects => EventArgs}/VideoAddEventArgs.cs (78%) create mode 100644 VDownload.Core/EventArgs/VideoSearchEventArgs.cs delete mode 100644 VDownload.Core/EventArgsObjects/PlaylistSearchEventArgs.cs delete mode 100644 VDownload.Core/EventArgsObjects/VideoSearchEventArgs.cs delete mode 100644 VDownload.Core/Interfaces/IAStream.cs rename VDownload.Core/Interfaces/{IVStream.cs => IBaseStream.cs} (69%) rename VDownload/Assets/{ => Images}/UnknownThumbnail.png (100%) create mode 100644 VDownload/Assets/Sources/Twitch.png create mode 100644 VDownload/Resources/Images.xaml rename VDownload/Views/Home/{HomeVideoPanel.xaml => HomeTaskPanel.xaml} (79%) rename VDownload/Views/Home/{HomeVideoPanel.xaml.cs => HomeTaskPanel.xaml.cs} (58%) delete mode 100644 VDownload/Views/Home/HomeVideosList.cs diff --git a/VDownload.Core/Enums/VideoStatus.cs b/VDownload.Core/Enums/TaskStatus.cs similarity index 78% rename from VDownload.Core/Enums/VideoStatus.cs rename to VDownload.Core/Enums/TaskStatus.cs index 8672007..f908ef6 100644 --- a/VDownload.Core/Enums/VideoStatus.cs +++ b/VDownload.Core/Enums/TaskStatus.cs @@ -1,6 +1,6 @@ namespace VDownload.Core.Enums { - public enum VideoStatus + public enum TaskStatus { Idle, Waiting, diff --git a/VDownload.Core/EventArgs/PlaylistSearchEventArgs.cs b/VDownload.Core/EventArgs/PlaylistSearchEventArgs.cs new file mode 100644 index 0000000..d0b9036 --- /dev/null +++ b/VDownload.Core/EventArgs/PlaylistSearchEventArgs.cs @@ -0,0 +1,8 @@ +namespace VDownload.Core.EventArgs +{ + public class PlaylistSearchEventArgs : System.EventArgs + { + public string Phrase { get; set; } + public int Count { get; set; } + } +} diff --git a/VDownload.Core/EventArgsObjects/VideoAddEventArgs.cs b/VDownload.Core/EventArgs/VideoAddEventArgs.cs similarity index 78% rename from VDownload.Core/EventArgsObjects/VideoAddEventArgs.cs rename to VDownload.Core/EventArgs/VideoAddEventArgs.cs index 0477e99..3e34a83 100644 --- a/VDownload.Core/EventArgsObjects/VideoAddEventArgs.cs +++ b/VDownload.Core/EventArgs/VideoAddEventArgs.cs @@ -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; } diff --git a/VDownload.Core/EventArgs/VideoSearchEventArgs.cs b/VDownload.Core/EventArgs/VideoSearchEventArgs.cs new file mode 100644 index 0000000..720fd91 --- /dev/null +++ b/VDownload.Core/EventArgs/VideoSearchEventArgs.cs @@ -0,0 +1,7 @@ +namespace VDownload.Core.EventArgs +{ + public class VideoSearchEventArgs : System.EventArgs + { + public string Phrase { get; set; } + } +} diff --git a/VDownload.Core/EventArgsObjects/PlaylistSearchEventArgs.cs b/VDownload.Core/EventArgsObjects/PlaylistSearchEventArgs.cs deleted file mode 100644 index 9a2e598..0000000 --- a/VDownload.Core/EventArgsObjects/PlaylistSearchEventArgs.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace VDownload.Core.EventArgsObjects -{ - public class PlaylistSearchEventArgs : EventArgs - { - public string Phrase { get; set; } - public int Count { get; set; } - } -} diff --git a/VDownload.Core/EventArgsObjects/VideoSearchEventArgs.cs b/VDownload.Core/EventArgsObjects/VideoSearchEventArgs.cs deleted file mode 100644 index c0d47e4..0000000 --- a/VDownload.Core/EventArgsObjects/VideoSearchEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace VDownload.Core.EventArgsObjects -{ - public class VideoSearchEventArgs : EventArgs - { - public string Phrase { get; set; } - } -} diff --git a/VDownload.Core/Interfaces/IAStream.cs b/VDownload.Core/Interfaces/IAStream.cs deleted file mode 100644 index fc8a86f..0000000 --- a/VDownload.Core/Interfaces/IAStream.cs +++ /dev/null @@ -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 - } -} diff --git a/VDownload.Core/Interfaces/IVStream.cs b/VDownload.Core/Interfaces/IBaseStream.cs similarity index 69% rename from VDownload.Core/Interfaces/IVStream.cs rename to VDownload.Core/Interfaces/IBaseStream.cs index d35b57b..5810605 100644 --- a/VDownload.Core/Interfaces/IVStream.cs +++ b/VDownload.Core/Interfaces/IBaseStream.cs @@ -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 } diff --git a/VDownload.Core/Interfaces/IPlaylistService.cs b/VDownload.Core/Interfaces/IPlaylistService.cs index 458bf53..57bfe7b 100644 --- a/VDownload.Core/Interfaces/IPlaylistService.cs +++ b/VDownload.Core/Interfaces/IPlaylistService.cs @@ -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 diff --git a/VDownload.Core/Interfaces/IVideoService.cs b/VDownload.Core/Interfaces/IVideoService.cs index 35a6d11..3834641 100644 --- a/VDownload.Core/Interfaces/IVideoService.cs +++ b/VDownload.Core/Interfaces/IVideoService.cs @@ -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 DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default); - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, CancellationToken cancellationToken = default); - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default); - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default); - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default); - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, CancellationToken cancellationToken = default); - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default); - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default); + Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IBaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default); #endregion diff --git a/VDownload.Core/Objects/Stream.cs b/VDownload.Core/Objects/Stream.cs index 3bb97d0..30d3449 100644 --- a/VDownload.Core/Objects/Stream.cs +++ b/VDownload.Core/Objects/Stream.cs @@ -4,7 +4,7 @@ using VDownload.Core.Interfaces; namespace VDownload.Core.Objects { - public class Stream : IVStream, IAStream + public class Stream : IBaseStream { #region CONSTRUCTORS diff --git a/VDownload.Core/Services/Config.cs b/VDownload.Core/Services/Config.cs index 3667dce..126a032 100644 --- a/VDownload.Core/Services/Config.cs +++ b/VDownload.Core/Services/Config.cs @@ -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; diff --git a/VDownload.Core/Services/MediaProcessor.cs b/VDownload.Core/Services/MediaProcessor.cs index fb26bbd..1f59839 100644 --- a/VDownload.Core/Services/MediaProcessor.cs +++ b/VDownload.Core/Services/MediaProcessor.cs @@ -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 transcodingTask = transcodingPreparated.TranscodeAsync(); - try - { - await transcodingTask.AsTask(cancellationToken, new Progress((percent) => { ProcessingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(percent), null)); })); - await outputFileOpened.FlushAsync(); - } - catch (TaskCanceledException) { } + await transcodingTask.AsTask(cancellationToken, new Progress((percent) => { ProcessingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(percent), null)); })); + await outputFileOpened.FlushAsync(); 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 getVideoFileTask = MediaClip.CreateFromFileAsync(videoFile).AsTask(); Task 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); } diff --git a/VDownload.Core/Services/Source.cs b/VDownload.Core/Services/Source.cs index 1286a8f..55e44e3 100644 --- a/VDownload.Core/Services/Source.cs +++ b/VDownload.Core/Services/Source.cs @@ -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/(?\d+)"), VideoSource.TwitchVod), @@ -19,6 +15,7 @@ namespace VDownload.Core.Services (new Regex(@"^https://clips.twitch.tv/(?[^?]+)"), 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/(?[^?]+)"), 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) diff --git a/VDownload.Core/Services/Sources/Twitch/Auth.cs b/VDownload.Core/Services/Sources/Twitch/Auth.cs index 7498dfd..8c13420 100644 --- a/VDownload.Core/Services/Sources/Twitch/Auth.cs +++ b/VDownload.Core/Services/Sources/Twitch/Auth.cs @@ -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 diff --git a/VDownload.Core/Services/Sources/Twitch/Channel.cs b/VDownload.Core/Services/Sources/Twitch/Channel.cs index dbc756f..0c85327 100644 --- a/VDownload.Core/Services/Sources/Twitch/Channel.cs +++ b/VDownload.Core/Services/Sources/Twitch/Channel.cs @@ -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"]; diff --git a/VDownload.Core/Services/Sources/Twitch/Clip.cs b/VDownload.Core/Services/Sources/Twitch/Clip.cs index 9e86fcc..48499b4 100644 --- a/VDownload.Core/Services/Sources/Twitch/Clip.cs +++ b/VDownload.Core/Services/Sources/Twitch/Clip.cs @@ -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 DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) + public async Task 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 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 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 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 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 DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioStream, extension, new TimeSpan(0), Duration, cancellationToken); } - public async Task 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 DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IVStream videoStream, VideoFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, videoStream, extension, new TimeSpan(0), Duration, cancellationToken); } #endregion diff --git a/VDownload.Core/Services/Sources/Twitch/Vod.cs b/VDownload.Core/Services/Sources/Twitch/Vod.cs index a9ffcaf..83e0841 100644 --- a/VDownload.Core/Services/Sources/Twitch/Vod.cs +++ b/VDownload.Core/Services/Sources/Twitch/Vod.cs @@ -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 DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default) + public async Task 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) + cancellationToken.ThrowIfCancellationRequested(); + StorageFile rawFile = await downloadingFolder.CreateFileAsync("raw.ts"); + + float chunksDownloaded = 0; + + Task downloadTask; + Task writeTask; + + cancellationToken.ThrowIfCancellationRequested(); + downloadTask = DownloadChunkAsync(chunksList[0].ChunkUrl); + await downloadTask; + for (int i = 1; i < chunksList.Count; i++) { - rawFile = await downloadingFolder.CreateFileAsync("raw.ts"); - float chunksDownloaded = 0; - - Task downloadTask; - Task writeTask; - - downloadTask = DownloadChunkAsync(chunksList[0].ChunkUrl); - await downloadTask; - for (int i = 1; i < chunksList.Count && !cancellationToken.IsCancellationRequested; i++) - { - 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) - { - await WriteChunkToFileAsync(rawFile, downloadTask.Result); - DownloadingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(++chunksDownloaded * 100 / chunksList.Count), null)); - - DownloadingCompleted?.Invoke(this, EventArgs.Empty); - } + 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)); } + cancellationToken.ThrowIfCancellationRequested(); + await WriteChunkToFileAsync(rawFile, downloadTask.Result); + DownloadingProgressChanged(this, new ProgressChangedEventArgs((int)Math.Round(++chunksDownloaded * 100 / chunksList.Count), null)); + + 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; - await mediaProcessor.Run(rawFile, extension, mediaType, cancellationToken); - } + 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 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 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 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 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 DownloadAndTranscodeAsync(StorageFolder downloadingFolder, IAStream audioStream, AudioFileExtension extension, CancellationToken cancellationToken = default) { return await DownloadAndTranscodeAsync(downloadingFolder, audioStream, extension, new TimeSpan(0), Duration, cancellationToken); } - public async Task 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 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> ExtractChunksFromM3U8Async(Uri streamUrl) + private static async Task> 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 DownloadChunkAsync(Uri chunkUrl) + private static async Task 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()) diff --git a/VDownload.Core/Services/TaskId.cs b/VDownload.Core/Services/TaskId.cs index 91161f8..dbb4e59 100644 --- a/VDownload.Core/Services/TaskId.cs +++ b/VDownload.Core/Services/TaskId.cs @@ -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 UsedIDs = new List(); - // 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 } } diff --git a/VDownload.Core/VDownload.Core.csproj b/VDownload.Core/VDownload.Core.csproj index 2c3a4cc..2604852 100644 --- a/VDownload.Core/VDownload.Core.csproj +++ b/VDownload.Core/VDownload.Core.csproj @@ -127,16 +127,15 @@ - - - - + + + + - + - diff --git a/VDownload/App.xaml.cs b/VDownload/App.xaml.cs index 53fdb16..f0c16c6 100644 --- a/VDownload/App.xaml.cs +++ b/VDownload/App.xaml.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 tempItems = await ApplicationData.Current.TemporaryFolder.GetItemsAsync(); + IReadOnlyList 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 tasks = new List(); 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 } } diff --git a/VDownload/Assets/UnknownThumbnail.png b/VDownload/Assets/Images/UnknownThumbnail.png similarity index 100% rename from VDownload/Assets/UnknownThumbnail.png rename to VDownload/Assets/Images/UnknownThumbnail.png diff --git a/VDownload/Assets/Sources/Twitch.png b/VDownload/Assets/Sources/Twitch.png new file mode 100644 index 0000000000000000000000000000000000000000..9989457627aa9a1fc7ad88cd4f551a5fce747b27 GIT binary patch literal 273790 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 + + diff --git a/VDownload/Strings/en-US/Resources.resw b/VDownload/Strings/en-US/Resources.resw index b8a1770..8d95290 100644 --- a/VDownload/Strings/en-US/Resources.resw +++ b/VDownload/Strings/en-US/Resources.resw @@ -196,6 +196,27 @@ The number in the numberbox indicades how many videos will be got from playlist. There is a problem with linked Twitch account. Check Twitch login status in Sources page. + + Cancelled + + + Done + + + Downloading + + + Finalizing + + + Idle + + + Processing + + + Queued + Downloading options @@ -220,27 +241,6 @@ The number in the numberbox indicades how many videos will be got from playlist. Trim - - Cancelled - - - Done - - - Downloading - - - Finalizing - - - Idle - - - Processing - - - Queued - Home diff --git a/VDownload/VDownload.csproj b/VDownload/VDownload.csproj index 15535cb..1cff800 100644 --- a/VDownload/VDownload.csproj +++ b/VDownload/VDownload.csproj @@ -140,10 +140,9 @@ HomeVideoAddingPanel.xaml - - HomeVideoPanel.xaml + + HomeTaskPanel.xaml - MainPage.xaml @@ -250,7 +249,8 @@ - + + @@ -269,6 +269,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -293,7 +297,7 @@ Designer MSBuild:Compile - + Designer MSBuild:Compile diff --git a/VDownload/Views/Home/HomeMain.xaml b/VDownload/Views/Home/HomeMain.xaml index b4233a8..f4529d1 100644 --- a/VDownload/Views/Home/HomeMain.xaml +++ b/VDownload/Views/Home/HomeMain.xaml @@ -54,13 +54,13 @@ - + - + diff --git a/VDownload/Views/Home/HomeMain.xaml.cs b/VDownload/Views/Home/HomeMain.xaml.cs index c44ac7b..a691f85 100644 --- a/VDownload/Views/Home/HomeMain.xaml.cs +++ b/VDownload/Views/Home/HomeMain.xaml.cs @@ -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 VideoPanelsList = new List(); + private static StackPanel HomeTasksListParent = null; + public static List TaskPanelsList = new List(); #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); } diff --git a/VDownload/Views/Home/HomeOptionsBarAddPlaylistControl.xaml.cs b/VDownload/Views/Home/HomeOptionsBarAddPlaylistControl.xaml.cs index 7503ee9..4dea2db 100644 --- a/VDownload/Views/Home/HomeOptionsBarAddPlaylistControl.xaml.cs +++ b/VDownload/Views/Home/HomeOptionsBarAddPlaylistControl.xaml.cs @@ -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 SearchButtonClicked; - #endregion @@ -56,5 +53,13 @@ namespace VDownload.Views.Home } #endregion + + + + #region EVENT HANDLERS + + public event EventHandler SearchButtonClicked; + + #endregion } } diff --git a/VDownload/Views/Home/HomeOptionsBarAddVideoControl.xaml.cs b/VDownload/Views/Home/HomeOptionsBarAddVideoControl.xaml.cs index a99852a..1b42947 100644 --- a/VDownload/Views/Home/HomeOptionsBarAddVideoControl.xaml.cs +++ b/VDownload/Views/Home/HomeOptionsBarAddVideoControl.xaml.cs @@ -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 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 SearchButtonClicked; + + #endregion } } diff --git a/VDownload/Views/Home/HomeVideoPanel.xaml b/VDownload/Views/Home/HomeTaskPanel.xaml similarity index 79% rename from VDownload/Views/Home/HomeVideoPanel.xaml rename to VDownload/Views/Home/HomeTaskPanel.xaml index dd1a4f0..6103ff1 100644 --- a/VDownload/Views/Home/HomeVideoPanel.xaml +++ b/VDownload/Views/Home/HomeTaskPanel.xaml @@ -1,10 +1,11 @@  - - - + + + - - - + + + diff --git a/VDownload/Views/Home/HomeVideoPanel.xaml.cs b/VDownload/Views/Home/HomeTaskPanel.xaml.cs similarity index 58% rename from VDownload/Views/Home/HomeVideoPanel.xaml.cs rename to VDownload/Views/Home/HomeTaskPanel.xaml.cs index 507a549..e619e78 100644 --- a/VDownload/Views/Home/HomeVideoPanel.xaml.cs +++ b/VDownload/Views/Home/HomeTaskPanel.xaml.cs @@ -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 } diff --git a/VDownload/Views/Home/HomeVideoAddingPanel.xaml.cs b/VDownload/Views/Home/HomeVideoAddingPanel.xaml.cs index 59e400c..47a5c21 100644 --- a/VDownload/Views/Home/HomeVideoAddingPanel.xaml.cs +++ b/VDownload/Views/Home/HomeVideoAddingPanel.xaml.cs @@ -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 diff --git a/VDownload/Views/Home/HomeVideosList.cs b/VDownload/Views/Home/HomeVideosList.cs deleted file mode 100644 index 0f9a9ef..0000000 --- a/VDownload/Views/Home/HomeVideosList.cs +++ /dev/null @@ -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 - { - } -} diff --git a/VDownload/Views/MainPage.xaml b/VDownload/Views/MainPage.xaml index 2fda7d2..dfa7bd9 100644 --- a/VDownload/Views/MainPage.xaml +++ b/VDownload/Views/MainPage.xaml @@ -10,9 +10,14 @@ - 0,48,0,0 - 0 - + + + + + 0,48,0,0 + 0 + + diff --git a/VDownload/Views/Sources/SourcesMain.xaml.cs b/VDownload/Views/Sources/SourcesMain.xaml.cs index d8b282a..bf56226 100644 --- a/VDownload/Views/Sources/SourcesMain.xaml.cs +++ b/VDownload/Views/Sources/SourcesMain.xaml.cs @@ -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;