diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5135af0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.exe filter=lfs diff=lfs merge=lfs -text diff --git a/.github/Images/Home.png b/.github/Images/Home.png index fc42a5b..401faab 100644 Binary files a/.github/Images/Home.png and b/.github/Images/Home.png differ diff --git a/.github/config/gitversion.yml b/.github/config/gitversion.yml new file mode 100644 index 0000000..b0eb3bf --- /dev/null +++ b/.github/config/gitversion.yml @@ -0,0 +1,18 @@ +next-version: 1.0.0 +assembly-versioning-scheme: MajorMinorPatch +assembly-file-versioning-scheme: MajorMinorPatch + +branches: + master: + regex: ^master$ + mode: ContinuousDelivery + increment: Patch + tag: '' + is-release-branch: true + test: + regex: ^features + mode: ContinuousDelivery + increment: Patch + tag: '' + is-release-branch: true + source-branches: [] diff --git a/.github/workflows/pull_request_dev.yml b/.github/workflows/pull_request_dev.yml new file mode 100644 index 0000000..5349bb1 --- /dev/null +++ b/.github/workflows/pull_request_dev.yml @@ -0,0 +1,26 @@ +name: Build application on dev pull request + + +on: + pull_request: + branches: + - "dev" + paths: + - "VDownload**" + + +jobs: + build: + name: Build + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Build + run: dotnet build \ No newline at end of file diff --git a/.github/workflows/pull_request_master.yml b/.github/workflows/pull_request_master.yml new file mode 100644 index 0000000..0853380 --- /dev/null +++ b/.github/workflows/pull_request_master.yml @@ -0,0 +1,26 @@ +name: Build application on master pull request + + +on: + pull_request: + branches: + - "master" + paths: + - "VDownload**" + + +jobs: + build: + name: Build + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Build + run: dotnet build diff --git a/.github/workflows/push_dev.yml b/.github/workflows/push_dev.yml new file mode 100644 index 0000000..49e3c90 --- /dev/null +++ b/.github/workflows/push_dev.yml @@ -0,0 +1,26 @@ +name: Build application on dev push + + +on: + push: + branches: + - "dev" + paths: + - "VDownload**" + + +jobs: + build: + name: Build + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Build + run: dotnet build diff --git a/.github/workflows/push_master.yml b/.github/workflows/push_master.yml new file mode 100644 index 0000000..accf086 --- /dev/null +++ b/.github/workflows/push_master.yml @@ -0,0 +1,66 @@ +name: Build and publish application on master push + + +on: + push: + branches: + - "master" + - "features/github_actions" + #paths: + #- "VDownload**" + + +jobs: + build: + name: Build + runs-on: windows-latest + env: + MSBUILD_PATH: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\devenv.com + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup GitVersion + uses: gittools/actions/gitversion/setup@v0.9.7 + with: + versionSpec: 5.x + - name: Determine Version + uses: gittools/actions/gitversion/execute@v0.9.7 + id: gitversion + with: + useConfigFile: true + configFilePath: .github/config/gitversion.yml + - name: Set version in VDownload.csproj file + id: package_version + uses: KageKirin/set-csproj-version@v0 + with: + file: VDownload/VDownload.csproj + version: ${{steps.gitversion.outputs.SemVer}} + - name: Set version in Package.appxmanifest file + uses: Nambers/ReplaceStringInFile@v1.3 + with: + path: VDownload/Package.appxmanifest + oldString: 0\.0\.0\.0 + newString: ${{steps.gitversion.outputs.SemVer}}.0 + showFileContent: true + escapeBackslash: true + - name: Set version in app.manifest file + uses: Nambers/ReplaceStringInFile@v1.3 + with: + path: VDownload/app.manifest + oldString: 0\.0\.0\.0 + newString: ${{steps.gitversion.outputs.SemVer}}.0 + showFileContent: true + escapeBackslash: true + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Build application + run: dotnet build -o build + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: build + path: build diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a1de5d7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +## Changelog + +* Migration to Windows App SDK and WinUI 3 +* Code refactoring, MVVM pattern and Dependency Injection applied +* Authentication data encryption added +* Replacing native Windows media transcoding libraries with FFmpeg +* Home - Cancel all button added +* Subscriptions and Home - Error display improved +* Subscriptions - New videos counter removed +* Minor interface changes diff --git a/README.md b/README.md index c8ea868..299c363 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ VDownload is universal video downloader written in .NET/C# and Universal Windows ## Download and installation -- Microsoft Store - GitHub Releases +- Microsoft Store (in the future) @@ -19,26 +19,24 @@ VDownload is universal video downloader written in .NET/C# and Universal Windows #### Supported sources - Twitch (VODs, clips and channels) -- YouTube (Videos, playlists and channels) #### Features - Intuitive and easy to use -- Modern GUI, consistent with Windows, touchscreen friendly - WinUI +- Modern GUI, consistent with Windows, touchscreen friendly - Multiple video service support - one app for all - Video downloading - Audio-only, video-only and audio-video downloading - Time trimming - download only the fragment you need -- Task scheduling - start downloading after a certain amount of time - Playlist downloading - Playlist filtering - download only the videos you need - Playlist subscription - download new videos from saved playlists - Downloading queue -- Use of native Windows media transcoding libraries - no FFmpeg included -- Highly configurable - from default video downloading parameters to transcoding algorithm +- FFmpeg transcoding +- Highly configurable - from default video downloading parameters to transcoding options -and everything for free with open source code +and all of this for free @@ -46,26 +44,10 @@ and everything for free with open source code VDownload is a project, which I develop in my free time as a hobby and a form of learning. -As the application is not 100% resistant to external factors (e.g. changes in APIs) and to be fair to creators and video services, it is available for free. +As the application is not 100% resistant to external factors (e.g. changes in services APIs) and to be fair to creators and video services, it is available for free with open source code. If you want to support me and the development of the project you can: - **Donate me** via Paypal (link in the "Sponsor this project" section in Github repository and in the app itself in the "About" page) - **Report bugs and errors** in the "Issues" section of Github repository - **Propose new features and support of video services** in the "Issues" section of Github repository -- **Translate the application into a language you know** - - -#### App development status - -At the moment, the application is in the **early stage of development**. Only source code is available to download. Check "App development plans" to learn about further project development plans. - - -#### App development plans - -- **1.0-prerelease1** - with Twitch support only, all video and playlist download options and playlist subscription *[Q2 2022]* -- **1.0-prerelease2** - Youtube support and issues from 1.0-prelease1 closed *[Q3 2022]* -- **1.0** - issues from 1.0-prelease2 closed and support for Polish (and maybe German) language *[Q3 2022]* -- **1.?** - new video services and language support and closing issues from previous versions. *[?]* -- **2.0** - switch from Universal Windows Platform to Windows App SDK *[when Windows App SDK will be more developed and will offer elements which I use in UWP (e.g. Mica)]* - -**dates are indicative only and may change* \ No newline at end of file +- **Translate the application into a language you know** diff --git a/VDownload.Core/Enums/AudioFileExtension.cs b/VDownload.Core/Enums/AudioFileExtension.cs deleted file mode 100644 index 505343f..0000000 --- a/VDownload.Core/Enums/AudioFileExtension.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace VDownload.Core.Enums -{ - public enum AudioFileExtension - { - MP3 = 3, - FLAC = 4, - WAV = 5, - M4A = 6, - ALAC = 7, - WMA = 8, - } -} diff --git a/VDownload.Core/Enums/DownloadTasksAddingRequestSource.cs b/VDownload.Core/Enums/DownloadTasksAddingRequestSource.cs deleted file mode 100644 index 4d86b43..0000000 --- a/VDownload.Core/Enums/DownloadTasksAddingRequestSource.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace VDownload.Core.Enums -{ - public enum DownloadTasksAddingRequestSource - { - Video, - Playlist, - Subscriptions, - } -} diff --git a/VDownload.Core/Enums/MediaFileExtension.cs b/VDownload.Core/Enums/MediaFileExtension.cs deleted file mode 100644 index db7a77a..0000000 --- a/VDownload.Core/Enums/MediaFileExtension.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace VDownload.Core.Enums -{ - public enum MediaFileExtension - { - MP4 = VideoFileExtension.MP4, - WMV = VideoFileExtension.WMV, - HEVC = VideoFileExtension.HEVC, - MP3 = AudioFileExtension.MP3, - FLAC = AudioFileExtension.FLAC, - WAV = AudioFileExtension.WAV, - M4A = AudioFileExtension.M4A, - ALAC = AudioFileExtension.ALAC, - WMA = AudioFileExtension.WMA, - } -} diff --git a/VDownload.Core/Enums/MediaType.cs b/VDownload.Core/Enums/MediaType.cs deleted file mode 100644 index 43b033d..0000000 --- a/VDownload.Core/Enums/MediaType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace VDownload.Core.Enums -{ - public enum MediaType - { - AudioVideo = 0, - OnlyAudio = 1, - OnlyVideo = 2, - } -} diff --git a/VDownload.Core/Enums/PlaylistSource.cs b/VDownload.Core/Enums/PlaylistSource.cs deleted file mode 100644 index 9cd1ffd..0000000 --- a/VDownload.Core/Enums/PlaylistSource.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace VDownload.Core.Enums -{ - public enum PlaylistSource - { - TwitchChannel, - Null - } -} diff --git a/VDownload.Core/Enums/VideoFileExtension.cs b/VDownload.Core/Enums/VideoFileExtension.cs deleted file mode 100644 index b9f986c..0000000 --- a/VDownload.Core/Enums/VideoFileExtension.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace VDownload.Core.Enums -{ - public enum VideoFileExtension - { - MP4 = 0, - WMV = 1, - HEVC = 2, - } -} diff --git a/VDownload.Core/Enums/VideoSource.cs b/VDownload.Core/Enums/VideoSource.cs deleted file mode 100644 index 04e6b9b..0000000 --- a/VDownload.Core/Enums/VideoSource.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace VDownload.Core.Enums -{ - public enum VideoSource - { - TwitchVod, - TwitchClip, - Null - } -} diff --git a/VDownload.Core/EventArgs/DownloadTaskStatusChangedEventArgs.cs b/VDownload.Core/EventArgs/DownloadTaskStatusChangedEventArgs.cs deleted file mode 100644 index 643ced4..0000000 --- a/VDownload.Core/EventArgs/DownloadTaskStatusChangedEventArgs.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using VDownload.Core.Enums; - -namespace VDownload.Core.EventArgs -{ - public class DownloadTaskStatusChangedEventArgs : System.EventArgs - { - #region CONSTRUCTORS - - public DownloadTaskStatusChangedEventArgs(DownloadTaskStatus status) - { - Status = status; - } - - public DownloadTaskStatusChangedEventArgs(DownloadTaskStatus status, DateTime scheduledFor) - { - Status = status; - ScheduledFor = scheduledFor; - } - - public DownloadTaskStatusChangedEventArgs(DownloadTaskStatus status, double progress) - { - Status = status; - if (Status == DownloadTaskStatus.Downloading) - { - DownloadingProgress = progress; - } - else if (Status == DownloadTaskStatus.Processing) - { - ProcessingProgress = progress; - } - } - - public DownloadTaskStatusChangedEventArgs(DownloadTaskStatus status, TimeSpan elapsedTime) - { - Status = status; - ElapsedTime = elapsedTime; - } - - public DownloadTaskStatusChangedEventArgs(DownloadTaskStatus status, Exception exception) - { - Status = status; - Exception = exception; - } - - #endregion - - - - #region PROPERTIES - - public DownloadTaskStatus Status { get; private set; } - public DateTime ScheduledFor { get; private set; } - public double DownloadingProgress { get; private set; } - public double ProcessingProgress { get; private set; } - public TimeSpan ElapsedTime { get; private set; } - public Exception Exception { get; private set; } - - #endregion - } -} diff --git a/VDownload.Core/EventArgs/DownloadTasksAddingRequestedEventArgs.cs b/VDownload.Core/EventArgs/DownloadTasksAddingRequestedEventArgs.cs deleted file mode 100644 index cadb7ff..0000000 --- a/VDownload.Core/EventArgs/DownloadTasksAddingRequestedEventArgs.cs +++ /dev/null @@ -1,27 +0,0 @@ -using VDownload.Core.Enums; -using VDownload.Core.Structs; - -namespace VDownload.Core.EventArgs -{ - public class DownloadTasksAddingRequestedEventArgs : System.EventArgs - { - #region CONSTRUCTORS - - public DownloadTasksAddingRequestedEventArgs(DownloadTask[] downloadTasks, DownloadTasksAddingRequestSource requestSource) - { - DownloadTasks = downloadTasks; - RequestSource = requestSource; - } - - #endregion - - - - #region PROPERTIES - - public DownloadTask[] DownloadTasks { get; private set; } - public DownloadTasksAddingRequestSource RequestSource { get; private set; } - - #endregion - } -} diff --git a/VDownload.Core/EventArgs/PlaylistSearchSuccessedEventArgs.cs b/VDownload.Core/EventArgs/PlaylistSearchSuccessedEventArgs.cs deleted file mode 100644 index 12f6232..0000000 --- a/VDownload.Core/EventArgs/PlaylistSearchSuccessedEventArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -using VDownload.Core.Interfaces; - -namespace VDownload.Core.EventArgs -{ - public class PlaylistSearchSuccessedEventArgs : System.EventArgs - { - #region CONSTRUCTORS - - public PlaylistSearchSuccessedEventArgs(IPlaylist playlist) - { - Playlist = playlist; - } - - #endregion - - - - #region PROPERTIES - - public IPlaylist Playlist { get; private set; } - - #endregion - } -} diff --git a/VDownload.Core/EventArgs/ProgressChangedEventArgs.cs b/VDownload.Core/EventArgs/ProgressChangedEventArgs.cs deleted file mode 100644 index ddaccae..0000000 --- a/VDownload.Core/EventArgs/ProgressChangedEventArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace VDownload.Core.EventArgs -{ - public class ProgressChangedEventArgs : System.EventArgs - { - #region CONSTRUCTORS - - public ProgressChangedEventArgs(double progress, bool isCompleted = false) - { - Progress = progress; - IsCompleted = isCompleted; - } - - #endregion - - - - #region PROPERTIES - - public double Progress { get; private set; } - public bool IsCompleted { get; private set; } - - #endregion - } -} diff --git a/VDownload.Core/EventArgs/SubscriptionLoadSuccessedEventArgs.cs b/VDownload.Core/EventArgs/SubscriptionLoadSuccessedEventArgs.cs deleted file mode 100644 index aacad34..0000000 --- a/VDownload.Core/EventArgs/SubscriptionLoadSuccessedEventArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -using VDownload.Core.Interfaces; - -namespace VDownload.Core.EventArgs -{ - public class SubscriptionLoadSuccessedEventArgs : System.EventArgs - { - #region CONSTRUCTORS - - public SubscriptionLoadSuccessedEventArgs(IVideo[] videos) - { - Videos = videos; - } - - #endregion - - - - #region PROPERTIES - - public IVideo[] Videos { get; private set; } - - #endregion - } -} diff --git a/VDownload.Core/EventArgs/VideoSearchSuccessedEventArgs.cs b/VDownload.Core/EventArgs/VideoSearchSuccessedEventArgs.cs deleted file mode 100644 index 8936ea5..0000000 --- a/VDownload.Core/EventArgs/VideoSearchSuccessedEventArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -using VDownload.Core.Interfaces; - -namespace VDownload.Core.EventArgs -{ - public class VideoSearchSuccessedEventArgs : System.EventArgs - { - #region CONSTRUCTORS - - public VideoSearchSuccessedEventArgs(IVideo video) - { - Video = video; - } - - #endregion - - - - #region PROPERTIES - - public IVideo Video { get; set; } - - #endregion - } -} diff --git a/VDownload.Core/Exceptions/MediaNotFoundException.cs b/VDownload.Core/Exceptions/MediaNotFoundException.cs deleted file mode 100644 index fe93561..0000000 --- a/VDownload.Core/Exceptions/MediaNotFoundException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace VDownload.Core.Exceptions -{ - public class MediaNotFoundException : Exception - { - public MediaNotFoundException() { } - public MediaNotFoundException(string message) : base(message) { } - public MediaNotFoundException(string message, Exception inner) : base(message, inner) { } - } -} diff --git a/VDownload.Core/Exceptions/SubscriptionExistsException.cs b/VDownload.Core/Exceptions/SubscriptionExistsException.cs deleted file mode 100644 index def8743..0000000 --- a/VDownload.Core/Exceptions/SubscriptionExistsException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VDownload.Core.Exceptions -{ - public class SubscriptionExistsException : Exception - { - public SubscriptionExistsException() { } - public SubscriptionExistsException(string message) : base(message) { } - public SubscriptionExistsException(string message, Exception inner) : base(message, inner) { } - } -} diff --git a/VDownload.Core/Exceptions/TwitchAccessTokenNotFoundException.cs b/VDownload.Core/Exceptions/TwitchAccessTokenNotFoundException.cs deleted file mode 100644 index 0e52ff9..0000000 --- a/VDownload.Core/Exceptions/TwitchAccessTokenNotFoundException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace VDownload.Core.Exceptions -{ - public class TwitchAccessTokenNotFoundException : Exception - { - public TwitchAccessTokenNotFoundException() { } - public TwitchAccessTokenNotFoundException(string message) : base(message) { } - public TwitchAccessTokenNotFoundException(string message, Exception inner) :base(message, inner) { } - } -} diff --git a/VDownload.Core/Exceptions/TwitchAccessTokenNotValidException.cs b/VDownload.Core/Exceptions/TwitchAccessTokenNotValidException.cs deleted file mode 100644 index f164324..0000000 --- a/VDownload.Core/Exceptions/TwitchAccessTokenNotValidException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace VDownload.Core.Exceptions -{ - public class TwitchAccessTokenNotValidException : Exception - { - public TwitchAccessTokenNotValidException() { } - public TwitchAccessTokenNotValidException(string message) : base(message) { } - public TwitchAccessTokenNotValidException(string message, Exception inner) : base(message, inner) { } - } -} diff --git a/VDownload.Core/Extensions/TimeSpanExtension.cs b/VDownload.Core/Extensions/TimeSpanExtension.cs deleted file mode 100644 index 6a83f57..0000000 --- a/VDownload.Core/Extensions/TimeSpanExtension.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Linq; -using System.Text; - -namespace VDownload.Core.Extensions -{ - public static class TimeSpanExtension - { - // To string (TH:)MM:SS - public static string ToStringOptTHBaseMMSS(this TimeSpan timeSpan, params TimeSpan[] formatBase) - { - StringBuilder formattedTimeSpan = new StringBuilder(); - - int maxTHLength = 0; - foreach (TimeSpan format in formatBase.Concat(new TimeSpan[] { timeSpan })) - { - int THLength = Math.Floor(format.TotalHours) > 0 ? Math.Floor(timeSpan.TotalHours).ToString().Length : 0; - if (THLength > maxTHLength) - { - maxTHLength = THLength; - } - } - if (maxTHLength > 0) - { - formattedTimeSpan.Append($"{((int)Math.Floor(timeSpan.TotalHours)).ToString($"D{maxTHLength}")}:"); - } - - formattedTimeSpan.Append(maxTHLength == 0 ? $"{timeSpan.Minutes}:" : $"{timeSpan.Minutes:00}:"); - - formattedTimeSpan.Append($"{timeSpan.Seconds:00}"); - - return formattedTimeSpan.ToString(); - } - - // To string ((TH:)MM:)SS - public static string ToStringOptTHMMBaseSS(this TimeSpan timeSpan, params TimeSpan[] formatBase) - { - StringBuilder formattedTimeSpan = new StringBuilder(); - - int maxTHLength = 0; - foreach (TimeSpan format in formatBase.Concat(new TimeSpan[] { timeSpan })) - { - int THLength = Math.Floor(format.TotalHours) > 0 ? Math.Floor(timeSpan.TotalHours).ToString().Length : 0; - if (THLength > maxTHLength) - { - maxTHLength = THLength; - } - } - if (maxTHLength > 0) - { - formattedTimeSpan.Append($"{((int)Math.Floor(timeSpan.TotalHours)).ToString($"D{maxTHLength}")}:"); - } - - bool MM = false; - if (Math.Floor(timeSpan.TotalMinutes) > 0 || maxTHLength > 0) - { - formattedTimeSpan.Append(maxTHLength > 0 ? $"{timeSpan.Minutes:00}:" : $"{timeSpan.Minutes}:"); - MM = true; - } - - formattedTimeSpan.Append(MM ? $"{timeSpan.Seconds:00}" : $"{timeSpan.Seconds}"); - - return formattedTimeSpan.ToString(); - } - } -} diff --git a/VDownload.Core/Interfaces/IPlaylist.cs b/VDownload.Core/Interfaces/IPlaylist.cs deleted file mode 100644 index a69e88b..0000000 --- a/VDownload.Core/Interfaces/IPlaylist.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; - -namespace VDownload.Core.Interfaces -{ - public interface IPlaylist - { - #region PROPERTIES - - PlaylistSource Source { get; } - string ID { get; } - Uri Url { get; } - string Name { get; } - IVideo[] Videos { get; } - - #endregion - - - - #region METHODS - - Task GetMetadataAsync(CancellationToken cancellationToken = default); - - Task GetVideosAsync(CancellationToken cancellationToken = default); - Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default); - - #endregion - } -} diff --git a/VDownload.Core/Interfaces/IVideo.cs b/VDownload.Core/Interfaces/IVideo.cs deleted file mode 100644 index e57b638..0000000 --- a/VDownload.Core/Interfaces/IVideo.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.ComponentModel; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.Structs; -using Windows.Storage; - -namespace VDownload.Core.Interfaces -{ - public interface IVideo - { - #region PROPERTIES - - VideoSource Source { get; } - string ID { get; } - Uri Url { get; } - string Title { get; } - string Author { get; } - DateTime Date { get; } - TimeSpan Duration { get; } - long Views { get; } - Uri Thumbnail { get; } - BaseStream[] BaseStreams { get; } - - #endregion - - - - #region METHODS - - Task GetMetadataAsync(CancellationToken cancellationToken = default); - - Task GetStreamsAsync(CancellationToken cancellationToken = default); - - Task DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TrimData trim, CancellationToken cancellationToken = default); - - #endregion - - - - #region EVENTS - - event EventHandler DownloadingProgressChanged; - event EventHandler ProcessingProgressChanged; - - #endregion - } -} diff --git a/VDownload.Core/Services/Config.cs b/VDownload.Core/Services/Config.cs deleted file mode 100644 index 0dc542d..0000000 --- a/VDownload.Core/Services/Config.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; -using VDownload.Core.Enums; -using Windows.Media.Editing; -using Windows.Media.Transcoding; -using Windows.Storage; - -namespace VDownload.Core.Services -{ - public static class Config - { - #region CONSTANTS - - private static readonly Dictionary DefaultSettings = new Dictionary() - { - { "delete_temp_on_start", true }, - { "twitch_vod_passive_trim", true }, - { "twitch_vod_downloading_chunk_retry_after_error", false }, - { "twitch_vod_downloading_chunk_max_retries", 10 }, - { "twitch_vod_downloading_chunk_retries_delay", 5000 }, - { "media_transcoding_use_hardware_acceleration", true }, - { "media_transcoding_algorithm", (int)MediaVideoProcessingAlgorithm.MrfCrf444 }, - { "media_editing_algorithm", (int)MediaTrimmingPreference.Fast }, - { "default_max_playlist_videos", 0 }, - { "default_media_type", (int)MediaType.AudioVideo }, - { "default_filename", "[] " }, - { "default_video_extension", (int)VideoFileExtension.MP4 }, - { "default_audio_extension", (int)AudioFileExtension.MP3 }, - { "custom_media_location", false }, - { "max_active_video_task", 5 }, - { "replace_output_file_if_exists", false }, - { "remove_task_when_successfully_ended", false }, - { "delete_task_temp_when_ended_with_error", false }, - { "show_notification_when_task_ended_successfully", false }, - { "show_notification_when_task_ended_unsuccessfully", false }, - { "show_warning_when_task_starts_on_metered_network", true }, - { "delay_task_when_queued_task_starts_on_metered_network", true } - }; - - #endregion - - - - #region PUBLIC METHODS - - public static object GetValue(string key) - { - return ApplicationData.Current.LocalSettings.Values[key]; - } - - public static void SetValue(string key, object value) - { - ApplicationData.Current.LocalSettings.Values[key] = value; - } - - public static void SetDefault() - { - foreach (KeyValuePair<string, object> s in DefaultSettings) - { - ApplicationData.Current.LocalSettings.Values[s.Key] = s.Value; - } - } - - public static void Rebuild() - { - foreach (KeyValuePair<string, object> s in DefaultSettings) - { - if (!ApplicationData.Current.LocalSettings.Values.ContainsKey(s.Key)) - { - ApplicationData.Current.LocalSettings.Values[s.Key] = s.Value; - } - } - } - - #endregion - } -} diff --git a/VDownload.Core/Services/DownloadTask.cs b/VDownload.Core/Services/DownloadTask.cs deleted file mode 100644 index 8f69415..0000000 --- a/VDownload.Core/Services/DownloadTask.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using Windows.ApplicationModel.ExtendedExecution; -using Windows.Storage; -using Windows.Storage.AccessCache; - -namespace VDownload.Core.Structs -{ - public class DownloadTask - { - #region CONSTRUCTORS - - public DownloadTask(string id, IVideo video, MediaType mediaType, BaseStream selectedStream, TrimData trim, OutputFile file, double schedule) - { - Id = id; - Video = video; - MediaType = mediaType; - SelectedStream = selectedStream; - Trim = trim; - File = file; - Schedule = schedule; - - Status = DownloadTaskStatus.Idle; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status); - CancellationTokenSource = new CancellationTokenSource(); - } - - #endregion - - - - #region PROPERTIES - - public string Id { get; set; } - public IVideo Video { get; set; } - public MediaType MediaType { get; set; } - public BaseStream SelectedStream { get; set; } - public TrimData Trim { get; set; } - public OutputFile File { get; set; } - public double Schedule { get; set; } - public DownloadTaskStatus Status { get; private set; } - public DownloadTaskStatusChangedEventArgs LastStatusChangedEventArgs { get; private set; } - public CancellationTokenSource CancellationTokenSource { get; private set; } - - #endregion - - - - #region METHODS - - public async Task Run(bool delayWhenOnMeteredConnection) - { - StatusChanged.Invoke(this, new DownloadTaskStatusChangedEventArgs(Status)); - - CancellationTokenSource = new CancellationTokenSource(); - - if (Schedule > 0) - { - DateTime scheduleFor = DateTime.Now.AddMinutes(Schedule); - Status = DownloadTaskStatus.Scheduled; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status, scheduleFor); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - while (DateTime.Now < scheduleFor && !CancellationTokenSource.Token.IsCancellationRequested) - { - await Task.Delay(100); - } - } - - Status = DownloadTaskStatus.Queued; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - await DownloadTasksCollectionManagement.WaitInQueue(delayWhenOnMeteredConnection, CancellationTokenSource.Token); - - if (!CancellationTokenSource.Token.IsCancellationRequested) - { - Status = DownloadTaskStatus.Downloading; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status, 0); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - - StorageFolder tempFolder; - if (StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_temp_location")) - { - tempFolder = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_temp_location"); - } - else - { - tempFolder = ApplicationData.Current.TemporaryFolder; - } - tempFolder = await tempFolder.CreateFolderAsync(Id); - - bool endedWithError = false; - - try - { - CancellationTokenSource.Token.ThrowIfCancellationRequested(); - - Stopwatch taskStopwatch = Stopwatch.StartNew(); - - ExtendedExecutionSession session = new ExtendedExecutionSession { Reason = ExtendedExecutionReason.Unspecified }; - await session.RequestExtensionAsync(); - CancellationTokenSource.Token.ThrowIfCancellationRequested(); - - Video.DownloadingProgressChanged += DownloadingProgressChanged; - Video.ProcessingProgressChanged += ProcessingProgressChanged; - StorageFile tempOutputFile = await Video.DownloadAndTranscodeAsync(tempFolder, SelectedStream, File.Extension, MediaType, Trim, CancellationTokenSource.Token); - - session.Dispose(); - - Status = DownloadTaskStatus.Finalizing; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - - StorageFile outputFile = await File.Create(); - - CancellationTokenSource.Token.ThrowIfCancellationRequested(); - await tempOutputFile.MoveAndReplaceAsync(outputFile); - - taskStopwatch.Stop(); - - Status = DownloadTaskStatus.EndedSuccessfully; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status, taskStopwatch.Elapsed); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - } - catch (Exception ex) - { - endedWithError = true; - Status = DownloadTaskStatus.EndedUnsuccessfully; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status, ex); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - } - finally - { - if (!endedWithError || (bool)Config.GetValue("delete_task_temp_when_ended_with_error")) - { - // Delete temporary files - await tempFolder.DeleteAsync(); - } - } - } - else - { - Status = DownloadTaskStatus.EndedUnsuccessfully; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status, new OperationCanceledException(CancellationTokenSource.Token)); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - } - } - - private void DownloadingProgressChanged(object sender, ProgressChangedEventArgs e) - { - Status = DownloadTaskStatus.Downloading; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status, e.Progress); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - } - - private void ProcessingProgressChanged(object sender, ProgressChangedEventArgs e) - { - Status = DownloadTaskStatus.Processing; - LastStatusChangedEventArgs = new DownloadTaskStatusChangedEventArgs(Status, e.Progress); - StatusChanged.Invoke(this, LastStatusChangedEventArgs); - } - - #endregion - - - - #region EVENT - - public event EventHandler<DownloadTaskStatusChangedEventArgs> StatusChanged; - - #endregion - } -} diff --git a/VDownload.Core/Services/DownloadTasksCollectionManagement.cs b/VDownload.Core/Services/DownloadTasksCollectionManagement.cs deleted file mode 100644 index 7f8ab4a..0000000 --- a/VDownload.Core/Services/DownloadTasksCollectionManagement.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Structs; - -namespace VDownload.Core.Services -{ - public static class DownloadTasksCollectionManagement - { - #region PROPERTIES - - private static readonly Dictionary<string, DownloadTask> ChangeableDownloadTasksCollection = new Dictionary<string, DownloadTask>(); - public static readonly ReadOnlyDictionary<string, DownloadTask> DownloadTasksCollection = new ReadOnlyDictionary<string, DownloadTask>(ChangeableDownloadTasksCollection); - - #endregion - - - - #region PUBLIC METHODS - - public static string GenerateID() - { - char[] idChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); - int idLength = 10; - - string id; - do - { - id = ""; - while (id.Length < idLength) - { - id += idChars[new Random().Next(0, idChars.Length)]; - } - } while (DownloadTasksCollection.Keys.Contains(id)); - - return id; - } - - public static void Add(DownloadTask downloadTask, string id) - { - ChangeableDownloadTasksCollection[id] = downloadTask; - } - - public static void Remove(string id) - { - ChangeableDownloadTasksCollection.Remove(id); - } - - public static async Task WaitInQueue(bool delayWhenOnMeteredConnection, CancellationToken cancellationToken = default) - { - while ((ChangeableDownloadTasksCollection.Values.Where((DownloadTask task) => task.Status == Enums.DownloadTaskStatus.Downloading || task.Status == Enums.DownloadTaskStatus.Processing || task.Status == Enums.DownloadTaskStatus.Finalizing).Count() >= (int)Config.GetValue("max_active_video_task") || (delayWhenOnMeteredConnection && NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)) && !cancellationToken.IsCancellationRequested) - { - await Task.Delay(100); - } - } - - #endregion - } -} diff --git a/VDownload.Core/Services/MediaProcessor.cs b/VDownload.Core/Services/MediaProcessor.cs deleted file mode 100644 index 966a350..0000000 --- a/VDownload.Core/Services/MediaProcessor.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.Structs; -using Windows.Foundation; -using Windows.Media.Editing; -using Windows.Media.MediaProperties; -using Windows.Media.Transcoding; -using Windows.Storage; -using Windows.Storage.Streams; - -namespace VDownload.Core.Services -{ - public class MediaProcessor - { - #region PUBLIC METHODS - - public async Task Run(StorageFile mediaFile, MediaFileExtension extension, MediaType mediaType, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0)); - - MediaTranscoder mediaTranscoder = new MediaTranscoder - { - HardwareAccelerationEnabled = (bool)Config.GetValue("media_transcoding_use_hardware_acceleration"), - VideoProcessingAlgorithm = (MediaVideoProcessingAlgorithm)Config.GetValue("media_transcoding_algorithm"), - }; - if (trim.Start != null) mediaTranscoder.TrimStartTime = trim.Start; - if (trim.End != null) mediaTranscoder.TrimStopTime = trim.End; - - using (IRandomAccessStream openedOutputFile = await outputFile.OpenAsync(FileAccessMode.ReadWrite)) - { - PrepareTranscodeResult transcodingPreparated = await mediaTranscoder.PrepareStreamTranscodeAsync(await mediaFile.OpenAsync(FileAccessMode.Read), openedOutputFile, await GetMediaEncodingProfile(mediaFile, extension, mediaType)); - - IAsyncActionWithProgress<double> transcodingTask = transcodingPreparated.TranscodeAsync(); - await transcodingTask.AsTask(cancellationToken, new Progress<double>((percent) => { ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(percent)); })); - cancellationToken.ThrowIfCancellationRequested(); - - await openedOutputFile.FlushAsync(); - transcodingTask.Close(); - } - - ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(100, true)); - } - public async Task Run(StorageFile audioFile, StorageFile videoFile, VideoFileExtension extension, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0)); - - MediaComposition mediaEditor = new MediaComposition(); - - cancellationToken.ThrowIfCancellationRequested(); - Task<MediaClip> getVideoFileTask = MediaClip.CreateFromFileAsync(videoFile).AsTask(); - Task<BackgroundAudioTrack> getAudioFileTask = BackgroundAudioTrack.CreateFromFileAsync(audioFile).AsTask(); - await Task.WhenAll(getVideoFileTask, getAudioFileTask); - - MediaClip videoElement = getVideoFileTask.Result; - if (trim.Start != null) videoElement.TrimTimeFromStart = trim.Start; - if (trim.End != null) videoElement.TrimTimeFromEnd = trim.End; - BackgroundAudioTrack audioElement = getAudioFileTask.Result; - if (trim.Start != null) audioElement.TrimTimeFromStart = trim.Start; - if (trim.End != null) audioElement.TrimTimeFromEnd = trim.End; - - mediaEditor.Clips.Add(videoElement); - mediaEditor.BackgroundAudioTracks.Add(audioElement); - - var renderOperation = mediaEditor.RenderToFileAsync(outputFile, (MediaTrimmingPreference)Config.GetValue("media_editing_algorithm"), await GetMediaEncodingProfile(videoFile, audioFile, (MediaFileExtension)extension, MediaType.AudioVideo)); - renderOperation.Progress += (info, progress) => { ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(progress)); }; - cancellationToken.ThrowIfCancellationRequested(); - await renderOperation.AsTask(cancellationToken); - - ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(100, true)); - } - public async Task Run(StorageFile audioFile, AudioFileExtension extension, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default) - { - await Run(audioFile, (MediaFileExtension)extension, MediaType.OnlyAudio, outputFile, trim, cancellationToken); - } - public async Task Run(StorageFile videoFile, VideoFileExtension extension, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default) - { - await Run(videoFile, (MediaFileExtension)extension, MediaType.OnlyVideo, outputFile, trim, cancellationToken); - } - - #endregion - - - - #region PRIVATE METHODS - - private static async Task<MediaEncodingProfile> GetMediaEncodingProfile(StorageFile videoFile, StorageFile audioFile, MediaFileExtension extension, MediaType mediaType) - { - MediaEncodingProfile profile; - - switch (extension) - { - default: - case MediaFileExtension.MP4: profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD1080p); break; - case MediaFileExtension.WMV: profile = MediaEncodingProfile.CreateWmv(VideoEncodingQuality.HD1080p); break; - case MediaFileExtension.HEVC: profile = MediaEncodingProfile.CreateHevc(VideoEncodingQuality.HD1080p); break; - case MediaFileExtension.MP3: profile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High); break; - case MediaFileExtension.FLAC: profile = MediaEncodingProfile.CreateFlac(AudioEncodingQuality.High); break; - case MediaFileExtension.WAV: profile = MediaEncodingProfile.CreateWav(AudioEncodingQuality.High); break; - case MediaFileExtension.M4A: profile = MediaEncodingProfile.CreateM4a(AudioEncodingQuality.High); break; - case MediaFileExtension.ALAC: profile = MediaEncodingProfile.CreateAlac(AudioEncodingQuality.High); break; - case MediaFileExtension.WMA: profile = MediaEncodingProfile.CreateWma(AudioEncodingQuality.High); break; - } - - if (mediaType != MediaType.OnlyAudio) - { - var videoData = await videoFile.Properties.GetVideoPropertiesAsync(); - profile.Video.Height = videoData.Height; - profile.Video.Width = videoData.Width; - profile.Video.Bitrate = videoData.Bitrate; - } - if (mediaType != MediaType.OnlyVideo) - { - var audioData = await audioFile.Properties.GetMusicPropertiesAsync(); - profile.Audio.Bitrate = audioData.Bitrate; - if (mediaType == MediaType.AudioVideo) profile.Video.Bitrate -= audioData.Bitrate; - } - if (mediaType == MediaType.OnlyVideo) - { - var audioTracks = profile.GetAudioTracks(); - audioTracks.Clear(); - profile.SetAudioTracks(audioTracks.AsEnumerable()); - } - - return profile; - } - private static async Task<MediaEncodingProfile> GetMediaEncodingProfile(StorageFile audioVideoFile, MediaFileExtension extension, MediaType mediaType) - { - return await GetMediaEncodingProfile(audioVideoFile, audioVideoFile, extension, mediaType); - } - - #endregion - - - - #region EVENTS - - public event EventHandler<EventArgs.ProgressChangedEventArgs> ProgressChanged; - - #endregion - } -} diff --git a/VDownload.Core/Services/OutputFile.cs b/VDownload.Core/Services/OutputFile.cs deleted file mode 100644 index 9aa2850..0000000 --- a/VDownload.Core/Services/OutputFile.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using Windows.Storage; - -namespace VDownload.Core.Services -{ - public class OutputFile - { - #region CONSTRUCTORS - - public OutputFile(string name, MediaFileExtension extension, StorageFolder location) - { - Name = name; - Extension = extension; - Location = location; - } - - public OutputFile(string name, MediaFileExtension extension) - { - Name = name; - Extension = extension; - Location = null; - } - - #endregion - - - - #region PROPERTIES - - public string Name { get; private set; } - public MediaFileExtension Extension { get; private set; } - public StorageFolder Location { get; private set; } - - #endregion - - - - #region PUBLIC METHODS - - public async Task<StorageFile> Create() - { - string filename = $"{Name}.{Extension.ToString().ToLower()}"; - CreationCollisionOption collisionOption = (bool)Config.GetValue("replace_output_file_if_exists") ? CreationCollisionOption.ReplaceExisting : CreationCollisionOption.GenerateUniqueName; - return await(!(Location is null) ? Location.CreateFileAsync(filename, collisionOption) : DownloadsFolder.CreateFileAsync(filename, collisionOption)); - } - - public string GetPath() => $@"{(Location != null ? Location.Path : $@"{UserDataPaths.GetDefault().Downloads}\VDownload")}\{Name}.{Extension.ToString().ToLower()}"; - - #endregion - } -} diff --git a/VDownload.Core/Services/Subscription.cs b/VDownload.Core/Services/Subscription.cs deleted file mode 100644 index 3b38836..0000000 --- a/VDownload.Core/Services/Subscription.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Interfaces; - -namespace VDownload.Core.Services -{ - [Serializable] - public class Subscription - { - #region CONSTRUCTORS - - public Subscription(IPlaylist playlist) - { - Playlist = playlist; - SavedVideos = Playlist.Videos; - } - - #endregion - - - - #region PROPERTIES - - public IPlaylist Playlist { get; private set; } - public IVideo[] SavedVideos { get; private set; } - - #endregion - - - - #region PUBLIC METHODS - - public async Task<IVideo[]> GetNewVideosAsync(CancellationToken cancellationToken = default) - { - await Playlist.GetVideosAsync(cancellationToken); - return GetUnsavedVideos(); - } - - public async Task<IVideo[]> GetNewVideosAndUpdateAsync(CancellationToken cancellationToken = default) - { - await Playlist.GetVideosAsync(cancellationToken); - IVideo[] newVideos = GetUnsavedVideos(); - SavedVideos = Playlist.Videos; - return newVideos; - } - - #endregion - - - - #region PRIVATE METHODS - - private IVideo[] GetUnsavedVideos() - { - List<IVideo> newVideos = Playlist.Videos.ToList(); - foreach (IVideo savedVideo in SavedVideos) - { - newVideos.RemoveAll((v) => v.Source == savedVideo.Source && v.ID == savedVideo.ID); - } - return newVideos.ToArray(); - } - - #endregion - } -} diff --git a/VDownload.Core/Services/SubscriptionsCollectionManagement.cs b/VDownload.Core/Services/SubscriptionsCollectionManagement.cs deleted file mode 100644 index 36470d0..0000000 --- a/VDownload.Core/Services/SubscriptionsCollectionManagement.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.Serialization.Formatters.Binary; -using System.Text; -using System.Threading.Tasks; -using VDownload.Core.Exceptions; -using Windows.Storage; - -namespace VDownload.Core.Services -{ - public static class SubscriptionsCollectionManagement - { - #region CONSTANTS - - private static readonly StorageFolder SubscriptionFolderLocation = ApplicationData.Current.LocalFolder; - private static readonly string SubscriptionsFolderName = "Subscriptions"; - private static readonly string SubscriptionFileExtension = "vsub"; - - #endregion - - - - #region PUBLIC METHODS - - public static async Task<(Subscription Subscription, StorageFile SubscriptionFile)[]> GetSubscriptionsAsync() - { - List<(Subscription Subscription, StorageFile SubscriptionFile)> subscriptions = new List<(Subscription Subscription,StorageFile SubscriptionFile)> (); - StorageFolder subscriptionsFolder = await SubscriptionFolderLocation.CreateFolderAsync(SubscriptionsFolderName, CreationCollisionOption.OpenIfExists); - BinaryFormatter formatter = new BinaryFormatter(); - foreach (StorageFile file in await subscriptionsFolder.GetFilesAsync()) - { - if (file.Name.EndsWith(SubscriptionFileExtension)) - { - Stream fileStream = await file.OpenStreamForReadAsync(); - Subscription subscription = (Subscription)formatter.Deserialize(fileStream); - subscriptions.Add((subscription, file)); - } - } - return subscriptions.ToArray(); - } - - public static async Task<StorageFile> CreateSubscriptionFileAsync(Subscription subscription) - { - StorageFolder subscriptionsFolder = await SubscriptionFolderLocation.CreateFolderAsync(SubscriptionsFolderName, CreationCollisionOption.OpenIfExists); - try - { - StorageFile subscriptionFile = await subscriptionsFolder.CreateFileAsync($"{(int)subscription.Playlist.Source}-{subscription.Playlist.ID}.{SubscriptionFileExtension}", CreationCollisionOption.FailIfExists); - BinaryFormatter formatter = new BinaryFormatter(); - Stream subscriptionFileStream = await subscriptionFile.OpenStreamForWriteAsync(); - formatter.Serialize(subscriptionFileStream, subscription); - return subscriptionFile; - } - catch (Exception ex) - { - if ((uint)ex.HResult == 0x800700B7) - { - throw new SubscriptionExistsException($"Subscription with id \"{(int)subscription.Playlist.Source}-{subscription.Playlist.ID}\" already exists"); - } - else - { - throw; - } - } - } - - public static async Task UpdateSubscriptionFileAsync(Subscription subscription, StorageFile subscriptionFile) - { - BinaryFormatter formatter = new BinaryFormatter(); - Stream subscriptionFileStream = await subscriptionFile.OpenStreamForWriteAsync(); - formatter.Serialize(subscriptionFileStream, subscription); - } - - public static async Task DeleteSubscriptionFileAsync(StorageFile subscriptionFile) => await subscriptionFile.DeleteAsync(StorageDeleteOption.PermanentDelete); - - #endregion - } -} diff --git a/VDownload.Core/Sources/AuthorizationData.cs b/VDownload.Core/Sources/AuthorizationData.cs deleted file mode 100644 index 7c0bbce..0000000 --- a/VDownload.Core/Sources/AuthorizationData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Windows.Storage; - -namespace VDownload.Core.Sources -{ - internal static class AuthorizationData - { - internal static StorageFolder FolderLocation = ApplicationData.Current.LocalCacheFolder; - internal static string FolderName = "AuthData"; - internal static string FilesExtension = "auth"; - } -} diff --git a/VDownload.Core/Sources/Source.cs b/VDownload.Core/Sources/Source.cs deleted file mode 100644 index 3712430..0000000 --- a/VDownload.Core/Sources/Source.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Text.RegularExpressions; -using VDownload.Core.Enums; -using VDownload.Core.Interfaces; - -namespace VDownload.Core.Services.Sources -{ - public static class Source - { - #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), - (new Regex(@"^https://www.twitch.tv/\S+/clip/(?<id>[^?]+)"), VideoSource.TwitchClip), - (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), - }; - - #endregion - - - - #region METHODS - - // GET VIDEO SOURCE - public static IVideo GetVideo(string url) - { - VideoSource source = VideoSource.Null; - string id = string.Empty; - foreach ((Regex Regex, VideoSource Type) Source in VideoSources) - { - Match sourceMatch = Source.Regex.Match(url); - if (sourceMatch.Success) - { - source = Source.Type; - id = sourceMatch.Groups["id"].Value; - } - } - return GetVideo(source, id); - } - public static IVideo GetVideo(VideoSource source, string id) - { - IVideo videoService = null; - switch (source) - { - case VideoSource.TwitchVod: videoService = new Twitch.Vod(id); break; - case VideoSource.TwitchClip: videoService = new Twitch.Clip(id); break; - } - return videoService; - } - - // GET PLAYLIST SOURCE - public static IPlaylist GetPlaylist(string url) - { - PlaylistSource source = PlaylistSource.Null; - string id = string.Empty; - foreach ((Regex Regex, PlaylistSource Type) Source in PlaylistSources) - { - Match sourceMatch = Source.Regex.Match(url); - if (sourceMatch.Success) - { - source = Source.Type; - id = sourceMatch.Groups["id"].Value; - } - } - return GetPlaylist(source, id); - } - public static IPlaylist GetPlaylist(PlaylistSource source, string id) - { - IPlaylist playlistService = null; - switch (source) - { - case PlaylistSource.TwitchChannel: playlistService = new Twitch.Channel(id); break; - } - return playlistService; - } - - #endregion - } -} diff --git a/VDownload.Core/Sources/Twitch/Channel.cs b/VDownload.Core/Sources/Twitch/Channel.cs deleted file mode 100644 index 97e5612..0000000 --- a/VDownload.Core/Sources/Twitch/Channel.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services.Sources.Twitch.Helpers; - -namespace VDownload.Core.Services.Sources.Twitch -{ - [Serializable] - public class Channel : IPlaylist - { - #region CONSTRUCTORS - - public Channel(string id) - { - Source = PlaylistSource.TwitchChannel; - ID = id; - Url = new Uri($"https://twitch.tv/{ID}"); - } - - #endregion - - - - #region PROPERTIES - - public PlaylistSource Source { get; private set; } - public string ID { get; private set; } - private string UniqueID { get; set; } - public Uri Url { get; private set; } - public string Name { get; private set; } - public IVideo[] Videos { get; private set; } - - #endregion - - - - #region PUBLIC METHODS - - public async Task GetMetadataAsync(CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - JToken response = null; - using (WebClient client = await Client.Helix()) - { - client.QueryString.Add("login", ID); - response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/users"))["data"]; - if (((JArray)response).Count > 0) response = response[0]; - else throw new MediaNotFoundException($"Twitch Channel (ID: {ID}) was not found"); - } - - UniqueID = (string)response["id"]; - Name = (string)response["display_name"]; - } - - public async Task GetVideosAsync(CancellationToken cancellationToken = default) => await GetVideosAsync(0, cancellationToken); - public async Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - string pagination = ""; - - List<Vod> videos = new List<Vod>(); - - bool getAll = numberOfVideos == 0; - int count; - JToken[] videosData; - List<Task> getStreamsTasks = new List<Task>(); - do - { - count = numberOfVideos < 100 && !getAll ? numberOfVideos : 100; - - JToken response = null; - using (WebClient client = await Client.Helix()) - { - client.QueryString.Add("user_id", UniqueID); - client.QueryString.Add("first", count.ToString()); - client.QueryString.Add("after", pagination); - response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/videos")); - } - - pagination = (string)response["pagination"]["cursor"]; - - videosData = response["data"].ToArray(); - foreach (JToken videoData in videosData) - { - Vod video = new Vod((string)videoData["id"]); - video.GetMetadataAsync(videoData); - getStreamsTasks.Add(video.GetStreamsAsync()); - videos.Add(video); - - numberOfVideos--; - } - } - while ((getAll || numberOfVideos > 0) && count == videosData.Length); - - await Task.WhenAll(getStreamsTasks); - - Videos = videos.ToArray(); - } - - #endregion - } -} diff --git a/VDownload.Core/Sources/Twitch/Clip.cs b/VDownload.Core/Sources/Twitch/Clip.cs deleted file mode 100644 index b95f17e..0000000 --- a/VDownload.Core/Sources/Twitch/Clip.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Web; -using VDownload.Core.Enums; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services.Sources.Twitch.Helpers; -using VDownload.Core.Structs; -using Windows.Storage; - -namespace VDownload.Core.Services.Sources.Twitch -{ - [Serializable] - public class Clip : IVideo - { - #region CONSTRUCTORS - - public Clip(string id) - { - Source = VideoSource.TwitchClip; - ID = id; - Url = new Uri($"https://clips.twitch.tv/{ID}"); - } - - #endregion - - - - #region PROPERTIES - - public VideoSource Source { get; private set; } - public string ID { get; private set; } - public Uri Url { get; private set; } - public string Title { get; private set; } - public string Author { get; private set; } - public DateTime Date { get; private set; } - public TimeSpan Duration { get; private set; } - public long Views { get; private set; } - public Uri Thumbnail { get; private set; } - public BaseStream[] BaseStreams { get; private set; } - - #endregion - - - - #region PUBLIC METHODS - - public async Task GetMetadataAsync(CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - JToken response = null; - using (WebClient client = await Client.Helix()) - { - client.QueryString.Add("id", ID); - response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/clips")).GetValue("data"); - if (((JArray)response).Count > 0) response = response[0]; - else throw new MediaNotFoundException($"Twitch Clip (ID: {ID}) was not found"); - } - - Title = (string)response["title"]; - Author = (string)response["broadcaster_name"]; - Date = Convert.ToDateTime(response["created_at"]); - Duration = TimeSpan.FromSeconds((double)response["duration"]); - Views = (long)response["view_count"]; - Thumbnail = new Uri((string)response["thumbnail_url"]); - } - - public async Task GetStreamsAsync(CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - JToken[] response; - using (WebClient client = Client.GQL()) - { - response = JArray.Parse(await client.UploadStringTaskAsync("https://gql.twitch.tv/gql", "[{\"operationName\":\"VideoAccessToken_Clip\",\"variables\":{\"slug\":\"" + ID + "\"},\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11\"}}}]"))[0]["data"]["clip"]["videoQualities"].ToArray(); - } - - List<BaseStream> streams = new List<BaseStream>(); - - foreach (JToken streamData in response) - { - BaseStream stream = new BaseStream() - { - Url = new Uri((string)streamData["sourceURL"]), - Height = int.Parse((string)streamData["quality"]), - FrameRate = (int)streamData["frameRate"], - }; - - streams.Add(stream); - } - - BaseStreams = streams.ToArray(); - } - - public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TrimData trim, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0)); - - JToken videoAccessToken = null; - using (WebClient client = Client.GQL()) - { - videoAccessToken = JArray.Parse(await client.UploadStringTaskAsync("https://gql.twitch.tv/gql", "[{\"operationName\":\"VideoAccessToken_Clip\",\"variables\":{\"slug\":\"" + ID + "\"},\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11\"}}}]"))[0]["data"]["clip"]["playbackAccessToken"]; - } - - // Download - cancellationToken.ThrowIfCancellationRequested(); - StorageFile rawFile = await downloadingFolder.CreateFileAsync("raw.mp4"); - using (WebClient client = new WebClient()) - { - client.DownloadProgressChanged += (s, a) => { DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(a.ProgressPercentage)); }; - client.QueryString.Add("sig", (string)videoAccessToken["signature"]); - client.QueryString.Add("token", HttpUtility.UrlEncode((string)videoAccessToken["value"])); - cancellationToken.ThrowIfCancellationRequested(); - using (cancellationToken.Register(client.CancelAsync)) - { - await client.DownloadFileTaskAsync(baseStream.Url, rawFile.Path); - } - } - DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(100, true)); - - // Processing - StorageFile outputFile = rawFile; - if (extension != MediaFileExtension.MP4 || mediaType != MediaType.AudioVideo || trim.Start != null || trim.End != null) - { - cancellationToken.ThrowIfCancellationRequested(); - outputFile = await downloadingFolder.CreateFileAsync($"transcoded.{extension.ToString().ToLower()}"); - - MediaProcessor mediaProcessor = new MediaProcessor(); - mediaProcessor.ProgressChanged += ProcessingProgressChanged; - - await mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trim, cancellationToken); - } - - return outputFile; - } - - #endregion - - - - #region EVENTS - - public event EventHandler<EventArgs.ProgressChangedEventArgs> DownloadingProgressChanged; - public event EventHandler<EventArgs.ProgressChangedEventArgs> ProcessingProgressChanged; - - #endregion - } -} diff --git a/VDownload.Core/Sources/Twitch/Helpers/Authorization.cs b/VDownload.Core/Sources/Twitch/Helpers/Authorization.cs deleted file mode 100644 index 6e9d480..0000000 --- a/VDownload.Core/Sources/Twitch/Helpers/Authorization.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using VDownload.Core.Sources; -using VDownload.Core.Structs; -using Windows.Storage; - -namespace VDownload.Core.Services.Sources.Twitch.Helpers -{ - public static class Authorization - { - #region CONSTANTS - - public readonly static string ClientID = "yukkqkwp61wsv3u1pya17crpyaa98y"; - public readonly static string GQLApiClientID = "kimne78kx3ncx6brgo4mv6wki5h1ko"; - public readonly static Uri RedirectUrl = new Uri("https://www.vd.com"); - - private readonly static string ResponseType = "token"; - private readonly static string[] Scopes = new[] - { - "user:read:subscriptions", - }; - public readonly static Uri AuthorizationUrl = new Uri($"https://id.twitch.tv/oauth2/authorize?client_id={ClientID}&redirect_uri={RedirectUrl.OriginalString}&response_type={ResponseType}&scope={string.Join(" ", Scopes)}"); - - #endregion - - - - #region METHODS - - public static async Task<string> ReadAccessTokenAsync() - { - try - { - StorageFolder authDataFolder = await AuthorizationData.FolderLocation.GetFolderAsync(AuthorizationData.FolderName); - StorageFile authDataFile = await authDataFolder.GetFileAsync($"Twitch.{AuthorizationData.FilesExtension}"); - - return await FileIO.ReadTextAsync(authDataFile); - } - catch (FileNotFoundException) - { - return null; - } - } - - public static async Task SaveAccessTokenAsync(string accessToken) - { - StorageFolder authDataFolder = await AuthorizationData.FolderLocation.CreateFolderAsync(AuthorizationData.FolderName, CreationCollisionOption.OpenIfExists); - StorageFile authDataFile = await authDataFolder.CreateFileAsync($"Twitch.{AuthorizationData.FilesExtension}", CreationCollisionOption.ReplaceExisting); - - await FileIO.WriteTextAsync(authDataFile, accessToken); - } - - public static async Task DeleteAccessTokenAsync() - { - try - { - StorageFolder authDataFolder = await AuthorizationData.FolderLocation.GetFolderAsync(AuthorizationData.FolderName); - StorageFile authDataFile = await authDataFolder.GetFileAsync($"Twitch.{AuthorizationData.FilesExtension}"); - - await authDataFile.DeleteAsync(); - } - catch (FileNotFoundException) { } - } - - public static async Task<TwitchAccessTokenValidationData> ValidateAccessTokenAsync(string accessToken) - { - WebClient client = new WebClient { Encoding = Encoding.UTF8 }; - client.Headers.Add("Authorization", $"Bearer {accessToken}"); - - try - { - JObject response = JObject.Parse(await client.DownloadStringTaskAsync("https://id.twitch.tv/oauth2/validate")); - - string login = response["login"].ToString(); - DateTime? expirationDate = DateTime.Now.AddSeconds(long.Parse(response["expires_in"].ToString())); - - return new TwitchAccessTokenValidationData(accessToken, true, login, expirationDate); - } - catch (WebException ex) - { - if (ex.Response is null) - { - throw; - } - else - { - JObject exInfo = JObject.Parse(new StreamReader(ex.Response.GetResponseStream()).ReadToEnd()); - if ((int)exInfo["status"] == 401) return new TwitchAccessTokenValidationData(accessToken, false, null, null); - else throw; - } - } - } - - public static async Task RevokeAccessTokenAsync(string accessToken) - { - WebClient client = new WebClient { Encoding = Encoding.UTF8 }; - - await client.UploadStringTaskAsync(new Uri("https://id.twitch.tv/oauth2/revoke"), $"client_id={ClientID}&token={accessToken}"); - } - - #endregion - } -} diff --git a/VDownload.Core/Sources/Twitch/Helpers/Client.cs b/VDownload.Core/Sources/Twitch/Helpers/Client.cs deleted file mode 100644 index d461b6b..0000000 --- a/VDownload.Core/Sources/Twitch/Helpers/Client.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Net; -using System.Threading.Tasks; -using VDownload.Core.Exceptions; - -namespace VDownload.Core.Services.Sources.Twitch.Helpers -{ - internal static class Client - { - internal static async Task<WebClient> Helix() - { - string accessToken = await Authorization.ReadAccessTokenAsync(); - if (accessToken == null) throw new TwitchAccessTokenNotFoundException(); - - var twitchAccessTokenValidation = await Authorization.ValidateAccessTokenAsync(accessToken); - if (!twitchAccessTokenValidation.IsValid) throw new TwitchAccessTokenNotValidException(); - - WebClient client = new WebClient(); - client.Headers.Add("Authorization", $"Bearer {accessToken}"); - client.Headers.Add("Client-Id", Authorization.ClientID); - - return client; - } - - internal static WebClient GQL() - { - WebClient client = new WebClient(); - client.Headers.Add("Client-Id", Authorization.GQLApiClientID); - - return client; - } - } -} diff --git a/VDownload.Core/Sources/Twitch/Vod.cs b/VDownload.Core/Sources/Twitch/Vod.cs deleted file mode 100644 index 5525b96..0000000 --- a/VDownload.Core/Sources/Twitch/Vod.cs +++ /dev/null @@ -1,285 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services.Sources.Twitch.Helpers; -using VDownload.Core.Structs; -using Windows.Storage; - -namespace VDownload.Core.Services.Sources.Twitch -{ - [Serializable] - public class Vod : IVideo - { - #region CONSTRUCTORS - - public Vod(string id) - { - Source = VideoSource.TwitchVod; - ID = id; - Url = new Uri($"https://www.twitch.tv/videos/{ID}"); - } - - #endregion - - - - #region PROPERTIES - - public VideoSource Source { get; private set; } - public string ID { get; private set; } - public Uri Url { get; private set; } - public string Title { get; private set; } - public string Author { get; private set; } - public DateTime Date { get; private set; } - public TimeSpan Duration { get; private set; } - public long Views { get; private set; } - public Uri Thumbnail { get; private set; } - public BaseStream[] BaseStreams { get; private set; } - - #endregion - - - - #region PUBLIC METHODS - - public async Task GetMetadataAsync(CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - JToken response = null; - using (WebClient client = await Client.Helix()) - { - client.QueryString.Add("id", ID); - cancellationToken.ThrowIfCancellationRequested(); - try - { - response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/videos")).GetValue("data")[0]; - } - catch (WebException ex) - { - if (ex.Response != null && new StreamReader(ex.Response.GetResponseStream()).ReadToEnd().Contains("Not Found")) throw new MediaNotFoundException($"Twitch VOD (ID: {ID}) was not found"); - else if (ex.Response != null && new StreamReader(ex.Response.GetResponseStream()).ReadToEnd() == string.Empty && ex.Message.Contains("400")) throw new MediaNotFoundException($"Twitch VOD (ID: {ID}) was not found"); - else throw; - } - } - - GetMetadataAsync(response); - } - internal void GetMetadataAsync(JToken response) - { - Title = ((string)response["title"]).Replace("\n", ""); - Author = (string)response["user_name"]; - Date = Convert.ToDateTime(response["created_at"]); - Duration = ParseDuration((string)response["duration"]); - Views = (long)response["view_count"]; - Thumbnail = (string)response["thumbnail_url"] == string.Empty ? null : new Uri(((string)response["thumbnail_url"]).Replace("%{width}", "1920").Replace("%{height}", "1080")); - } - - public async Task GetStreamsAsync(CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - string[] response = null; - using (WebClient client = Client.GQL()) - { - 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"]; - - cancellationToken.ThrowIfCancellationRequested(); - response = (await client.DownloadStringTaskAsync($"http://usher.twitch.tv/vod/{ID}?nauth={videoAccessToken["value"]}&nauthsig={videoAccessToken["signature"]}&allow_source=true&player=twitchweb")).Split("\n"); - } - - List<BaseStream> streams = new List<BaseStream>(); - - Regex streamDataL2Regex = new Regex(@"^#EXT-X-STREAM-INF:BANDWIDTH=\d+,CODECS=""\S+,\S+"",RESOLUTION=\d+x(?<height>\d+),VIDEO=""\w+""(,FRAME-RATE=(?<frame_rate>\d+.\d+))?"); - - for (int i = 2; i < response.Length; i += 3) - { - Match line2 = streamDataL2Regex.Match(response[i + 1]); - - BaseStream stream = new BaseStream() - { - Url = new Uri(response[i + 2]), - Height = int.Parse(line2.Groups["height"].Value), - FrameRate = line2.Groups["frame_rate"].Value != string.Empty ? (int)Math.Round(double.Parse(line2.Groups["frame_rate"].Value)) : 0, - }; - - streams.Add(stream); - } - - BaseStreams = streams.ToArray(); - } - - public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TrimData trim, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - DownloadingProgressChanged.Invoke(this, new EventArgs.ProgressChangedEventArgs(0)); - List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList = await ExtractChunksFromM3U8Async(baseStream.Url, cancellationToken); - - TimeSpan duration = Duration; - - // Passive trim - if ((bool)Config.GetValue("twitch_vod_passive_trim") && trim.Start != TimeSpan.Zero && trim.End != duration) (trim, duration) = PassiveVideoTrim(chunksList, trim, Duration); - - // Download - cancellationToken.ThrowIfCancellationRequested(); - StorageFile rawFile = await downloadingFolder.CreateFileAsync("raw.ts"); - - double chunksDownloaded = 0; - - Task<byte[]> downloadTask; - Task writeTask; - - cancellationToken.ThrowIfCancellationRequested(); - downloadTask = DownloadChunkAsync(chunksList[0].ChunkUrl); - await downloadTask; - 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 EventArgs.ProgressChangedEventArgs(++chunksDownloaded * 100 / chunksList.Count)); - } - cancellationToken.ThrowIfCancellationRequested(); - await WriteChunkToFileAsync(rawFile, downloadTask.Result); - DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(100, true)); - - // Processing - cancellationToken.ThrowIfCancellationRequested(); - StorageFile outputFile = await downloadingFolder.CreateFileAsync($"transcoded.{extension.ToString().ToLower()}"); - - MediaProcessor mediaProcessor = new MediaProcessor(); - mediaProcessor.ProgressChanged += ProcessingProgressChanged; - - await mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trim, cancellationToken); - - // Return output file - return outputFile; - } - - #endregion - - - - #region PRIVATE METHODS - - private static async Task<List<(Uri ChunkUrl, TimeSpan ChunkDuration)>> ExtractChunksFromM3U8Async(Uri streamUrl, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - string response = null; - using (WebClient client = Client.GQL()) - { - response = await client.DownloadStringTaskAsync(streamUrl); - } - - List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunks = new List<(Uri ChunkUrl, TimeSpan ChunkDuration)>(); - - Regex chunkDataRegex = new Regex(@"#EXTINF:(?<duration>\d+.\d+),\n(?<filename>\S+.ts)"); - - string chunkLocationPath = streamUrl.AbsoluteUri.Replace(Path.GetFileName(streamUrl.AbsoluteUri), ""); - - foreach (Match chunk in chunkDataRegex.Matches(response)) - { - Uri chunkUrl = new Uri($"{chunkLocationPath}{chunk.Groups["filename"].Value}"); - TimeSpan chunkDuration = TimeSpan.FromSeconds(double.Parse(chunk.Groups["duration"].Value)); - chunks.Add((chunkUrl, chunkDuration)); - } - - return chunks; - } - - private static (TrimData Trim, TimeSpan NewDuration) PassiveVideoTrim(List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList, TrimData trim, TimeSpan duration) - { - TimeSpan newDuration = duration; - - while (chunksList[0].ChunkDuration <= trim.Start) - { - trim.Start = trim.Start.Subtract(chunksList[0].ChunkDuration); - trim.End = trim.End.Subtract(chunksList[0].ChunkDuration); - newDuration = newDuration.Subtract(chunksList[0].ChunkDuration); - chunksList.RemoveAt(0); - } - - while (chunksList.Last().ChunkDuration <= newDuration.Subtract(trim.End)) - { - newDuration = newDuration.Subtract(chunksList.Last().ChunkDuration); - chunksList.RemoveAt(chunksList.Count - 1); - } - - return (trim, newDuration); - } - - private static async Task<byte[]> DownloadChunkAsync(Uri chunkUrl, CancellationToken cancellationToken = default) - { - int retriesCount = 0; - while (true) - { - cancellationToken.ThrowIfCancellationRequested(); - try - { - using (WebClient client = new WebClient()) - { - return await client.DownloadDataTaskAsync(chunkUrl); - } - } - catch (WebException wex) - { - if ((bool)Config.GetValue("twitch_vod_downloading_chunk_retry_after_error") && retriesCount < (int)Config.GetValue("twitch_vod_downloading_chunk_max_retries")) - { - retriesCount++; - await Task.Delay((int)Config.GetValue("twitch_vod_downloading_chunk_retries_delay")); - } - else throw wex; - } - } - } - - private static Task WriteChunkToFileAsync(StorageFile file, byte[] chunk) - { - return Task.Factory.StartNew(() => - { - using (var stream = new FileStream(file.Path, FileMode.Append)) - { - stream.Write(chunk, 0, chunk.Length); - stream.Close(); - } - }); - } - - private static TimeSpan ParseDuration(string duration) - { - char[] separators = { 'h', 'm', 's' }; - string[] durationParts = duration.Split(separators, StringSplitOptions.RemoveEmptyEntries).Reverse().ToArray(); - - TimeSpan timeSpan = new TimeSpan( - durationParts.Count() > 2 ? int.Parse(durationParts[2]) : 0, - durationParts.Count() > 1 ? int.Parse(durationParts[1]) : 0, - int.Parse(durationParts[0])); - - return timeSpan; - } - - #endregion - - - - #region EVENTS - - public event EventHandler<EventArgs.ProgressChangedEventArgs> DownloadingProgressChanged; - public event EventHandler<EventArgs.ProgressChangedEventArgs> ProcessingProgressChanged; - - #endregion - } -} diff --git a/VDownload.Core/Structs/BaseStream.cs b/VDownload.Core/Structs/BaseStream.cs deleted file mode 100644 index 321204e..0000000 --- a/VDownload.Core/Structs/BaseStream.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VDownload.Core.Structs -{ - [Serializable] - public struct BaseStream - { - #region PROPERTIES - - public Uri Url { get; set; } - public int Height { get; set; } - public int FrameRate { get; set; } - - #endregion - - - - #region METHODS - - public override string ToString() => $"{Height}p{(FrameRate > 0 ? FrameRate.ToString() : "N/A")}"; - - #endregion - } -} diff --git a/VDownload.Core/Structs/TwitchAccessTokenValidationData.cs b/VDownload.Core/Structs/TwitchAccessTokenValidationData.cs deleted file mode 100644 index b0e3ee9..0000000 --- a/VDownload.Core/Structs/TwitchAccessTokenValidationData.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace VDownload.Core.Structs -{ - public struct TwitchAccessTokenValidationData - { - #region CONSTRUCTORS - - public TwitchAccessTokenValidationData(string accessToken, bool isValid, string login, DateTime? expirationDate) - { - AccessToken = accessToken; - IsValid = isValid; - Login = login; - ExpirationDate = expirationDate; - } - - public static TwitchAccessTokenValidationData Null = new TwitchAccessTokenValidationData(string.Empty, false, string.Empty, null); - - #endregion - - - - #region PROPERTIES - - public string AccessToken { get; private set; } - public bool IsValid { get; private set; } - public string Login { get; private set; } - public DateTime? ExpirationDate { get; private set; } - - #endregion - } -} diff --git a/VDownload.Core/VDownload.Core.Strings/StringResource.cs b/VDownload.Core/VDownload.Core.Strings/StringResource.cs new file mode 100644 index 0000000..5b2fc9f --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/StringResource.cs @@ -0,0 +1,41 @@ +using Windows.ApplicationModel.Resources; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace VDownload.Core.Strings +{ + public class StringResource + { + #region FIELDS + + protected ResourceLoader _resourceLoader; + + #endregion + + + + #region CONSTRUCTORS + + internal StringResource(ResourceLoader resourceLoader) + { + _resourceLoader = resourceLoader; + } + + #endregion + + + + #region PUBLIC METHODS + + public string Get(string key) + { + return _resourceLoader.GetString(key); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Strings/StringResourcesManager.cs b/VDownload.Core/VDownload.Core.Strings/StringResourcesManager.cs new file mode 100644 index 0000000..3bcdad2 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/StringResourcesManager.cs @@ -0,0 +1,57 @@ +using Microsoft.UI.Xaml; +using Windows.ApplicationModel.Resources; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace VDownload.Core.Strings +{ + public static class StringResourcesManager + { + #region PROPERTIES + + public static StringResource BaseView { get; } = BuildResource("BaseViewResources"); + public static StringResource HomeView { get; } = BuildResource("HomeViewResources"); + public static StringResource HomeVideoView { get; } = BuildResource("HomeVideoViewResources"); + public static StringResource HomeVideoCollectionView { get; } = BuildResource("HomeVideoCollectionViewResources"); + public static StringResource HomeDownloadsView { get; } = BuildResource("HomeDownloadsViewResources"); + public static StringResource AuthenticationView { get; } = BuildResource("AuthenticationViewResources"); + public static StringResource Notifications { get; } = BuildResource("NotificationsResources"); + public static StringResource Search { get; } = BuildResource("SearchResources"); + public static StringResource Common { get; } = BuildResource("CommonResources"); + public static StringResource DialogButtons { get; } = BuildResource("DialogButtonsResources"); + public static StringResource SettingsView { get; } = BuildResource("SettingsViewResources"); + public static StringResource FilenameTemplate { get; } = BuildResource("FilenameTemplateResources"); + public static StringResource AboutView { get; } = BuildResource("AboutViewResources"); + public static StringResource SubscriptionsView { get; } = BuildResource("SubscriptionsViewResources"); + + #endregion + + + + #region PRIVATE METHODS + + private static StringResource BuildResource(string resourceName) + { + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", $"teststring {resourceName}\n"); + ResourceLoader loader; + try + { + loader = new ResourceLoader($"VDownload.Core.Strings/{resourceName}"); + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", $"afterteststring {resourceName}\n"); + } + catch (Exception e) + { + + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", $"teststringerror {e.Message}\n"); + throw; + } + return new StringResource(loader); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/AboutViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/AboutViewResources.resw new file mode 100644 index 0000000..f4bd306 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/AboutViewResources.resw @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Developers.Text" xml:space="preserve"> + <value>Developers</value> + </data> + <data name="Donation.Text" xml:space="preserve"> + <value>Donation</value> + </data> + <data name="More.Text" xml:space="preserve"> + <value>More</value> + </data> + <data name="Repository.Text" xml:space="preserve"> + <value>Repository</value> + </data> + <data name="SelfbuiltVersion" xml:space="preserve"> + <value>Self-built version</value> + </data> + <data name="Translation.Text" xml:space="preserve"> + <value>Translation (English (US))</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/AuthenticationViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/AuthenticationViewResources.resw new file mode 100644 index 0000000..5943cab --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/AuthenticationViewResources.resw @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AuthenticationButtonSignIn.Text" xml:space="preserve"> + <value>Sign in</value> + </data> + <data name="AuthenticationButtonSignOut.Text" xml:space="preserve"> + <value>Sign out</value> + </data> + <data name="AuthenticationDescriptionLoading.Value" xml:space="preserve"> + <value>Loading...</value> + </data> + <data name="Header.Text" xml:space="preserve"> + <value>Authentication</value> + </data> + <data name="TwitchAuthenticationDescriptionAuthenticated" xml:space="preserve"> + <value>Signed in as {0}. Expiration date: {1}</value> + </data> + <data name="TwitchAuthenticationDescriptionAuthenticationInvalid" xml:space="preserve"> + <value>Token expired or is invalid. Please log in again</value> + </data> + <data name="TwitchAuthenticationDescriptionCannotValidate" xml:space="preserve"> + <value>Cannot validate token. Check your internet connection and try again later.</value> + </data> + <data name="TwitchAuthenticationDescriptionNotAuthenticated" xml:space="preserve"> + <value>You are not authenticated. Please sign in</value> + </data> + <data name="TwitchAuthenticationDescriptionNotAuthenticatedNoInternetConnection" xml:space="preserve"> + <value>You are not authenticated and there is no internet connection. Check your internet connection and try again later.</value> + </data> + <data name="TwitchAuthenticationDialogMessage" xml:space="preserve"> + <value>An unknown error occured during Twitch login</value> + </data> + <data name="TwitchAuthenticationDialogTitle" xml:space="preserve"> + <value>Twitch authentication error</value> + </data> + <data name="TwitchAuthenticationWindowTitle" xml:space="preserve"> + <value>Sign in to Twitch</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/BaseViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/BaseViewResources.resw new file mode 100644 index 0000000..749a837 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/BaseViewResources.resw @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutNavigationViewItem" xml:space="preserve"> + <value>About</value> + </data> + <data name="AuthenticationNavigationViewItem" xml:space="preserve"> + <value>Authentication</value> + </data> + <data name="HomeNavigationViewItem" xml:space="preserve"> + <value>Home</value> + </data> + <data name="SubscriptionsNavigationViewItem" xml:space="preserve"> + <value>Subscriptions</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/CommonResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/CommonResources.resw new file mode 100644 index 0000000..6aa130d --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/CommonResources.resw @@ -0,0 +1,165 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="MediaTypeOnlyAudio.Text" xml:space="preserve"> + <value>Only audio</value> + </data> + <data name="MediaTypeOnlyVideo.Text" xml:space="preserve"> + <value>Only video</value> + </data> + <data name="MediaTypeOriginal.Text" xml:space="preserve"> + <value>Original</value> + </data> + <data name="ProcessingSpeedFast.Text" xml:space="preserve"> + <value>Fast</value> + </data> + <data name="ProcessingSpeedFaster.Text" xml:space="preserve"> + <value>Faster</value> + </data> + <data name="ProcessingSpeedMedium.Text" xml:space="preserve"> + <value>Medium</value> + </data> + <data name="ProcessingSpeedSlow.Text" xml:space="preserve"> + <value>Slow</value> + </data> + <data name="ProcessingSpeedSlower.Text" xml:space="preserve"> + <value>Slower</value> + </data> + <data name="ProcessingSpeedSuperFast.Text" xml:space="preserve"> + <value>Super fast</value> + </data> + <data name="ProcessingSpeedUltraFast.Text" xml:space="preserve"> + <value>Ultra fast</value> + </data> + <data name="ProcessingSpeedVeryFast.Text" xml:space="preserve"> + <value>Very fast</value> + </data> + <data name="ProcessingSpeedVerySlow.Text" xml:space="preserve"> + <value>Very slow</value> + </data> + <data name="StartAtMeteredConnectionDialogMessage" xml:space="preserve"> + <value>You are trying to download a video using a metered connection. Do you want to continue?</value> + </data> + <data name="StartAtMeteredConnectionDialogTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="SubscriptionVideoListName" xml:space="preserve"> + <value>Subscriptions</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/DialogButtonsResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/DialogButtonsResources.resw new file mode 100644 index 0000000..7470494 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/DialogButtonsResources.resw @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Cancel</value> + </data> + <data name="Close" xml:space="preserve"> + <value>Close</value> + </data> + <data name="No" xml:space="preserve"> + <value>No</value> + </data> + <data name="OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="Yes" xml:space="preserve"> + <value>Yes</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/FilenameTemplateResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/FilenameTemplateResources.resw new file mode 100644 index 0000000..cc05822 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/FilenameTemplateResources.resw @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="author" xml:space="preserve"> + <value>{author} = author</value> + </data> + <data name="date" xml:space="preserve"> + <value>{date:<format>} = publish date in specified format</value> + </data> + <data name="duration" xml:space="preserve"> + <value>{duration:<format>} = duration in specified format</value> + </data> + <data name="id" xml:space="preserve"> + <value>{id} = id (from url)</value> + </data> + <data name="source" xml:space="preserve"> + <value>{source} = source</value> + </data> + <data name="title" xml:space="preserve"> + <value>{title} = title</value> + </data> + <data name="views" xml:space="preserve"> + <value>{views} = views</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeDownloadsViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeDownloadsViewResources.resw new file mode 100644 index 0000000..07e88ad --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeDownloadsViewResources.resw @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="DialogErrorMessageNoInternetConnection" xml:space="preserve"> + <value>No internet connection. Try again later.</value> + </data> + <data name="DialogErrorTitle" xml:space="preserve"> + <value>Error</value> + </data> + <data name="ErrorDownloadingTimeout" xml:space="preserve"> + <value>Downloading timeout. Check your internet connection and try again later.</value> + </data> + <data name="ErrorFFmpeg" xml:space="preserve"> + <value>FFmpeg exited with error.</value> + </data> + <data name="ErrorFFmpegPath" xml:space="preserve"> + <value>No FFmpeg executables found. Check path in settings and try again.</value> + </data> + <data name="StatusCancelled.Text" xml:space="preserve"> + <value>Cancelled</value> + </data> + <data name="StatusDone.Text" xml:space="preserve"> + <value>Done</value> + </data> + <data name="StatusDownloading.Text" xml:space="preserve"> + <value>Downloading</value> + </data> + <data name="StatusError.Text" xml:space="preserve"> + <value>Error</value> + </data> + <data name="StatusFinalizing.Text" xml:space="preserve"> + <value>Finalizing</value> + </data> + <data name="StatusIdle.Text" xml:space="preserve"> + <value>Idle</value> + </data> + <data name="StatusInitializing.Text" xml:space="preserve"> + <value>Initializing</value> + </data> + <data name="StatusProcessing.Text" xml:space="preserve"> + <value>Processing</value> + </data> + <data name="StatusQueued.Text" xml:space="preserve"> + <value>Queued</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeVideoCollectionViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeVideoCollectionViewResources.resw new file mode 100644 index 0000000..512a21d --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeVideoCollectionViewResources.resw @@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="ApplyDirectoryButton.ToolTipService.ToolTip" xml:space="preserve"> + <value>Selected directory will be applied to all videos in playlist</value> + </data> + <data name="CreateAndStartButton.Content" xml:space="preserve"> + <value>Create download tasks and start</value> + </data> + <data name="CreateButton.Content" xml:space="preserve"> + <value>Create download tasks</value> + </data> + <data name="DirectorySettingsCard.Header" xml:space="preserve"> + <value>Directory</value> + </data> + <data name="DirectorySettingsCardButton.Content" xml:space="preserve"> + <value>Browse</value> + </data> + <data name="FilenameSettingsCard.Header" xml:space="preserve"> + <value>Filename</value> + </data> + <data name="FileOptionsHeader.Text" xml:space="preserve"> + <value>File options</value> + </data> + <data name="FileTypeSettingsCard.Description" xml:space="preserve"> + <value>If original video is not in selected type, it will be converted</value> + </data> + <data name="FileTypeSettingsCard.Header" xml:space="preserve"> + <value>File type</value> + </data> + <data name="FilterAuthorTextBlock.Text" xml:space="preserve"> + <value>Author</value> + </data> + <data name="FilterAuthorTextBox.PlaceholderText" xml:space="preserve"> + <value>Enter regular expression</value> + </data> + <data name="FilterDateTextBlock.Text" xml:space="preserve"> + <value>Date</value> + </data> + <data name="FilterDurationTextBlock.Text" xml:space="preserve"> + <value>Duration</value> + </data> + <data name="FilterRemovedButton.Content" xml:space="preserve"> + <value>Restore</value> + </data> + <data name="FilterRemovedTextBlock.Text" xml:space="preserve"> + <value>Removed</value> + </data> + <data name="FilterTitleTextBlock.Text" xml:space="preserve"> + <value>Title</value> + </data> + <data name="FilterTitleTextBox.PlaceholderText" xml:space="preserve"> + <value>Enter regular expression</value> + </data> + <data name="FilterViewsTextBlock.Text" xml:space="preserve"> + <value>Views</value> + </data> + <data name="FilterWindow.Title" xml:space="preserve"> + <value>Filter</value> + </data> + <data name="HiddenTextBlock.Text" xml:space="preserve"> + <value>Hidden: </value> + </data> + <data name="MediaOptionsHeader.Text" xml:space="preserve"> + <value>Media options</value> + </data> + <data name="MediaTypeSettingsCard.Header" xml:space="preserve"> + <value>Media type</value> + </data> + <data name="QualitySettingsCard.Header" xml:space="preserve"> + <value>Quality</value> + </data> + <data name="TrimEndSettingsCard.Header" xml:space="preserve"> + <value>End at</value> + </data> + <data name="TrimSettingsGroup.Header" xml:space="preserve"> + <value>Trim</value> + </data> + <data name="TrimStartSettingsCard.Header" xml:space="preserve"> + <value>Start at</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeVideoViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeVideoViewResources.resw new file mode 100644 index 0000000..5e860a6 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeVideoViewResources.resw @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="CreateAndStartButton.Content" xml:space="preserve"> + <value>Create download task and start</value> + </data> + <data name="CreateButton.Content" xml:space="preserve"> + <value>Create download task</value> + </data> + <data name="DirectorySettingsCard.Header" xml:space="preserve"> + <value>Directory</value> + </data> + <data name="DirectorySettingsCardButton.Content" xml:space="preserve"> + <value>Browse</value> + </data> + <data name="FilenameSettingsCard.Header" xml:space="preserve"> + <value>Filename</value> + </data> + <data name="FileOptionsHeader.Text" xml:space="preserve"> + <value>File options</value> + </data> + <data name="FileTypeSettingsCard.Description" xml:space="preserve"> + <value>If original video is not in selected type, it will be converted</value> + </data> + <data name="FileTypeSettingsCard.Header" xml:space="preserve"> + <value>File type</value> + </data> + <data name="MediaOptionsHeader.Text" xml:space="preserve"> + <value>Media options</value> + </data> + <data name="MediaTypeSettingsCard.Header" xml:space="preserve"> + <value>Media type</value> + </data> + <data name="QualitySettingsCard.Header" xml:space="preserve"> + <value>Quality</value> + </data> + <data name="TrimEndSettingsCard.Header" xml:space="preserve"> + <value>End at</value> + </data> + <data name="TrimSettingsGroup.Header" xml:space="preserve"> + <value>Trim</value> + </data> + <data name="TrimStartSettingsCard.Header" xml:space="preserve"> + <value>Start at</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeViewResources.resw new file mode 100644 index 0000000..6096f2a --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/HomeViewResources.resw @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="ErrorInfoBar.Title" xml:space="preserve"> + <value>Error</value> + </data> + <data name="ErrorInfoBarNoInternetConnection" xml:space="preserve"> + <value>No internet connection</value> + </data> + <data name="OptionBarCancelAll.Label" xml:space="preserve"> + <value>Cancel all</value> + </data> + <data name="OptionBarCancelAll.Width" xml:space="preserve"> + <value>80</value> + </data> + <data name="OptionBarDownloadAll.Label" xml:space="preserve"> + <value>Download all</value> + </data> + <data name="OptionBarDownloadAll.Width" xml:space="preserve"> + <value>100</value> + </data> + <data name="OptionBarLoadSubscription.Label" xml:space="preserve"> + <value>Load from subscriptions</value> + </data> + <data name="OptionBarLoadSubscription.Width" xml:space="preserve"> + <value>150</value> + </data> + <data name="OptionBarMessageLoading" xml:space="preserve"> + <value>Loading...</value> + </data> + <data name="OptionBarMessageVideosFound" xml:space="preserve"> + <value>New videos:</value> + </data> + <data name="OptionBarMessageVideosNotFound" xml:space="preserve"> + <value>No new videos</value> + </data> + <data name="OptionBarPlaylistSearch.Label" xml:space="preserve"> + <value>Playlist search</value> + </data> + <data name="OptionBarPlaylistSearch.Width" xml:space="preserve"> + <value>100</value> + </data> + <data name="OptionBarPlaylistSearchContentNumberBox.ToolTipService.ToolTip" xml:space="preserve"> + <value>Number of videos to get from playlist +0 = Get all</value> + </data> + <data name="OptionBarPlaylistSearchContentTextBox.PlaceholderText" xml:space="preserve"> + <value>Playlist URL</value> + </data> + <data name="OptionBarSearchButton.Content" xml:space="preserve"> + <value>Search</value> + </data> + <data name="OptionBarVideoSearch.Label" xml:space="preserve"> + <value>Video search</value> + </data> + <data name="OptionBarVideoSearch.Width" xml:space="preserve"> + <value>100</value> + </data> + <data name="OptionBarVideoSearchContentTextBox.PlaceholderText" xml:space="preserve"> + <value>Video URL</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/NotificationsResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/NotificationsResources.resw new file mode 100644 index 0000000..2096eb0 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/NotificationsResources.resw @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Author" xml:space="preserve"> + <value>Author</value> + </data> + <data name="Message" xml:space="preserve"> + <value>Message</value> + </data> + <data name="OnSuccessfulTitle" xml:space="preserve"> + <value>Task ended successfully</value> + </data> + <data name="OnUnsuccessfulTitle" xml:space="preserve"> + <value>Task ended unsuccessfully</value> + </data> + <data name="Title" xml:space="preserve"> + <value>Title</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SearchResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SearchResources.resw new file mode 100644 index 0000000..bb15590 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SearchResources.resw @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="EmptyPlaylist" xml:space="preserve"> + <value>Playlist is empty</value> + </data> + <data name="EmptyUrl" xml:space="preserve"> + <value>Invalid url. Url cannot be empty.</value> + </data> + <data name="SearchTimeout" xml:space="preserve"> + <value>Search timeout. Check your internet connection.</value> + </data> + <data name="SourceNotSupported" xml:space="preserve"> + <value>Invalid url. Url does not match any of supported source.</value> + </data> + <data name="TwitchChannelNotFound" xml:space="preserve"> + <value>Invalid url. Twitch channel not found.</value> + </data> + <data name="TwitchClipNotFound" xml:space="preserve"> + <value>Invalid url. Twitch clip not found.</value> + </data> + <data name="TwitchNotAuthenticated" xml:space="preserve"> + <value>Twitch authentication error. Not authenticated to Twitch.</value> + </data> + <data name="TwitchTokenValidationUnsuccessful" xml:space="preserve"> + <value>Twitch authentication error. Authentication token is invalid.</value> + </data> + <data name="TwitchVodNotFound" xml:space="preserve"> + <value>Invalid url. Twitch VOD not found.</value> + </data> + <data name="UnknownMediaType" xml:space="preserve"> + <value>Invalid url. Url does not match any of supported source's media types.</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload/Strings/en-US/DialogResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SettingsViewResources.resw similarity index 53% rename from VDownload/Strings/en-US/DialogResources.resw rename to VDownload.Core/VDownload.Core.Strings/Strings/en-US/SettingsViewResources.resw index 33f7103..f72871d 100644 --- a/VDownload/Strings/en-US/DialogResources.resw +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SettingsViewResources.resw @@ -117,100 +117,124 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> - <data name="Base_CancelButtonText" xml:space="preserve"> - <value>Cancel</value> + <data name="Header.Text" xml:space="preserve"> + <value>Settings</value> </data> - <data name="Base_CloseButtonText" xml:space="preserve"> - <value>OK</value> + <data name="NotificationsHeader.Text" xml:space="preserve"> + <value>Notifications</value> </data> - <data name="Home_DownloadAll_MeteredConnection_Content" xml:space="preserve"> - <value>You are on the metered connection now. You can delay tasks until the network changes. Do you want to start the tasks?</value> + <data name="NotificationsOnSuccessful.Header" xml:space="preserve"> + <value>Show notifications when task ended successfully</value> </data> - <data name="Home_DownloadAll_MeteredConnection_StartWithDelayButtonText" xml:space="preserve"> - <value>Yes (With delay)</value> + <data name="NotificationsOnUnsuccessful.Header" xml:space="preserve"> + <value>Show notifications when task ended unsuccessfully</value> </data> - <data name="Home_DownloadAll_MeteredConnection_StartWithoutDelayButtonText" xml:space="preserve"> - <value>Yes (Without delay)</value> + <data name="ProcessingFFmpegLocation.Header" xml:space="preserve"> + <value>FFmpeg location</value> </data> - <data name="Home_DownloadAll_MeteredConnection_Title" xml:space="preserve"> - <value>Metered connection detected</value> + <data name="ProcessingFFmpegLocationButton.Content" xml:space="preserve"> + <value>Browse</value> </data> - <data name="Home_DownloadTaskControl_Start_MeteredConnection_Content" xml:space="preserve"> - <value>You are on the metered connection now. You can delay task until the network changes. Do you want to start the task?</value> + <data name="ProcessingHeader.Text" xml:space="preserve"> + <value>Processing</value> </data> - <data name="Home_DownloadTaskControl_Start_MeteredConnection_StartWithDelayButtonText" xml:space="preserve"> - <value>Yes (With delay)</value> + <data name="ProcessingSpeed.Header" xml:space="preserve"> + <value>Speed</value> </data> - <data name="Home_DownloadTaskControl_Start_MeteredConnection_StartWithoutDelayButtonText1" xml:space="preserve"> - <value>Yes (Without delay)</value> + <data name="ProcessingUseHardwareAcceleration.Header" xml:space="preserve"> + <value>Use hardware acceleration</value> </data> - <data name="Home_DownloadTaskControl_Start_MeteredConnection_Title" xml:space="preserve"> - <value>Metered connection detected</value> + <data name="ProcessingUseMultithreading.Header" xml:space="preserve"> + <value>Use multithreading</value> </data> - <data name="Home_OptionsBar_PlaylistSearchControl_Base_Title" xml:space="preserve"> - <value>Playlist search error</value> + <data name="RestoreToDefaultButton.Content" xml:space="preserve"> + <value>Restore settings to default</value> </data> - <data name="Home_OptionsBar_PlaylistSearchControl_InternetNotAvailable_Content" xml:space="preserve"> - <value>Unable to connect to servers. Check your internet connection.</value> + <data name="SearchingHeader.Text" xml:space="preserve"> + <value>Searching</value> </data> - <data name="Home_OptionsBar_PlaylistSearchControl_TwitchAccessTokenNotFound_Content" xml:space="preserve"> - <value>To get information about Twitch playlists (Channels), you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value> + <data name="SearchingPlaylistCount.Description" xml:space="preserve"> + <value>0 = Get all</value> </data> - <data name="Home_OptionsBar_PlaylistSearchControl_TwitchAccessTokenNotValid_Content" xml:space="preserve"> - <value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value> + <data name="SearchingPlaylistCount.Header" xml:space="preserve"> + <value>Default number of videos fetched from playlist</value> </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_Base_Title" xml:space="preserve"> - <value>Subscriptions loading error</value> + <data name="TasksAudioExtension.Header" xml:space="preserve"> + <value>Audio extension</value> </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_InternetNotAvailable_Content" xml:space="preserve"> - <value>Unable to connect to servers. Check your internet connection.</value> + <data name="TasksDefaultMediaOptions.Header" xml:space="preserve"> + <value>Default media options</value> </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_MediaNotFound_Content" xml:space="preserve"> - <value>Subscribed playlist not found. Name:</value> + <data name="TasksDefaultOutputDirectory.Header" xml:space="preserve"> + <value>Default output directory</value> </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_TwitchAccessTokenNotFound_Content" xml:space="preserve"> - <value>To get information about Twitch videos and playlists, you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value> + <data name="TasksDefaultOutputDirectoryButton.Content" xml:space="preserve"> + <value>Browse</value> </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_TwitchAccessTokenNotValid_Content" xml:space="preserve"> - <value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value> + <data name="TasksFilenameTemplate.Header" xml:space="preserve"> + <value>Filename template</value> </data> - <data name="Home_OptionsBar_VideoSearchControl_Base_Title" xml:space="preserve"> - <value>Video search error</value> + <data name="TasksHeader.Text" xml:space="preserve"> + <value>Tasks</value> </data> - <data name="Home_OptionsBar_VideoSearchControl_InternetNotAvailable_Content" xml:space="preserve"> - <value>Unable to connect to servers. Check your internet connection.</value> + <data name="TasksMediaType.Header" xml:space="preserve"> + <value>Media type</value> </data> - <data name="Home_OptionsBar_VideoSearchControl_TwitchAccessTokenNotFound_Content" xml:space="preserve"> - <value>To get information about Twitch videos (VODs and Clips), you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value> + <data name="TasksMeteredConnectionWarning.Header" xml:space="preserve"> + <value>Show warning before downloading on a metered connection</value> </data> - <data name="Home_OptionsBar_VideoSearchControl_TwitchAccessTokenNotValid_Content" xml:space="preserve"> - <value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value> + <data name="TasksReplaceOutputFile.Description" xml:space="preserve"> + <value>If file with same name as output file, already exists, it will be replaced. Otherwise new file with unique name will be created.</value> </data> - <data name="Sources_TwitchLogin_Base_Title" xml:space="preserve"> - <value>Login to Twitch failed</value> + <data name="TasksReplaceOutputFile.Header" xml:space="preserve"> + <value>Replace output file if exists</value> </data> - <data name="Sources_TwitchLogin_InternetNotAvailable_Content" xml:space="preserve"> - <value>Unable to connect to servers. Check your internet connection.</value> + <data name="TasksRunningTasks.Header" xml:space="preserve"> + <value>Maximum number of tasks running in parallel</value> </data> - <data name="Sources_TwitchLogin_Unknown_Content" xml:space="preserve"> - <value>Unknown error. </value> + <data name="TasksSaveLastOutputDirectory.Description" xml:space="preserve"> + <value>Otherwise, default directory defined below (after expanding) will be used</value> </data> - <data name="Subscription_Adding_Base_Title" xml:space="preserve"> - <value>Playlist adding error</value> + <data name="TasksSaveLastOutputDirectory.Header" xml:space="preserve"> + <value>Save last output directory</value> </data> - <data name="Subscription_Adding_InternetNotAvailable_Content" xml:space="preserve"> - <value>Unable to connect to servers. Check your internet connection.</value> + <data name="TasksVideoExtension.Header" xml:space="preserve"> + <value>Video extension</value> </data> - <data name="Subscription_Adding_PlaylistNotFound_Content" xml:space="preserve"> - <value>Playlist not found. Check the URL.</value> + <data name="TempDeleteOnFail.Header" xml:space="preserve"> + <value>Delete task's temporary files when an error occurs</value> </data> - <data name="Subscription_Adding_SubscriptionExists_Content" xml:space="preserve"> - <value>This playlist has been already subscribed</value> + <data name="TempDirectory.Header" xml:space="preserve"> + <value>Temporary files location</value> </data> - <data name="Subscription_Adding_TwitchAccessTokenNotFound_Content" xml:space="preserve"> - <value>To be able to subscribe Twitch playlists (Channels), you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value> + <data name="TempDirectoryButton.Content" xml:space="preserve"> + <value>Browse</value> </data> - <data name="Subscription_Adding_TwitchAccessTokenNotValid_Content" xml:space="preserve"> - <value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value> + <data name="TempHeader.Text" xml:space="preserve"> + <value>Temporary files</value> + </data> + <data name="TwitchHeader.Text" xml:space="preserve"> + <value>Twitch</value> + </data> + <data name="TwitchVodChunkDownloadingErrorRetry.Header" xml:space="preserve"> + <value>Retry when downloading single VOD chunk fails</value> + </data> + <data name="TwitchVodChunkDownloadingErrorRetryCount.Header" xml:space="preserve"> + <value>Number of retries</value> + </data> + <data name="TwitchVodChunkDownloadingErrorRetryDelay.Header" xml:space="preserve"> + <value>Time between fail and retry (in miliseconds)</value> + </data> + <data name="TwitchVodParallelDownloads.Description" xml:space="preserve"> + <value>WARNING: Too many chunks downloaded at once may cause performance problems</value> + </data> + <data name="TwitchVodParallelDownloads.Header" xml:space="preserve"> + <value>Maximum number of VOD chunks downloaded in parallel</value> + </data> + <data name="TwitchVodPassiveTrimming.Description" xml:space="preserve"> + <value>Only chunks from trim start to trim end range will be downloaded</value> + </data> + <data name="TwitchVodPassiveTrimming.Header" xml:space="preserve"> + <value>VOD passive trimming</value> </data> </root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SubscriptionsViewResources.resw b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SubscriptionsViewResources.resw new file mode 100644 index 0000000..8f03e2f --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/Strings/en-US/SubscriptionsViewResources.resw @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="DuplicateError" xml:space="preserve"> + <value>Playlist has been already added</value> + </data> + <data name="ErrorInfoBar.Title" xml:space="preserve"> + <value>Error</value> + </data> + <data name="Header.Text" xml:space="preserve"> + <value>Subscriptions</value> + </data> + <data name="NoInternetConnectionError" xml:space="preserve"> + <value>No internet connection</value> + </data> + <data name="PlaylistSearchButton.Content" xml:space="preserve"> + <value>Add</value> + </data> + <data name="PlaylistUrlTextBox.PlaceholderText" xml:space="preserve"> + <value>Playlist URL</value> + </data> + <data name="RemovePlaylistButton.Content" xml:space="preserve"> + <value>Remove</value> + </data> +</root> \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Strings/VDownload.Core.Strings.csproj b/VDownload.Core/VDownload.Core.Strings/VDownload.Core.Strings.csproj new file mode 100644 index 0000000..146088e --- /dev/null +++ b/VDownload.Core/VDownload.Core.Strings/VDownload.Core.Strings.csproj @@ -0,0 +1,62 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Core.Strings</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + </ItemGroup> + + <ItemGroup> + <PRIResource Update="Strings\en-US\AboutViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\AuthenticationViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\BaseViewResources.resw"> + <Generator></Generator> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\CommonResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\DialogButtonsResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\FilenameTemplateResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\HomeDownloadsViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\HomeVideoCollectionViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\HomeVideoViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\HomeViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\NotificationsResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\SearchResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\SettingsViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + <PRIResource Update="Strings\en-US\SubscriptionsViewResources.resw"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </PRIResource> + </ItemGroup> +</Project> diff --git a/VDownload.Core/VDownload.Core.Tasks/DownloadTask.cs b/VDownload.Core/VDownload.Core.Tasks/DownloadTask.cs new file mode 100644 index 0000000..f855683 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Tasks/DownloadTask.cs @@ -0,0 +1,292 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using VDownload.Models; +using VDownload.Services.Utility.FFmpeg; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using Windows.System; +using System.Threading; +using System.Threading.Tasks; +using System; +using System.Linq; +using Windows.Storage; +using System.IO; +using VDownload.Services.UI.Notifications; +using System.Collections.Generic; +using System.Net.Http; +using Instances.Exceptions; +using FFMpegCore.Exceptions; +using VDownload.Core.Strings; + +namespace VDownload.Core.Tasks +{ + public partial class DownloadTask : ObservableObject + { + #region ENUMS + + private enum TaskResult + { + Success, + Cancellation, + Error + } + + #endregion + + + + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + protected readonly IFFmpegService _ffmpegService; + protected readonly INotificationsService _notificationsService; + + #endregion + + + + #region FIELDS + + private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + + private CancellationTokenSource? _cancellationTokenSource; + + private Task _downloadTask; + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + protected Guid _id; + + [ObservableProperty] + protected Video _video; + + [ObservableProperty] + protected VideoDownloadOptions _downloadOptions; + + [ObservableProperty] + protected DownloadTaskStatus _status; + + [ObservableProperty] + protected DateTime _createDate; + + [ObservableProperty] + protected double _progress; + + [ObservableProperty] + protected string? _error; + + #endregion + + + + #region CONSTRUCTORS + + internal DownloadTask(Video video, VideoDownloadOptions downloadOptions, IConfigurationService configurationService, ISettingsService settingsService, IFFmpegService ffmpegService, INotificationsService notificationsService) + { + _configurationService = configurationService; + _settingsService = settingsService; + _ffmpegService = ffmpegService; + _notificationsService = notificationsService; + + _video = video; + _downloadOptions = downloadOptions; + _id = Guid.NewGuid(); + _status = DownloadTaskStatus.Idle; + _createDate = DateTime.Now; + _progress = 0; + } + + #endregion + + + + #region PUBLIC METHODS + + public void Enqueue() + { + DownloadTaskStatus[] statuses = + [ + DownloadTaskStatus.Idle, + DownloadTaskStatus.EndedUnsuccessfully, + DownloadTaskStatus.EndedSuccessfully, + DownloadTaskStatus.EndedCancelled, + ]; + if (statuses.Contains(Status)) + { + Status = DownloadTaskStatus.Queued; + } + } + + internal void Start() + { + _cancellationTokenSource = new CancellationTokenSource(); + + UpdateStatusWithDispatcher(DownloadTaskStatus.Initializing); + + _downloadTask = Download(); + } + + public async Task Cancel() + { + if (_cancellationTokenSource is not null) + { + _cancellationTokenSource.Cancel(); + await _downloadTask; + _cancellationTokenSource.Dispose(); + _cancellationTokenSource = null; + } + } + + #endregion + + + + #region PRIVATE METHODS + + private async Task Download() + { + CancellationToken token = _cancellationTokenSource.Token; + + await _settingsService.Load(); + + string tempDirectory = $"{_settingsService.Data.Common.Temp.Directory}\\{_configurationService.Common.Path.Temp.TasksDirectory}\\{Guid.NewGuid()}"; + if (Directory.Exists(tempDirectory)) + { + Directory.Delete(tempDirectory, true); + } + Directory.CreateDirectory(tempDirectory); + + List<string> content = new List<string>() + { + $"{StringResourcesManager.Notifications.Get("Title")}: {Video.Title}", + $"{StringResourcesManager.Notifications.Get("Author")}: {Video.Author}" + }; + + string errorMessage = null; + TaskResult? endingStatus = null; + try + { + IProgress<double> onProgressDownloading = new Progress<double>((value) => + { + UpdateStatusWithDispatcher(DownloadTaskStatus.Downloading); + UpdateProgressWithDispatcher(value); + }); + + VideoStreamDownloadResult downloadResult = await DownloadOptions.SelectedStream.Download(tempDirectory, onProgressDownloading, token, Video.Duration, DownloadOptions.TrimStart, DownloadOptions.TrimEnd); + + Action<double> onProgressProcessing = (value) => + { + UpdateStatusWithDispatcher(DownloadTaskStatus.Processing); + UpdateProgressWithDispatcher(value); + }; + string outputPath = $"{tempDirectory}\\processed.{DownloadOptions.Extension}"; + + FFmpegBuilder ffmpegBuilder = _ffmpegService.CreateBuilder(); + if (downloadResult.NewTrimStart != TimeSpan.Zero) + { + ffmpegBuilder.TrimStart(downloadResult.NewTrimStart); + } + if (downloadResult.NewTrimEnd != downloadResult.NewDuration) + { + ffmpegBuilder.TrimEnd(downloadResult.NewTrimEnd); + } + ffmpegBuilder.SetMediaType(DownloadOptions.MediaType) + .JoinCancellationToken(token) + .JoinProgressReporter(onProgressProcessing, downloadResult.NewDuration); + await ffmpegBuilder.RunAsync(downloadResult.File, outputPath); + + StorageFile outputFile = await StorageFile.GetFileFromPathAsync(outputPath); + + UpdateStatusWithDispatcher(DownloadTaskStatus.Finalizing); + + await Finalizing(outputFile); + + UpdateStatusWithDispatcher(DownloadTaskStatus.EndedSuccessfully); + endingStatus = TaskResult.Success; + } + catch (OperationCanceledException) + { + UpdateStatusWithDispatcher(DownloadTaskStatus.EndedCancelled); + endingStatus = TaskResult.Cancellation; + } + catch (Exception ex) + { + string message; + + if (ex is TaskCanceledException || ex is HttpRequestException) + { + message = StringResourcesManager.HomeDownloadsView.Get("ErrorDownloadingTimeout"); + } + else if (ex is InstanceFileNotFoundException) + { + message = StringResourcesManager.HomeDownloadsView.Get("ErrorFFmpegPath"); + } + else if (ex is FFMpegException) + { + message = StringResourcesManager.HomeDownloadsView.Get("ErrorFFmpeg"); + } + else + { + message = ex.Message; + } + + UpdateErrorWithDispatcher(message); + UpdateStatusWithDispatcher(DownloadTaskStatus.EndedUnsuccessfully); + endingStatus = TaskResult.Error; + errorMessage = message; + } + finally + { + switch (endingStatus) + { + case TaskResult.Error: + if (_settingsService.Data.Common.Notifications.OnUnsuccessful) + { + string title = StringResourcesManager.Notifications.Get("OnUnsuccessfulTitle"); + content.Add($"{StringResourcesManager.Notifications.Get("Message")}: {errorMessage}"); + _notificationsService.SendNotification(title, content); + } + break; + case TaskResult.Success: + if (_settingsService.Data.Common.Notifications.OnSuccessful) + { + string title = StringResourcesManager.Notifications.Get("OnSuccessfulTitle"); + _notificationsService.SendNotification(title, content); + } + break; + } + + if (Status != DownloadTaskStatus.EndedUnsuccessfully || _settingsService.Data.Common.Temp.DeleteOnError) + { + Directory.Delete(tempDirectory, true); + } + } + } + + protected async Task Finalizing(StorageFile outputFile) + { + StorageFolder destination = await StorageFolder.GetFolderFromPathAsync(DownloadOptions.Directory); + + string filename = $"{DownloadOptions.Filename}.{DownloadOptions.Extension}"; + + NameCollisionOption nameCollisionOption = _settingsService.Data.Common.Tasks.ReplaceOutputFileIfExists + ? NameCollisionOption.ReplaceExisting + : NameCollisionOption.GenerateUniqueName; + + await outputFile.CopyAsync(destination, filename, nameCollisionOption); + } + + protected void UpdateStatusWithDispatcher(DownloadTaskStatus status) => _dispatcherQueue.TryEnqueue(() => Status = status); + + protected void UpdateProgressWithDispatcher(double progress) => _dispatcherQueue.TryEnqueue(() => Progress = progress); + + protected void UpdateErrorWithDispatcher(string message) => _dispatcherQueue.TryEnqueue(() => Error = message); + + #endregion + } +} \ No newline at end of file diff --git a/VDownload.Core/VDownload.Core.Tasks/DownloadTaskFactoryService.cs b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskFactoryService.cs new file mode 100644 index 0000000..1e25a64 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskFactoryService.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using VDownload.Services.UI.Notifications; +using VDownload.Services.Utility.FFmpeg; + +namespace VDownload.Core.Tasks +{ + public interface IDownloadTaskFactoryService + { + DownloadTask Create(Video video, VideoDownloadOptions downloadOptions); + } + + + + public class DownloadTaskFactoryService : IDownloadTaskFactoryService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + protected readonly IFFmpegService _ffmpegService; + protected readonly INotificationsService _notificationsService; + + #endregion + + + + #region CONSTRUCTORS + + public DownloadTaskFactoryService(IConfigurationService configurationService, ISettingsService settingsService, IFFmpegService ffmpegService, INotificationsService notificationsService) + { + _configurationService = configurationService; + _settingsService = settingsService; + _ffmpegService = ffmpegService; + _notificationsService = notificationsService; + } + + #endregion + + + + #region PUBLIC METHODS + + public DownloadTask Create(Video video, VideoDownloadOptions downloadOptions) + { + return new DownloadTask(video, downloadOptions, _configurationService, _settingsService, _ffmpegService, _notificationsService); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Tasks/DownloadTaskManager.cs b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskManager.cs new file mode 100644 index 0000000..7b29197 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskManager.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Settings; + +namespace VDownload.Core.Tasks +{ + public interface IDownloadTaskManager + { + #region PROPERTIES + + ReadOnlyObservableCollection<DownloadTask> Tasks { get; } + + #endregion + + + + #region EVENTS + + event NotifyCollectionChangedEventHandler TaskCollectionChanged; + + #endregion + + + + #region METHODS + + DownloadTask AddTask(Video video, VideoDownloadOptions downloadOptions); + void RemoveTask(DownloadTask task); + + #endregion + } + + + + public class DownloadTaskManager : IDownloadTaskManager + { + #region SERVICES + + protected readonly IDownloadTaskFactoryService _downloadTaskFactoryService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region FIELDS + + private readonly Thread _taskMonitorThread; + + private readonly ObservableCollection<DownloadTask> _tasksMain; + + #endregion + + + + #region PROPERTIES + + public ReadOnlyObservableCollection<DownloadTask> Tasks { get; protected set; } + + #endregion + + + + #region EVENTS + + public event NotifyCollectionChangedEventHandler TaskCollectionChanged; + + #endregion + + + + #region CONSTRUCTORS + + public DownloadTaskManager(IDownloadTaskFactoryService downloadTaskFactoryService, ISettingsService settingsService) + { + _downloadTaskFactoryService = downloadTaskFactoryService; + _settingsService = settingsService; + + _tasksMain = new ObservableCollection<DownloadTask>(); + _tasksMain.CollectionChanged += Tasks_CollectionChanged; + Tasks = new ReadOnlyObservableCollection<DownloadTask>(_tasksMain); + + _taskMonitorThread = new Thread(TaskMonitor) + { + IsBackground = true + }; + _taskMonitorThread.Start(); + } + + #endregion + + + + #region PUBLIC METHODS + + public DownloadTask AddTask(Video video, VideoDownloadOptions downloadOptions) + { + DownloadTask task = _downloadTaskFactoryService.Create(video, downloadOptions); + _tasksMain.Add(task); + return task; + } + + public void RemoveTask(DownloadTask task) + { + _tasksMain.Remove(task); + } + + #endregion + + + + #region PRIVATE METHODS + + private async void TaskMonitor() + { + await _settingsService.Load(); + + DownloadTaskStatus[] pendingStatuses = + [ + DownloadTaskStatus.Initializing, + DownloadTaskStatus.Downloading, + DownloadTaskStatus.Processing, + DownloadTaskStatus.Finalizing + ]; + while (true) + { + try + { + IEnumerable<DownloadTask> pendingTasks = Tasks.Where(x => pendingStatuses.Contains(x.Status)); + int freeSlots = _settingsService.Data.Common.Tasks.MaxNumberOfRunningTasks - pendingTasks.Count(); + if (freeSlots > 0) + { + IEnumerable<DownloadTask> queuedTasks = Tasks.Where(x => x.Status == DownloadTaskStatus.Queued).OrderBy(x => x.CreateDate).Take(freeSlots); + foreach (DownloadTask queuedTask in queuedTasks) + { + queuedTask.Start(); + } + } + } + catch (InvalidOperationException) + { + Console.WriteLine("TaskMonitor: Collection locked - skipping"); + } + } + } + + #endregion + + + + #region EVENT HANDLERS + + private void Tasks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + TaskCollectionChanged?.Invoke(this, e); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Tasks/DownloadTaskNotInitializedException.cs b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskNotInitializedException.cs new file mode 100644 index 0000000..4b47b20 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskNotInitializedException.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Core.Tasks +{ + public class DownloadTaskNotInitializedException : Exception + { + #region CONSTRUCTORS + + public DownloadTaskNotInitializedException() : base() { } + + public DownloadTaskNotInitializedException(string message) : base(message) { } + + public DownloadTaskNotInitializedException(string message, Exception inner) : base(message, inner) { } + + #endregion + } +} diff --git a/VDownload.Core/Enums/DownloadTaskStatus.cs b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskStatus.cs similarity index 50% rename from VDownload.Core/Enums/DownloadTaskStatus.cs rename to VDownload.Core/VDownload.Core.Tasks/DownloadTaskStatus.cs index 0997584..b6fa6d1 100644 --- a/VDownload.Core/Enums/DownloadTaskStatus.cs +++ b/VDownload.Core/VDownload.Core.Tasks/DownloadTaskStatus.cs @@ -1,14 +1,21 @@ -namespace VDownload.Core.Enums +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Core.Tasks { public enum DownloadTaskStatus { Idle, - Scheduled, - Queued, - Downloading, - Processing, - Finalizing, EndedSuccessfully, EndedUnsuccessfully, + EndedCancelled, + Queued, + Initializing, + Finalizing, + Processing, + Downloading, } } diff --git a/VDownload.Core/VDownload.Core.Tasks/VDownload.Core.Tasks.csproj b/VDownload.Core/VDownload.Core.Tasks/VDownload.Core.Tasks.csproj new file mode 100644 index 0000000..ae5c5fc --- /dev/null +++ b/VDownload.Core/VDownload.Core.Tasks/VDownload.Core.Tasks.csproj @@ -0,0 +1,26 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Core.Tasks</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Models\VDownload.Models.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Settings\VDownload.Services.Data.Settings.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.Notifications\VDownload.Services.UI.Notifications.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.FFmpeg\VDownload.Services.Utility.FFmpeg.csproj" /> + <ProjectReference Include="..\VDownload.Core.Strings\VDownload.Core.Strings.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Core/VDownload.Core.ViewModels/About/AboutViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/About/AboutViewModel.cs new file mode 100644 index 0000000..6d824a3 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/About/AboutViewModel.cs @@ -0,0 +1,86 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Core.ViewModels.About.Helpers; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Configuration.Models; +using Windows.System.UserProfile; + +namespace VDownload.Core.ViewModels.About +{ + public partial class AboutViewModel : ObservableObject + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + protected string _version; + + [ObservableProperty] + protected ObservableCollection<PersonViewModel> _developers; + + [ObservableProperty] + protected ObservableCollection<PersonViewModel> _translators; + + [ObservableProperty] + protected Uri _repositoryUrl; + + [ObservableProperty] + protected Uri _donationUrl; + + #endregion + + + + #region CONSTRUCTORS + + public AboutViewModel(IConfigurationService configurationService) + { + _configurationService = configurationService; + + string version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion; + if (version == "0.0.0") + { + version = StringResourcesManager.AboutView.Get("SelfbuiltVersion"); + } + _version = version; + + _developers = new ObservableCollection<PersonViewModel>(_configurationService.Common.About.Developers.Select(x => new PersonViewModel(x.Name, x.Url))); + + _repositoryUrl = new Uri(_configurationService.Common.About.RepositoryUrl); + _donationUrl = new Uri(_configurationService.Common.About.DonationUrl); + } + + #endregion + + + + #region COMMANDS + + [RelayCommand] + public void Navigation() + { + string languageCode = "en-US"; + Language language = _configurationService.Common.About.Translation.FirstOrDefault(x => x.Code == languageCode); + Translators = new ObservableCollection<PersonViewModel>(language.Translators.Select(x => new PersonViewModel(x.Name, x.Url))); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/About/Helpers/PersonViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/About/Helpers/PersonViewModel.cs new file mode 100644 index 0000000..979fcc1 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/About/Helpers/PersonViewModel.cs @@ -0,0 +1,34 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Core.ViewModels.About.Helpers +{ + public partial class PersonViewModel : ObservableObject + { + #region PROPERTIES + + [ObservableProperty] + protected string _name; + + [ObservableProperty] + protected Uri _url; + + #endregion + + + + #region CONSTRUCTORS + + public PersonViewModel(string name, string url) + { + _name = name; + _url = new Uri(url); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Authentication/AuthenticationViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Authentication/AuthenticationViewModel.cs new file mode 100644 index 0000000..1c210df --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Authentication/AuthenticationViewModel.cs @@ -0,0 +1,178 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.WinUI.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Services.Data.Configuration; +using VDownload.Services.UI.Dialogs; +using VDownload.Services.UI.WebView; +using VDownload.Sources.Twitch.Authentication; + +namespace VDownload.Core.ViewModels.Authentication +{ + public partial class AuthenticationViewModel : ObservableObject + { + #region ENUMS + + public enum AuthenticationButton + { + SignIn, + SignOut, + Loading + } + + #endregion + + + + #region SERVICES + + protected readonly IDialogsService _dialogsService; + protected readonly IWebViewService _webViewService; + protected readonly IConfigurationService _configurationService; + protected readonly ITwitchAuthenticationService _twitchAuthenticationService; + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + protected AuthenticationButton _twitchButtonState = AuthenticationButton.Loading; + + [ObservableProperty] + protected bool _twitchButtonEnable = true; + + [ObservableProperty] + protected string _twitchDescription; + + #endregion + + + + #region CONSTRUCTORS + + public AuthenticationViewModel(IDialogsService dialogsService, IWebViewService webViewService, IConfigurationService configurationService, ITwitchAuthenticationService twitchAuthenticationService) + { + _dialogsService = dialogsService; + _webViewService = webViewService; + _configurationService = configurationService; + _twitchAuthenticationService = twitchAuthenticationService; + } + + #endregion + + + + #region PUBLIC METHODS + + [RelayCommand] + public async Task Navigation() + { + List<Task> refreshTasks = new List<Task> + { + TwitchAuthenticationRefresh() + }; + await Task.WhenAll(refreshTasks); + } + + [RelayCommand] + public async Task TwitchAuthentication() + { + AuthenticationButton state = TwitchButtonState; + TwitchButtonState = AuthenticationButton.Loading; + + if (state == AuthenticationButton.SignOut) + { + await _twitchAuthenticationService.DeleteToken(); + } + else + { + Sources.Twitch.Configuration.Models.Authentication auth = _configurationService.Twitch.Authentication; + string authUrl = string.Format(auth.Url, auth.ClientId, auth.RedirectUrl, auth.ResponseType, string.Join(' ', auth.Scopes)); + + string url = await _webViewService.Show(new Uri(authUrl), (url) => url.StartsWith(auth.RedirectUrl), StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationWindowTitle")); + + Regex regex = new Regex(auth.RedirectUrlRegex); + Match match = regex.Match(url); + + if (match.Success) + { + string token = match.Groups[1].Value; + await _twitchAuthenticationService.SetToken(Encoding.UTF8.GetBytes(token)); + } + else + { + string title = StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationDialogTitle"); + string message = StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationDialogMessage"); + await _dialogsService.ShowOk(title, message); + } + } + await TwitchAuthenticationRefresh(); + } + + #endregion + + + + #region PRIVATE METHODS + + private async Task TwitchAuthenticationRefresh() + { + TwitchButtonState = AuthenticationButton.Loading; + TwitchButtonEnable = true; + + byte[]? token = await _twitchAuthenticationService.GetToken(); + + if (token is null) + { + if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) + { + TwitchButtonEnable = false; + TwitchDescription = StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationDescriptionNotAuthenticatedNoInternetConnection"); + } + else + { + TwitchDescription = StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationDescriptionNotAuthenticated"); + } + TwitchButtonState = AuthenticationButton.SignIn; + } + else + { + TwitchValidationResult validationResult; + try + { + validationResult = await _twitchAuthenticationService.ValidateToken(token); + } + catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException) + { + TwitchDescription = StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationDescriptionCannotValidate"); + TwitchButtonState = AuthenticationButton.SignIn; + TwitchButtonEnable = false; + return; + } + + if (validationResult.Success) + { + TwitchDescription = string.Format(StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationDescriptionAuthenticated"), validationResult.TokenData.Login, validationResult.TokenData.ExpirationDate); + TwitchButtonState = AuthenticationButton.SignOut; + } + else + { + await _twitchAuthenticationService.DeleteToken(); + TwitchDescription = StringResourcesManager.AuthenticationView.Get("TwitchAuthenticationDescriptionAuthenticationInvalid"); + TwitchButtonState = AuthenticationButton.SignIn; + } + } + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/BaseViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/BaseViewModel.cs new file mode 100644 index 0000000..4c36354 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/BaseViewModel.cs @@ -0,0 +1,122 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.ViewModels.Authentication; +using VDownload.Core.ViewModels.Home; +using VDownload.Core.ViewModels.Settings; +using VDownload.Services.UI.DictionaryResources; +using SimpleToolkit.UI.Models; +using VDownload.Core.ViewModels.About; +using VDownload.Core.ViewModels.Subscriptions; +using VDownload.Core.Strings; + +namespace VDownload.Core.ViewModels +{ + public partial class BaseViewModel : ObservableObject + { + #region SERVICES + + protected readonly IDictionaryResourcesService _dictionaryResourcesService; + + #endregion + + + + #region FIELDS + + protected readonly Type _settingsViewModel = typeof(SettingsViewModel); + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + private Type _currentViewModel; + + [ObservableProperty] + private NavigationViewItem _selectedItem; + + public ReadOnlyObservableCollection<NavigationViewItem> Items { get; protected set; } + public ReadOnlyObservableCollection<NavigationViewItem> FooterItems { get; protected set; } + + #endregion + + + + #region CONSTRUCTORS + + public BaseViewModel(IDictionaryResourcesService dictionaryResourcesService) + { + _dictionaryResourcesService = dictionaryResourcesService; + + Items = new ReadOnlyObservableCollection<NavigationViewItem> + ( + new ObservableCollection<NavigationViewItem> + { + new NavigationViewItem() + { + Name = StringResourcesManager.BaseView.Get("HomeNavigationViewItem"), + IconSource = _dictionaryResourcesService.Get<string>("ImageBaseViewHome"), + ViewModel = typeof(HomeViewModel), + }, + new NavigationViewItem() + { + Name = StringResourcesManager.BaseView.Get("SubscriptionsNavigationViewItem"), + IconSource = _dictionaryResourcesService.Get<string>("ImageBaseViewSubscriptions"), + ViewModel = typeof(SubscriptionsViewModel), + }, + } + ); + FooterItems = new ReadOnlyObservableCollection<NavigationViewItem> + ( + new ObservableCollection<NavigationViewItem> + { + new NavigationViewItem() + { + Name = StringResourcesManager.BaseView.Get("AboutNavigationViewItem"), + IconSource = _dictionaryResourcesService.Get<string>("ImageBaseViewAbout"), + ViewModel = typeof(AboutViewModel), + }, + new NavigationViewItem() + { + Name = StringResourcesManager.BaseView.Get("AuthenticationNavigationViewItem"), + IconSource = _dictionaryResourcesService.Get<string>("ImageBaseViewAuthentication"), + ViewModel = typeof(AuthenticationViewModel), + } + } + ); + + SelectedItem = Items.First(); + CurrentViewModel = SelectedItem.ViewModel; + } + + #endregion + + + + #region PUBLIC METHODS + + [RelayCommand] + public void Navigate(Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs e) + { + if (e.IsSettingsInvoked) + { + CurrentViewModel = _settingsViewModel; + } + else + { + NavigationViewItem item = e.InvokedItemContainer.DataContext as NavigationViewItem; + CurrentViewModel = item.ViewModel; + } + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Home/Helpers/VideoViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Home/Helpers/VideoViewModel.cs new file mode 100644 index 0000000..41a6e39 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Home/Helpers/VideoViewModel.cs @@ -0,0 +1,163 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Application; +using VDownload.Services.Data.Settings; +using VDownload.Services.UI.StoragePicker; +using VDownload.Services.Utility.Filename; + +namespace VDownload.Core.ViewModels.Home.Helpers +{ + public partial class VideoViewModel : ObservableObject + { + #region SERVICES + + protected readonly ISettingsService _settingsService; + protected readonly IStoragePickerService _storagePickerService; + protected readonly IFilenameService _filenameService; + protected readonly IApplicationDataService _applicationDataService; + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + protected Uri _thumbnailUrl; + + [ObservableProperty] + protected string _title; + + [ObservableProperty] + protected string _author; + + [ObservableProperty] + protected DateTime _publishDate; + + [ObservableProperty] + protected TimeSpan _duration; + + [ObservableProperty] + protected long _views; + + + [ObservableProperty] + protected ObservableCollection<VideoStream> _streams; + + [ObservableProperty] + protected VideoStream _selectedStream; + + [ObservableProperty] + protected MediaType _mediaType; + + [ObservableProperty] + protected TimeSpan _trimStart; + + [ObservableProperty] + protected TimeSpan _trimEnd; + + [ObservableProperty] + protected string _directoryPath; + + [ObservableProperty] + protected string _filename; + + [ObservableProperty] + protected VideoExtension _videoExtension; + + [ObservableProperty] + protected AudioExtension _audioExtension; + + public Video Video { get; protected set; } + + #endregion + + + + #region CONSTRUCTORS + + public VideoViewModel(Video video, ISettingsService settingsService, IStoragePickerService storagePickerService, IFilenameService filenameService, IApplicationDataService applicationDataService) + { + _settingsService = settingsService; + _storagePickerService = storagePickerService; + _filenameService = filenameService; + _applicationDataService = applicationDataService; + + Video = video; + + ThumbnailUrl = video.ThumbnailUrl; + Title = video.Title; + Author = video.Author; + PublishDate = video.PublishDate; + Duration = video.Duration; + Views = video.Views; + + Streams = [.. video.Streams]; + SelectedStream = Streams[0]; + MediaType = _settingsService.Data.Common.Tasks.DefaultMediaType; + TrimStart = TimeSpan.Zero; + TrimEnd = Duration; + Filename = _filenameService.CreateFilename(_settingsService.Data.Common.Tasks.FilenameTemplate, video); + VideoExtension = _settingsService.Data.Common.Tasks.DefaultVideoExtension; + AudioExtension = _settingsService.Data.Common.Tasks.DefaultAudioExtension; + + if (_settingsService.Data.Common.Tasks.SaveLastOutputDirectory && !string.IsNullOrWhiteSpace(_applicationDataService.Data.Common.LastOutputDirectory)) + { + DirectoryPath = _applicationDataService.Data.Common.LastOutputDirectory; + } + else + { + DirectoryPath = _settingsService.Data.Common.Tasks.DefaultOutputDirectory; + } + } + + #endregion + + + + #region PUBLIC METHODS + + public VideoDownloadOptions BuildDownloadOptions() + { + return new VideoDownloadOptions(Duration) + { + MediaType = this.MediaType, + SelectedStream = this.SelectedStream, + TrimStart = this.TrimStart, + TrimEnd = this.TrimEnd, + Directory = this.DirectoryPath, + Filename = _filenameService.SanitizeFilename(this.Filename), + Extension = (this.MediaType == MediaType.OnlyAudio ? this.AudioExtension.ToString() : this.VideoExtension.ToString()).ToLower(), + }; + } + + #endregion + + + + #region COMMANDS + + [RelayCommand] + public async Task Browse() + { + string? newDirectory = await _storagePickerService.OpenDirectory(); + if (newDirectory is not null) + { + this.DirectoryPath = newDirectory; + + _applicationDataService.Data.Common.LastOutputDirectory = newDirectory; + await _applicationDataService.Save(); + } + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Home/HomeDownloadsViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeDownloadsViewModel.cs new file mode 100644 index 0000000..e32dbc4 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeDownloadsViewModel.cs @@ -0,0 +1,125 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.WinUI.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Core.Tasks; +using VDownload.Services.Data.Settings; +using VDownload.Services.UI.Dialogs; + +namespace VDownload.Core.ViewModels.Home +{ + public partial class HomeDownloadsViewModel : ObservableObject + { + #region SERVICES + + protected readonly IDownloadTaskManager _tasksManager; + + protected readonly IDialogsService _dialogsService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region PROPERTIES + + public ReadOnlyObservableCollection<DownloadTask> Tasks => _tasksManager.Tasks; + + [ObservableProperty] + private bool _taskListIsEmpty; + + #endregion + + + + #region CONSTRUCTORS + + public HomeDownloadsViewModel(IDownloadTaskManager tasksManager, IDialogsService dialogsService, ISettingsService settingsService) + { + _tasksManager = tasksManager; + _tasksManager.TaskCollectionChanged += Tasks_CollectionChanged; + + _dialogsService = dialogsService; + _settingsService = settingsService; + + _taskListIsEmpty = _tasksManager.Tasks.Count == 0; + } + + #endregion + + + + #region PUBLIC METHODS + + [RelayCommand] + public async Task StartCancelTask(DownloadTask task) + { + DownloadTaskStatus[] idleStatuses = + [ + DownloadTaskStatus.Idle, + DownloadTaskStatus.EndedUnsuccessfully, + DownloadTaskStatus.EndedSuccessfully, + DownloadTaskStatus.EndedCancelled + ]; + if (idleStatuses.Contains(task.Status)) + { + if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) + { + string title = StringResourcesManager.HomeDownloadsView.Get("DialogErrorTitle"); + string message = StringResourcesManager.HomeDownloadsView.Get("DialogErrorMessageNoInternetConnection"); + await _dialogsService.ShowOk(title, message); + return; + } + + bool continueEnqueue = true; + if + ( + _settingsService.Data.Common.Tasks.ShowMeteredConnectionWarnings + && + NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection + ) + { + string title = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogTitle"); + string message = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogMessage"); + DialogResultYesNo result = await _dialogsService.ShowYesNo(title, message); + continueEnqueue = result == DialogResultYesNo.Yes; + } + + if (continueEnqueue) + { + task.Enqueue(); + } + } + else + { + await task.Cancel(); + } + } + + [RelayCommand] + public async Task RemoveTask(DownloadTask task) + { + await task.Cancel(); + _tasksManager.RemoveTask(task); + } + + #endregion + + + + #region PRIVATE METHODS + + private void Tasks_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + TaskListIsEmpty = Tasks.Count == 0; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Home/HomeVideoCollectionViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeVideoCollectionViewModel.cs new file mode 100644 index 0000000..430cb2b --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeVideoCollectionViewModel.cs @@ -0,0 +1,374 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Tasks; +using VDownload.Core.ViewModels.Home.Helpers; +using VDownload.Models; +using VDownload.Services.Data.Settings; +using VDownload.Services.UI.StoragePicker; +using VDownload.Sources.Twitch.Configuration.Models; +using SimpleToolkit.MVVM; +using System.Text.RegularExpressions; +using VDownload.Services.Utility.Filename; +using VDownload.Services.UI.Dialogs; +using CommunityToolkit.WinUI.Helpers; +using VDownload.Services.Data.Application; +using VDownload.Core.Strings; + +namespace VDownload.Core.ViewModels.Home +{ + public partial class HomeVideoCollectionViewModel : ObservableObject + { + #region SERVICES + + protected readonly IDownloadTaskManager _tasksManager; + + protected readonly ISettingsService _settingsService; + protected readonly IStoragePickerService _storagePickerService; + protected readonly IFilenameService _filenameService; + protected readonly IDialogsService _dialogsService; + protected readonly IApplicationDataService _applicationDataService; + + #endregion + + + + #region FIELDS + + protected VideoCollection _collection; + + protected List<VideoViewModel> _removedVideos; + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + protected string _name; + + [ObservableProperty] + protected ObservableDictionary<VideoViewModel, bool> _videos; + + [ObservableProperty] + protected int _removedCount; + + [ObservableProperty] + protected int _hiddenCount; + + [ObservableProperty] + protected bool _isSomethingHidden; + + public string TitleFilter + { + get => _titleFilter; + set + { + SetProperty(ref _titleFilter, value, nameof(TitleFilter)); + UpdateFilter(); + } + } + protected string _titleFilter; + + public string AuthorFilter + { + get => _authorFilter; + set + { + SetProperty(ref _authorFilter, value, nameof(AuthorFilter)); + UpdateFilter(); + } + } + protected string _authorFilter; + + public long MinViewsFilter + { + get => _minViewsFilter; + set + { + SetProperty(ref _minViewsFilter, value, nameof(MinViewsFilter)); + UpdateFilter(); + } + } + protected long _minViewsFilter; + + public long MaxViewsFilter + { + get => _maxViewsFilter; + set + { + SetProperty(ref _maxViewsFilter, value, nameof(MaxViewsFilter)); + UpdateFilter(); + } + } + protected long _maxViewsFilter; + + [ObservableProperty] + protected long _minViews; + + [ObservableProperty] + protected long _maxViews; + + public DateTimeOffset MinDateFilter + { + get => _minDateFilter; + set + { + SetProperty(ref _minDateFilter, value, nameof(MinDateFilter)); + UpdateFilter(); + } + } + protected DateTimeOffset _minDateFilter; + + public DateTimeOffset MaxDateFilter + { + get => _maxDateFilter; + set + { + SetProperty(ref _maxDateFilter, value, nameof(MaxDateFilter)); + UpdateFilter(); + } + } + protected DateTimeOffset _maxDateFilter; + + [ObservableProperty] + protected DateTimeOffset _minDate; + + [ObservableProperty] + protected DateTimeOffset _maxDate; + + + public TimeSpan MinDurationFilter + { + get => _minDurationFilter; + set + { + SetProperty(ref _minDurationFilter, value, nameof(MinDurationFilter)); + UpdateFilter(); + } + } + protected TimeSpan _minDurationFilter; + + public TimeSpan MaxDurationFilter + { + get => _maxDurationFilter; + set + { + SetProperty(ref _maxDurationFilter, value, nameof(MaxDurationFilter)); + UpdateFilter(); + } + } + protected TimeSpan _maxDurationFilter; + + [ObservableProperty] + protected TimeSpan _minDuration; + + [ObservableProperty] + protected TimeSpan _maxDuration; + + #endregion + + + + #region CONSTRUCTORS + + public HomeVideoCollectionViewModel(IDownloadTaskManager tasksManager, ISettingsService settingsService, IStoragePickerService storagePickerService, IFilenameService filenameService, IDialogsService dialogsService, IApplicationDataService applicationDataService) + { + _tasksManager = tasksManager; + _settingsService = settingsService; + _storagePickerService = storagePickerService; + _filenameService = filenameService; + _dialogsService = dialogsService; + _applicationDataService = applicationDataService; + + _removedVideos = new List<VideoViewModel>(); + + _videos = new ObservableDictionary<VideoViewModel, bool>(); + } + + #endregion + + + + #region PUBLIC METHODS + + public void LoadCollection(VideoCollection collection) + { + _collection = collection; + ParallelQuery<Video> playlistQuery = _collection.AsParallel(); + + _removedVideos.Clear(); + + _titleFilter = string.Empty; + _authorFilter = string.Empty; + + IEnumerable<long> views = playlistQuery.Select(x => x.Views); + MinViews = views.Min(); + MaxViews = views.Max(); + _minViewsFilter = MinViews; + _maxViewsFilter = MaxViews; + + IEnumerable<DateTimeOffset> date = _collection.Select(x => new DateTimeOffset(x.PublishDate)); + MinDate = date.Min(); + MaxDate = date.Max(); + _minDateFilter = MinDate; + _maxDateFilter = MaxDate; + + IEnumerable<TimeSpan> duration = playlistQuery.Select(x => x.Duration); + MinDuration = duration.Min(); + MaxDuration = duration.Max(); + _minDurationFilter = MinDuration; + _maxDurationFilter = MaxDuration; + + Name = _collection.Name; + Videos.Clear(); + foreach (Video video in _collection) + { + Videos.Add(new VideoViewModel(video, _settingsService, _storagePickerService, _filenameService, _applicationDataService), true); + } + UpdateFilter(); + } + + #endregion + + + + #region COMMANDS + + [RelayCommand] + public async Task SelectDirectory() + { + string? newDirectory = await _storagePickerService.OpenDirectory(); + if (newDirectory is not null) + { + foreach (VideoViewModel video in Videos.Keys) + { + video.DirectoryPath = newDirectory; + } + + _applicationDataService.Data.Common.LastOutputDirectory = newDirectory; + await _applicationDataService.Save(); + } + } + + [RelayCommand] + public void RemoveVideo(VideoViewModel video) + { + Videos[video] = false; + + _removedVideos.Add(video); + + UpdateFilter(); + } + + [RelayCommand] + public void RestoreRemovedVideos() + { + foreach(VideoViewModel video in _removedVideos) + { + Videos[video] = true; + } + _removedVideos.Clear(); + + UpdateFilter(); + } + + [RelayCommand] + public async Task CreateTasksAndDownload() => await CreateTasks(true); + + [RelayCommand] + public async Task CreateTasks() => await CreateTasks(false); + + #endregion + + + + #region PRIVATE METHODS + + protected async Task CreateTasks(bool download) + { + if + ( + download + && + _settingsService.Data.Common.Tasks.ShowMeteredConnectionWarnings + && + NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection + ) + { + string title = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogTitle"); + string message = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogMessage"); + DialogResultYesNo result = await _dialogsService.ShowYesNo(title, message); + download = result == DialogResultYesNo.Yes; + } + + IEnumerable<VideoViewModel> videos = Videos.Cast<ObservableKeyValuePair<VideoViewModel, bool>>() + .Where(x => x.Value) + .Select(x => x.Key); + + foreach (VideoViewModel video in videos) + { + DownloadTask task = _tasksManager.AddTask(video.Video, video.BuildDownloadOptions()); + if (download) + { + task.Enqueue(); + } + } + CloseRequested?.Invoke(this, EventArgs.Empty); + } + + protected void UpdateFilter() + { + Regex titleRegex = new Regex(TitleFilter); + Regex authorRegex = new Regex(AuthorFilter); + + foreach (ObservableKeyValuePair<VideoViewModel, bool> item in Videos) + { + VideoViewModel video = item.Key; + bool hide = + ( + _removedVideos.Contains(video) + || + !titleRegex.IsMatch(video.Title) + || + !authorRegex.IsMatch(video.Author) + || + MinViewsFilter > video.Views + || + MaxViewsFilter < video.Views + || + MinDateFilter.Date > video.PublishDate.Date + || + MaxDateFilter.Date < video.PublishDate.Date + || + MinDurationFilter > video.Duration + || + MaxDurationFilter < video.Duration + ); + item.Value = !hide; + } + + RemovedCount = _removedVideos.Count; + HiddenCount = Videos.Values.Where(x => !x).Count(); + IsSomethingHidden = HiddenCount > 0; + } + + #endregion + + + + #region EVENTS + + public event EventHandler CloseRequested; + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Home/HomeVideoViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeVideoViewModel.cs new file mode 100644 index 0000000..8ebb554 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeVideoViewModel.cs @@ -0,0 +1,223 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.WinUI.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Core.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Application; +using VDownload.Services.Data.Settings; +using VDownload.Services.UI.Dialogs; +using VDownload.Services.UI.StoragePicker; +using VDownload.Services.Utility.Filename; + +namespace VDownload.Core.ViewModels.Home +{ + public partial class HomeVideoViewModel : ObservableObject + { + #region SERVICES + + protected readonly IDownloadTaskManager _tasksManager; + + protected readonly ISettingsService _settingsService; + protected readonly IStoragePickerService _storagePickerService; + protected readonly IFilenameService _filenameService; + protected readonly IDialogsService _dialogsService; + protected readonly IApplicationDataService _applicationDataService; + + #endregion + + + + #region FIELDS + + protected Video _video; + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + protected Uri _thumbnailUrl; + + [ObservableProperty] + protected string _title; + + [ObservableProperty] + protected string _author; + + [ObservableProperty] + protected DateTime _publishDate; + + [ObservableProperty] + protected TimeSpan _duration; + + [ObservableProperty] + protected long _views; + + + [ObservableProperty] + protected ObservableCollection<VideoStream> _streams; + + [ObservableProperty] + protected VideoStream _selectedStream; + + [ObservableProperty] + protected MediaType _mediaType; + + [ObservableProperty] + protected TimeSpan _trimStart; + + [ObservableProperty] + protected TimeSpan _trimEnd; + + [ObservableProperty] + protected string _directoryPath; + + [ObservableProperty] + protected string _filename; + + [ObservableProperty] + protected VideoExtension _videoExtension; + + [ObservableProperty] + protected AudioExtension _audioExtension; + + #endregion + + + + #region CONSTRUCTORS + + public HomeVideoViewModel(IDownloadTaskManager tasksManager, ISettingsService settingsService, IStoragePickerService storagePickerService, IFilenameService filenameService, IDialogsService dialogsService, IApplicationDataService applicationDataService) + { + _tasksManager = tasksManager; + _settingsService = settingsService; + _storagePickerService = storagePickerService; + _filenameService = filenameService; + _dialogsService = dialogsService; + _applicationDataService = applicationDataService; + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task LoadVideo(Video video) + { + await _settingsService.Load(); + + _video = video; + + ThumbnailUrl = video.ThumbnailUrl; + Title = video.Title; + Author = video.Author; + PublishDate = video.PublishDate; + Duration = video.Duration; + Views = video.Views; + + Streams = [.. video.Streams]; + SelectedStream = Streams[0]; + MediaType = _settingsService.Data.Common.Tasks.DefaultMediaType; + TrimStart = TimeSpan.Zero; + TrimEnd = Duration; + Filename = _filenameService.CreateFilename(_settingsService.Data.Common.Tasks.FilenameTemplate, video); + VideoExtension = _settingsService.Data.Common.Tasks.DefaultVideoExtension; + AudioExtension = _settingsService.Data.Common.Tasks.DefaultAudioExtension; + + if (_settingsService.Data.Common.Tasks.SaveLastOutputDirectory && !string.IsNullOrWhiteSpace(_applicationDataService.Data.Common.LastOutputDirectory)) + { + DirectoryPath = _applicationDataService.Data.Common.LastOutputDirectory; + } + else + { + DirectoryPath = _settingsService.Data.Common.Tasks.DefaultOutputDirectory; + } + } + + + [RelayCommand] + public async Task Browse() + { + string? newDirectory = await _storagePickerService.OpenDirectory(); + if (newDirectory is not null) + { + this.DirectoryPath = newDirectory; + + _applicationDataService.Data.Common.LastOutputDirectory = newDirectory; + await _applicationDataService.Save(); + } + } + + [RelayCommand] + public async Task CreateTask() => await CreateTask(false); + + [RelayCommand] + public async Task CreateTaskAndDownload() => await CreateTask(true); + + #endregion + + + + #region PRIVATE METHODS + + protected async Task CreateTask(bool download) + { + if + ( + download + && + _settingsService.Data.Common.Tasks.ShowMeteredConnectionWarnings + && + NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection + ) + { + string title = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogTitle"); + string message = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogMessage"); + DialogResultYesNo result = await _dialogsService.ShowYesNo(title, message); + download = result == DialogResultYesNo.Yes; + } + + DownloadTask task = _tasksManager.AddTask(_video, BuildDownloadOptions()); + CloseRequested?.Invoke(this, EventArgs.Empty); + if (download) + { + task.Enqueue(); + } + } + + protected VideoDownloadOptions BuildDownloadOptions() + { + return new VideoDownloadOptions(Duration) + { + MediaType = this.MediaType, + SelectedStream = this.SelectedStream, + TrimStart = this.TrimStart, + TrimEnd = this.TrimEnd, + Directory = this.DirectoryPath, + Filename = _filenameService.SanitizeFilename(this.Filename), + Extension = (this.MediaType == MediaType.OnlyAudio ? this.AudioExtension.ToString() : this.VideoExtension.ToString()).ToLower(), + }; + } + + #endregion + + + + #region EVENTS + + public event EventHandler CloseRequested; + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Home/HomeViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeViewModel.cs new file mode 100644 index 0000000..050750e --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Home/HomeViewModel.cs @@ -0,0 +1,429 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.WinUI.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Core.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using VDownload.Services.Data.Subscriptions; +using VDownload.Services.UI.Dialogs; +using VDownload.Sources; +using VDownload.Sources.Common; +using VDownload.Sources.Twitch.Configuration.Models; + +namespace VDownload.Core.ViewModels.Home +{ + public partial class HomeViewModel : ObservableObject + { + #region ENUMS + + public enum OptionBarContentType + { + None, + VideoSearch, + PlaylistSearch + } + + public enum MainContentType + { + Downloads, + Video + } + + #endregion + + + + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + protected readonly ISearchService _searchService; + protected readonly ISubscriptionsDataService _subscriptionsDataService; + protected readonly IDialogsService _dialogsService; + + protected readonly IDownloadTaskManager _downloadTaskManager; + + protected readonly HomeVideoViewModel _videoViewModel; + protected readonly HomeVideoCollectionViewModel _videoCollectionViewModel; + + #endregion + + + + #region FIELDS + + protected readonly Type _downloadsView = typeof(HomeDownloadsViewModel); + protected readonly Type _videoView = typeof(HomeVideoViewModel); + protected readonly Type _videoCollectionView = typeof(HomeVideoCollectionViewModel); + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + private Type _mainContent; + + [ObservableProperty] + private string _optionBarError; + + [ObservableProperty] + private bool _optionBarIsErrorOpened; + + [ObservableProperty] + private OptionBarContentType _optionBarContent; + + [ObservableProperty] + private string _optionBarMessage; + + [ObservableProperty] + private bool _optionBarLoading; + + [ObservableProperty] + private bool _optionBarLoadSubscriptionButtonChecked; + + [ObservableProperty] + private bool _optionBarVideoSearchButtonChecked; + + [ObservableProperty] + private bool _optionBarPlaylistSearchButtonChecked; + + [ObservableProperty] + private bool _optionBarSearchNotPending; + + [ObservableProperty] + private string _optionBarVideoSearchTBValue; + + [ObservableProperty] + private string _optionBarPlaylistSearchTBValue; + + [ObservableProperty] + private int _optionBarPlaylistSearchNBValue; + + #endregion + + + + #region CONSTRUCTORS + + public HomeViewModel(IConfigurationService configurationService, ISettingsService settingsService, ISearchService searchService, ISubscriptionsDataService subscriptionsDataService, IDialogsService dialogsService, IDownloadTaskManager downloadTaskManager, HomeVideoViewModel videoViewModel, HomeVideoCollectionViewModel videoCollectionViewModel) + { + _configurationService = configurationService; + _settingsService = settingsService; + _searchService = searchService; + _subscriptionsDataService = subscriptionsDataService; + _dialogsService = dialogsService; + + _downloadTaskManager = downloadTaskManager; + + _videoViewModel = videoViewModel; + _videoViewModel.CloseRequested += BackToDownload_EventHandler; + + _videoCollectionViewModel = videoCollectionViewModel; + _videoCollectionViewModel.CloseRequested += BackToDownload_EventHandler; + } + + #endregion + + + + #region COMMANDS + + [RelayCommand] + public async Task Navigation() + { + await _settingsService.Load(); + + MainContent = _downloadsView; + + OptionBarContent = OptionBarContentType.None; + OptionBarLoading = false; + OptionBarMessage = null; + OptionBarError = null; + OptionBarIsErrorOpened = true; + OptionBarLoadSubscriptionButtonChecked = false; + OptionBarVideoSearchButtonChecked = false; + OptionBarPlaylistSearchButtonChecked = false; + OptionBarSearchNotPending = true; + OptionBarVideoSearchTBValue = string.Empty; + OptionBarPlaylistSearchNBValue = _settingsService.Data.Common.Searching.MaxNumberOfVideosToGetFromPlaylist; + OptionBarPlaylistSearchTBValue = string.Empty; + } + + [RelayCommand] + public async Task LoadFromSubscription() + { + SearchButtonClicked(); + + if (OptionBarLoadSubscriptionButtonChecked) + { + if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) + { + ShowError(ErrorNoInternetConnection()); + OptionBarLoadSubscriptionButtonChecked = false; + return; + } + + OptionBarContent = OptionBarContentType.None; + OptionBarVideoSearchButtonChecked = false; + OptionBarPlaylistSearchButtonChecked = false; + + StartSearch(); + + SubscriptionsVideoList subList = new SubscriptionsVideoList { Name = StringResourcesManager.Common.Get("SubscriptionVideoListName") }; + List<Task> tasks = new List<Task>(); + try + { + foreach (Subscription sub in _subscriptionsDataService.Data.ToArray()) + { + tasks.Add(Task.Run(async () => + { + Playlist playlist; + try + { + playlist = await _searchService.SearchPlaylist(sub.Url.OriginalString, 0); + } + catch (MediaSearchException) + { + return; + } + + IEnumerable<Video> newIds = playlist.Where(x => !sub.VideoIds.Contains(x.Id)); + subList.AddRange(newIds); + foreach (Video video in newIds) + { + sub.VideoIds.Add(video.Id); + } + })); + } + await Task.WhenAll(tasks); + await _subscriptionsDataService.Save(); + } + catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException) + { + ShowError(ErrorSearchTimeout()); + return; + } + + if (subList.Count > 0) + { + OptionBarMessage = $"{StringResourcesManager.HomeView.Get("OptionBarMessageVideosFound")} {subList.Count}"; + + _videoCollectionViewModel.LoadCollection(subList); + MainContent = _videoCollectionView; + } + else + { + OptionBarMessage = StringResourcesManager.HomeView.Get("OptionBarMessageVideosNotFound"); + } + + OptionBarSearchNotPending = true; + OptionBarLoading = false; + } + } + + [RelayCommand] + public void VideoSearchShow() + { + SearchButtonClicked(); + + if (OptionBarContent != OptionBarContentType.VideoSearch) + { + OptionBarContent = OptionBarContentType.VideoSearch; + OptionBarPlaylistSearchButtonChecked = false; + OptionBarLoadSubscriptionButtonChecked = false; + } + else + { + OptionBarContent = OptionBarContentType.None; + } + } + + [RelayCommand] + public void PlaylistSearchShow() + { + SearchButtonClicked(); + + if (OptionBarContent != OptionBarContentType.PlaylistSearch) + { + OptionBarContent = OptionBarContentType.PlaylistSearch; + OptionBarVideoSearchButtonChecked = false; + OptionBarLoadSubscriptionButtonChecked = false; + } + else + { + OptionBarContent = OptionBarContentType.None; + } + } + + [RelayCommand] + public async Task VideoSearchStart() + { + if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) + { + ShowError(ErrorNoInternetConnection()); + return; + } + + StartSearch(); + + Video video; + try + { + video = await _searchService.SearchVideo(OptionBarVideoSearchTBValue); + } + catch (MediaSearchException ex) + { + ShowError(StringResourcesManager.Search.Get(ex.StringCode)); + return; + } + catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException) + { + ShowError(ErrorSearchTimeout()); + return; + } + + await _videoViewModel.LoadVideo(video); + + MainContent = _videoView; + + OptionBarSearchNotPending = true; + OptionBarLoading = false; + OptionBarMessage = null; + } + + [RelayCommand] + public async Task PlaylistSearchStart() + { + if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) + { + ShowError(ErrorNoInternetConnection()); + return; + } + + StartSearch(); + + Playlist playlist; + try + { + playlist = await _searchService.SearchPlaylist(OptionBarPlaylistSearchTBValue, OptionBarPlaylistSearchNBValue); + } + catch (MediaSearchException ex) + { + ShowError(StringResourcesManager.Search.Get(ex.StringCode)); + return; + } + catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException) + { + ShowError(ErrorSearchTimeout()); + return; + } + + _videoCollectionViewModel.LoadCollection(playlist); + + MainContent = _videoCollectionView; + + OptionBarSearchNotPending = true; + OptionBarLoading = false; + OptionBarMessage = null; + } + + [RelayCommand] + public async Task Download() + { + if (_downloadTaskManager.Tasks.Count > 0) + { + if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) + { + ShowError(ErrorNoInternetConnection()); + return; + } + + if + ( + _settingsService.Data.Common.Tasks.ShowMeteredConnectionWarnings + && + NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection + ) + { + string title = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogTitle"); + string message = StringResourcesManager.Common.Get("StartAtMeteredConnectionDialogMessage"); + DialogResultYesNo result = await _dialogsService.ShowYesNo(title, message); + if (result == DialogResultYesNo.No) + { + return; + } + } + + foreach (DownloadTask task in _downloadTaskManager.Tasks) + { + task.Enqueue(); + } + } + } + + [RelayCommand] + public async Task Cancel() + { + List<Task> tasks = new List<Task>(); + foreach (DownloadTask task in _downloadTaskManager.Tasks) + { + tasks.Add(task.Cancel()); + } + await Task.WhenAll(tasks); + } + + [RelayCommand] + public void CloseError() + { + OptionBarError = null; + OptionBarIsErrorOpened = true; + } + + #endregion + + + + #region PRIVATE METHODS + + protected void ShowError(string message) + { + OptionBarError = message; + OptionBarSearchNotPending = true; + OptionBarLoading = false; + OptionBarMessage = null; + } + + protected void SearchButtonClicked() + { + OptionBarSearchNotPending = true; + OptionBarLoading = false; + OptionBarMessage = null; + MainContent = _downloadsView; + } + + protected void StartSearch() + { + OptionBarSearchNotPending = false; + OptionBarLoading = true; + OptionBarMessage = StringResourcesManager.HomeView.Get("OptionBarMessageLoading"); + } + + protected async void BackToDownload_EventHandler(object sender, EventArgs e) => await Navigation(); + + protected string ErrorNoInternetConnection() => StringResourcesManager.HomeView.Get("ErrorInfoBarNoInternetConnection"); + + protected string ErrorSearchTimeout() => StringResourcesManager.Search.Get("SearchTimeout"); + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Settings/SettingsViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Settings/SettingsViewModel.cs new file mode 100644 index 0000000..1330ecc --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Settings/SettingsViewModel.cs @@ -0,0 +1,248 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using VDownload.Services.UI.StoragePicker; + +namespace VDownload.Core.ViewModels.Settings +{ + public partial class SettingsViewModel : ObservableObject + { + #region SERVICES + + protected readonly ISettingsService _settingsService; + protected readonly IConfigurationService _configurationService; + protected readonly IStoragePickerService _storagePickerService; + + #endregion + + + + #region PROPERTIES + + public int SearchingPlaylistCount + { + get => _settingsService.Data.Common.Searching.MaxNumberOfVideosToGetFromPlaylist; + set => SetProperty(_settingsService.Data.Common.Searching.MaxNumberOfVideosToGetFromPlaylist, value, _settingsService.Data.Common.Searching, (u, n) => u.MaxNumberOfVideosToGetFromPlaylist = n); + } + + public int TasksRunningTasks + { + get => _settingsService.Data.Common.Tasks.MaxNumberOfRunningTasks; + set => SetProperty(_settingsService.Data.Common.Tasks.MaxNumberOfRunningTasks, value, _settingsService.Data.Common.Tasks, (u, n) => u.MaxNumberOfRunningTasks = n); + } + + public MediaType TasksMediaType + { + get => _settingsService.Data.Common.Tasks.DefaultMediaType; + set => SetProperty(_settingsService.Data.Common.Tasks.DefaultMediaType, value, _settingsService.Data.Common.Tasks, (u, n) => u.DefaultMediaType = n); + } + + public VideoExtension TasksVideoExtension + { + get => _settingsService.Data.Common.Tasks.DefaultVideoExtension; + set => SetProperty(_settingsService.Data.Common.Tasks.DefaultVideoExtension, value, _settingsService.Data.Common.Tasks, (u, n) => u.DefaultVideoExtension = n); + } + + public AudioExtension TasksAudioExtension + { + get => _settingsService.Data.Common.Tasks.DefaultAudioExtension; + set => SetProperty(_settingsService.Data.Common.Tasks.DefaultAudioExtension, value, _settingsService.Data.Common.Tasks, (u, n) => u.DefaultAudioExtension = n); + } + + public string TasksFilenameTemplate + { + get => _settingsService.Data.Common.Tasks.FilenameTemplate; + set => SetProperty(_settingsService.Data.Common.Tasks.FilenameTemplate, value, _settingsService.Data.Common.Tasks, (u, n) => u.FilenameTemplate = n); + } + + public bool TasksMeteredConnectionWarning + { + get => _settingsService.Data.Common.Tasks.ShowMeteredConnectionWarnings; + set => SetProperty(_settingsService.Data.Common.Tasks.ShowMeteredConnectionWarnings, value, _settingsService.Data.Common.Tasks, (u, n) => u.ShowMeteredConnectionWarnings = n); + } + + public bool TasksSaveLastOutputDirectory + { + get => _settingsService.Data.Common.Tasks.SaveLastOutputDirectory; + set => SetProperty(_settingsService.Data.Common.Tasks.SaveLastOutputDirectory, value, _settingsService.Data.Common.Tasks, (u, n) => u.SaveLastOutputDirectory = n); + } + + public string TasksDefaultOutputDirectory + { + get => _settingsService.Data.Common.Tasks.DefaultOutputDirectory; + set => SetProperty(_settingsService.Data.Common.Tasks.DefaultOutputDirectory, value, _settingsService.Data.Common.Tasks, (u, n) => u.DefaultOutputDirectory = n); + } + + public bool TasksReplaceOutputFile + { + get => _settingsService.Data.Common.Tasks.ReplaceOutputFileIfExists; + set => SetProperty(_settingsService.Data.Common.Tasks.ReplaceOutputFileIfExists, value, _settingsService.Data.Common.Tasks, (u, n) => u.ReplaceOutputFileIfExists = n); + } + + public string ProcessingFFmpegLocation + { + get => _settingsService.Data.Common.Processing.FFmpegLocation; + set => SetProperty(_settingsService.Data.Common.Processing.FFmpegLocation, value, _settingsService.Data.Common.Processing, (u, n) => u.FFmpegLocation = n); + } + + public bool ProcessingUseHardwareAcceleration + { + get => _settingsService.Data.Common.Processing.UseHardwareAcceleration; + set => SetProperty(_settingsService.Data.Common.Processing.UseHardwareAcceleration, value, _settingsService.Data.Common.Processing, (u, n) => u.UseHardwareAcceleration = n); + } + + public bool ProcessingUseMultithreading + { + get => _settingsService.Data.Common.Processing.UseMultithreading; + set => SetProperty(_settingsService.Data.Common.Processing.UseMultithreading, value, _settingsService.Data.Common.Processing, (u, n) => u.UseMultithreading = n); + } + + public ProcessingSpeed ProcessingSpeed + { + get => _settingsService.Data.Common.Processing.Speed; + set => SetProperty(_settingsService.Data.Common.Processing.Speed, value, _settingsService.Data.Common.Processing, (u, n) => u.Speed = n); + } + + public bool NotificationsOnSuccessful + { + get => _settingsService.Data.Common.Notifications.OnSuccessful; + set => SetProperty(_settingsService.Data.Common.Notifications.OnSuccessful, value, _settingsService.Data.Common.Notifications, (u, n) => u.OnSuccessful = n); + } + + public bool NotificationsOnUnsuccessful + { + get => _settingsService.Data.Common.Notifications.OnUnsuccessful; + set => SetProperty(_settingsService.Data.Common.Notifications.OnUnsuccessful, value, _settingsService.Data.Common.Notifications, (u, n) => u.OnUnsuccessful = n); + } + + public string TempDirectory + { + get => _settingsService.Data.Common.Temp.Directory; + set => SetProperty(_settingsService.Data.Common.Temp.Directory, value, _settingsService.Data.Common.Temp, (u, n) => u.Directory = n); + } + + public bool TempDeleteOnFail + { + get => _settingsService.Data.Common.Temp.DeleteOnError; + set => SetProperty(_settingsService.Data.Common.Temp.DeleteOnError, value, _settingsService.Data.Common.Temp, (u, n) => u.DeleteOnError = n); + } + + public bool TwitchVodPassiveTrimming + { + get => _settingsService.Data.Twitch.Vod.PassiveTrimming; + set => SetProperty(_settingsService.Data.Twitch.Vod.PassiveTrimming, value, _settingsService.Data.Twitch.Vod, (u, n) => u.PassiveTrimming = n); + } + + public int TwitchVodParallelDownloads + { + get => _settingsService.Data.Twitch.Vod.MaxNumberOfParallelDownloads; + set => SetProperty(_settingsService.Data.Twitch.Vod.MaxNumberOfParallelDownloads, value, _settingsService.Data.Twitch.Vod, (u, n) => u.MaxNumberOfParallelDownloads = n); + } + + public bool TwitchVodChunkDownloadingErrorRetry + { + get => _settingsService.Data.Twitch.Vod.ChunkDownloadingError.Retry; + set => SetProperty(_settingsService.Data.Twitch.Vod.ChunkDownloadingError.Retry, value, _settingsService.Data.Twitch.Vod.ChunkDownloadingError, (u, n) => u.Retry = n); + } + + public int TwitchVodChunkDownloadingErrorRetryCount + { + get => _settingsService.Data.Twitch.Vod.ChunkDownloadingError.RetriesCount; + set => SetProperty(_settingsService.Data.Twitch.Vod.ChunkDownloadingError.RetriesCount, value, _settingsService.Data.Twitch.Vod.ChunkDownloadingError, (u, n) => u.RetriesCount = n); + } + + public int TwitchVodChunkDownloadingErrorRetryDelay + { + get => _settingsService.Data.Twitch.Vod.ChunkDownloadingError.RetryDelay; + set => SetProperty(_settingsService.Data.Twitch.Vod.ChunkDownloadingError.RetryDelay, value, _settingsService.Data.Twitch.Vod.ChunkDownloadingError, (u, n) => u.RetryDelay = n); + } + + [ObservableProperty] + protected string _tasksFilenameTemplateTooltip; + + #endregion + + + + #region CONSTRUCTORS + + public SettingsViewModel(ISettingsService settingsService, IConfigurationService configurationService, IStoragePickerService storagePickerService) + { + _settingsService = settingsService; + _configurationService = configurationService; + _storagePickerService = storagePickerService; + + base.PropertyChanged += PropertyChangedEventHandler; + + _tasksFilenameTemplateTooltip = string.Join('\n', _configurationService.Common.FilenameTemplates.Select(x => StringResourcesManager.FilenameTemplate.Get(x.Name))); + } + + #endregion + + + + #region COMMANDS + + [RelayCommand] + public async Task BrowseTasksDefaultOutputDirectory() + { + string? newDirectory = await _storagePickerService.OpenDirectory(); + if (newDirectory is not null) + { + this.TasksDefaultOutputDirectory = newDirectory; + } + } + + [RelayCommand] + public async Task BrowseTempDirectory() + { + string? newDirectory = await _storagePickerService.OpenDirectory(); + if (newDirectory is not null) + { + this.TempDirectory = newDirectory; + } + } + + [RelayCommand] + public async Task BrowseProcessingFFmpegLocation() + { + string? newDirectory = await _storagePickerService.OpenDirectory(); + if (newDirectory is not null) + { + this.ProcessingFFmpegLocation = newDirectory; + } + } + + [RelayCommand] + public async Task RestoreToDefault() + { + await _settingsService.Restore(); + foreach (PropertyInfo property in this.GetType().GetProperties()) + { + base.OnPropertyChanged(property.Name); + } + } + + #endregion + + + + #region PRIVATE METHODS + + private async void PropertyChangedEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + await _settingsService.Save(); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Subscriptions/Helpers/PlaylistViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Subscriptions/Helpers/PlaylistViewModel.cs new file mode 100644 index 0000000..8f77732 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Subscriptions/Helpers/PlaylistViewModel.cs @@ -0,0 +1,39 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Core.ViewModels.Subscriptions.Helpers +{ + public partial class PlaylistViewModel : ObservableObject + { + #region PROPERTIES + + [ObservableProperty] + protected Guid _guid; + + [ObservableProperty] + protected string _name; + + [ObservableProperty] + protected Source _source; + + #endregion + + + + #region CONSTRUCTORS + + public PlaylistViewModel(string name, Source source, Guid guid) + { + _name = name; + _source = source; + _guid = guid; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/Subscriptions/SubscriptionsViewModel.cs b/VDownload.Core/VDownload.Core.ViewModels/Subscriptions/SubscriptionsViewModel.cs new file mode 100644 index 0000000..63bfefb --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/Subscriptions/SubscriptionsViewModel.cs @@ -0,0 +1,161 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.WinUI.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Core.ViewModels.Subscriptions.Helpers; +using VDownload.Models; +using VDownload.Services.Data.Subscriptions; +using VDownload.Sources; +using VDownload.Sources.Common; + +namespace VDownload.Core.ViewModels.Subscriptions +{ + public partial class SubscriptionsViewModel : ObservableObject + { + #region SERVICES + + protected readonly ISearchService _searchService; + protected readonly ISubscriptionsDataService _subscriptionsDataService; + + #endregion + + + + #region PROPERTIES + + [ObservableProperty] + protected ObservableCollection<PlaylistViewModel> _playlists; + + [ObservableProperty] + protected string _url; + + [ObservableProperty] + protected bool _loading; + + [ObservableProperty] + protected string _error; + + [ObservableProperty] + protected bool _isErrorOpened; + + #endregion + + + + #region CONSTRUCTORS + + public SubscriptionsViewModel(ISearchService searchService, ISubscriptionsDataService subscriptionsDataService) + { + _searchService = searchService; + _subscriptionsDataService = subscriptionsDataService; + + _playlists = new ObservableCollection<PlaylistViewModel>(); + _loading = false; + _isErrorOpened = true; + _error = null; + } + + #endregion + + + + #region COMMANDS + + [RelayCommand] + public void Navigation() + { + Playlists.Clear(); + foreach (Subscription sub in _subscriptionsDataService.Data) + { + Playlists.Add(new PlaylistViewModel(sub.Name, sub.Source, sub.Guid)); + } + } + + [RelayCommand] + public async Task RemovePlaylist(PlaylistViewModel playlist) + { + Playlists.Remove(playlist); + + Subscription sub = _subscriptionsDataService.Data.FirstOrDefault(x => x.Guid == playlist.Guid); + _subscriptionsDataService.Data.Remove(sub); + await _subscriptionsDataService.Save(); + } + + [RelayCommand] + public async Task Add() + { + if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) + { + ShowError(StringResourcesManager.SubscriptionsView.Get("NoInternetConnectionError")); + return; + } + + Loading = true; + + Playlist playlist; + try + { + playlist = await _searchService.SearchPlaylist(Url, 0); + } + catch (MediaSearchException ex) + { + ShowError(StringResourcesManager.Search.Get(ex.StringCode)); + return; + } + catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException) + { + ShowError(StringResourcesManager.Search.Get("SearchTimeout")); + return; + } + + if (_subscriptionsDataService.Data.Any(x => x.Source == playlist.Source && x.Name == playlist.Name)) + { + ShowError(StringResourcesManager.SubscriptionsView.Get("DuplicateError")); + return; + } + + Subscription subscription = new Subscription + { + Name = playlist.Name, + Source = playlist.Source, + Url = playlist.Url, + }; + playlist.ForEach(x => subscription.VideoIds.Add(x.Id)); + + _subscriptionsDataService.Data.Add(subscription); + await _subscriptionsDataService.Save(); + Playlists.Add(new PlaylistViewModel(subscription.Name, subscription.Source, subscription.Guid)); + + Loading = false; + } + + [RelayCommand] + public void CloseError() + { + Error = null; + IsErrorOpened = true; + } + + #endregion + + + + #region PRIVATE METHODS + + protected void ShowError(string message) + { + Error = message; + Loading = false; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.ViewModels/VDownload.Core.ViewModels.csproj b/VDownload.Core/VDownload.Core.ViewModels/VDownload.Core.ViewModels.csproj new file mode 100644 index 0000000..bf03ae7 --- /dev/null +++ b/VDownload.Core/VDownload.Core.ViewModels/VDownload.Core.ViewModels.csproj @@ -0,0 +1,34 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Core.ViewModels</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.0.240109" /> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /> + <PackageReference Include="SimpleToolkit.MVVM" Version="1.7.4" /> + <PackageReference Include="SimpleToolkit.UI.Models" Version="1.7.4" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Application\VDownload.Services.Data.Application.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Settings\VDownload.Services.Data.Settings.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.Dialogs\VDownload.Services.UI.Dialogs.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.DictionaryResources\VDownload.Services.UI.DictionaryResources.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.StoragePicker\VDownload.Services.UI.StoragePicker.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.WebView\VDownload.Services.UI.WebView.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.Filename\VDownload.Services.Utility.Filename.csproj" /> + <ProjectReference Include="..\..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Authentication\VDownload.Sources.Twitch.Authentication.csproj" /> + <ProjectReference Include="..\..\VDownload.Sources\VDownload.Sources\VDownload.Sources.csproj" /> + <ProjectReference Include="..\VDownload.Core.Tasks\VDownload.Core.Tasks.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Core/VDownload.Core.Views/About/AboutView.xaml b/VDownload.Core/VDownload.Core.Views/About/AboutView.xaml new file mode 100644 index 0000000..67ed693 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/About/AboutView.xaml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.About.AboutView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Core.Views.About" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + mc:Ignorable="d" + Background="Transparent"> + <i:Interaction.Behaviors> + <ic:EventTriggerBehavior EventName="Loaded"> + <ic:InvokeCommandAction Command="{Binding NavigationCommand}"/> + </ic:EventTriggerBehavior> + </i:Interaction.Behaviors> + <Grid> + <StackPanel HorizontalAlignment="Center" + VerticalAlignment="Center" Spacing="20"> + <StackPanel HorizontalAlignment="Center"> + <Image Source="{StaticResource ImageLogo}" + Width="150" + Height="150" + HorizontalAlignment="Center"/> + <TextBlock Text="VDownload" + HorizontalAlignment="Center" + FontWeight="SemiBold" + FontSize="30"/> + <TextBlock Text="{Binding Version}" + HorizontalAlignment="Center" + FontSize="16"/> + </StackPanel> + <StackPanel HorizontalAlignment="Center" + Spacing="2"> + <TextBlock x:Uid="/VDownload.Core.Strings/AboutViewResources/Developers" + HorizontalAlignment="Center" + FontSize="17" + FontWeight="SemiBold"/> + <ItemsRepeater HorizontalAlignment="Center" + ItemsSource="{Binding Developers}"> + <ItemsRepeater.ItemTemplate> + <DataTemplate> + <HyperlinkButton HorizontalAlignment="Center" + NavigateUri="{Binding Url}"> + <TextBlock FontSize="12" + Text="{Binding Name}"/> + </HyperlinkButton> + </DataTemplate> + </ItemsRepeater.ItemTemplate> + </ItemsRepeater> + </StackPanel> + <StackPanel HorizontalAlignment="Center" + Spacing="2"> + <TextBlock x:Uid="/VDownload.Core.Strings/AboutViewResources/Translation" + HorizontalAlignment="Center" + FontSize="17" + FontWeight="SemiBold"/> + <ItemsRepeater HorizontalAlignment="Center" + ItemsSource="{Binding Translators}"> + <ItemsRepeater.ItemTemplate> + <DataTemplate> + <HyperlinkButton HorizontalAlignment="Center" + NavigateUri="{Binding Url}"> + <TextBlock FontSize="12" + Text="{Binding Name}"/> + </HyperlinkButton> + </DataTemplate> + </ItemsRepeater.ItemTemplate> + </ItemsRepeater> + </StackPanel> + <StackPanel HorizontalAlignment="Center" + Spacing="2"> + <TextBlock x:Uid="/VDownload.Core.Strings/AboutViewResources/More" + HorizontalAlignment="Center" + FontSize="17" + FontWeight="SemiBold"/> + <StackPanel Orientation="Horizontal"> + <HyperlinkButton HorizontalAlignment="Center" + NavigateUri="{Binding RepositoryUrl}"> + <TextBlock x:Uid="/VDownload.Core.Strings/AboutViewResources/Repository" + FontSize="12"/> + </HyperlinkButton> + <HyperlinkButton HorizontalAlignment="Center" + NavigateUri="{Binding DonationUrl}"> + <TextBlock x:Uid="/VDownload.Core.Strings/AboutViewResources/Donation" + FontSize="12"/> + </HyperlinkButton> + </StackPanel> + </StackPanel> + </StackPanel> + </Grid> +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/About/AboutView.xaml.cs b/VDownload.Core/VDownload.Core.Views/About/AboutView.xaml.cs new file mode 100644 index 0000000..1fbcd3e --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/About/AboutView.xaml.cs @@ -0,0 +1,32 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using VDownload.Core.ViewModels.About; +using VDownload.Core.ViewModels.Home; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace VDownload.Core.Views.About +{ + public sealed partial class AboutView : Page + { + #region CONSTRUCTORS + + public AboutView(AboutViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/Authentication/AuthenticationView.xaml b/VDownload.Core/VDownload.Core.Views/Authentication/AuthenticationView.xaml new file mode 100644 index 0000000..c1d6420 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Authentication/AuthenticationView.xaml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.Authentication.AuthenticationView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Core.Views.Authentication" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ctuc="using:CommunityToolkit.WinUI.UI.Controls" + xmlns:ctc="using:CommunityToolkit.WinUI.Controls" + xmlns:ct="using:CommunityToolkit.WinUI" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + mc:Ignorable="d" + Background="{ThemeResource ViewBackgroundColor}"> + <i:Interaction.Behaviors> + <ic:EventTriggerBehavior EventName="Loaded"> + <ic:InvokeCommandAction Command="{Binding NavigationCommand}"/> + </ic:EventTriggerBehavior> + </i:Interaction.Behaviors> + <Grid Padding="20" + RowSpacing="20"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + </Grid.RowDefinitions> + <TextBlock x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/Header" + Grid.Row="0" + FontSize="28" + FontWeight="SemiBold"/> + <StackPanel Grid.Row="1" + Spacing="10"> + <ctc:SettingsCard Header="Twitch"> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding TwitchButtonState, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="NotEqual" + Value="Loading"> + <ic:ChangePropertyAction PropertyName="Description" + Value="{Binding TwitchDescription}"/> + <ic:ChangePropertyAction PropertyName="Content"> + <ic:ChangePropertyAction.Value> + <Button Command="{Binding TwitchAuthenticationCommand}" + IsEnabled="{Binding TwitchButtonEnable}"> + <Button.Content> + <ctuc:SwitchPresenter Value="{Binding TwitchButtonState, Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="SignIn"> + <TextBlock x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/AuthenticationButtonSignIn"/> + </ctuc:Case> + <ctuc:Case Value="SignOut"> + <TextBlock x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/AuthenticationButtonSignOut"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + </Button.Content> + </Button> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding TwitchButtonState, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Loading"> + <ic:ChangePropertyAction x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/AuthenticationDescriptionLoading" + PropertyName="Description"/> + <ic:ChangePropertyAction PropertyName="Content"> + <ic:ChangePropertyAction.Value> + <ProgressRing Width="20" + Height="20"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{StaticResource ImageSourcesTwitch}"/> + </ctc:SettingsCard.HeaderIcon> + </ctc:SettingsCard> + </StackPanel> + </Grid> +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/Authentication/AuthenticationView.xaml.cs b/VDownload.Core/VDownload.Core.Views/Authentication/AuthenticationView.xaml.cs new file mode 100644 index 0000000..8fb837d --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Authentication/AuthenticationView.xaml.cs @@ -0,0 +1,18 @@ +using Microsoft.UI.Xaml.Controls; +using VDownload.Core.ViewModels.Authentication; + +namespace VDownload.Core.Views.Authentication +{ + public sealed partial class AuthenticationView : Page + { + #region CONSTRUCTORS + + public AuthenticationView(AuthenticationViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/BaseWindow.xaml b/VDownload.Core/VDownload.Core.Views/BaseWindow.xaml new file mode 100644 index 0000000..47ac369 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/BaseWindow.xaml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<Window + x:Class="VDownload.Core.Views.BaseWindow" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + xmlns:cb="using:SimpleToolkit.UI.WinUI.Behaviors" + mc:Ignorable="d"> + <Window.SystemBackdrop> + <MicaBackdrop Kind="Base"/> + </Window.SystemBackdrop> + <Grid x:Name="Root" + Loaded="Root_Loaded"> + <Border x:Name="AppTitleBar" + IsHitTestVisible="True" + VerticalAlignment="Top" + Height="40" + Canvas.ZIndex="1" + Margin="55,4,0,0"> + <StackPanel Orientation="Horizontal"> + <Image HorizontalAlignment="Left" + VerticalAlignment="Center" + Source="{StaticResource ImageLogo}" + Width="16" + Height="16"/> + <TextBlock Text="VDownload" + VerticalAlignment="Center" + Margin="12, 0, 0, 0" + Style="{StaticResource CaptionTextBlockStyle}"/> + </StackPanel> + </Border> + <NavigationView IsTitleBarAutoPaddingEnabled="True" + IsBackButtonVisible="Collapsed" + PaneDisplayMode="LeftCompact" + Canvas.ZIndex="0" + MenuItemsSource="{Binding Items}" + FooterMenuItemsSource="{Binding FooterItems}" + SelectedItem="{Binding SelectedItem}" + Background="Transparent"> + <i:Interaction.Behaviors> + <cb:EventToCommandBehavior Command="{Binding NavigateCommand}" + Event="ItemInvoked" + PassArguments="True"/> + </i:Interaction.Behaviors> + <NavigationView.Resources> + <SolidColorBrush x:Key="NavigationViewContentBackground" + Color="Transparent"/> + </NavigationView.Resources> + <NavigationView.MenuItemTemplate> + <DataTemplate> + <NavigationViewItem Content="{Binding Name}" Tag="{Binding}"> + <NavigationViewItem.Icon> + <ImageIcon Source="{Binding IconSource}"/> + </NavigationViewItem.Icon> + </NavigationViewItem> + </DataTemplate> + </NavigationView.MenuItemTemplate> + <Frame Margin="0,48,0,0" + CornerRadius="10" + Content="{Binding CurrentViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ViewModelToViewConverter}}"> + </Frame> + </NavigationView> + </Grid> +</Window> diff --git a/VDownload.Core/VDownload.Core.Views/BaseWindow.xaml.cs b/VDownload.Core/VDownload.Core.Views/BaseWindow.xaml.cs new file mode 100644 index 0000000..ade7603 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/BaseWindow.xaml.cs @@ -0,0 +1,70 @@ +using Microsoft.UI; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using VDownload.Core.ViewModels; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace VDownload.Core.Views +{ + public sealed partial class BaseWindow : Window + { + #region PROPERTIES + + public XamlRoot XamlRoot => this.Root.XamlRoot; + + #endregion + + + + #region EVENTS + + public event EventHandler RootLoaded; + + #endregion + + + + #region CONSTRUCTORS + + public BaseWindow(BaseViewModel viewModel) + { + this.InitializeComponent(); + this.Activated += BaseWindow_Activated; + + this.ExtendsContentIntoTitleBar = true; + this.SetTitleBar(this.AppTitleBar); + + this.Root.DataContext = viewModel; + } + + #endregion + + + + #region PRIVATE METHODS + + private void Root_Loaded(object sender, RoutedEventArgs e) => RootLoaded?.Invoke(this, EventArgs.Empty); + + private void BaseWindow_Activated(object sender, WindowActivatedEventArgs args) + { + IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); + WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle); + AppWindow appWindow = AppWindow.GetFromWindowId(windowId); + appWindow.SetIcon(@"Assets\Logo\Logo.ico"); + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeDownloadsView.xaml b/VDownload.Core/VDownload.Core.Views/Home/HomeDownloadsView.xaml new file mode 100644 index 0000000..b2a9a6d --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeDownloadsView.xaml @@ -0,0 +1,397 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.Home.HomeDownloadsView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Core.Views.Home" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + xmlns:m="using:VDownload.Models" + xmlns:ct="using:CommunityToolkit.WinUI" + xmlns:ctc="using:CommunityToolkit.WinUI.Controls" + xmlns:ctuc="using:CommunityToolkit.WinUI.UI.Controls" + xmlns:ctb="using:CommunityToolkit.WinUI.Behaviors" + mc:Ignorable="d" + x:Name="Root" + Background="Transparent"> + <ctuc:SwitchPresenter Value="{Binding TaskListIsEmpty, Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="True"> + <StackPanel HorizontalAlignment="Center" + VerticalAlignment="Center"> + <Image Source="{StaticResource ImageHomeDownloadsViewNoTasks}" + Width="100"/> + <TextBlock Text="Click Video/Playlist search button to add new tasks" + Foreground="{StaticResource GreyText}"/> + </StackPanel> + </ctuc:Case> + <ctuc:Case Value="False"> + <ScrollViewer> + <ItemsControl ItemsSource="{Binding Tasks}"> + <ItemsControl.ItemTemplate> + <DataTemplate> + <Grid Background="{ThemeResource ViewBackgroundColor}" + CornerRadius="10" + Height="150" + Margin="0,0,0,10"> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + Source="{Binding Video.ThumbnailUrl, TargetNullValue={StaticResource ImageOtherThumbnail}}" + VerticalAlignment="Stretch"/> + <Grid Grid.Column="1" + Margin="10" + RowSpacing="10"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + </Grid.RowDefinitions> + <Grid Grid.Row="0" + ColumnSpacing="10" + HorizontalAlignment="Left"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <TextBlock Grid.Column="0" + FontWeight="SemiBold" + FontSize="18" + Text="{Binding Video.Title}" + TextTrimming="CharacterEllipsis"/> + <TextBlock Grid.Column="1" + VerticalAlignment="Center" + FontWeight="Light" + FontSize="12" + Text="{Binding Video.Author}"/> + </Grid> + <Grid Grid.Row="1" + RowSpacing="10" + ColumnSpacing="10"> + <Grid.Resources> + <x:Double x:Key="TextSize">12</x:Double> + </Grid.Resources> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition/> + <RowDefinition/> + <RowDefinition/> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + </Grid.ColumnDefinitions> + <Image Grid.Row="0" + Grid.Column="0" + Source="{ThemeResource ImageHomeDownloadsViewQuality}"/> + <StackPanel Grid.Row="0" + Grid.Column="1" + VerticalAlignment="Center" + Orientation="Horizontal"> + <ctuc:SwitchPresenter Value="{Binding DownloadOptions.MediaType, Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="Original"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOriginal" + FontSize="{StaticResource TextSize}"/> + </ctuc:Case> + <ctuc:Case Value="OnlyVideo"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyVideo" + FontSize="{StaticResource TextSize}"/> + </ctuc:Case> + <ctuc:Case Value="OnlyAudio"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyAudio" + FontSize="{StaticResource TextSize}"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + <TextBlock FontSize="{StaticResource TextSize}"> + <Run Text=" ("/><Run Text="{Binding DownloadOptions.SelectedStream.Name}"/>) + </TextBlock> + </StackPanel> + <Image Grid.Row="1" + Grid.Column="0" + Source="{ThemeResource ImageHomeDownloadsViewTime}"/> + <StackPanel Grid.Row="1" + Grid.Column="1" + Orientation="Horizontal" + VerticalAlignment="Center"> + <TextBlock Text="{Binding DownloadOptions.DurationAfterTrim}" + FontSize="{StaticResource TextSize}"/> + <TextBlock Visibility="{Binding DownloadOptions.IsTrimmed, Converter={StaticResource BoolToVisibilityConverter}}" + FontSize="{StaticResource TextSize}"> + <Run Text=" "/>(<Run Text="{Binding DownloadOptions.TrimStart}"/> - <Run Text="{Binding DownloadOptions.TrimEnd}"/>) + </TextBlock> + </StackPanel> + <Image Grid.Row="2" + Grid.Column="0" + Source="{ThemeResource ImageHomeDownloadsViewFile}"/> + <TextBlock Grid.Row="2" + Grid.Column="1" + FontSize="{StaticResource TextSize}" + VerticalAlignment="Center"> + <Run Text="{Binding DownloadOptions.Directory}"/>\<Run Text="{Binding DownloadOptions.Filename}"/>.<Run Text="{Binding DownloadOptions.Extension}"/> + </TextBlock> + <Image Grid.Row="3" + Grid.Column="0"> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Idle"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewIdle}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Queued"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewQueued}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Initializing"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewInitializing}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Downloading"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewDownloading}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Processing"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewProcessing}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Finalizing"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewFinalizing}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="EndedUnsuccessfully"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewError}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="EndedSuccessfully"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewDone}"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="EndedCancelled"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{ThemeResource ImageHomeDownloadsViewCancelled}"/> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </Image> + <Border Grid.Row="3" + Grid.Column="1" + VerticalAlignment="Center"> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Idle"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusIdle" + FontSize="{StaticResource TextSize}"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Queued"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusQueued" + FontSize="{StaticResource TextSize}"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Initializing"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusInitializing" + FontSize="{StaticResource TextSize}"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Downloading"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <StackPanel Orientation="Horizontal"> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusDownloading" + FontSize="{StaticResource TextSize}"/> + <TextBlock Text="{Binding Progress, Converter={StaticResource StringFormatConverter}, ConverterParameter='{} ({0:0.##}%)'}" + FontSize="{StaticResource TextSize}"/> + </StackPanel> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Processing"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock FontSize="{StaticResource TextSize}"> + <Run x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusProcessing"/><Run Text=" ("/><Run Text="{Binding Progress, Converter={StaticResource StringFormatConverter}, ConverterParameter='{}{0:0.##}'}"/>%) + </TextBlock> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Finalizing"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusFinalizing" + FontSize="{StaticResource TextSize}"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="EndedUnsuccessfully"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock FontSize="{StaticResource TextSize}"> + <Run x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusError"/><Run Text=" ("/><Run Text="{Binding Error}"/>) + </TextBlock> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="EndedSuccessfully"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusDone" + FontSize="{StaticResource TextSize}"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="EndedCancelled"> + <ic:ChangePropertyAction PropertyName="Child"> + <ic:ChangePropertyAction.Value> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeDownloadsViewResources/StatusCancelled" + FontSize="{StaticResource TextSize}"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </Border> + </Grid> + </Grid> + <Grid Grid.Column="2" + Margin="0,0,5,0"> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition/> + <RowDefinition/> + </Grid.RowDefinitions> + <AppBarButton Grid.Row="0" + Width="40" + Height="48"> + <i:Interaction.Behaviors> + <ic:EventTriggerBehavior EventName="Click"> + <ctb:NavigateToUriAction NavigateUri="{Binding Video.Url}"/> + </ic:EventTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Video.Source, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="Twitch"> + <ic:ChangePropertyAction PropertyName="Icon"> + <ic:ChangePropertyAction.Value> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{StaticResource ImageSourcesTwitch}"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </AppBarButton> + <AppBarButton Grid.Row="1" + Width="40" + Height="48" + Command="{Binding ElementName=Root, Path=DataContext.StartCancelTaskCommand}" + CommandParameter="{Binding}"> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToIntConverter}}" + ComparisonCondition="LessThan" + Value="4"> + <ic:ChangePropertyAction PropertyName="Icon"> + <ic:ChangePropertyAction.Value> + <SymbolIcon Symbol="Download"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToIntConverter}}" + ComparisonCondition="GreaterThanOrEqual" + Value="4"> + <ic:ChangePropertyAction PropertyName="Icon"> + <ic:ChangePropertyAction.Value> + <SymbolIcon Symbol="Cancel"/> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </AppBarButton> + <AppBarButton Grid.Row="2" + Icon="Delete" + Width="40" + Height="48" + Command="{Binding ElementName=Root, Path=DataContext.RemoveTaskCommand}" + CommandParameter="{Binding}"/> + </Grid> + </Grid> + <ProgressBar Grid.Row="1" + Value="{Binding Progress}" + Maximum="100"> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToIntConverter}}" + ComparisonCondition="LessThan" + Value="5"> + <ic:ChangePropertyAction PropertyName="Visibility" Value="Collapsed"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToIntConverter}}" + ComparisonCondition="GreaterThanOrEqual" + Value="5"> + <ic:ChangePropertyAction PropertyName="Visibility" Value="Visible"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToIntConverter}}" + ComparisonCondition="LessThan" + Value="7"> + <ic:ChangePropertyAction PropertyName="IsIndeterminate" Value="True"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Status, Converter={StaticResource ObjectToIntConverter}}" + ComparisonCondition="GreaterThanOrEqual" + Value="7"> + <ic:ChangePropertyAction PropertyName="IsIndeterminate" Value="False"/> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </ProgressBar> + </Grid> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </ScrollViewer> + </ctuc:Case> + </ctuc:SwitchPresenter> + +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeDownloadsView.xaml.cs b/VDownload.Core/VDownload.Core.Views/Home/HomeDownloadsView.xaml.cs new file mode 100644 index 0000000..18e1b93 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeDownloadsView.xaml.cs @@ -0,0 +1,31 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using VDownload.Core.ViewModels.Home; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace VDownload.Core.Views.Home +{ + public sealed partial class HomeDownloadsView : Page + { + #region CONSTRUCTORS + + public HomeDownloadsView(HomeDownloadsViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeVideoCollectionView.xaml b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoCollectionView.xaml new file mode 100644 index 0000000..6c5d31f --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoCollectionView.xaml @@ -0,0 +1,424 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.Home.HomeVideoCollectionView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Core.Views.Home" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + xmlns:m="using:VDownload.Models" + xmlns:ct="using:CommunityToolkit.WinUI" + xmlns:ctc="using:CommunityToolkit.WinUI.Controls" + xmlns:ctuc="using:CommunityToolkit.WinUI.UI.Controls" + xmlns:c="using:SimpleToolkit.UI.WinUI.Controls" + mc:Ignorable="d" + Background="{ThemeResource ViewBackgroundColor}" + x:Name="Root"> + + <Grid Padding="15" + RowSpacing="20"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <Grid Grid.Row="0"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <TextBlock Grid.Column="0" + VerticalAlignment="Center" + Text="{Binding Name}" + FontWeight="Bold" + FontSize="20" + TextWrapping="WrapWholeWords"/> + <StackPanel Grid.Column="1" + Orientation="Horizontal" + Margin="-5" + Spacing="10"> + <TextBlock VerticalAlignment="Center" + Visibility="{Binding IsSomethingHidden, Converter={StaticResource BoolToVisibilityConverter}}"> + <Run x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/HiddenTextBlock"/><Run Text="{Binding HiddenCount}"/> + </TextBlock> + <AppBarToggleButton x:Name="FilterButton" + Icon="Filter" + Width="40" + Height="48"> + <AppBarToggleButton.Resources> + <TeachingTip x:Name="FilterWindow" + x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterWindow" + Target="{Binding ElementName=FilterButton}" + PreferredPlacement="BottomLeft" + IsOpen="{Binding ElementName=FilterButton, Path=IsChecked, Mode=TwoWay}"> + <Grid VerticalAlignment="Top" + Margin="0,10,0,0" + ColumnSpacing="10" + RowSpacing="10"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterTitleTextBlock" + Grid.Row="0" + Grid.Column="0" + VerticalAlignment="Center"/> + <TextBox x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterTitleTextBox" + Grid.Row="0" + Grid.Column="1" + Text="{Binding TitleFilter, Mode=TwoWay}"/> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterAuthorTextBlock" + Grid.Row="1" + Grid.Column="0" + VerticalAlignment="Center"/> + <TextBox x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterAuthorTextBox" + Grid.Row="1" + Grid.Column="1" + Text="{Binding AuthorFilter, Mode=TwoWay}"/> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterViewsTextBlock" + Grid.Row="2" + Grid.Column="0" + VerticalAlignment="Center"/> + <Grid Grid.Row="2" + Grid.Column="1" + ColumnSpacing="5"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + </Grid.ColumnDefinitions> + <NumberBox Grid.Column="0" + SpinButtonPlacementMode="Compact" + Minimum="{Binding MinViews}" + Value="{Binding MinViewsFilter, Mode=TwoWay}" + Maximum="{Binding MaxViewsFilter}" + SmallChange="1" + LargeChange="10"/> + <TextBlock Grid.Column="1" + VerticalAlignment="Center" + Text="-"/> + <NumberBox Grid.Column="2" + SpinButtonPlacementMode="Compact" + Minimum="{Binding MinViewsFilter}" + Value="{Binding MaxViewsFilter, Mode=TwoWay}" + Maximum="{Binding MaxViews}" + SmallChange="1" + LargeChange="10"/> + </Grid> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterDateTextBlock" + Grid.Row="3" + Grid.Column="0" + VerticalAlignment="Center"/> + <Grid Grid.Row="3" + Grid.Column="1" + ColumnSpacing="5"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + </Grid.ColumnDefinitions> + <CalendarDatePicker Grid.Column="0" + MinDate="{Binding MinDate}" + Date="{Binding MinDateFilter, Mode=TwoWay}" + MaxDate="{Binding MaxDateFilter}"/> + <TextBlock Grid.Column="1" + VerticalAlignment="Center" + Text="-"/> + <CalendarDatePicker Grid.Column="2" + MinDate="{Binding MinDateFilter}" + Date="{Binding MaxDateFilter, Mode=TwoWay}" + MaxDate="{Binding MaxDate}"/> + </Grid> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterDurationTextBlock" + Grid.Row="4" + Grid.Column="0" + VerticalAlignment="Center"/> + <Grid Grid.Row="4" + Grid.Column="1" + RowSpacing="5"> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition/> + </Grid.RowDefinitions> + <c:TimeSpanControl Grid.Row="0" + Minimum="{Binding MinDuration}" + Value="{Binding MinDurationFilter, Mode=TwoWay}" + Maximum="{Binding MaxDurationFilter}"/> + <c:TimeSpanControl Grid.Row="1" + Minimum="{Binding MinDurationFilter}" + Value="{Binding MaxDurationFilter, Mode=TwoWay}" + Maximum="{Binding MaxDuration}"/> + </Grid> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterRemovedTextBlock" + Grid.Row="5" + Grid.Column="0" + VerticalAlignment="Center"/> + <Grid Grid.Row="5" + Grid.Column="1"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <TextBlock Grid.Column="0" + VerticalAlignment="Center" + Text="{Binding RemovedCount}"/> + <Button x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilterRemovedButton" + Grid.Column="1" + Command="{Binding RestoreRemovedVideosCommand}"/> + </Grid> + </Grid> + </TeachingTip> + </AppBarToggleButton.Resources> + </AppBarToggleButton> + <AppBarButton x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/ApplyDirectoryButton" + Icon="Folder" + Width="40" + Height="48" + Command="{Binding SelectDirectoryCommand}"/> + </StackPanel> + </Grid> + <ScrollViewer Grid.Row="1"> + <ItemsControl ItemsSource="{Binding Videos}"> + <ItemsControl.ItemTemplate> + <DataTemplate> + <Expander HorizontalAlignment="Stretch" + Margin="0,0,0,10" + CornerRadius="10" + HorizontalContentAlignment="Stretch" + Visibility="{Binding Value, Converter={StaticResource BoolToVisibilityConverter}}"> + <Expander.Header> + <Grid Padding="-16,0,-16,0"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + Source="{Binding Key.ThumbnailUrl, TargetNullValue={StaticResource ImageOtherThumbnail}}" + Height="100"/> + <Grid Grid.Column="1" + Margin="10" + RowSpacing="10"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + </Grid.RowDefinitions> + <TextBlock Grid.Row="0" + FontSize="16" + Text="{Binding Key.Title}" + FontWeight="SemiBold" + TextTrimming="CharacterEllipsis"/> + <Grid Grid.Row="1" + RowSpacing="10" + ColumnSpacing="10"> + <Grid.Resources> + <x:Double x:Key="IconSize">17</x:Double> + <x:Double x:Key="FontSize">13</x:Double> + </Grid.Resources> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition/> + </Grid.RowDefinitions> + <Image Grid.Column="0" + Grid.Row="0" + VerticalAlignment="Center" + Source="{ThemeResource ImageHomeVideoCollectionViewAuthor}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="1" + Grid.Row="0" + FontSize="{StaticResource FontSize}" + VerticalAlignment="Center" + Text="{Binding Key.Author}"/> + <Image Grid.Column="0" + Grid.Row="1" + VerticalAlignment="Center" + Source="{ThemeResource ImageHomeVideoCollectionViewDate}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="1" + Grid.Row="1" + FontSize="{StaticResource FontSize}" + VerticalAlignment="Center" + Text="{Binding Key.PublishDate}"/> + <Image Grid.Column="2" + Grid.Row="0" + VerticalAlignment="Center" + Source="{ThemeResource ImageHomeVideoCollectionViewTime}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="3" + Grid.Row="0" + FontSize="{StaticResource FontSize}" + VerticalAlignment="Center" + Text="{Binding Key.Duration}"/> + <Image Grid.Column="2" + Grid.Row="1" + VerticalAlignment="Center" + Source="{ThemeResource ImageHomeVideoCollectionViewViews}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="3" + Grid.Row="1" + FontSize="{StaticResource FontSize}" + VerticalAlignment="Center" + Text="{Binding Key.Views}"/> + </Grid> + </Grid> + <AppBarButton Grid.Column="2" + Width="40" + Height="48" + Icon="Delete" + VerticalAlignment="Center" + Command="{Binding ElementName=Root, Path=DataContext.RemoveVideoCommand}" + CommandParameter="{Binding Key}"/> + </Grid> + </Expander.Header> + <Expander.Content> + <StackPanel Spacing="20"> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/MediaOptionsHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/QualitySettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoCollectionViewMedia}"/> + </ctc:SettingsCard.HeaderIcon> + <ComboBox ItemsSource="{Binding Key.Streams}" + SelectedItem="{Binding Key.SelectedStream, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/MediaTypeSettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoCollectionViewMedia}"/> + </ctc:SettingsCard.HeaderIcon> + <ComboBox ItemsSource="{ct:EnumValues Type=m:MediaType}" + SelectedItem="{Binding Key.MediaType, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <ctuc:SwitchPresenter Value="{Binding Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="Original"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOriginal"/> + </ctuc:Case> + <ctuc:Case Value="OnlyVideo"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyVideo"/> + </ctuc:Case> + <ctuc:Case Value="OnlyAudio"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyAudio"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ctc:SettingsCard> + <ctc:SettingsExpander x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/TrimSettingsGroup"> + <ctc:SettingsExpander.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoCollectionViewTrim}"/> + </ctc:SettingsExpander.HeaderIcon> + <ctc:SettingsExpander.Items> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/TrimStartSettingsCard"> + <c:TimeSpanControl Value="{Binding Key.TrimStart, Mode=TwoWay}" + Maximum="{Binding Key.TrimEnd, Mode=OneWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/TrimEndSettingsCard"> + <c:TimeSpanControl Minimum="{Binding Key.TrimStart, Mode=OneWay}" + Value="{Binding Key.TrimEnd, Mode=TwoWay}" + Maximum="{Binding Key.Duration, Mode=OneWay}"/> + </ctc:SettingsCard> + </ctc:SettingsExpander.Items> + </ctc:SettingsExpander> + </StackPanel> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FileOptionsHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/DirectorySettingsCard" + Description="{Binding Key.DirectoryPath}"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoCollectionViewDirectory}"/> + </ctc:SettingsCard.HeaderIcon> + <Button x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/DirectorySettingsCardButton" + Command="{Binding Key.BrowseCommand}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FilenameSettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoCollectionViewFilename}"/> + </ctc:SettingsCard.HeaderIcon> + <TextBox Text="{Binding Key.Filename, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/FileTypeSettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoCollectionViewExtension}"/> + </ctc:SettingsCard.HeaderIcon> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding Key.MediaType, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="OnlyAudio"> + <ic:ChangePropertyAction PropertyName="Content"> + <ic:ChangePropertyAction.Value> + <ComboBox ItemsSource="{ct:EnumValues Type=m:AudioExtension}" + SelectedItem="{Binding Key.AudioExtension, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding}"/> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding Key.MediaType, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="NotEqual" + Value="OnlyAudio"> + <ic:ChangePropertyAction PropertyName="Content"> + <ic:ChangePropertyAction.Value> + <ComboBox ItemsSource="{ct:EnumValues Type=m:VideoExtension}" + SelectedItem="{Binding Key.VideoExtension, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding}"/> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </ctc:SettingsCard> + </StackPanel> + </StackPanel> + </Expander.Content> + </Expander> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </ScrollViewer> + <StackPanel Grid.Row="2" + HorizontalAlignment="Right" + Orientation="Horizontal" + Spacing="10"> + <Button x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/CreateAndStartButton" + Command="{Binding CreateTasksAndDownloadCommand}"/> + <Button x:Uid="/VDownload.Core.Strings/HomeVideoCollectionViewResources/CreateButton" + Style="{StaticResource AccentButtonStyle}" + Command="{Binding CreateTasksCommand}"/> + </StackPanel> + </Grid> +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeVideoCollectionView.xaml.cs b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoCollectionView.xaml.cs new file mode 100644 index 0000000..7238b2b --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoCollectionView.xaml.cs @@ -0,0 +1,31 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using VDownload.Core.ViewModels.Home; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace VDownload.Core.Views.Home +{ + public sealed partial class HomeVideoCollectionView : Page + { + #region CONSTRUCTORS + + public HomeVideoCollectionView(HomeVideoCollectionViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeVideoView.xaml b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoView.xaml new file mode 100644 index 0000000..aff7cb3 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoView.xaml @@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.Home.HomeVideoView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Core.Views.Home" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + xmlns:m="using:VDownload.Models" + xmlns:ct="using:CommunityToolkit.WinUI" + xmlns:ctc="using:CommunityToolkit.WinUI.Controls" + xmlns:ctuc="using:CommunityToolkit.WinUI.UI.Controls" + xmlns:c="using:SimpleToolkit.UI.WinUI.Controls" + mc:Ignorable="d" + Background="{ThemeResource ViewBackgroundColor}"> + + <Grid Padding="15" + RowSpacing="20"> + <Grid.RowDefinitions> + <RowDefinition Height="150"/> + <RowDefinition/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <Grid Grid.Row="0" + ColumnSpacing="10"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*"/> + </Grid.ColumnDefinitions> + <Border Grid.Column="0" + CornerRadius="{ThemeResource ControlCornerRadius}"> + <Image Source="{Binding ThumbnailUrl, TargetNullValue={StaticResource ImageOtherThumbnail}}" + VerticalAlignment="Stretch"/> + </Border> + <StackPanel Grid.Column="1" + Spacing="15"> + <TextBlock Text="{Binding Title}" + FontWeight="Bold" + FontSize="20" + TextWrapping="WrapWholeWords"/> + <Grid ColumnSpacing="10" + RowSpacing="10"> + <Grid.Resources> + <x:Double x:Key="IconSize">18</x:Double> + </Grid.Resources> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition/> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <Image Grid.Column="0" + Grid.Row="0" + Source="{ThemeResource ImageHomeVideoViewAuthor}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="1" + Grid.Row="0" + Text="{Binding Author}"/> + <Image Grid.Column="0" + Grid.Row="1" + Source="{ThemeResource ImageHomeVideoViewDate}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="1" + Grid.Row="1" + Text="{Binding PublishDate}"/> + <Image Grid.Column="0" + Grid.Row="2" + Source="{ThemeResource ImageHomeVideoViewTime}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="1" + Grid.Row="2" + Text="{Binding Duration}"/> + <Image Grid.Column="0" + Grid.Row="3" + Source="{ThemeResource ImageHomeVideoViewViews}" + Width="{StaticResource IconSize}"/> + <TextBlock Grid.Column="1" + Grid.Row="3" + Text="{Binding Views}"/> + </Grid> + </StackPanel> + </Grid> + <ScrollViewer Grid.Row="1"> + <StackPanel Spacing="20"> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/MediaOptionsHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/QualitySettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoViewQuality}"/> + </ctc:SettingsCard.HeaderIcon> + <ComboBox ItemsSource="{Binding Streams}" + SelectedItem="{Binding SelectedStream, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/MediaTypeSettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoViewMedia}"/> + </ctc:SettingsCard.HeaderIcon> + <ComboBox ItemsSource="{ct:EnumValues Type=m:MediaType}" + SelectedItem="{Binding MediaType, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <ctuc:SwitchPresenter Value="{Binding Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="Original"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOriginal"/> + </ctuc:Case> + <ctuc:Case Value="OnlyVideo"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyVideo"/> + </ctuc:Case> + <ctuc:Case Value="OnlyAudio"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyAudio"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ctc:SettingsCard> + <ctc:SettingsExpander x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/TrimSettingsGroup"> + <ctc:SettingsExpander.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoViewTrim}"/> + </ctc:SettingsExpander.HeaderIcon> + <ctc:SettingsExpander.Items> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/TrimStartSettingsCard"> + <c:TimeSpanControl Value="{Binding TrimStart, Mode=TwoWay}" + Maximum="{Binding TrimEnd, Mode=OneWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/TrimEndSettingsCard"> + <c:TimeSpanControl Minimum="{Binding TrimStart, Mode=OneWay}" + Value="{Binding TrimEnd, Mode=TwoWay}" + Maximum="{Binding Duration, Mode=OneWay}"/> + </ctc:SettingsCard> + </ctc:SettingsExpander.Items> + </ctc:SettingsExpander> + </StackPanel> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/FileOptionsHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/DirectorySettingsCard" + Description="{Binding DirectoryPath}"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoViewDirectory}"/> + </ctc:SettingsCard.HeaderIcon> + <Button x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/DirectorySettingsCardButton" + Command="{Binding BrowseCommand}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/FilenameSettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoViewFilename}"/> + </ctc:SettingsCard.HeaderIcon> + <TextBox Text="{Binding Filename, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/FileTypeSettingsCard"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageHomeVideoViewExtension}"/> + </ctc:SettingsCard.HeaderIcon> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding MediaType, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="OnlyAudio"> + <ic:ChangePropertyAction PropertyName="Content"> + <ic:ChangePropertyAction.Value> + <ComboBox ItemsSource="{ct:EnumValues Type=m:AudioExtension}" + SelectedItem="{Binding AudioExtension, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding}"/> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding MediaType, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="NotEqual" + Value="OnlyAudio"> + <ic:ChangePropertyAction PropertyName="Content"> + <ic:ChangePropertyAction.Value> + <ComboBox ItemsSource="{ct:EnumValues Type=m:VideoExtension}" + SelectedItem="{Binding VideoExtension, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding}"/> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ic:ChangePropertyAction.Value> + </ic:ChangePropertyAction> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </ctc:SettingsCard> + </StackPanel> + </StackPanel> + </ScrollViewer> + <StackPanel Grid.Row="2" + HorizontalAlignment="Right" + Orientation="Horizontal" + Spacing="10"> + <Button x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/CreateAndStartButton" + Command="{Binding CreateTaskAndDownloadCommand}"/> + <Button x:Uid="/VDownload.Core.Strings/HomeVideoViewResources/CreateButton" + Style="{StaticResource AccentButtonStyle}" + Command="{Binding CreateTaskCommand}"/> + </StackPanel> + + </Grid> +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeVideoView.xaml.cs b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoView.xaml.cs new file mode 100644 index 0000000..cc9c57a --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeVideoView.xaml.cs @@ -0,0 +1,31 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using VDownload.Core.ViewModels.Home; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace VDownload.Core.Views.Home +{ + public sealed partial class HomeVideoView : Page + { + #region CONSTRUCTORS + + public HomeVideoView(HomeVideoViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeView.xaml b/VDownload.Core/VDownload.Core.Views/Home/HomeView.xaml new file mode 100644 index 0000000..263bcdc --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeView.xaml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.Home.HomeView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + xmlns:ctuc="using:CommunityToolkit.WinUI.UI.Controls" + mc:Ignorable="d" + Background="Transparent" + x:Name="Root"> + <i:Interaction.Behaviors> + <ic:EventTriggerBehavior EventName="Loaded"> + <ic:InvokeCommandAction Command="{Binding NavigationCommand}"/> + </ic:EventTriggerBehavior> + </i:Interaction.Behaviors> + <Grid RowSpacing="10" + Margin="10"> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <Frame Content="{Binding MainContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ViewModelToViewConverter}}"> + + </Frame> + <ctuc:SwitchPresenter Grid.Row="1" + Value="{Binding OptionBarError, Converter={StaticResource IsNotNullConverter}}"> + <ctuc:Case Value="False"> + <Grid Background="{ThemeResource OptionBarBackgroundColor}" + CornerRadius="10"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="50"/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <ctuc:UniformGrid Grid.Column="0" + Rows="1" + Margin="15,0,0,0"> + <ctuc:UniformGrid.RowDefinitions> + <RowDefinition/> + </ctuc:UniformGrid.RowDefinitions> + <ctuc:UniformGrid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </ctuc:UniformGrid.ColumnDefinitions> + <ctuc:SwitchPresenter Grid.Row="0" + VerticalAlignment="Stretch" + Margin="0,0,15,0" + Value="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}"> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="Equal" + Value="None"> + <ic:ChangePropertyAction PropertyName="Visibility" + Value="Collapsed"/> + </ic:DataTriggerBehavior> + <ic:DataTriggerBehavior Binding="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}" + ComparisonCondition="NotEqual" + Value="None"> + <ic:ChangePropertyAction PropertyName="Visibility" + Value="Visible"/> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + <ctuc:Case Value="VideoSearch"> + <Grid ColumnSpacing="10" + VerticalAlignment="Stretch"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <TextBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarVideoSearchContentTextBox" + Grid.Column="0" + VerticalAlignment="Center" + Text="{Binding OptionBarVideoSearchTBValue, Mode=TwoWay}"/> + <Button x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarSearchButton" + Grid.Column="1" + IsEnabled="{Binding OptionBarSearchNotPending}" + Command="{Binding VideoSearchStartCommand}"/> + </Grid> + </ctuc:Case> + <ctuc:Case Value="PlaylistSearch"> + <Grid ColumnSpacing="10"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <TextBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearchContentTextBox" + Grid.Column="0" + VerticalAlignment="Center" + Text="{Binding OptionBarPlaylistSearchTBValue, Mode=TwoWay}"/> + <NumberBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearchContentNumberBox" + Grid.Column="1" + VerticalAlignment="Center" + SpinButtonPlacementMode="Compact" + SmallChange="1" + LargeChange="10" + Value="{Binding OptionBarPlaylistSearchNBValue, Mode=TwoWay}" + Minimum="0"/> + <Button x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarSearchButton" + Grid.Column="2" + IsEnabled="{Binding OptionBarSearchNotPending}" + Command="{Binding PlaylistSearchStartCommand}"/> + </Grid> + </ctuc:Case> + </ctuc:SwitchPresenter> + <StackPanel VerticalAlignment="Center" + Orientation="Horizontal"> + <ProgressRing Width="20" + Height="20" + Margin="0,0,10,0" + Visibility="{Binding OptionBarLoading, Converter={StaticResource BoolToVisibilityConverter}}"/> + <TextBlock Text="{Binding OptionBarMessage}"/> + </StackPanel> + </ctuc:UniformGrid> + <StackPanel Grid.Column="2" + Orientation="Horizontal" + Margin="0,0,4,0"> + <AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarLoadSubscription" + Icon="Favorite" + IsEnabled="{Binding OptionBarSearchNotPending}" + IsChecked="{Binding OptionBarLoadSubscriptionButtonChecked, Mode=TwoWay}" + Command="{Binding LoadFromSubscriptionCommand}"/> + <AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarVideoSearch" + Icon="Video" + IsEnabled="{Binding OptionBarSearchNotPending}" + IsChecked="{Binding OptionBarVideoSearchButtonChecked, Mode=TwoWay}" + Command="{Binding VideoSearchShowCommand}"/> + <AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearch" + Icon="List" + IsEnabled="{Binding OptionBarSearchNotPending}" + IsChecked="{Binding OptionBarPlaylistSearchButtonChecked, Mode=TwoWay}" + Command="{Binding PlaylistSearchShowCommand}"/> + <AppBarSeparator/> + <AppBarButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarCancelAll" + Icon="Cancel" + Command="{Binding CancelCommand}"/> + <AppBarButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarDownloadAll" + Icon="Download" + Command="{Binding DownloadCommand}"/> + </StackPanel> + </Grid> + </ctuc:Case> + <ctuc:Case Value="True"> + <InfoBar x:Uid="/VDownload.Core.Strings/HomeViewResources/ErrorInfoBar" + Severity="Error" + IsOpen="{Binding OptionBarIsErrorOpened, Mode=TwoWay}" + Message="{Binding OptionBarError}" + CloseButtonCommand="{Binding CloseErrorCommand}"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + + </Grid> +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/Home/HomeView.xaml.cs b/VDownload.Core/VDownload.Core.Views/Home/HomeView.xaml.cs new file mode 100644 index 0000000..b27b4a4 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Home/HomeView.xaml.cs @@ -0,0 +1,18 @@ +using Microsoft.UI.Xaml.Controls; +using VDownload.Core.ViewModels.Home; + +namespace VDownload.Core.Views.Home +{ + public sealed partial class HomeView : Page + { + #region CONSTRUCTORS + + public HomeView(HomeViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/Settings/SettingsView.xaml b/VDownload.Core/VDownload.Core.Views/Settings/SettingsView.xaml new file mode 100644 index 0000000..96e06c9 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Settings/SettingsView.xaml @@ -0,0 +1,312 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.Settings.SettingsView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Core.Views.Settings" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ctuc="using:CommunityToolkit.WinUI.UI.Controls" + xmlns:ctc="using:CommunityToolkit.WinUI.Controls" + xmlns:ct="using:CommunityToolkit.WinUI" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + xmlns:m="using:VDownload.Models" + mc:Ignorable="d" + Background="{ThemeResource ViewBackgroundColor}"> + + <Grid Padding="20" + RowSpacing="20"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + </Grid.RowDefinitions> + <TextBlock x:Uid="/VDownload.Core.Strings/SettingsViewResources/Header" + Grid.Row="0" + FontSize="28" + FontWeight="SemiBold"/> + <ScrollViewer Grid.Row="1"> + <StackPanel Spacing="20"> + <!-- Searching --> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/SettingsViewResources/SearchingHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/SearchingPlaylistCount"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewSearchingPlaylistCount}"/> + </ctc:SettingsCard.HeaderIcon> + <NumberBox Value="{Binding SearchingPlaylistCount, Mode=TwoWay}" + Minimum="0" + SmallChange="1" + LargeChange="10" + SpinButtonPlacementMode="Compact"/> + </ctc:SettingsCard> + </StackPanel> + + <!-- Tasks --> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksRunningTasks"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTasksRunningTasks}"/> + </ctc:SettingsCard.HeaderIcon> + <NumberBox Value="{Binding TasksRunningTasks, Mode=TwoWay}" + Minimum="1" + SmallChange="1" + LargeChange="10" + SpinButtonPlacementMode="Compact"/> + </ctc:SettingsCard> + <ctc:SettingsExpander x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksDefaultMediaOptions"> + <ctc:SettingsExpander.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTasksDefaultMediaOptions}"/> + </ctc:SettingsExpander.HeaderIcon> + <ctc:SettingsExpander.Items> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksMediaType"> + <ComboBox ItemsSource="{ct:EnumValues Type=m:MediaType}" + SelectedItem="{Binding TasksMediaType, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <ctuc:SwitchPresenter Value="{Binding Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="Original"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOriginal"/> + </ctuc:Case> + <ctuc:Case Value="OnlyVideo"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyVideo"/> + </ctuc:Case> + <ctuc:Case Value="OnlyAudio"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/MediaTypeOnlyAudio"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksVideoExtension"> + <ComboBox ItemsSource="{ct:EnumValues Type=m:VideoExtension}" + SelectedItem="{Binding TasksVideoExtension, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksAudioExtension"> + <ComboBox ItemsSource="{ct:EnumValues Type=m:AudioExtension}" + SelectedItem="{Binding TasksAudioExtension, Mode=TwoWay}"/> + </ctc:SettingsCard> + </ctc:SettingsExpander.Items> + </ctc:SettingsExpander> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksFilenameTemplate"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTasksFilenameTemplate}"/> + </ctc:SettingsCard.HeaderIcon> + <TextBox Text="{Binding TasksFilenameTemplate, Mode=TwoWay}" + ToolTipService.ToolTip="{Binding TasksFilenameTemplateTooltip}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksMeteredConnectionWarning"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTasksMeteredConnectionWarning}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding TasksMeteredConnectionWarning, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsExpander x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksSaveLastOutputDirectory"> + <ctc:SettingsExpander.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTasksOutputDirectory}"/> + </ctc:SettingsExpander.HeaderIcon> + <ToggleSwitch IsOn="{Binding TasksSaveLastOutputDirectory, Mode=TwoWay}"/> + <ctc:SettingsExpander.Items> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksDefaultOutputDirectory" + IsEnabled="{Binding TasksSaveLastOutputDirectory, Converter={StaticResource BoolNegationConverter}}" + Description="{Binding TasksDefaultOutputDirectory}"> + <Button x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksDefaultOutputDirectoryButton" + Command="{Binding BrowseTasksDefaultOutputDirectoryCommand}"/> + </ctc:SettingsCard> + </ctc:SettingsExpander.Items> + </ctc:SettingsExpander> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TasksReplaceOutputFile"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTasksReplaceOutputFile}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding TasksReplaceOutputFile, Mode=TwoWay}"/> + </ctc:SettingsCard> + </StackPanel> + + <!-- Processing --> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/SettingsViewResources/ProcessingHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/ProcessingFFmpegLocation" + Description="{Binding ProcessingFFmpegLocation}"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewProcessingFFmpegLocation}"/> + </ctc:SettingsCard.HeaderIcon> + <Button x:Uid="/VDownload.Core.Strings/SettingsViewResources/ProcessingFFmpegLocationButton" + Command="{Binding BrowseProcessingFFmpegLocationCommand}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/ProcessingUseMultithreading"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewProcessingUseMultithreading}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding ProcessingUseMultithreading, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/ProcessingUseHardwareAcceleration"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewProcessingUseHardwareAcceleration}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding ProcessingUseHardwareAcceleration, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/ProcessingSpeed"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewProcessingSpeed}"/> + </ctc:SettingsCard.HeaderIcon> + <ComboBox ItemsSource="{ct:EnumValues Type=m:ProcessingSpeed}" + SelectedItem="{Binding ProcessingSpeed, Mode=TwoWay}"> + <ComboBox.ItemTemplate> + <DataTemplate> + <ctuc:SwitchPresenter Value="{Binding Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="VerySlow"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedVerySlow"/> + </ctuc:Case> + <ctuc:Case Value="Slower"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedSlower"/> + </ctuc:Case> + <ctuc:Case Value="Slow"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedSlow"/> + </ctuc:Case> + <ctuc:Case Value="Medium"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedMedium"/> + </ctuc:Case> + <ctuc:Case Value="Fast"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedFast"/> + </ctuc:Case> + <ctuc:Case Value="Faster"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedFaster"/> + </ctuc:Case> + <ctuc:Case Value="VeryFast"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedVeryFast"/> + </ctuc:Case> + <ctuc:Case Value="SuperFast"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedSuperFast"/> + </ctuc:Case> + <ctuc:Case Value="UltraFast"> + <TextBlock x:Uid="/VDownload.Core.Strings/CommonResources/ProcessingSpeedUltraFast"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </ctc:SettingsCard> + </StackPanel> + + <!-- Notifications --> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/SettingsViewResources/NotificationsHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/NotificationsOnSuccessful"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewNotificationOnSuccessful}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding NotificationsOnSuccessful, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/NotificationsOnUnsuccessful"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewNotificationOnUnsuccessful}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding NotificationsOnUnsuccessful, Mode=TwoWay}"/> + </ctc:SettingsCard> + </StackPanel> + + <!-- Temporary files --> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/SettingsViewResources/TempHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TempDirectory" + Description="{Binding TempDirectory}"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTempDirectory}"/> + </ctc:SettingsCard.HeaderIcon> + <Button x:Uid="/VDownload.Core.Strings/SettingsViewResources/TempDirectoryButton" + Command="{Binding BrowseTempDirectoryCommand}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TempDeleteOnFail"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{ThemeResource ImageSettingsViewTempDeleteOnFail}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding TempDeleteOnFail, Mode=TwoWay}"/> + </ctc:SettingsCard> + </StackPanel> + + <!-- Twitch --> + <StackPanel Spacing="5"> + <TextBlock x:Uid="/VDownload.Core.Strings/SettingsViewResources/TwitchHeader" + FontWeight="Bold" + FontSize="15"/> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TwitchVodPassiveTrimming"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{StaticResource ImageSourcesTwitch}"/> + </ctc:SettingsCard.HeaderIcon> + <ToggleSwitch IsOn="{Binding TwitchVodPassiveTrimming, Mode=TwoWay}"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TwitchVodParallelDownloads"> + <ctc:SettingsCard.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{StaticResource ImageSourcesTwitch}"/> + </ctc:SettingsCard.HeaderIcon> + <NumberBox Value="{Binding TwitchVodParallelDownloads, Mode=TwoWay}" + Minimum="1" + SmallChange="1" + LargeChange="10" + SpinButtonPlacementMode="Compact"/> + </ctc:SettingsCard> + <ctc:SettingsExpander x:Uid="/VDownload.Core.Strings/SettingsViewResources/TwitchVodChunkDownloadingErrorRetry"> + <ctc:SettingsExpander.HeaderIcon> + <BitmapIcon ShowAsMonochrome="False" + UriSource="{StaticResource ImageSourcesTwitch}"/> + </ctc:SettingsExpander.HeaderIcon> + <ToggleSwitch IsOn="{Binding TwitchVodChunkDownloadingErrorRetry, Mode=TwoWay}"/> + <ctc:SettingsExpander.Items> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TwitchVodChunkDownloadingErrorRetryCount" + IsEnabled="{Binding TwitchVodChunkDownloadingErrorRetry}"> + <NumberBox Value="{Binding TwitchVodChunkDownloadingErrorRetryCount, Mode=TwoWay}" + Minimum="1" + SmallChange="1" + LargeChange="10" + SpinButtonPlacementMode="Compact"/> + </ctc:SettingsCard> + <ctc:SettingsCard x:Uid="/VDownload.Core.Strings/SettingsViewResources/TwitchVodChunkDownloadingErrorRetryDelay" + IsEnabled="{Binding TwitchVodChunkDownloadingErrorRetry}"> + <NumberBox Value="{Binding TwitchVodChunkDownloadingErrorRetryDelay, Mode=TwoWay}" + Minimum="0" + SmallChange="1" + LargeChange="10" + SpinButtonPlacementMode="Compact"/> + </ctc:SettingsCard> + </ctc:SettingsExpander.Items> + </ctc:SettingsExpander> + </StackPanel> + + <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> + <Button x:Uid="/VDownload.Core.Strings/SettingsViewResources/RestoreToDefaultButton" + Command="{Binding RestoreToDefaultCommand}"/> + </StackPanel> + </StackPanel> + </ScrollViewer> + </Grid> +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/Settings/SettingsView.xaml.cs b/VDownload.Core/VDownload.Core.Views/Settings/SettingsView.xaml.cs new file mode 100644 index 0000000..5f5399f --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Settings/SettingsView.xaml.cs @@ -0,0 +1,18 @@ +using Microsoft.UI.Xaml.Controls; +using VDownload.Core.ViewModels.Settings; + +namespace VDownload.Core.Views.Settings +{ + public sealed partial class SettingsView : Page + { + #region CONSTRUCTORS + + public SettingsView(SettingsViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/Subscriptions/SubscriptionsView.xaml b/VDownload.Core/VDownload.Core.Views/Subscriptions/SubscriptionsView.xaml new file mode 100644 index 0000000..feb9aca --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Subscriptions/SubscriptionsView.xaml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="utf-8"?> +<Page + x:Class="VDownload.Core.Views.Subscriptions.SubscriptionsView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Core.Views.Subscriptions" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ctuc="using:CommunityToolkit.WinUI.UI.Controls" + xmlns:ctc="using:CommunityToolkit.WinUI.Controls" + xmlns:ct="using:CommunityToolkit.WinUI" + xmlns:i="using:Microsoft.Xaml.Interactivity" + xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + mc:Ignorable="d" + Background="{ThemeResource ViewBackgroundColor}" + x:Name="Root"> + <i:Interaction.Behaviors> + <ic:EventTriggerBehavior EventName="Loaded"> + <ic:InvokeCommandAction Command="{Binding NavigationCommand}"/> + </ic:EventTriggerBehavior> + </i:Interaction.Behaviors> + + <Grid Padding="20" + RowSpacing="20"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <TextBlock x:Uid="/VDownload.Core.Strings/SubscriptionsViewResources/Header" + Grid.Row="0" + FontSize="28" + FontWeight="SemiBold"/> + <ScrollViewer Grid.Row="1"> + <ItemsControl ItemsSource="{Binding Playlists}"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <ctuc:StaggeredPanel ColumnSpacing="10" + DesiredColumnWidth="120" + RowSpacing="10"/> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <Grid RowSpacing="10" + Padding="10" + Background="{ThemeResource PanelBackgroundColor}" + CornerRadius="10"> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <Image Grid.Row="0"> + <i:Interaction.Behaviors> + <ic:DataTriggerBehavior Binding="{Binding Source, Converter={StaticResource ObjectToStringConverter}}" + Value="Twitch"> + <ic:ChangePropertyAction PropertyName="Source" + Value="{StaticResource ImageSourcesTwitch}"/> + </ic:DataTriggerBehavior> + </i:Interaction.Behaviors> + </Image> + <TextBlock Grid.Row="1" + Text="{Binding Name}" + HorizontalAlignment="Center" + TextTrimming="CharacterEllipsis"/> + <HyperlinkButton x:Uid="/VDownload.Core.Strings/SubscriptionsViewResources/RemovePlaylistButton" + Grid.Row="2" + Content="Remove" + HorizontalAlignment="Center" + Command="{Binding DataContext.RemovePlaylistCommand, ElementName=Root}" + CommandParameter="{Binding}"/> + </Grid> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </ScrollViewer> + <ctuc:SwitchPresenter Grid.Row="2" + Value="{Binding Error, Converter={StaticResource IsNotNullConverter}}"> + <ctuc:Case Value="False"> + <Grid Background="{ThemeResource PanelBackgroundColor}" + Padding="10" + ColumnSpacing="10" + CornerRadius="10"> + <Grid.ColumnDefinitions> + <ColumnDefinition/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <TextBox x:Uid="/VDownload.Core.Strings/SubscriptionsViewResources/PlaylistUrlTextBox" + Grid.Column="0" + Text="{Binding Url, Mode=TwoWay}" + IsEnabled="{Binding Loading, Converter={StaticResource BoolNegationConverter}}"/> + <ctuc:SwitchPresenter Grid.Column="1" + Value="{Binding Loading, Converter={StaticResource ObjectToStringConverter}}"> + <ctuc:Case Value="True"> + <ProgressRing IsIndeterminate="True" + Width="20" + Height="20"/> + </ctuc:Case> + <ctuc:Case Value="False"> + <Button x:Uid="/VDownload.Core.Strings/SubscriptionsViewResources/PlaylistSearchButton" + Command="{Binding AddCommand}"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + </Grid> + </ctuc:Case> + <ctuc:Case Value="True"> + <InfoBar x:Uid="/VDownload.Core.Strings/SubscriptionsViewResources/ErrorInfoBar" + Severity="Error" + IsOpen="{Binding IsErrorOpened, Mode=TwoWay}" + Message="{Binding Error}" + CloseButtonCommand="{Binding CloseErrorCommand}"/> + </ctuc:Case> + </ctuc:SwitchPresenter> + </Grid> +</Page> diff --git a/VDownload.Core/VDownload.Core.Views/Subscriptions/SubscriptionsView.xaml.cs b/VDownload.Core/VDownload.Core.Views/Subscriptions/SubscriptionsView.xaml.cs new file mode 100644 index 0000000..abf79c5 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/Subscriptions/SubscriptionsView.xaml.cs @@ -0,0 +1,32 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using VDownload.Core.ViewModels.About; +using VDownload.Core.ViewModels.Subscriptions; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace VDownload.Core.Views.Subscriptions +{ + public sealed partial class SubscriptionsView : Page + { + #region CONSTRUCTORS + + public SubscriptionsView(SubscriptionsViewModel viewModel) + { + this.InitializeComponent(); + this.DataContext = viewModel; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.Views/VDownload.Core.Views.csproj b/VDownload.Core/VDownload.Core.Views/VDownload.Core.Views.csproj new file mode 100644 index 0000000..7f6d8a0 --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/VDownload.Core.Views.csproj @@ -0,0 +1,97 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Core.Views</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + <ItemGroup> + <None Remove="About\AboutView.xaml" /> + <None Remove="Home\HomeVideoCollectionView.xaml" /> + <None Remove="Subscriptions\SubscriptionsView.xaml" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="CommunityToolkit.WinUI" Version="7.1.2" /> + <PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.0.240109" /> + <PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" /> + <PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.0.240109" /> + <PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" /> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + <PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" /> + <PackageReference Include="SimpleToolkit.UI.WinUI.Behaviors" Version="1.7.4" /> + <PackageReference Include="SimpleToolkit.UI.WinUI.Controls" Version="1.7.4" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\VDownload.Core.Strings\VDownload.Core.Strings.csproj" /> + <ProjectReference Include="..\VDownload.Core.ViewModels\VDownload.Core.ViewModels.csproj" /> + </ItemGroup> + + <ItemGroup> + <Page Update="About\AboutView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <None Update="Authentication\AuthenticationView.xaml"> + <Generator>MSBuild:Compile</Generator> + </None> + <None Update="BaseWindow.xaml"> + <Generator>MSBuild:Compile</Generator> + </None> + <None Update="Home\HomeDownloadsView.xaml"> + <Generator>MSBuild:Compile</Generator> + </None> + <Page Update="Subscriptions\SubscriptionsView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Home\HomeVideoCollectionView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <None Update="Home\HomeVideoView.xaml"> + <Generator>MSBuild:Compile</Generator> + </None> + <None Update="Settings\SettingsView.xaml"> + <Generator>MSBuild:Compile</Generator> + </None> + </ItemGroup> + + <ItemGroup> + <Page Update="Home\HomeVideoView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + + <ItemGroup> + <Page Update="BaseWindow.xaml"> + <XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime> + </Page> + </ItemGroup> + + <ItemGroup> + <Page Update="Home\HomeView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + + <ItemGroup> + <Page Update="Home\HomeDownloadsView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + + <ItemGroup> + <Page Update="Authentication\AuthenticationView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + + <ItemGroup> + <Page Update="Settings\SettingsView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> +</Project> diff --git a/VDownload.Core/VDownload.Core.Views/ViewModelToViewConverter.cs b/VDownload.Core/VDownload.Core.Views/ViewModelToViewConverter.cs new file mode 100644 index 0000000..79b497a --- /dev/null +++ b/VDownload.Core/VDownload.Core.Views/ViewModelToViewConverter.cs @@ -0,0 +1,73 @@ +using Microsoft.UI.Xaml.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using VDownload.Core.ViewModels.About; +using VDownload.Core.ViewModels.Authentication; +using VDownload.Core.ViewModels.Home; +using VDownload.Core.ViewModels.Settings; +using VDownload.Core.ViewModels.Subscriptions; +using VDownload.Core.Views.About; +using VDownload.Core.Views.Authentication; +using VDownload.Core.Views.Home; +using VDownload.Core.Views.Settings; +using VDownload.Core.Views.Subscriptions; + +namespace VDownload.Core.Views +{ + public class ViewModelToViewConverter : IValueConverter + { + #region FIELDS + + private readonly Dictionary<Type, Type> _viewModelViewBinding = new Dictionary<Type, Type> + { + { typeof(HomeViewModel), typeof(HomeView) }, + { typeof(HomeDownloadsViewModel), typeof(HomeDownloadsView) }, + { typeof(HomeVideoViewModel), typeof(HomeVideoView) }, + { typeof(HomeVideoCollectionViewModel), typeof(HomeVideoCollectionView) }, + { typeof(SubscriptionsViewModel), typeof(SubscriptionsView) }, + { typeof(SettingsViewModel), typeof(SettingsView) }, + { typeof(AboutViewModel), typeof(AboutView) }, + { typeof(AuthenticationViewModel), typeof(AuthenticationView) } + }; + + protected static IServiceProvider _serviceProvider; + + #endregion + + + + #region PUBLIC METHODS + + public static void Initialize(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is null) + { + return null; + } + if (value is Type type && _viewModelViewBinding.ContainsKey(type)) + { + return _serviceProvider.GetService(_viewModelViewBinding[type]); + } + if (_viewModelViewBinding.ContainsKey(value.GetType())) + { + return _serviceProvider.GetService(_viewModelViewBinding[value.GetType()]); + } + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + Dictionary<Type, Type> viewViewModelBinding = _viewModelViewBinding.ToDictionary(x => x.Value, x => x.Key); + if (viewViewModelBinding.ContainsKey(value.GetType())) + { + return viewViewModelBinding[value.GetType()]; + } + return null; + } + + #endregion + } +} diff --git a/VDownload.Core/VDownload.Core.csproj b/VDownload.Core/VDownload.Core.csproj deleted file mode 100644 index b879400..0000000 --- a/VDownload.Core/VDownload.Core.csproj +++ /dev/null @@ -1,190 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProjectGuid>{65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}</ProjectGuid> - <OutputType>Library</OutputType> - <AppDesignerFolder>Properties</AppDesignerFolder> - <RootNamespace>VDownload.Core</RootNamespace> - <AssemblyName>VDownload.Core</AssemblyName> - <DefaultLanguage>en-US</DefaultLanguage> - <TargetPlatformIdentifier>UAP</TargetPlatformIdentifier> - <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22000.0</TargetPlatformVersion> - <TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion> - <MinimumVisualStudioVersion>14</MinimumVisualStudioVersion> - <FileAlignment>512</FileAlignment> - <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <PlatformTarget>AnyCPU</PlatformTarget> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <PlatformTarget>AnyCPU</PlatformTarget> - <DebugType>pdbonly</DebugType> - <Optimize>true</Optimize> - <OutputPath>bin\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> - <PlatformTarget>x86</PlatformTarget> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\x86\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> - <PlatformTarget>x86</PlatformTarget> - <OutputPath>bin\x86\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'"> - <PlatformTarget>ARM</PlatformTarget> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\ARM\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'"> - <PlatformTarget>ARM</PlatformTarget> - <OutputPath>bin\ARM\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'"> - <PlatformTarget>ARM64</PlatformTarget> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\ARM64\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'"> - <PlatformTarget>ARM64</PlatformTarget> - <OutputPath>bin\ARM64\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> - <PlatformTarget>x64</PlatformTarget> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\x64\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> - <PlatformTarget>x64</PlatformTarget> - <OutputPath>bin\x64\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - </PropertyGroup> - <PropertyGroup> - <RestoreProjectStyle>PackageReference</RestoreProjectStyle> - </PropertyGroup> - <ItemGroup> - <Compile Include="Enums\AudioFileExtension.cs" /> - <Compile Include="Enums\MediaFileExtension.cs" /> - <Compile Include="Enums\MediaType.cs" /> - <Compile Include="Enums\PlaylistSource.cs" /> - <Compile Include="Enums\DownloadTasksAddingRequestSource.cs" /> - <Compile Include="Enums\VideoFileExtension.cs" /> - <Compile Include="Enums\VideoSource.cs" /> - <Compile Include="Enums\DownloadTaskStatus.cs" /> - <Compile Include="EventArgs\DownloadTasksAddingRequestedEventArgs.cs" /> - <Compile Include="EventArgs\DownloadTaskStatusChangedEventArgs.cs" /> - <Compile Include="EventArgs\ProgressChangedEventArgs.cs" /> - <Compile Include="EventArgs\PlaylistSearchSuccessedEventArgs.cs" /> - <Compile Include="EventArgs\SubscriptionLoadSuccessedEventArgs.cs" /> - <Compile Include="EventArgs\VideoSearchSuccessedEventArgs.cs" /> - <Compile Include="Exceptions\MediaNotFoundException.cs" /> - <Compile Include="Exceptions\SubscriptionExistsException.cs" /> - <Compile Include="Exceptions\TwitchAccessTokenNotFoundException.cs" /> - <Compile Include="Exceptions\TwitchAccessTokenNotValidException.cs" /> - <Compile Include="Extensions\TimeSpanExtension.cs" /> - <Compile Include="Interfaces\IPlaylist.cs" /> - <Compile Include="Interfaces\IVideo.cs" /> - <Compile Include="Services\DownloadTasksCollectionManagement.cs" /> - <Compile Include="Services\OutputFile.cs" /> - <Compile Include="Sources\AuthorizationData.cs" /> - <Compile Include="Sources\Twitch\Helpers\Client.cs" /> - <Compile Include="Services\Subscription.cs" /> - <Compile Include="Services\SubscriptionsCollectionManagement.cs" /> - <Compile Include="Structs\BaseStream.cs" /> - <Compile Include="Services\DownloadTask.cs" /> - <Compile Include="Services\Config.cs" /> - <Compile Include="Services\MediaProcessor.cs" /> - <Compile Include="Sources\Source.cs" /> - <Compile Include="Sources\Twitch\Helpers\Authorization.cs" /> - <Compile Include="Sources\Twitch\Channel.cs" /> - <Compile Include="Sources\Twitch\Clip.cs" /> - <Compile Include="Sources\Twitch\Vod.cs" /> - <Compile Include="Structs\TrimData.cs" /> - <Compile Include="Structs\TwitchAccessTokenValidationData.cs" /> - </ItemGroup> - <ItemGroup> - <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform"> - <Version>6.2.13</Version> - </PackageReference> - <PackageReference Include="Microsoft.Toolkit.Uwp.Connectivity"> - <Version>7.1.2</Version> - </PackageReference> - <PackageReference Include="Newtonsoft.Json"> - <Version>13.0.1</Version> - </PackageReference> - <PackageReference Include="System.ValueTuple"> - <Version>4.5.0</Version> - </PackageReference> - </ItemGroup> - <ItemGroup> - <Folder Include="Properties\" /> - </ItemGroup> - <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' "> - <VisualStudioVersion>14.0</VisualStudioVersion> - </PropertyGroup> - <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" /> - <!-- To modify your build process, add your task inside one of the targets below and uncomment it. - Other similar extension points exist, see Microsoft.Common.targets. - <Target Name="BeforeBuild"> - </Target> - <Target Name="AfterBuild"> - </Target> - --> -</Project> \ No newline at end of file diff --git a/VDownload.Models/AudioExtension.cs b/VDownload.Models/AudioExtension.cs new file mode 100644 index 0000000..e66e4e1 --- /dev/null +++ b/VDownload.Models/AudioExtension.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public enum AudioExtension + { + MP3, + FLAC, + WAV, + OGG + } +} diff --git a/VDownload.Models/MediaType.cs b/VDownload.Models/MediaType.cs new file mode 100644 index 0000000..89fd094 --- /dev/null +++ b/VDownload.Models/MediaType.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public enum MediaType + { + [Description("Original")] + Original, + + [Description("Only video")] + OnlyVideo, + + [Description("Only audio")] + OnlyAudio, + } +} diff --git a/VDownload.Models/Playlist.cs b/VDownload.Models/Playlist.cs new file mode 100644 index 0000000..d176dd0 --- /dev/null +++ b/VDownload.Models/Playlist.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public abstract class Playlist : VideoCollection + { + #region PROPERTIES + + public required string Description { get; set; } + public required Uri Url { get; set; } + public Source Source { get; protected set; } + + #endregion + } +} diff --git a/VDownload.Models/ProcessingSpeed.cs b/VDownload.Models/ProcessingSpeed.cs new file mode 100644 index 0000000..a0c8b36 --- /dev/null +++ b/VDownload.Models/ProcessingSpeed.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public enum ProcessingSpeed + { + VerySlow = 0, + Slower = 1, + Slow = 2, + Medium = 3, + Fast = 4, + Faster = 5, + VeryFast = 6, + SuperFast = 7, + UltraFast = 8 + } +} diff --git a/VDownload.Models/Source.cs b/VDownload.Models/Source.cs new file mode 100644 index 0000000..4bc4528 --- /dev/null +++ b/VDownload.Models/Source.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public enum Source + { + Twitch + } +} diff --git a/VDownload.Models/SubscriptionsVideoList.cs b/VDownload.Models/SubscriptionsVideoList.cs new file mode 100644 index 0000000..4030f75 --- /dev/null +++ b/VDownload.Models/SubscriptionsVideoList.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public class SubscriptionsVideoList : VideoCollection + { + } +} diff --git a/VDownload.Models/VDownload.Models.csproj b/VDownload.Models/VDownload.Models.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/VDownload.Models/VDownload.Models.csproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + +</Project> diff --git a/VDownload.Models/Video.cs b/VDownload.Models/Video.cs new file mode 100644 index 0000000..348565c --- /dev/null +++ b/VDownload.Models/Video.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public abstract class Video + { + #region PROPERTIES + + public string Id { get; set; } + public string Title { get; set; } + public string Author { get; set; } + public DateTime PublishDate { get; set; } + public TimeSpan Duration { get; set; } + public long Views { get; set; } + public Uri? ThumbnailUrl { get; set; } + public Uri Url { get; set; } + public ICollection<VideoStream> Streams { get; private set; } + public Source Source { get; protected set; } + + #endregion + + + + #region CONSTRUCTORS + + protected Video() + { + Streams = new List<VideoStream>(); + } + + #endregion + } +} diff --git a/VDownload.Core/Structs/TrimData.cs b/VDownload.Models/VideoCollection.cs similarity index 55% rename from VDownload.Core/Structs/TrimData.cs rename to VDownload.Models/VideoCollection.cs index 2ba586f..221314f 100644 --- a/VDownload.Core/Structs/TrimData.cs +++ b/VDownload.Models/VideoCollection.cs @@ -4,14 +4,13 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace VDownload.Core.Structs +namespace VDownload.Models { - public struct TrimData + public abstract class VideoCollection : List<Video> { #region PROPERTIES - public TimeSpan Start { get; set; } - public TimeSpan End { get; set; } + public required string Name { get; init; } #endregion } diff --git a/VDownload.Models/VideoDownloadOptions.cs b/VDownload.Models/VideoDownloadOptions.cs new file mode 100644 index 0000000..7786323 --- /dev/null +++ b/VDownload.Models/VideoDownloadOptions.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public class VideoDownloadOptions + { + #region FIELDS + + private readonly TimeSpan _originalDuration; + + #endregion + + + + #region PROPERTIES + + public required VideoStream SelectedStream { get; set; } + public required TimeSpan TrimStart { get; set; } + public required TimeSpan TrimEnd { get; set; } + public required MediaType MediaType { get; set; } + public required string Directory { get; set; } + public required string Filename { get; set; } + public required string Extension { get; set; } + public TimeSpan DurationAfterTrim => TrimEnd - TrimStart; + public bool IsTrimmed => _originalDuration != DurationAfterTrim; + + #endregion + + + + #region CONSTRUCTORS + + public VideoDownloadOptions(TimeSpan originalDuration) + { + _originalDuration = originalDuration; + } + + #endregion + } +} diff --git a/VDownload.Models/VideoExtension.cs b/VDownload.Models/VideoExtension.cs new file mode 100644 index 0000000..2d49ad8 --- /dev/null +++ b/VDownload.Models/VideoExtension.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public enum VideoExtension + { + MP4, + MKV, + AVI, + MOV, + WEBM, + WMV, + } +} diff --git a/VDownload.Models/VideoStream.cs b/VDownload.Models/VideoStream.cs new file mode 100644 index 0000000..045e292 --- /dev/null +++ b/VDownload.Models/VideoStream.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public abstract class VideoStream + { + #region PROPERTIES + + public string Name { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public override string ToString() => Name; + + public abstract Task<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> onProgress, CancellationToken token, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd); + + #endregion + } +} diff --git a/VDownload.Models/VideoStreamDownloadResult.cs b/VDownload.Models/VideoStreamDownloadResult.cs new file mode 100644 index 0000000..c804349 --- /dev/null +++ b/VDownload.Models/VideoStreamDownloadResult.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Models +{ + public struct VideoStreamDownloadResult + { + #region PROPERTIES + + public required string File { get; init; } + public required TimeSpan NewTrimStart { get; init; } + public required TimeSpan NewTrimEnd { get; init; } + public required TimeSpan NewDuration { get; init; } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Common/IInitializableService.cs b/VDownload.Services/VDownload.Services.Common/IInitializableService.cs new file mode 100644 index 0000000..726f8e3 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Common/IInitializableService.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Common +{ + public interface IInitializableService + { + #region METHODS + + Task Initialize(); + + #endregion + } + + public interface IInitializableService<T> + { + #region METHODS + + Task Initialize(T arg); + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Common/VDownload.Services.Common.csproj b/VDownload.Services/VDownload.Services.Common/VDownload.Services.Common.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/VDownload.Services/VDownload.Services.Common/VDownload.Services.Common.csproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/ApplicationData.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/ApplicationData.cs new file mode 100644 index 0000000..c0870c7 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/ApplicationData.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Application +{ + public class ApplicationData + { + [JsonProperty("common")] + public CommonApplicationData Common { get; set; } = new CommonApplicationData(); + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/ApplicationDataService.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/ApplicationDataService.cs new file mode 100644 index 0000000..90f84c8 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/ApplicationDataService.cs @@ -0,0 +1,107 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Services.Common; +using VDownload.Services.Data.Configuration; + +namespace VDownload.Services.Data.Application +{ + public interface IApplicationDataService : IInitializableService + { + #region PROPERTIES + + ApplicationData Data { get; } + + #endregion + + + + #region METHODS + + Task Load(); + Task Restore(); + Task Save(); + + #endregion + } + + + + public class ApplicationDataService : IApplicationDataService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + + #endregion + + + + #region FIELDS + + protected readonly string _filePath; + + #endregion + + + + #region PROPERTIES + + public ApplicationData Data { get; private set; } + + #endregion + + + + #region CONSTRUCTORS + + public ApplicationDataService(IConfigurationService configurationService) + { + _configurationService = configurationService; + + string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string appdataDirectoryName = _configurationService.Common.Path.Appdata.DirectoryName; + string appdataAuthenticationFilename = _configurationService.Common.Path.Appdata.SettingsFile; + _filePath = Path.Combine(appdataPath, appdataDirectoryName, appdataAuthenticationFilename); + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize() => await Load(); + + public async Task Load() + { + if (File.Exists(_filePath)) + { + string content = await File.ReadAllTextAsync(_filePath); + Data = JsonConvert.DeserializeObject<ApplicationData>(content); + } + else + { + Data = new ApplicationData(); + } + } + + public async Task Save() + { + Directory.CreateDirectory(Path.GetDirectoryName(_filePath)); + string content = JsonConvert.SerializeObject(Data); + await File.WriteAllTextAsync(_filePath, content); + } + + public async Task Restore() + { + Data = new ApplicationData(); + await Save(); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/CommonApplicationData.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/CommonApplicationData.cs new file mode 100644 index 0000000..6898866 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/CommonApplicationData.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Application +{ + public class CommonApplicationData + { + [JsonProperty("last_output_directory")] + public string? LastOutputDirectory { get; set; } = null; + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/VDownload.Services.Data.Application.csproj b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/VDownload.Services.Data.Application.csproj new file mode 100644 index 0000000..62d33a0 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Application/VDownload.Services.Data.Application.csproj @@ -0,0 +1,18 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + <ProjectReference Include="..\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/AuthenticationData.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/AuthenticationData.cs new file mode 100644 index 0000000..f036d91 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/AuthenticationData.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using VDownload.Services.Data.Authentication.Models; + +namespace VDownload.Services.Data.Authentication +{ + public class AuthenticationData + { + #region PROPERTIES + + [JsonProperty("twitch")] + public TokenAuthenticationData Twitch { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public AuthenticationData() + { + Twitch = new TokenAuthenticationData(); + } + + #endregion + } +} \ No newline at end of file diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/AuthenticationDataService.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/AuthenticationDataService.cs new file mode 100644 index 0000000..f97de09 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/AuthenticationDataService.cs @@ -0,0 +1,98 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Services.Common; +using VDownload.Services.Data.Configuration; + +namespace VDownload.Services.Data.Authentication +{ + public interface IAuthenticationDataService : IInitializableService + { + #region PROPERTIES + + AuthenticationData Data { get; } + + #endregion + + + + #region METHODS + + Task Load(); + Task Save(); + + #endregion + } + + public class AuthenticationDataService : IAuthenticationDataService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + + #endregion + + + + #region FIELDS + + protected readonly string _filePath; + + #endregion + + + + #region PROPERTIES + + public AuthenticationData Data { get; protected set; } + + #endregion + + + + #region CONSTRUCTORS + + public AuthenticationDataService(IConfigurationService configurationService) + { + _configurationService = configurationService; + + string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string appdataDirectoryName = _configurationService.Common.Path.Appdata.DirectoryName; + string appdataAuthenticationFilename = _configurationService.Common.Path.Appdata.AuthenticationFile; + _filePath = Path.Combine(appdataPath, appdataDirectoryName, appdataAuthenticationFilename); + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize() => await Load(); + + public async Task Load() + { + Data = null; + + if (File.Exists(_filePath)) + { + string content = await File.ReadAllTextAsync(_filePath); + Data = JsonConvert.DeserializeObject<AuthenticationData>(content); + } + + Data ??= new AuthenticationData(); + } + + public async Task Save() + { + Directory.CreateDirectory(Path.GetDirectoryName(_filePath)); + string content = JsonConvert.SerializeObject(Data); + await File.WriteAllTextAsync(_filePath, content); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/Models/TokenAuthenticationData.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/Models/TokenAuthenticationData.cs new file mode 100644 index 0000000..00189d5 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/Models/TokenAuthenticationData.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Authentication.Models +{ + public class TokenAuthenticationData + { + [JsonProperty("token")] + public byte[]? Token { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/VDownload.Services.Data.Authentication.csproj b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/VDownload.Services.Data.Authentication.csproj new file mode 100644 index 0000000..62d33a0 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Authentication/VDownload.Services.Data.Authentication.csproj @@ -0,0 +1,18 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + <ProjectReference Include="..\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/CommonConfiguration.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/CommonConfiguration.cs new file mode 100644 index 0000000..831dba5 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/CommonConfiguration.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Configuration; +using System.Text.Json.Serialization; +using VDownload.Services.Data.Configuration.Models; + +namespace VDownload.Services.Data.Configuration +{ + public class CommonConfiguration + { + [ConfigurationKeyName("about")] + public About About { get; set; } + + [ConfigurationKeyName("filename_templates")] + public IEnumerable<FilenameTemplate> FilenameTemplates { get; set; } + + [ConfigurationKeyName("path")] + public Models.Path Path { get; set; } + + [ConfigurationKeyName("processing")] + public Processing Processing { get; set; } + + [ConfigurationKeyName("string_resources_assembly")] + public string StringResourcesAssembly { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/ConfigurationService.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/ConfigurationService.cs new file mode 100644 index 0000000..91a411c --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/ConfigurationService.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Configuration; + +namespace VDownload.Services.Data.Configuration +{ + public interface IConfigurationService + { + CommonConfiguration Common { get; } + TwitchConfiguration Twitch { get; } + } + + + + public class ConfigurationService : IConfigurationService + { + #region SERVICES + + protected readonly IConfiguration _configuration; + + #endregion + + + + #region PROPERTIES + + public CommonConfiguration Common { get; protected set; } + public TwitchConfiguration Twitch { get; protected set; } + + #endregion + + + + #region CONSTRUCTORS + + public ConfigurationService(IConfiguration configuration) + { + Common = configuration.GetSection("common").Get<CommonConfiguration>()!; + Twitch = configuration.GetSection("twitch").Get<TwitchConfiguration>()!; + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/About.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/About.cs new file mode 100644 index 0000000..594f411 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/About.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class About + { + [ConfigurationKeyName("repository_url")] + public string RepositoryUrl { get; set; } + + [ConfigurationKeyName("donation_url")] + public string DonationUrl { get; set; } + + [ConfigurationKeyName("developers")] + public IEnumerable<Person> Developers { get; set; } + + [ConfigurationKeyName("translation")] + public IEnumerable<Language> Translation { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Appdata.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Appdata.cs new file mode 100644 index 0000000..5f815e6 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Appdata.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class Appdata + { + [ConfigurationKeyName("directory_name")] + public string DirectoryName { get; set; } + + [ConfigurationKeyName("authentication_file")] + public string AuthenticationFile { get; set; } + + [ConfigurationKeyName("settings_file")] + public string SettingsFile { get; set; } + + [ConfigurationKeyName("data_file")] + public string DataFile { get; set; } + + [ConfigurationKeyName("subscriptions_file")] + public string SubscriptionsFile { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/FilenameTemplate.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/FilenameTemplate.cs new file mode 100644 index 0000000..49738dd --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/FilenameTemplate.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class FilenameTemplate + { + [ConfigurationKeyName("name")] + public string Name { get; set; } + + [ConfigurationKeyName("wildcard")] + public string Wildcard { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Language.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Language.cs new file mode 100644 index 0000000..bd507af --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Language.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class Language + { + [ConfigurationKeyName("code")] + public string Code { get; set; } + + [ConfigurationKeyName("translators")] + public IEnumerable<Person> Translators { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Muxer.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Muxer.cs new file mode 100644 index 0000000..ac6a047 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Muxer.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class Muxer + { + [ConfigurationKeyName("extension")] + public string Extension { get; set; } + + [ConfigurationKeyName("video_codecs")] + public List<string> VideoCodecs { get; } = new List<string>(); + + [ConfigurationKeyName("audio_codecs")] + public List<string> AudioCodecs { get; } = new List<string>(); + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Path.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Path.cs new file mode 100644 index 0000000..acf168a --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Path.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class Path + { + [ConfigurationKeyName("appdata")] + public Appdata Appdata { get; set; } + + [ConfigurationKeyName("temp")] + public Temp Temp { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Person.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Person.cs new file mode 100644 index 0000000..19058db --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Person.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class Person + { + [ConfigurationKeyName("name")] + public string Name { get; set; } + + [ConfigurationKeyName("url")] + public string Url { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Processing.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Processing.cs new file mode 100644 index 0000000..848d2f0 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Processing.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class Processing + { + [ConfigurationKeyName("muxers")] + public List<Muxer> Muxers { get; } = new List<Muxer>(); + + [ConfigurationKeyName("processed_filename")] + public string ProcessedFilename { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Temp.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Temp.cs new file mode 100644 index 0000000..f1fa8fa --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/Models/Temp.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Configuration.Models +{ + public class Temp + { + [ConfigurationKeyName("tasks_directory")] + public string TasksDirectory { get; set; } + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/VDownload.Services.Data.Configuration.csproj b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/VDownload.Services.Data.Configuration.csproj new file mode 100644 index 0000000..92db40e --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Configuration/VDownload.Services.Data.Configuration.csproj @@ -0,0 +1,18 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Configuration\VDownload.Sources.Twitch.Configuration.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/CommonSettings.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/CommonSettings.cs new file mode 100644 index 0000000..fd0fbc7 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/CommonSettings.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Settings.Models; + +namespace VDownload.Services.Data.Settings +{ + public class CommonSettings + { + [JsonProperty("searching")] + public Searching Searching { get; set; } = new Searching(); + + [JsonProperty("temp")] + public Temp Temp { get; set; } = new Temp(); + + [JsonProperty("tasks")] + public Tasks Tasks { get; set; } = new Tasks(); + + [JsonProperty("notifications")] + public Notifications Notifications { get; set; } = new Notifications(); + + [JsonProperty("processing")] + public Processing Processing { get; set; } = new Processing(); + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Notifications.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Notifications.cs new file mode 100644 index 0000000..3b3beb9 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Notifications.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Settings.Models +{ + public class Notifications + { + [JsonProperty("on_successful")] + public bool OnSuccessful { get; set; } = false; + + [JsonProperty("on_unsuccessful")] + public bool OnUnsuccessful { get; set; } = false; + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Processing.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Processing.cs new file mode 100644 index 0000000..ecee50b --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Processing.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Services.Data.Settings.Models +{ + public class Processing + { + [JsonProperty("ffmpeg_location")] + public string FFmpegLocation { get; set; } = $"{AppDomain.CurrentDomain.BaseDirectory}\\FFmpeg"; + + [JsonProperty("use_multithreading")] + public bool UseMultithreading { get; set; } = true; + + [JsonProperty("use_hardware_acceleration")] + public bool UseHardwareAcceleration { get; set; } = true; + + [JsonProperty("speed")] + public ProcessingSpeed Speed { get; set; } = ProcessingSpeed.UltraFast; + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Searching.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Searching.cs new file mode 100644 index 0000000..cba8144 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Searching.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Settings.Models +{ + public class Searching + { + [JsonProperty("max_number_of_videos_to_get_from_playlist")] + public int MaxNumberOfVideosToGetFromPlaylist { get; set; } = 0; + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Tasks.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Tasks.cs new file mode 100644 index 0000000..70bed9d --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Tasks.cs @@ -0,0 +1,40 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Services.Data.Settings.Models +{ + public class Tasks + { + [JsonProperty("default_media_type")] + public MediaType DefaultMediaType { get; set; } = MediaType.Original; + + [JsonProperty("default_video_extension")] + public VideoExtension DefaultVideoExtension { get; set; } = VideoExtension.MP4; + + [JsonProperty("default_audio_extension")] + public AudioExtension DefaultAudioExtension { get; set; } = AudioExtension.MP3; + + [JsonProperty("filename_template")] + public string FilenameTemplate { get; set; } = "{title}"; + + [JsonProperty("default_output_directory")] + public string DefaultOutputDirectory { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + + [JsonProperty("save_last_output_directory")] + public bool SaveLastOutputDirectory { get; set; } = false; + + [JsonProperty("max_number_of_running_tasks")] + public int MaxNumberOfRunningTasks { get; set; } = 5; + + [JsonProperty("show_metered_connection_warnings")] + public bool ShowMeteredConnectionWarnings { get; set; } = true; + + [JsonProperty("replace_output_file_if_exists")] + public bool ReplaceOutputFileIfExists { get; set; } = false; + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Temp.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Temp.cs new file mode 100644 index 0000000..cb7fd90 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/Models/Temp.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Data.Settings.Models +{ + public class Temp + { + [JsonProperty("directory")] + public string Directory { get; set; } = $"{Path.GetTempPath()}VDownload"; + + [JsonProperty("delete_on_error")] + public bool DeleteOnError { get; set; } = true; + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/SettingsData.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/SettingsData.cs new file mode 100644 index 0000000..c83f011 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/SettingsData.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; +using VDownload.Sources.Twitch.Settings; + +namespace VDownload.Services.Data.Settings +{ + public class SettingsData + { + #region PROPERTIES + + [JsonProperty("common")] + public CommonSettings Common { get; set; } + + [JsonProperty("twitch")] + public TwitchSettings Twitch { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + internal SettingsData() + { + Common = new CommonSettings(); + Twitch = new TwitchSettings(); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/SettingsService.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/SettingsService.cs new file mode 100644 index 0000000..898eb44 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/SettingsService.cs @@ -0,0 +1,107 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Services.Common; +using VDownload.Services.Data.Configuration; + +namespace VDownload.Services.Data.Settings +{ + public interface ISettingsService : IInitializableService + { + #region PROPERTIES + + SettingsData Data { get; } + + #endregion + + + + #region METHODS + + Task Load(); + Task Save(); + Task Restore(); + + #endregion + } + + + + public class SettingsService : ISettingsService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + + #endregion + + + + #region FIELDS + + protected readonly string _filePath; + + #endregion + + + + #region PROPERTIES + + public SettingsData Data { get; private set; } + + #endregion + + + + #region CONSTRUCTORS + + public SettingsService(IConfigurationService configurationService) + { + _configurationService = configurationService; + + string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string appdataDirectoryName = _configurationService.Common.Path.Appdata.DirectoryName; + string appdataAuthenticationFilename = _configurationService.Common.Path.Appdata.DataFile; + _filePath = Path.Combine(appdataPath, appdataDirectoryName, appdataAuthenticationFilename); + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize() => await Load(); + + public async Task Load() + { + if (File.Exists(_filePath)) + { + string content = await File.ReadAllTextAsync(_filePath); + Data = JsonConvert.DeserializeObject<SettingsData>(content); + } + else + { + Data = new SettingsData(); + } + } + + public async Task Save() + { + Directory.CreateDirectory(Path.GetDirectoryName(_filePath)); + string content = JsonConvert.SerializeObject(Data); + await File.WriteAllTextAsync(_filePath, content); + } + + public async Task Restore() + { + Data = new SettingsData(); + await Save(); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/VDownload.Services.Data.Settings.csproj b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/VDownload.Services.Data.Settings.csproj new file mode 100644 index 0000000..e689809 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Settings/VDownload.Services.Data.Settings.csproj @@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" /> + <ProjectReference Include="..\..\..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Settings\VDownload.Sources.Twitch.Settings.csproj" /> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + <ProjectReference Include="..\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/Subscription.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/Subscription.cs new file mode 100644 index 0000000..b4516a9 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/Subscription.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Services.Data.Subscriptions +{ + public class Subscription + { + #region PROPERTIES + + [JsonProperty("guid")] + public Guid Guid { get; private set; } = Guid.NewGuid(); + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("source")] + public Source Source { get; set; } + + [JsonProperty("url")] + public Uri Url { get; set; } + + [JsonProperty("video_ids")] + public ICollection<string> VideoIds { get; private set; } = new List<string>(); + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/SubscriptionsDataService.cs b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/SubscriptionsDataService.cs new file mode 100644 index 0000000..db08b2d --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/SubscriptionsDataService.cs @@ -0,0 +1,101 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using VDownload.Services.Common; +using VDownload.Services.Data.Configuration; + +namespace VDownload.Services.Data.Subscriptions +{ + public interface ISubscriptionsDataService : IInitializableService + { + #region PROPERTIES + + ICollection<Subscription> Data { get; } + + #endregion + + + + #region METHODS + + Task Load(); + Task Save(); + + #endregion + } + + + + public class SubscriptionsDataService : ISubscriptionsDataService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + + #endregion + + + + #region FIELDS + + protected readonly string _filePath; + + #endregion + + + + #region PROPERTIES + + public ICollection<Subscription> Data { get; private set; } + + #endregion + + + + #region CONSTRUCTORS + + public SubscriptionsDataService(IConfigurationService configurationService) + { + _configurationService = configurationService; + + string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string appdataDirectoryName = _configurationService.Common.Path.Appdata.DirectoryName; + string appdataAuthenticationFilename = _configurationService.Common.Path.Appdata.SubscriptionsFile; + _filePath = Path.Combine(appdataPath, appdataDirectoryName, appdataAuthenticationFilename); + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize() => await Load(); + + public async Task Load() + { + if (File.Exists(_filePath)) + { + string content = await File.ReadAllTextAsync(_filePath); + Data = JsonConvert.DeserializeObject<ICollection<Subscription>>(content); + } + else + { + Data = new List<Subscription>(); + } + } + + public async Task Save() + { + Directory.CreateDirectory(Path.GetDirectoryName(_filePath)); + string content = JsonConvert.SerializeObject(Data); + await File.WriteAllTextAsync(_filePath, content); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/VDownload.Services.Data.Subscriptions.csproj b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/VDownload.Services.Data.Subscriptions.csproj new file mode 100644 index 0000000..4c44878 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Data/VDownload.Services.Data.Subscriptions/VDownload.Services.Data.Subscriptions.csproj @@ -0,0 +1,19 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" /> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + <ProjectReference Include="..\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResult.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResult.cs new file mode 100644 index 0000000..802765f --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResult.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.Dialogs +{ + public enum DialogResult + { + Primary, + Secondary, + Cancelled + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultOkCancel.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultOkCancel.cs new file mode 100644 index 0000000..2946af6 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultOkCancel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.Dialogs +{ + public enum DialogResultOkCancel + { + Ok, + Cancel + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultYesNo.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultYesNo.cs new file mode 100644 index 0000000..6b5317d --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultYesNo.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.Dialogs +{ + public enum DialogResultYesNo + { + Yes, + No + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultYesNoCancel.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultYesNoCancel.cs new file mode 100644 index 0000000..8c7fde1 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogResultYesNoCancel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.Dialogs +{ + public enum DialogResultYesNoCancel + { + Yes, + No, + Cancelled + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogsService.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogsService.cs new file mode 100644 index 0000000..2e91dea --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/DialogsService.cs @@ -0,0 +1,119 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Strings; +using VDownload.Services.Common; + +namespace VDownload.Services.UI.Dialogs +{ + public interface IDialogsService : IInitializableService<XamlRoot> + { + Task ShowClose(string title, string message); + Task<DialogResult> ShowDouble(string title, string message, string primaryButtonText, string secondaryButtonText); + Task ShowOk(string title, string message); + Task<DialogResultOkCancel> ShowOkCancel(string title, string message); + Task ShowSingle(string title, string message, string buttonText); + Task<DialogResult> ShowTriple(string title, string message, string primaryButtonText, string secondaryButtonText, string cancelButtonText); + Task<DialogResultYesNo> ShowYesNo(string title, string message); + Task<DialogResultYesNoCancel> ShowYesNoCancel(string title, string message); + } + + + + public class DialogsService : IDialogsService + { + #region FIELDS + + protected string _okString = StringResourcesManager.DialogButtons.Get("OK"); + protected string _closeString = StringResourcesManager.DialogButtons.Get("Close"); + protected string _cancelString = StringResourcesManager.DialogButtons.Get("Cancel"); + protected string _yesString = StringResourcesManager.DialogButtons.Get("Yes"); + protected string _noString = StringResourcesManager.DialogButtons.Get("No"); + + protected XamlRoot _root; + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize(XamlRoot arg) => await Task.Run(() => _root = arg); + + public async Task ShowOk(string title, string message) => await ShowSingle(title, message, _okString); + public async Task ShowClose(string title, string message) => await ShowSingle(title, message, _closeString); + public async Task ShowSingle(string title, string message, string buttonText) + { + ContentDialog contentDialog = BuildDialog(title, message); + contentDialog.CloseButtonText = buttonText; + await ShowDialog(contentDialog); + } + + public async Task<DialogResultOkCancel> ShowOkCancel(string title, string message) => await ShowDouble(title, message, _okString, _cancelString) switch + { + DialogResult.Primary => DialogResultOkCancel.Ok, + _ => DialogResultOkCancel.Cancel + }; + public async Task<DialogResultYesNo> ShowYesNo(string title, string message) => await ShowDouble(title, message, _yesString, _noString) switch + { + DialogResult.Primary => DialogResultYesNo.Yes, + _ => DialogResultYesNo.No + }; + public async Task<DialogResult> ShowDouble(string title, string message, string primaryButtonText, string secondaryButtonText) + { + ContentDialog contentDialog = BuildDialog(title, message); + contentDialog.PrimaryButtonText = primaryButtonText; + contentDialog.SecondaryButtonText = secondaryButtonText; + return await ShowDialog(contentDialog); + } + + public async Task<DialogResultYesNoCancel> ShowYesNoCancel(string title, string message) => await ShowTriple(title, message, _yesString, _noString, _cancelString) switch + { + DialogResult.Primary => DialogResultYesNoCancel.Yes, + DialogResult.Secondary => DialogResultYesNoCancel.Yes, + _ => DialogResultYesNoCancel.Cancelled + }; + public async Task<DialogResult> ShowTriple(string title, string message, string primaryButtonText, string secondaryButtonText, string cancelButtonText) + { + ContentDialog contentDialog = BuildDialog(title, message); + contentDialog.PrimaryButtonText = primaryButtonText; + contentDialog.SecondaryButtonText = secondaryButtonText; + contentDialog.CloseButtonText = cancelButtonText; + return await ShowDialog(contentDialog); + } + + #endregion + + + + #region PRIVATE METHODS + + private ContentDialog BuildDialog(string title, string message) + { + return new ContentDialog() + { + Title = title, + Content = message, + XamlRoot = _root + }; + } + + private async Task<DialogResult> ShowDialog(ContentDialog dialog) + { + ContentDialogResult result = await dialog.ShowAsync(); + return result switch + { + ContentDialogResult.Primary => DialogResult.Primary, + ContentDialogResult.Secondary => DialogResult.Secondary, + _ => DialogResult.Cancelled + }; ; + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/VDownload.Services.UI.Dialogs.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/VDownload.Services.UI.Dialogs.csproj new file mode 100644 index 0000000..6167d47 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Dialogs/VDownload.Services.UI.Dialogs.csproj @@ -0,0 +1,21 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Services.UI.Dialogs</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Core\VDownload.Core.Strings\VDownload.Core.Strings.csproj" /> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + </ItemGroup> +</Project> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/DictionaryResourcesService.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/DictionaryResourcesService.cs new file mode 100644 index 0000000..4c4ae40 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/DictionaryResourcesService.cs @@ -0,0 +1,45 @@ +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Services.Common; +using Windows.Media.Core; + +namespace VDownload.Services.UI.DictionaryResources +{ + public interface IDictionaryResourcesService : IInitializableService<ResourceDictionary> + { + T Get<T>(string key); + } + + + + public class DictionaryResourcesService : IDictionaryResourcesService + { + #region FIELDS + + protected ResourceDictionary _resources; + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize(ResourceDictionary arg) => await Task.Run(() => _resources = arg); + + public T Get<T>(string key) + { + _resources.TryGetValue(key, out object value); + if (value is not null && value is T cast) + { + return cast; + } + throw new KeyNotFoundException(); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/VDownload.Services.UI.DictionaryResources.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/VDownload.Services.UI.DictionaryResources.csproj new file mode 100644 index 0000000..54b8411 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.DictionaryResources/VDownload.Services.UI.DictionaryResources.csproj @@ -0,0 +1,21 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Services.UI.DictionaryResources</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + <ProjectReference Include="..\VDownload.Services.UI.Notifications\VDownload.Services.UI.Notifications.csproj" /> + </ItemGroup> +</Project> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/NotificationsService.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/NotificationsService.cs new file mode 100644 index 0000000..527dbd2 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/NotificationsService.cs @@ -0,0 +1,78 @@ +using Microsoft.Toolkit.Uwp.Notifications; +using Microsoft.UI.Xaml; +using Microsoft.Windows.AppNotifications; +using Microsoft.Windows.AppNotifications.Builder; +using SimpleToolkit.UI.WinUI.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Services.Common; + +namespace VDownload.Services.UI.Notifications +{ + public interface INotificationsService : IInitializableService<Window> + { + void SendNotification(string title, IEnumerable<string> message); + } + + + + public class NotificationsService : INotificationsService + { + #region FIELDS + + protected Window _window; + + #endregion + + + + #region CONSTRUCTORS + + ~NotificationsService() + { + AppNotificationManager.Default.Unregister(); + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize(Window window) => await Task.Run(() => + { + _window = window; + + AppNotificationManager.Default.NotificationInvoked += NotificationInvoked; + + AppNotificationManager.Default.Register(); + }); + + public void SendNotification(string title, IEnumerable<string> message) + { + AppNotificationBuilder builder = new AppNotificationBuilder(); + builder.AddText(title); + foreach (string text in message) + { + builder.AddText(text); + } + + AppNotification notification = builder.BuildNotification(); + + AppNotificationManager.Default.Show(notification); + } + + #endregion + + + + #region PRIVATE METHODS + + private void NotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args) => WindowHelper.ShowWindow(_window); + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/VDownload.Services.UI.Notifications.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/VDownload.Services.UI.Notifications.csproj new file mode 100644 index 0000000..e6fa01d --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.Notifications/VDownload.Services.UI.Notifications.csproj @@ -0,0 +1,22 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Services.UI.Notifications</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" /> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + <PackageReference Include="SimpleToolkit.UI.WinUI.Helpers" Version="1.7.5" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + </ItemGroup> +</Project> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/FileSavePickerFileTypeChoice.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/FileSavePickerFileTypeChoice.cs new file mode 100644 index 0000000..5b4d194 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/FileSavePickerFileTypeChoice.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.StoragePicker +{ + public class FileSavePickerFileTypeChoice + { + #region PROPERTIES + + public string Description { get; private set; } + public IEnumerable<string> Extensions { get; private set; } + + #endregion + + + + #region CONSTRUCTORS + + public FileSavePickerFileTypeChoice(string description, string[] extensions) + { + Description = description; + Extensions = extensions.Select(x => x.StartsWith('.') ? x : $".{x}"); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/StoragePickerService.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/StoragePickerService.cs new file mode 100644 index 0000000..effbd19 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/StoragePickerService.cs @@ -0,0 +1,157 @@ +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using VDownload.Services.Common; +using Windows.Storage; +using Windows.Storage.Pickers; +using WinRT.Interop; + +namespace VDownload.Services.UI.StoragePicker +{ + public interface IStoragePickerService : IInitializableService<Window> + { + Task<string?> OpenDirectory(); + Task<string?> OpenDirectory(StoragePickerStartLocation startLocation); + Task<IEnumerable<string>> OpenMultipleFiles(); + Task<IEnumerable<string>> OpenMultipleFiles(StoragePickerStartLocation startLocation); + Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes); + Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes, StoragePickerStartLocation startLocation); + Task<string?> OpenSingleFile(); + Task<string?> OpenSingleFile(StoragePickerStartLocation startLocation); + Task<string?> OpenSingleFile(string[] fileTypes); + Task<string?> OpenSingleFile(string[] fileTypes, StoragePickerStartLocation startLocation); + Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType); + Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType, StoragePickerStartLocation startLocation); + } + + + + public class StoragePickerService : IStoragePickerService + { + #region FIELDS + + protected Window _root; + + #endregion + + + + #region PUBLIC METHODS + + public async Task Initialize(Window arg) => await Task.Run(() => _root = arg); + + public async Task<string?> OpenDirectory() => await OpenDirectory(StoragePickerStartLocation.Unspecified); + public async Task<string?> OpenDirectory(StoragePickerStartLocation startLocation) + { + FolderPicker picker = new FolderPicker(); + InitializePicker(picker); + + ConfigureFolderPicker(picker, startLocation); + + StorageFolder directory = await picker.PickSingleFolderAsync(); + return directory?.Path; + } + + public async Task<string?> OpenSingleFile() => await OpenSingleFile(["*"], StoragePickerStartLocation.Unspecified); + public async Task<string?> OpenSingleFile(string[] fileTypes) => await OpenSingleFile(fileTypes, StoragePickerStartLocation.Unspecified); + public async Task<string?> OpenSingleFile(StoragePickerStartLocation startLocation) => await OpenSingleFile(["*"], startLocation); + public async Task<string?> OpenSingleFile(string[] fileTypes, StoragePickerStartLocation startLocation) + { + FileOpenPicker picker = new FileOpenPicker(); + InitializePicker(picker); + + ConfigureFileOpenPicker(picker, fileTypes, startLocation); + + StorageFile storageFile = await picker.PickSingleFileAsync(); + return storageFile?.Path; + } + + public async Task<IEnumerable<string>> OpenMultipleFiles() => await OpenMultipleFiles(["*"], StoragePickerStartLocation.Unspecified); + public async Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes) => await OpenMultipleFiles(fileTypes, StoragePickerStartLocation.Unspecified); + public async Task<IEnumerable<string>> OpenMultipleFiles(StoragePickerStartLocation startLocation) => await OpenMultipleFiles(["*"], startLocation); + public async Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes, StoragePickerStartLocation startLocation) + { + FileOpenPicker picker = new FileOpenPicker(); + InitializePicker(picker); + + ConfigureFileOpenPicker(picker, fileTypes, startLocation); + + IEnumerable<StorageFile> list = await picker.PickMultipleFilesAsync(); + return list.Select(x => x.Path); + } + + public async Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType) => await SaveFile(fileTypes, defaultFileType, StoragePickerStartLocation.Unspecified); + public async Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType, StoragePickerStartLocation startLocation) + { + FileSavePicker picker = new FileSavePicker(); + InitializePicker(picker); + + ConfigureFileSavePicker(picker, fileTypes, defaultFileType, startLocation); + + StorageFile file = await picker.PickSaveFileAsync(); + return file?.Path; + } + + #endregion + + + + #region PRIVATE METHODS + + protected void InitializePicker(object picker) + { + var hwnd = WindowNative.GetWindowHandle(_root); + InitializeWithWindow.Initialize(picker, hwnd); + } + + protected void ConfigureFolderPicker(FolderPicker picker, StoragePickerStartLocation startLocation) + { + if (startLocation != StoragePickerStartLocation.Unspecified) + { + picker.SuggestedStartLocation = (PickerLocationId)startLocation; + } + } + + protected void ConfigureFileOpenPicker(FileOpenPicker picker, string[] fileTypes, StoragePickerStartLocation startLocation) + { + foreach (string fileType in fileTypes) + { + picker.FileTypeFilter.Add(fileType); + } + + if (startLocation != StoragePickerStartLocation.Unspecified) + { + picker.SuggestedStartLocation = (PickerLocationId)startLocation; + } + } + + protected void ConfigureFileSavePicker(FileSavePicker picker, FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType, StoragePickerStartLocation startLocation) + { + if (startLocation != StoragePickerStartLocation.Unspecified) + { + picker.SuggestedStartLocation = (PickerLocationId)startLocation; + } + + foreach (FileSavePickerFileTypeChoice fileType in fileTypes) + { + picker.FileTypeChoices.Add(fileType.Description, fileType.Extensions.ToList()); + } + + if (!defaultFileType.StartsWith('.')) + { + defaultFileType = $".{defaultFileType}"; + } + + if (!fileTypes.Any(x => x.Extensions.Contains(defaultFileType))) + { + picker.FileTypeChoices.Add("Default", [defaultFileType]); + } + + picker.DefaultFileExtension = defaultFileType; + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/StoragePickerStartLocation.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/StoragePickerStartLocation.cs new file mode 100644 index 0000000..09123fa --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/StoragePickerStartLocation.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.StoragePicker +{ + public enum StoragePickerStartLocation + { + Documents = 0, + Computer = 1, + Desktop = 2, + Downloads = 3, + Music = 5, + Pictures = 6, + Videos = 7, + Unspecified = 999 + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/VDownload.Services.UI.StoragePicker.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/VDownload.Services.UI.StoragePicker.csproj new file mode 100644 index 0000000..8407c20 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StoragePicker/VDownload.Services.UI.StoragePicker.csproj @@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Services.UI.StoragePicker</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + </ItemGroup> +</Project> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/StringResources.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/StringResources.cs new file mode 100644 index 0000000..e3a9691 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/StringResources.cs @@ -0,0 +1,37 @@ +using Windows.ApplicationModel.Resources; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.StringResources +{ + public class StringResources + { + #region FIELDS + + protected ResourceLoader _resourceLoader; + + #endregion + + + + #region CONSTRUCTORS + + internal StringResources(ResourceLoader resourceLoader) + { + _resourceLoader = resourceLoader; + } + + #endregion + + + + #region PUBLIC METHODS + + public string Get(string key) => _resourceLoader.GetString(key); + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/StringResourcesService.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/StringResourcesService.cs new file mode 100644 index 0000000..97f9e2c --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/StringResourcesService.cs @@ -0,0 +1,93 @@ +using VDownload.Services.Data.Configuration; +using Windows.ApplicationModel.Resources; + +namespace VDownload.Services.UI.StringResources +{ + public interface IStringResourcesService + { + StringResources BaseViewResources { get; } + StringResources HomeViewResources { get; } + StringResources HomeVideoViewResources { get; } + StringResources HomeVideoCollectionViewResources { get; } + StringResources HomeDownloadsViewResources { get; } + StringResources AuthenticationViewResources { get; } + StringResources NotificationsResources { get; } + StringResources SearchResources { get; } + StringResources CommonResources { get; } + StringResources DialogButtonsResources { get; } + StringResources SettingsViewResources { get; } + StringResources FilenameTemplateResources { get; } + StringResources AboutViewResources { get; } + StringResources SubscriptionsViewResources { get; } + } + + + + public class StringResourcesService : IStringResourcesService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + + #endregion + + + + #region PROPERTIES + + public StringResources BaseViewResources { get; protected set; } + public StringResources HomeViewResources { get; protected set; } + public StringResources HomeVideoViewResources { get; protected set; } + public StringResources HomeVideoCollectionViewResources { get; protected set; } + public StringResources HomeDownloadsViewResources { get; protected set; } + public StringResources AuthenticationViewResources { get; protected set; } + public StringResources NotificationsResources { get; protected set; } + public StringResources SearchResources { get; protected set; } + public StringResources CommonResources { get; protected set; } + public StringResources DialogButtonsResources { get; protected set; } + public StringResources SettingsViewResources { get; protected set; } + public StringResources FilenameTemplateResources { get; protected set; } + public StringResources AboutViewResources { get; protected set; } + public StringResources SubscriptionsViewResources { get; protected set; } + + #endregion + + + + #region CONSTRUCTORS + + public StringResourcesService(IConfigurationService configurationService) + { + _configurationService = configurationService; + + BaseViewResources = BuildResource("BaseViewResources"); + HomeViewResources = BuildResource("HomeViewResources"); + HomeVideoViewResources = BuildResource("HomeVideoViewResources"); + HomeVideoCollectionViewResources = BuildResource("HomeVideoCollectionViewResources"); + HomeDownloadsViewResources = BuildResource("HomeDownloadsViewResources"); + AuthenticationViewResources = BuildResource("AuthenticationViewResources"); + NotificationsResources = BuildResource("NotificationsResources"); + SearchResources = BuildResource("SearchResources"); + CommonResources = BuildResource("CommonResources"); + DialogButtonsResources = BuildResource("DialogButtonsResources"); + SettingsViewResources = BuildResource("SettingsViewResources"); + FilenameTemplateResources = BuildResource("FilenameTemplateResources"); + AboutViewResources = BuildResource("AboutViewResources"); + SubscriptionsViewResources = BuildResource("SubscriptionsViewResources"); + } + + #endregion + + + + #region PRIVATE METHODS + + protected StringResources BuildResource(string resourceName) + { + ResourceLoader loader = new ResourceLoader($"{_configurationService.Common.StringResourcesAssembly}/{resourceName}"); + return new StringResources(loader); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/VDownload.Services.UI.StringResources.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/VDownload.Services.UI.StringResources.csproj new file mode 100644 index 0000000..50e13b6 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.StringResources/VDownload.Services.UI.StringResources.csproj @@ -0,0 +1,21 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Services.UI.StringResources</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Core\VDownload.Core.Strings\VDownload.Core.Strings.csproj" /> + <ProjectReference Include="..\..\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + </ItemGroup> +</Project> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/VDownload.Services.UI.WebView.csproj b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/VDownload.Services.UI.WebView.csproj new file mode 100644 index 0000000..aa294dd --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/VDownload.Services.UI.WebView.csproj @@ -0,0 +1,25 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload.Services.UI.WebView</RootNamespace> + <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <UseWinUI>true</UseWinUI> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + </PropertyGroup> + <ItemGroup> + <None Remove="WebViewWindow.xaml" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + </ItemGroup> + + <ItemGroup> + <Page Update="WebViewWindow.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> +</Project> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewService.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewService.cs new file mode 100644 index 0000000..e6a1b2c --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewService.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.UI.WebView +{ + public interface IWebViewService + { + Task<string> Show(Uri url, Predicate<string> closePredicate, string name); + } + + + + public class WebViewService : IWebViewService + { + #region METHODS + + public async Task<string> Show(Uri url, Predicate<string> closePredicate, string name) + { + WebViewWindow window = new WebViewWindow(name); + return await window.Show(url, closePredicate); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewWindow.xaml b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewWindow.xaml new file mode 100644 index 0000000..7ae602f --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewWindow.xaml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<Window + x:Class="VDownload.Services.UI.WebView.WebViewWindow" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:VDownload.Services.UI.WebView" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" + Closed="Window_Closed" + Activated="Window_Activated"> + <WebView2 x:Name="WebView" NavigationCompleted="WebView_NavigationCompleted"/> +</Window> diff --git a/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewWindow.xaml.cs b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewWindow.xaml.cs new file mode 100644 index 0000000..a08a635 --- /dev/null +++ b/VDownload.Services/VDownload.Services.UI/VDownload.Services.UI.WebView/WebViewWindow.xaml.cs @@ -0,0 +1,99 @@ +using Microsoft.UI; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Microsoft.Web.WebView2.Core; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace VDownload.Services.UI.WebView +{ + public sealed partial class WebViewWindow : Window + { + #region FIEDLS + + private readonly Predicate<string> _defaultClosePredicate = args => false; + + private bool _isOpened; + private Predicate<string> _closePredicate; + + #endregion + + + + #region CONSTRUCTORS + + public WebViewWindow(string name) + { + this.InitializeComponent(); + this.Title = name; + + _isOpened = false; + _closePredicate = _defaultClosePredicate; + } + + #endregion + + + + #region PUBLIC METHODS + + internal async Task<string> Show(Uri url, Predicate<string> closePredicate) + { + this.WebView.Source = url; + _closePredicate = closePredicate; + + this.Activate(); + _isOpened = true; + while (_isOpened) + { + await Task.Delay(10); + } + + _closePredicate = _defaultClosePredicate; + + return this.WebView.Source.ToString(); + } + + #endregion + + + + #region EVENT HANDLER + + + private void WebView_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args) + { + if (_closePredicate.Invoke(this.WebView.Source.ToString())) + { + this.Close(); + } + } + + private void Window_Closed(object sender, WindowEventArgs args) + { + _isOpened = false; + } + + private void Window_Activated(object sender, WindowActivatedEventArgs args) + { + IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); + WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle); + AppWindow appWindow = AppWindow.GetFromWindowId(windowId); + appWindow.SetIcon(@"Assets\Logo\Logo.ico"); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Encryption/EncryptionService.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Encryption/EncryptionService.cs new file mode 100644 index 0000000..1fdaf56 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Encryption/EncryptionService.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.Cryptography; +using System.Security; +using System.Runtime.InteropServices; + +namespace VDownload.Services.Utility.Encryption +{ + public interface IEncryptionService + { + byte[] Decrypt(byte[] ciphertext); + byte[] Encrypt(byte[] plaintext); + } + + + + public class EncryptionService : IEncryptionService + { + #region PUBLIC METHODS + + public byte[] Encrypt(byte[] plaintext) + { + return ProtectedData.Protect(plaintext, null, DataProtectionScope.CurrentUser); + } + + public byte[] Decrypt(byte[] ciphertext) + { + return ProtectedData.Unprotect(ciphertext, null, DataProtectionScope.CurrentUser); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Encryption/VDownload.Services.Utility.Encryption.csproj b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Encryption/VDownload.Services.Utility.Encryption.csproj new file mode 100644 index 0000000..7c7b745 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Encryption/VDownload.Services.Utility.Encryption.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegBuilder.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegBuilder.cs new file mode 100644 index 0000000..33ea457 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegBuilder.cs @@ -0,0 +1,195 @@ +using FFMpegCore; +using FFMpegCore.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; + +namespace VDownload.Services.Utility.FFmpeg +{ + public class FFmpegBuilder + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region FIELDS + + protected TimeSpan? _trimStart; + protected TimeSpan? _trimEnd; + + protected bool _progressReporterJoined = false; + protected Action<double> _progressReporter; + protected TimeSpan _progressReporterVideoDuration; + + protected bool _cancellationTokenJoined = false; + protected CancellationToken _cancellationToken; + + protected MediaType _mediaType = MediaType.Original; + + protected string _inputFile; + protected string _outputFile; + + protected string _audioCodec; + protected string _videoCodec; + + protected FFOptions _options; + + #endregion + + + + #region CONSTRUCTORS + + internal FFmpegBuilder(IConfigurationService configurationService, ISettingsService settingsService) + { + _configurationService = configurationService; + _settingsService = settingsService; + + _options = new FFOptions + { + BinaryFolder = _settingsService.Data.Common.Processing.FFmpegLocation, + }; + } + + #endregion + + + + #region PUBLIC METHODS + + public FFmpegBuilder SetMediaType(MediaType mediaType) + { + _mediaType = mediaType; + return this; + } + + public FFmpegBuilder TrimStart(TimeSpan start) + { + _trimStart = start; + return this; + } + + public FFmpegBuilder TrimEnd(TimeSpan end) + { + _trimEnd = end; + return this; + } + + public FFmpegBuilder JoinProgressReporter(Action<double> progressReporter, TimeSpan videoDuration) + { + _progressReporterJoined = true; + _progressReporter = progressReporter; + _progressReporterVideoDuration = videoDuration; + return this; + } + + public FFmpegBuilder JoinCancellationToken(CancellationToken cancellationToken) + { + _cancellationTokenJoined = true; + _cancellationToken = cancellationToken; + return this; + } + + public async Task RunAsync(string inputFile, string outputFile) + { + _inputFile = inputFile; + _outputFile = outputFile; + + IMediaAnalysis analysis = await FFProbe.AnalyseAsync(_inputFile, _options); + _audioCodec = analysis.AudioStreams.First().CodecName; + _videoCodec = analysis.VideoStreams.First().CodecName; + + FFMpegArgumentProcessor ffmpegArguments = FFMpegArguments.FromFileInput(inputFile, true, async (options) => await BuildInputArgumentOptions(options)) + .OutputToFile(outputFile, true, async (options) => await BuildOutputArgumentOptions(options)); + + if (_cancellationTokenJoined) + { + ffmpegArguments = ffmpegArguments.CancellableThrough(_cancellationToken); + } + + if (_progressReporterJoined) + { + ffmpegArguments = ffmpegArguments.NotifyOnProgress(_progressReporter, _progressReporterVideoDuration); + } + + try + { + await ffmpegArguments.ProcessAsynchronously(true, _options); + } + catch (Exception ex) + { + Console.WriteLine(ex); + throw; + } + } + + #endregion + + + + #region PRIVATE METHODS + + private async Task BuildInputArgumentOptions(FFMpegArgumentOptions options) + { + options.UsingMultithreading(_settingsService.Data.Common.Processing.UseMultithreading); + if (_settingsService.Data.Common.Processing.UseHardwareAcceleration) + { + options.WithHardwareAcceleration(HardwareAccelerationDevice.Auto); + } + + if (_trimStart is not null) + { + options.WithCustomArgument($"-ss {_trimStart}"); + } + + if (_trimEnd is not null) + { + options.WithCustomArgument($"-to {_trimEnd}"); + } + } + + private async Task BuildOutputArgumentOptions(FFMpegArgumentOptions options) + { + string extension = Path.GetExtension(_outputFile).Replace(".", string.Empty); + Data.Configuration.Models.Muxer muxer = _configurationService.Common.Processing.Muxers.First(x => x.Extension == extension); + + if (_mediaType != MediaType.OnlyAudio) + { + IEnumerable<string> availableCodecs = muxer.VideoCodecs; + string selectedCodec = availableCodecs.Contains(_videoCodec) ? "copy" : availableCodecs.First(); + options.WithCustomArgument($"-vcodec {selectedCodec}"); + } + else + { + options.WithCustomArgument("-vn"); + } + + if (_mediaType != MediaType.OnlyVideo) + { + IEnumerable<string> availableCodecs = muxer.AudioCodecs; + string selectedCodec = availableCodecs.Contains(_audioCodec) ? "copy" : availableCodecs.First(); + options.WithCustomArgument($"-acodec {selectedCodec}"); + } + else + { + options.WithCustomArgument("-an"); + } + + options.WithSpeedPreset((Speed)_settingsService.Data.Common.Processing.Speed); + } + + #endregion + + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegService.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegService.cs new file mode 100644 index 0000000..abbc6f4 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/FFmpegService.cs @@ -0,0 +1,48 @@ +using FFMpegCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; + +namespace VDownload.Services.Utility.FFmpeg +{ + public interface IFFmpegService + { + FFmpegBuilder CreateBuilder(); + } + + + + public class FFmpegService : IFFmpegService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region CONSTRUCTORS + + public FFmpegService(IConfigurationService configurationService, ISettingsService settingsService) + { + _configurationService = configurationService; + _settingsService = settingsService; + } + + #endregion + + + + #region PUBLIC METHODS + + public FFmpegBuilder CreateBuilder() => new FFmpegBuilder(_configurationService, _settingsService); + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/VDownload.Services.Utility.FFmpeg.csproj b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/VDownload.Services.Utility.FFmpeg.csproj new file mode 100644 index 0000000..205d8dd --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.FFmpeg/VDownload.Services.Utility.FFmpeg.csproj @@ -0,0 +1,18 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="FFMpegCore" Version="5.1.0" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" /> + <ProjectReference Include="..\..\VDownload.Services.Data\VDownload.Services.Data.Settings\VDownload.Services.Data.Settings.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Filename/FilenameService.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Filename/FilenameService.cs new file mode 100644 index 0000000..f69a246 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Filename/FilenameService.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Configuration.Models; + +namespace VDownload.Services.Utility.Filename +{ + public interface IFilenameService + { + string CreateFilename(string template, Video video); + string SanitizeFilename(string filename); + } + + + + public class FilenameService : IFilenameService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + + #endregion + + + + #region CONSTRUCTORS + + public FilenameService(IConfigurationService configurationService) + { + _configurationService = configurationService; + } + + #endregion + + + + #region PUBLIC METHODS + + public string CreateFilename(string template, Video video) + { + string filename = template; + foreach (FilenameTemplate templateElement in _configurationService.Common.FilenameTemplates) + { + switch (templateElement.Name) + { + case "id": ReplaceInPlace(ref filename, templateElement.Wildcard, video.Id); break; + case "title": ReplaceInPlace(ref filename, templateElement.Wildcard, video.Title); break; + case "author": ReplaceInPlace(ref filename, templateElement.Wildcard, video.Author); break; + case "views": ReplaceInPlace(ref filename, templateElement.Wildcard, video.Views.ToString()); break; + case "source": ReplaceInPlace(ref filename, templateElement.Wildcard, video.Source.ToString()); break; + case "date": + { + Regex regex = new Regex(templateElement.Wildcard); + foreach (Match match in regex.Matches(filename)) + { + ReplaceInPlace(ref filename, match.Value, video.PublishDate.ToString(match.Groups[1].Value)); + } + break; + } + case "duration": + { + Regex regex = new Regex(templateElement.Wildcard); + foreach (Match match in regex.Matches(filename)) + { + ReplaceInPlace(ref filename, match.Value, video.Duration.ToString(match.Groups[1].Value)); + } + break; + } + default: throw new Exception("Invalid template"); + } + } + filename = SanitizeFilename(filename); + return filename; + } + + public string SanitizeFilename(string filename) + { + char[] invalidChars = System.IO.Path.GetInvalidFileNameChars(); + foreach (char c in invalidChars) + { + filename = filename.Replace(c, '_'); + } + return filename; + } + + #endregion + + + + #region PRIVATE METHODS + + protected void ReplaceInPlace(ref string filename, string oldValue, string newValue) => filename = filename.Replace(oldValue, newValue); + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Filename/VDownload.Services.Utility.Filename.csproj b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Filename/VDownload.Services.Utility.Filename.csproj new file mode 100644 index 0000000..69c8a87 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.Filename/VDownload.Services.Utility.Filename.csproj @@ -0,0 +1,14 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" /> + <ProjectReference Include="..\..\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpClientService.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpClientService.cs new file mode 100644 index 0000000..8a5c4e5 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpClientService.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Net.Http; +using Newtonsoft.Json; +using System.Xml.Serialization; +using System.Reflection.PortableExecutable; +using System.Collections; + +namespace VDownload.Services.Utility.HttpClient +{ + public interface IHttpClientService + { + Task<T?> SendRequestAsync<T>(HttpRequest request); + Task<string> SendRequestAsync(HttpRequest request); + } + + + + public class HttpClientService : IHttpClientService + { + #region SERVICES + + private readonly System.Net.Http.HttpClient _httpClient; + + #endregion + + + + #region CONSTRUCTORS + + public HttpClientService(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task<T?> SendRequestAsync<T>(HttpRequest request) => JsonConvert.DeserializeObject<T>(await SendRequestAsync(request)); + public async Task<string> SendRequestAsync(HttpRequest request) + { + StringBuilder urlBuilder = new StringBuilder(request.Url); + if (request.Query.Count > 0) + { + Dictionary<string, object> query = request.Query.ToDictionary(); + KeyValuePair<string, object> queryElement = query.ElementAt(0); + query.Remove(queryElement.Key); + + urlBuilder.Append($"?{queryElement.Key}={queryElement.Value}"); + + foreach (KeyValuePair<string, object> queryElementLoop in query) + { + urlBuilder.Append($"&{queryElementLoop.Key}={queryElementLoop.Value}"); + } + } + + HttpMethod method = request.MethodType switch + { + HttpMethodType.GET => HttpMethod.Get, + HttpMethodType.POST => HttpMethod.Post, + HttpMethodType.PUT => HttpMethod.Put, + HttpMethodType.PATCH => HttpMethod.Patch, + HttpMethodType.DELETE => HttpMethod.Delete, + }; + + HttpRequestMessage httpRequest = new HttpRequestMessage(method, urlBuilder.ToString()); + + if (request.Body is not null) + { + string json = JsonConvert.SerializeObject(request.Body); + HttpContent content = new StringContent(json); + content.Headers.ContentType.MediaType = "application/json"; + httpRequest.Content = content; + } + + foreach (KeyValuePair<string, string> header in request.Headers) + { + httpRequest.Headers.Add(header.Key, header.Value); + } + + HttpResponseMessage response = await _httpClient.SendAsync(httpRequest); + + string responseString = await response.Content.ReadAsStringAsync(); + + return responseString; + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpMethodType.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpMethodType.cs new file mode 100644 index 0000000..eb445fe --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpMethodType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Utility.HttpClient +{ + public enum HttpMethodType + { + GET, + POST, + PUT, + PATCH, + DELETE + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpRequest.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpRequest.cs new file mode 100644 index 0000000..aa06fe0 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/HttpRequest.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Utility.HttpClient +{ + public class HttpRequest + { + #region PROPERTIES + + public HttpMethodType MethodType { get; private set; } + public string Url { get; private set; } + public Dictionary<string, string> Headers { get; private set; } + public Dictionary<string, object> Query { get; private set; } + public object? Body { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public HttpRequest(HttpMethodType methodType, string url) + { + MethodType = methodType; + Url = url; + Headers = new Dictionary<string, string>(); + Query = new Dictionary<string, object>(); + } + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/Token.cs b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/Token.cs new file mode 100644 index 0000000..893cea2 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/Token.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Services.Utility.HttpClient +{ + public class Token + { + #region PROPERTIES + + public string Schema { get; private set; } + public byte[] TokenValue { get; private set; } + + #endregion + + + + #region CONSTRUCTORS + + public Token(string schema, byte[] tokenValue) + { + Schema = schema; + TokenValue = tokenValue; + } + + #endregion + + + + #region PUBLIC METHODS + + public override string ToString() => $"{Schema} {Encoding.UTF8.GetString(TokenValue)}"; + + #endregion + } +} diff --git a/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/VDownload.Services.Utility.HttpClient.csproj b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/VDownload.Services.Utility.HttpClient.csproj new file mode 100644 index 0000000..3b5b170 --- /dev/null +++ b/VDownload.Services/VDownload.Services.Utility/VDownload.Services.Utility.HttpClient/VDownload.Services.Utility.HttpClient.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources.Common/MediaSearchException.cs b/VDownload.Sources/VDownload.Sources.Common/MediaSearchException.cs new file mode 100644 index 0000000..8db706c --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Common/MediaSearchException.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Common +{ + public class MediaSearchException : Exception + { + #region PROPERTIES + + public string StringCode { get; protected set; } + + #endregion + + + + #region CONSTRUCTORS + + public MediaSearchException(string stringCode) : this(stringCode, stringCode) { } + public MediaSearchException(string stringCode, string message) : base(message) + { + StringCode = stringCode; + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs b/VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs new file mode 100644 index 0000000..4bcb6ac --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Common/SearchRegex.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace VDownload.Sources.Common +{ + public class SearchRegex<TFunc> where TFunc : Delegate + { + #region PROPERTIES + + public required Regex Regex { get; init; } + + public TFunc SearchFunction { get; set; } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Common/SearchRegexPlaylist.cs b/VDownload.Sources/VDownload.Sources.Common/SearchRegexPlaylist.cs new file mode 100644 index 0000000..505a4d9 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Common/SearchRegexPlaylist.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Sources.Common +{ + public class SearchRegexPlaylist : SearchRegex<Func<string, int, Task<Playlist>>> + { + } +} diff --git a/VDownload.Sources/VDownload.Sources.Common/SearchRegexVideo.cs b/VDownload.Sources/VDownload.Sources.Common/SearchRegexVideo.cs new file mode 100644 index 0000000..98b539d --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Common/SearchRegexVideo.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Sources.Common +{ + public class SearchRegexVideo : SearchRegex<Func<string, Task<Video>>> + { + } +} diff --git a/VDownload.Sources/VDownload.Sources.Common/SourceSearchService.cs b/VDownload.Sources/VDownload.Sources.Common/SourceSearchService.cs new file mode 100644 index 0000000..cc56a0c --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Common/SourceSearchService.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Sources.Common +{ + public interface ISourceSearchService + { + Task<Video> SearchVideo(string url); + Task<Playlist> SearchPlaylist(string url, int maxVideoCount); + } + + + + public abstract class SourceSearchService : ISourceSearchService + { + #region PUBLIC METHODS + + public async Task<Video> SearchVideo(string url) + { + foreach (SearchRegexVideo regex in GetVideoRegexes()) + { + Match match = regex.Regex.Match(url); + if (match.Success) + { + string id = match.Groups[1].Value; + Video video = await regex.SearchFunction.Invoke(id); + return video; + } + } + throw CreateExceptionUnknownMediaType(); + } + + public async Task<Playlist> SearchPlaylist(string url, int maxVideoCount) + { + foreach (SearchRegexPlaylist regex in GetPlaylistRegexes()) + { + Match match = regex.Regex.Match(url); + if (match.Success) + { + string id = match.Groups[1].Value; + Playlist video = await regex.SearchFunction.Invoke(id, maxVideoCount); + return video; + } + } + throw CreateExceptionUnknownMediaType(); + } + + #endregion + + + + #region PRIVATE METHODS + + protected abstract IEnumerable<SearchRegexVideo> GetVideoRegexes(); + + protected abstract IEnumerable<SearchRegexPlaylist> GetPlaylistRegexes(); + + protected MediaSearchException CreateExceptionUnknownMediaType() => new MediaSearchException("UnknownMediaType"); + protected MediaSearchException CreateExceptionEmptyPlaylist() => new MediaSearchException("EmptyPlaylist"); + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Common/VDownload.Sources.Common.csproj b/VDownload.Sources/VDownload.Sources.Common/VDownload.Sources.Common.csproj new file mode 100644 index 0000000..9917f35 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Common/VDownload.Sources.Common.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Models\VDownload.Models.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenExtensions.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenExtensions.cs new file mode 100644 index 0000000..e908c11 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenExtensions.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenExtensions + { + [JsonProperty("persistedQuery")] + public GetClipTokenPersistedQuery PersistedQuery { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenPersistedQuery.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenPersistedQuery.cs new file mode 100644 index 0000000..9774414 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenPersistedQuery.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenPersistedQuery + { + [JsonProperty("version")] + public int Version { get; set; } + + [JsonProperty("sha256Hash")] + public string Sha256Hash { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenRequest.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenRequest.cs new file mode 100644 index 0000000..d3c7af5 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenRequest.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenRequest + { + [JsonProperty("operationName")] + public string OperationName { get; set; } + + [JsonProperty("variables")] + public GetClipTokenVariables Variables { get; set; } + + [JsonProperty("extensions")] + public GetClipTokenExtensions Extensions { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenVariables.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenVariables.cs new file mode 100644 index 0000000..570ac6d --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Request/GetClipTokenVariables.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request +{ + public class GetClipTokenVariables + { + [JsonProperty("slug")] + public string Slug { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenClip.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenClip.cs new file mode 100644 index 0000000..c44dac7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenClip.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenClip + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("playbackAccessToken")] + public GetClipTokenPlaybackAccessToken PlaybackAccessToken { get; set; } + + [JsonProperty("videoQualities")] + public List<GetClipTokenVideoQuality> VideoQualities { get; set; } + + [JsonProperty("__typename")] + public string Typename { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenData.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenData.cs new file mode 100644 index 0000000..79ebe8a --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenData.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Configuration.Models; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenData + { + [JsonProperty("clip")] + public GetClipTokenClip Clip { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenExtensions.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenExtensions.cs new file mode 100644 index 0000000..66d294d --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenExtensions.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenExtensions + { + [JsonProperty("durationMilliseconds")] + public int DurationMilliseconds { get; set; } + + [JsonProperty("operationName")] + public string OperationName { get; set; } + + [JsonProperty("requestID")] + public string RequestID { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenPlaybackAccessToken.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenPlaybackAccessToken.cs new file mode 100644 index 0000000..a69a3fd --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenPlaybackAccessToken.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenPlaybackAccessToken + { + [JsonProperty("signature")] + public string Signature { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("__typename")] + public string Typename { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenResponse.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenResponse.cs new file mode 100644 index 0000000..0fca77c --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenResponse + { + [JsonProperty("data")] + public GetClipTokenData Data { get; set; } + + [JsonProperty("extensions")] + public Response.GetClipTokenExtensions Extensions { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenVideoQuality.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenVideoQuality.cs new file mode 100644 index 0000000..758e9dc --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetClipToken/Response/GetClipTokenVideoQuality.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response +{ + public class GetClipTokenVideoQuality + { + [JsonProperty("frameRate")] + public double FrameRate { get; set; } + + [JsonProperty("quality")] + public string Quality { get; set; } + + [JsonProperty("sourceURL")] + public string SourceURL { get; set; } + + [JsonProperty("__typename")] + public string Typename { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenExtensions.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenExtensions.cs new file mode 100644 index 0000000..9962098 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenExtensions.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Request +{ + public class GetVideoTokenExtensions + { + [JsonProperty("durationMilliseconds")] + public int DurationMilliseconds { get; set; } + + [JsonProperty("operationName")] + public string OperationName { get; set; } + + [JsonProperty("requestID")] + public string RequestID { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenRequest.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenRequest.cs new file mode 100644 index 0000000..78a9d43 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenRequest.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Search.Models.GetVideoToken.Request +{ + public class GetVideoTokenRequest + { + [JsonProperty("operationName")] + public string OperationName { get; set; } + + [JsonProperty("query")] + public string Query { get; set; } + + [JsonProperty("variables")] + public GetVideoTokenVariables Variables { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenVariables.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenVariables.cs new file mode 100644 index 0000000..4d486b7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Request/GetVideoTokenVariables.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Search.Models.GetVideoToken.Request +{ + public class GetVideoTokenVariables + { + [JsonProperty("isLive")] + public bool IsLive { get; set; } + + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("isVod")] + public bool IsVod { get; set; } + + [JsonProperty("vodID")] + public string VodID { get; set; } + + [JsonProperty("playerType")] + public string PlayerType { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenData.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenData.cs new file mode 100644 index 0000000..1ef98a5 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenData.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response +{ + public class GetVideoTokenData + { + [JsonProperty("videoPlaybackAccessToken")] + public GetVideoTokenVideoPlaybackAccessToken VideoPlaybackAccessToken { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenResponse.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenResponse.cs new file mode 100644 index 0000000..f48bcf5 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Request; + +namespace VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response +{ + public class GetVideoTokenResponse + { + [JsonProperty("data")] + public GetVideoTokenData Data { get; set; } + + [JsonProperty("extensions")] + public GetVideoTokenExtensions Extensions { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenVideoPlaybackAccessToken.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenVideoPlaybackAccessToken.cs new file mode 100644 index 0000000..c0d7eba --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/GQL/GetVideoToken/Response/GetVideoTokenVideoPlaybackAccessToken.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response +{ + public class GetVideoTokenVideoPlaybackAccessToken + { + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("signature")] + public string Signature { get; set; } + + [JsonProperty("__typename")] + public string Typename { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Data.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Data.cs new file mode 100644 index 0000000..62c6edf --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Data.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.Helix.GetClips.Response +{ + public class Data + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("embed_url")] + public string EmbedUrl { get; set; } + + [JsonProperty("broadcaster_id")] + public string BroadcasterId { get; set; } + + [JsonProperty("broadcaster_name")] + public string BroadcasterName { get; set; } + + [JsonProperty("creator_id")] + public string CreatorId { get; set; } + + [JsonProperty("creator_name")] + public string CreatorName { get; set; } + + [JsonProperty("video_id")] + public string VideoId { get; set; } + + [JsonProperty("game_id")] + public string GameId { get; set; } + + [JsonProperty("language")] + public string Language { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("view_count")] + public long ViewCount { get; set; } + + [JsonProperty("created_at")] + public DateTime CreatedAt { get; set; } + + [JsonProperty("thumbnail_url")] + public string ThumbnailUrl { get; set; } + + [JsonProperty("duration")] + public double Duration { get; set; } + + [JsonProperty("vod_offset")] + public int VodOffset { get; set; } + + [JsonProperty("is_featured")] + public bool IsFeatured { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/GetClipsResponse.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/GetClipsResponse.cs new file mode 100644 index 0000000..35de292 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/GetClipsResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Api.Helix.GetVideos.Response; + +namespace VDownload.Sources.Twitch.Api.Helix.GetClips.Response +{ + public class GetClipsResponse + { + [JsonProperty("data")] + public List<Data> Data { get; set; } + + [JsonProperty("pagination")] + public Pagination Pagination { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Pagination.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Pagination.cs new file mode 100644 index 0000000..961edb7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetClips/Response/Pagination.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.Helix.GetClips.Response +{ + public class Pagination + { + [JsonProperty("cursor")] + public string Cursor { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetUsers/Response/Data.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetUsers/Response/Data.cs new file mode 100644 index 0000000..9e3a6cf --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetUsers/Response/Data.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.Helix.GetUsers.Response +{ + public class Data + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("display_name")] + public string DisplayName { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("broadcaster_type")] + public string BroadcasterType { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("profile_image_url")] + public string ProfileImageUrl { get; set; } + + [JsonProperty("offline_image_url")] + public string OfflineImageUrl { get; set; } + + [JsonProperty("view_count")] + public int ViewCount { get; set; } + + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("created_at")] + public DateTime CreatedAt { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetUsers/Response/GetUsersResponse.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetUsers/Response/GetUsersResponse.cs new file mode 100644 index 0000000..255274a --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetUsers/Response/GetUsersResponse.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Api.Helix.GetUsers.Response +{ + public class GetUsersResponse + { + [JsonProperty("data")] + public List<Data> Data { get; } = new List<Data>(); + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/Data.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/Data.cs new file mode 100644 index 0000000..600f539 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/Data.cs @@ -0,0 +1,62 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System; + +namespace VDownload.Sources.Twitch.Api.Helix.GetVideos.Response +{ + + public class Data + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("stream_id")] + public object StreamId { get; set; } + + [JsonProperty("user_id")] + public string UserId { get; set; } + + [JsonProperty("user_login")] + public string UserLogin { get; set; } + + [JsonProperty("user_name")] + public string UserName { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("created_at")] + public DateTime CreatedAt { get; set; } + + [JsonProperty("published_at")] + public DateTime PublishedAt { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("thumbnail_url")] + public string ThumbnailUrl { get; set; } + + [JsonProperty("viewable")] + public string Viewable { get; set; } + + [JsonProperty("view_count")] + public int ViewCount { get; set; } + + [JsonProperty("language")] + public string Language { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("duration")] + public string Duration { get; set; } + + [JsonProperty("muted_segments")] + public List<MutedSegment> MutedSegments { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/GetVideosResponse.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/GetVideosResponse.cs new file mode 100644 index 0000000..4cd669f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/GetVideosResponse.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace VDownload.Sources.Twitch.Api.Helix.GetVideos.Response +{ + + public class GetVideosResponse + { + [JsonProperty("data")] + public List<Data> Data { get; set; } + + [JsonProperty("pagination")] + public Pagination Pagination { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/MutedSegment.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/MutedSegment.cs new file mode 100644 index 0000000..cc6a644 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/MutedSegment.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +namespace VDownload.Sources.Twitch.Api.Helix.GetVideos.Response +{ + + public class MutedSegment + { + [JsonProperty("duration")] + public int Duration { get; set; } + + [JsonProperty("offset")] + public int Offset { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/Pagination.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/Pagination.cs new file mode 100644 index 0000000..7000d3f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/Helix/GetVideos/Response/Pagination.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; +namespace VDownload.Sources.Twitch.Api.Helix.GetVideos.Response +{ + + public class Pagination + { + [JsonProperty("cursor")] + public string Cursor { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/TwitchApiService.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/TwitchApiService.cs new file mode 100644 index 0000000..7b4f934 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/TwitchApiService.cs @@ -0,0 +1,194 @@ +using System.Diagnostics; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Utility.HttpClient; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Request; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response; +using VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response; +using VDownload.Sources.Twitch.Api.Helix.GetClips.Response; +using VDownload.Sources.Twitch.Api.Helix.GetUsers.Response; +using VDownload.Sources.Twitch.Api.Helix.GetVideos.Response; +using VDownload.Sources.Twitch.Configuration.Models; +using VDownload.Sources.Twitch.Search.Models.GetVideoToken.Request; + +namespace VDownload.Sources.Twitch.Api +{ + public interface ITwitchApiService + { + Task<string> AuthValidate(byte[] token); + Task<GetVideoTokenResponse> GQLGetVideoToken(string id); + Task<GetClipTokenResponse> GQLGetClipToken(string id); + Task<GetUsersResponse> HelixGetUser(string login, byte[] token); + Task<GetVideosResponse> HelixGetVideo(string id, byte[] token); + Task<GetVideosResponse> HelixGetUserVideos(string user_id, byte[] token, int count, string? cursor = null); + Task<GetClipsResponse> HelixGetClip(string id, byte[] token); + Task<string> UsherGetVideoPlaylist(string id, string videoToken, string videoTokenSignature); + } + + + + public class TwitchApiService : ITwitchApiService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly IHttpClientService _httpClientService; + + #endregion + + + + #region CONSTRUCTORS + + public TwitchApiService(IConfigurationService configurationService, IHttpClientService httpClientService) + { + _configurationService = configurationService; + _httpClientService = httpClientService; + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task<string> AuthValidate(byte[] token) + { + Token tokenData = new Token(_configurationService.Twitch.Api.Auth.TokenSchema, token); + HttpRequest request = new HttpRequest(HttpMethodType.GET, _configurationService.Twitch.Api.Auth.Endpoints.Validate); + request.Headers.Add("Authorization", $"{tokenData}"); + return await _httpClientService.SendRequestAsync(request); + } + + public async Task<GetUsersResponse> HelixGetUser(string login, byte[] token) + { + Token tokenData = new Token(_configurationService.Twitch.Api.Helix.TokenSchema, token); + + HttpRequest request = new HttpRequest(HttpMethodType.GET, _configurationService.Twitch.Api.Helix.Endpoints.GetUsers); + + request.Query.Add("login", login); + + request.Headers.Add("Authorization", $"{tokenData}"); + request.Headers.Add("Client-Id", _configurationService.Twitch.Api.Helix.ClientId); + + return await _httpClientService.SendRequestAsync<GetUsersResponse>(request); + } + + public async Task<GetVideosResponse> HelixGetVideo(string id, byte[] token) + { + Token tokenData = new Token(_configurationService.Twitch.Api.Helix.TokenSchema, token); + + HttpRequest request = new HttpRequest(HttpMethodType.GET, _configurationService.Twitch.Api.Helix.Endpoints.GetVideos); + + request.Query.Add("id", id); + + request.Headers.Add("Authorization", tokenData.ToString()); + request.Headers.Add("Client-Id", _configurationService.Twitch.Api.Helix.ClientId); + + return await _httpClientService.SendRequestAsync<GetVideosResponse>(request); + } + + public async Task<GetVideosResponse> HelixGetUserVideos(string user_id, byte[] token, int count, string? cursor = null) + { + Token tokenData = new Token(_configurationService.Twitch.Api.Helix.TokenSchema, token); + + HttpRequest request = new HttpRequest(HttpMethodType.GET, _configurationService.Twitch.Api.Helix.Endpoints.GetVideos); + + request.Query.Add("user_id", user_id); + request.Query.Add("first", count); + if (cursor is not null) + { + request.Query.Add("after", cursor); + } + + request.Headers.Add("Authorization", tokenData.ToString()); + request.Headers.Add("Client-Id", _configurationService.Twitch.Api.Helix.ClientId); + + return await _httpClientService.SendRequestAsync<GetVideosResponse>(request); + } + + public async Task<GetClipsResponse> HelixGetClip(string id, byte[] token) + { + Token tokenData = new Token(_configurationService.Twitch.Api.Helix.TokenSchema, token); + + HttpRequest request = new HttpRequest(HttpMethodType.GET, _configurationService.Twitch.Api.Helix.Endpoints.GetClips); + + request.Query.Add("id", id); + + request.Headers.Add("Authorization", tokenData.ToString()); + request.Headers.Add("Client-Id", _configurationService.Twitch.Api.Helix.ClientId); + + return await _httpClientService.SendRequestAsync<GetClipsResponse>(request); + } + + public async Task<GetVideoTokenResponse> GQLGetVideoToken(string id) + { + GetVideoTokenRequest requestBody = new GetVideoTokenRequest + { + OperationName = _configurationService.Twitch.Api.Gql.Queries.GetVideoToken.OperationName, + Query = _configurationService.Twitch.Api.Gql.Queries.GetVideoToken.Query, + Variables = new GetVideoTokenVariables + { + IsLive = false, + Login = string.Empty, + IsVod = true, + VodID = id, + PlayerType = "embed" + } + }; + + HttpRequest request = new HttpRequest(HttpMethodType.POST, _configurationService.Twitch.Api.Gql.Endpoint) + { + Body = requestBody, + }; + request.Headers.Add("Client-Id", _configurationService.Twitch.Api.Gql.ClientId); + return await _httpClientService.SendRequestAsync<GetVideoTokenResponse>(request); + } + + public async Task<GetClipTokenResponse> GQLGetClipToken(string id) + { + Gql config = _configurationService.Twitch.Api.Gql; + + GetClipTokenRequest requestBody = new GetClipTokenRequest + { + OperationName = config.Queries.GetClipToken.OperationName, + Variables = new GetClipTokenVariables + { + Slug = id + }, + Extensions = new GQL.GetClipToken.Request.GetClipTokenExtensions + { + PersistedQuery = new GetClipTokenPersistedQuery + { + Version = config.Queries.GetClipToken.PersistedQueryVersion, + Sha256Hash = config.Queries.GetClipToken.PersistedQueryHash, + } + } + }; + + HttpRequest request = new HttpRequest(HttpMethodType.POST, config.Endpoint) + { + Body = requestBody, + }; + request.Headers.Add("Client-Id", config.ClientId); + + return await _httpClientService.SendRequestAsync<GetClipTokenResponse>(request); + } + + public async Task<string> UsherGetVideoPlaylist(string id, string videoToken, string videoTokenSignature) + { + string url = string.Format(_configurationService.Twitch.Api.Usher.Endpoints.GetVideoPlaylist, id); + HttpRequest request = new HttpRequest(HttpMethodType.GET, url); + request.Query.Add("token", videoToken); + request.Query.Add("sig", videoTokenSignature); + request.Query.Add("allow_source", true); + request.Query.Add("allow_audio_only", true); + request.Query.Add("platform", "web"); + request.Query.Add("player_backend", "mediaplayer"); + request.Query.Add("playlist_include_framerate", true); + request.Query.Add("supported_codecs", "av1,h264"); + return await _httpClientService.SendRequestAsync(request); + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/VDownload.Sources.Twitch.Api.csproj b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/VDownload.Sources.Twitch.Api.csproj new file mode 100644 index 0000000..b43303d --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Api/VDownload.Sources.Twitch.Api.csproj @@ -0,0 +1,14 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.HttpClient\VDownload.Services.Utility.HttpClient.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/Models/ValidateResponseFail.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/Models/ValidateResponseFail.cs new file mode 100644 index 0000000..8aaf6f1 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/Models/ValidateResponseFail.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Authentication.Models +{ + internal class ValidateResponseFail + { + [JsonProperty("status", Required = Required.Always)] + public int Status { get; set; } + + [JsonProperty("message", Required = Required.Always)] + public string Message { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/Models/ValidateResponseSuccess.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/Models/ValidateResponseSuccess.cs new file mode 100644 index 0000000..c0423ec --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/Models/ValidateResponseSuccess.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Authentication.Models +{ + internal class ValidateResponseSuccess + { + [JsonProperty("client_id", Required = Required.Always)] + public string ClientId { get; set; } + + [JsonProperty("login", Required = Required.Always)] + public string Login { get; set; } + + [JsonProperty("scopes", Required = Required.Always)] + public List<string> Scopes { get; set; } + + [JsonProperty("user_id", Required = Required.Always)] + public string UserId { get; set; } + + [JsonProperty("expires_in", Required = Required.Always)] + public int ExpiresIn { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchAuthenticationService.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchAuthenticationService.cs new file mode 100644 index 0000000..13c912c --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchAuthenticationService.cs @@ -0,0 +1,113 @@ +using Newtonsoft.Json; +using VDownload.Services.Data.Authentication; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Utility.Encryption; +using VDownload.Sources.Twitch.Api; +using VDownload.Sources.Twitch.Authentication.Models; + +namespace VDownload.Sources.Twitch.Authentication +{ + public interface ITwitchAuthenticationService + { + Task DeleteToken(); + Task<byte[]?> GetToken(); + Task SetToken(byte[] token); + Task<TwitchValidationResult> ValidateToken(byte[] token); + } + + + + public class TwitchAuthenticationService : ITwitchAuthenticationService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly ITwitchApiService _apiService; + protected readonly IAuthenticationDataService _authenticationDataService; + protected readonly IEncryptionService _encryptionService; + + #endregion + + + + #region CONSTRUCTORS + + public TwitchAuthenticationService(IConfigurationService configurationService, ITwitchApiService apiService, IAuthenticationDataService authenticationDataService, IEncryptionService encryptionService) + { + _configurationService = configurationService; + _apiService = apiService; + _authenticationDataService = authenticationDataService; + _encryptionService = encryptionService; + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task<byte[]?> GetToken() + { + await _authenticationDataService.Load(); + + byte[]? tokenEncrypted = _authenticationDataService.Data.Twitch.Token; + + if (tokenEncrypted is not null && tokenEncrypted.Length == 0) + { + tokenEncrypted = null; + } + + if (tokenEncrypted is not null) + { + tokenEncrypted = _encryptionService.Decrypt(tokenEncrypted); + } + + return tokenEncrypted; + } + + public async Task SetToken(byte[] token) + { + Task loadTask = _authenticationDataService.Load(); + + byte[] tokenEncrypted = _encryptionService.Encrypt(token); + + await loadTask; + + _authenticationDataService.Data.Twitch.Token = tokenEncrypted; + + await _authenticationDataService.Save(); + } + + public async Task DeleteToken() + { + await _authenticationDataService.Load(); + _authenticationDataService.Data.Twitch.Token = null; + await _authenticationDataService.Save(); + } + + public async Task<TwitchValidationResult> ValidateToken(byte[] token) + { + string response = await _apiService.AuthValidate(token); + + try + { + ValidateResponseSuccess success = JsonConvert.DeserializeObject<ValidateResponseSuccess>(response); + return new TwitchValidationResult(success); + } + catch (JsonSerializationException) + { } + + try + { + ValidateResponseFail fail = JsonConvert.DeserializeObject<ValidateResponseFail>(response); + return new TwitchValidationResult(fail); + } + catch (JsonSerializationException) + { } + + throw new Exception(response); + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchValidationResult.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchValidationResult.cs new file mode 100644 index 0000000..9c96c4a --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchValidationResult.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Authentication.Models; + +namespace VDownload.Sources.Twitch.Authentication +{ + public class TwitchValidationResult + { + #region PROPERTIES + + public bool Success { get; private set; } + public int? FailStatusCode { get; private set; } + public string? FailMessage { get; private set; } + public TwitchValidationTokenData TokenData { get; private set; } + public DateTime ValidationDate { get; private set; } + + #endregion + + + + #region CONSTRUCTORS + + private TwitchValidationResult() + { + ValidationDate = DateTime.Now; + } + + internal TwitchValidationResult(ValidateResponseFail fail) + { + Success = false; + FailStatusCode = fail.Status; + FailMessage = fail.Message; + } + + internal TwitchValidationResult(ValidateResponseSuccess success) + { + Success = true; + TokenData = new TwitchValidationTokenData(success); + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchValidationTokenData.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchValidationTokenData.cs new file mode 100644 index 0000000..9932906 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/TwitchValidationTokenData.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Authentication.Models; + +namespace VDownload.Sources.Twitch.Authentication +{ + public class TwitchValidationTokenData + { + #region PROPERTIES + + public string ClientId { get; private set; } + public string Login { get; private set; } + public IEnumerable<string> Scopes { get; private set; } + public string UserId { get; private set; } + public DateTime ExpirationDate { get; private set; } + + #endregion + + + + #region CONSTRUCTORS + + internal TwitchValidationTokenData(ValidateResponseSuccess response) + { + ClientId = response.ClientId; + Login = response.Login; + Scopes = response.Scopes; + UserId = response.UserId; + ExpirationDate = DateTime.Now.AddSeconds(response.ExpiresIn); + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/VDownload.Sources.Twitch.Authentication.csproj b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/VDownload.Sources.Twitch.Authentication.csproj new file mode 100644 index 0000000..0358038 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Authentication/VDownload.Sources.Twitch.Authentication.csproj @@ -0,0 +1,16 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Authentication\VDownload.Services.Data.Authentication.csproj" /> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.Encryption\VDownload.Services.Utility.Encryption.csproj" /> + <ProjectReference Include="..\VDownload.Sources.Twitch.Api\VDownload.Sources.Twitch.Api.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Api.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Api.cs new file mode 100644 index 0000000..8a42839 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Api.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Configuration; +using System.Text.Json.Serialization; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Api + { + [ConfigurationKeyName("auth")] + public Auth Auth { get; set; } + + [ConfigurationKeyName("helix")] + public Helix Helix { get; set; } + + [ConfigurationKeyName("gql")] + public Gql Gql { get; set; } + + [ConfigurationKeyName("usher")] + public Usher Usher { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Auth.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Auth.cs new file mode 100644 index 0000000..37fd341 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Auth.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Auth + { + [ConfigurationKeyName("token_schema")] + public string TokenSchema { get; set; } + + [ConfigurationKeyName("client_id")] + public string ClientId { get; set; } + + [ConfigurationKeyName("endpoints")] + public EndpointsAuth Endpoints { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Authentication.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Authentication.cs new file mode 100644 index 0000000..908e9b0 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Authentication.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class Authentication + { + [ConfigurationKeyName("url")] + public string Url { get; set; } + + [ConfigurationKeyName("redirect_url")] + public string RedirectUrl { get; set; } + + [ConfigurationKeyName("redirect_url_regex")] + public string RedirectUrlRegex { get; set; } + + [ConfigurationKeyName("client_id")] + public string ClientId { get; set; } + + [ConfigurationKeyName("response_type")] + public string ResponseType { get; set; } + + [ConfigurationKeyName("scopes")] + public List<string> Scopes { get; } = new List<string>(); + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Channel.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Channel.cs new file mode 100644 index 0000000..bcc75f7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Channel.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class Channel + { + [ConfigurationKeyName("regexes")] + public List<string> Regexes { get; } = new List<string>(); + + [ConfigurationKeyName("url")] + public string Url { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Clip.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Clip.cs new file mode 100644 index 0000000..3aa78d0 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Clip.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class Clip + { + [ConfigurationKeyName("regexes")] + public List<string> Regexes { get; } = new List<string>(); + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Download.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Download.cs new file mode 100644 index 0000000..c50a01f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Download.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Download + { + [ConfigurationKeyName("vod")] + public DownloadVod Vod { get; set; } + + [ConfigurationKeyName("clip")] + public DownloadClip Clip { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadClip.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadClip.cs new file mode 100644 index 0000000..6d1a39a --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadClip.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class DownloadClip + { + [ConfigurationKeyName("file_name")] + public string FileName { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadVod.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadVod.cs new file mode 100644 index 0000000..25a9130 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/DownloadVod.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class DownloadVod + { + [ConfigurationKeyName("chunk_regex")] + public string ChunkRegex { get; set; } + + [ConfigurationKeyName("file_name")] + public string FileName { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsAuth.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsAuth.cs new file mode 100644 index 0000000..030bc2e --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsAuth.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class EndpointsAuth + { + [ConfigurationKeyName("validate")] + public string Validate { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsHelix.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsHelix.cs new file mode 100644 index 0000000..0748159 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsHelix.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Configuration; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class EndpointsHelix + { + [ConfigurationKeyName("get_videos")] + public string GetVideos { get; set; } + + [ConfigurationKeyName("get_clips")] + public string GetClips { get; set; } + + [ConfigurationKeyName("get_users")] + public string GetUsers { get; set; } + } +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsUsher.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsUsher.cs new file mode 100644 index 0000000..8894b66 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/EndpointsUsher.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.Configuration; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class EndpointsUsher + { + [ConfigurationKeyName("get_video_playlist")] + public string GetVideoPlaylist { get; set; } + } +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetClipToken.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetClipToken.cs new file mode 100644 index 0000000..e3ec148 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetClipToken.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Configuration.Models +{ + public class GetClipToken + { + [ConfigurationKeyName("operation_name")] + public string OperationName { get; set; } + + [ConfigurationKeyName("persisted_query_version")] + public int PersistedQueryVersion { get; set; } + + [ConfigurationKeyName("persisted_query_hash")] + public string PersistedQueryHash { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetVideoToken.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetVideoToken.cs new file mode 100644 index 0000000..f8ae61f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/GetVideoToken.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class GetVideoToken + { + [ConfigurationKeyName("operation_name")] + public string OperationName { get; set; } + + [ConfigurationKeyName("query")] + public string Query { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Gql.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Gql.cs new file mode 100644 index 0000000..c18d88f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Gql.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Gql + { + [ConfigurationKeyName("client_id")] + public string ClientId { get; set; } + + [ConfigurationKeyName("endpoint")] + public string Endpoint { get; set; } + + [ConfigurationKeyName("queries")] + public Queries Queries { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Helix.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Helix.cs new file mode 100644 index 0000000..41b9ba2 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Helix.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Helix + { + [ConfigurationKeyName("token_schema")] + public string TokenSchema { get; set; } + + [ConfigurationKeyName("client_id")] + public string ClientId { get; set; } + + [ConfigurationKeyName("endpoints")] + public EndpointsHelix Endpoints { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Queries.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Queries.cs new file mode 100644 index 0000000..2d8a1d9 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Queries.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Queries + { + [ConfigurationKeyName("get_video_token")] + public GetVideoToken GetVideoToken { get; set; } + + [ConfigurationKeyName("get_clip_token")] + public GetClipToken GetClipToken { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Search.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Search.cs new file mode 100644 index 0000000..759ea4f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Search.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Search + { + [ConfigurationKeyName("general_regexes")] + public List<string> GeneralRegexes { get; } = new List<string>(); + + [ConfigurationKeyName("vod")] + public SearchVod Vod { get; set; } + + [ConfigurationKeyName("clip")] + public Clip Clip { get; set; } + + [ConfigurationKeyName("channel")] + public Channel Channel { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/SearchVod.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/SearchVod.cs new file mode 100644 index 0000000..eb56648 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/SearchVod.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class SearchVod + { + [ConfigurationKeyName("regexes")] + public List<string> Regexes { get; } = new List<string>(); + + [ConfigurationKeyName("thumbnail")] + public Thumbnail Thumbnail { get; set; } + + [ConfigurationKeyName("live_thumbnail_url_regex")] + public string LiveThumbnailUrlRegex { get; set; } + + [ConfigurationKeyName("stream_playlist_regex")] + public string StreamPlaylistRegex { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Thumbnail.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Thumbnail.cs new file mode 100644 index 0000000..78bb488 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Thumbnail.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Thumbnail + { + [ConfigurationKeyName("width")] + public int Width { get; set; } + + [ConfigurationKeyName("height")] + public int Height { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Usher.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Usher.cs new file mode 100644 index 0000000..793c2ae --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/Models/Usher.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.Configuration; +namespace VDownload.Sources.Twitch.Configuration.Models{ + + public class Usher + { + [ConfigurationKeyName("endpoints")] + public EndpointsUsher Endpoints { get; set; } + } + +} \ No newline at end of file diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/TwitchConfiguration.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/TwitchConfiguration.cs new file mode 100644 index 0000000..03ac634 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/TwitchConfiguration.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Configuration.Models; + +namespace VDownload.Sources.Twitch.Configuration +{ + public class TwitchConfiguration + { + [ConfigurationKeyName("api")] + public Api Api { get; set; } + + [ConfigurationKeyName("search")] + public Search Search { get; set; } + + [ConfigurationKeyName("download")] + public Download Download { get; set; } + + [ConfigurationKeyName("authentication")] + public Authentication Authentication { get; set; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/VDownload.Sources.Twitch.Configuration.csproj b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/VDownload.Sources.Twitch.Configuration.csproj new file mode 100644 index 0000000..85f7f36 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Configuration/VDownload.Sources.Twitch.Configuration.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/Internal/TwitchVodChunk.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/Internal/TwitchVodChunk.cs new file mode 100644 index 0000000..3820447 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/Internal/TwitchVodChunk.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Models.Internal +{ + internal class TwitchVodChunk + { + public required string Location { get; init; } + public required string Url { get; init; } + public required long Index { get; init; } + public required TimeSpan Duration { get; init; } + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchChannel.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchChannel.cs new file mode 100644 index 0000000..4a757bf --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchChannel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Models +{ + public class TwitchChannel : TwitchPlaylist + { + #region PROPERTIES + + public required string Id { get; set; } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClip.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClip.cs new file mode 100644 index 0000000..8cddf43 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClip.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Models +{ + public class TwitchClip : TwitchVideo + { + #region PROPERTIES + + public string Creator { get; set; } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClipStream.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClipStream.cs new file mode 100644 index 0000000..b075bdc --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchClipStream.cs @@ -0,0 +1,83 @@ +using SimpleToolkit.Extensions; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using System.Web; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using VDownload.Sources.Twitch.Models.Internal; + +namespace VDownload.Sources.Twitch.Models +{ + public class TwitchClipStream : VideoStream + { + #region SERVICES + + protected readonly HttpClient _httpClient; + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region PROPERTIES + + public int Height { get; set; } + public double FrameRate { get; set; } + public Uri Url { get; set; } + public string Signature { get; set; } + public string Token { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public TwitchClipStream(HttpClient httpClient, IConfigurationService configurationService, ISettingsService settingsService) + { + _httpClient = httpClient; + + _configurationService = configurationService; + _settingsService = settingsService; + } + + #endregion + + + + #region PUBLIC METHODS + + public async override Task<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> onProgress, CancellationToken token, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd) + { + token.ThrowIfCancellationRequested(); + + string location = Path.Combine(taskTemporaryDirectory, _configurationService.Twitch.Download.Clip.FileName); + + string url = $"{Url.OriginalString}?sig={Signature}&token={HttpUtility.UrlEncode(Token)}"; + using (FileStream fileStream = File.Create(location)) + { + await _httpClient.DownloadAsync(url, fileStream, token, onProgress); + token.ThrowIfCancellationRequested(); + } + + return new VideoStreamDownloadResult + { + File = location, + NewDuration = duration, + NewTrimEnd = trimEnd, + NewTrimStart = trimStart + }; + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchPlaylist.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchPlaylist.cs new file mode 100644 index 0000000..e6fa019 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchPlaylist.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Models; + +namespace VDownload.Sources.Twitch.Models +{ + public abstract class TwitchPlaylist : Playlist + { + #region CONSTRUCTORS + + protected TwitchPlaylist() + { + Source = Source.Twitch; + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVideo.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVideo.cs new file mode 100644 index 0000000..0e56392 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVideo.cs @@ -0,0 +1,16 @@ +using VDownload.Models; + +namespace VDownload.Sources.Twitch.Models +{ + public abstract class TwitchVideo : Video + { + #region CONSTRUCTORS + + protected TwitchVideo() + { + Source = Source.Twitch; + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVod.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVod.cs new file mode 100644 index 0000000..97f523f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVod.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Models +{ + public class TwitchVod : TwitchVideo + { + #region PROPERTIES + + public string Description { get; set; } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVodStream.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVodStream.cs new file mode 100644 index 0000000..2e052c2 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/TwitchVodStream.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using VDownload.Sources.Twitch.Models.Internal; + +namespace VDownload.Sources.Twitch.Models +{ + public class TwitchVodStream : VideoStream + { + #region SERVICES + + protected readonly HttpClient _httpClient; + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region PROPERTIES + + public string UrlM3U8 { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public string VideoCodec { get; set; } + public string AudioCodec { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + public TwitchVodStream(HttpClient httpClient, IConfigurationService configurationService, ISettingsService settingsService) + { + _httpClient = httpClient; + + _configurationService = configurationService; + _settingsService = settingsService; + } + + #endregion + + + + #region PUBLIC METHODS + + public async override Task<VideoStreamDownloadResult> Download(string taskTemporaryDirectory, IProgress<double> onProgress, CancellationToken token, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd) + { + token.ThrowIfCancellationRequested(); + + string m3u8 = await _httpClient.GetStringAsync(UrlM3U8, token); + + token.ThrowIfCancellationRequested(); + + string m3u8BaseUrl = Path.GetDirectoryName(UrlM3U8).Replace("https:\\", "https://").Replace("http:\\", "http://").Replace('\\', '/'); + Regex regex = new Regex(_configurationService.Twitch.Download.Vod.ChunkRegex); + MatchCollection matches = regex.Matches(m3u8); + long index = 0; + + List<TwitchVodChunk> chunks = new List<TwitchVodChunk>(); + foreach (Match match in matches) + { + token.ThrowIfCancellationRequested(); + + string filename = match.Groups["file"].Value; + string durationString = match.Groups["duration"].Value; + + TimeSpan chunkDuration = TimeSpan.FromSeconds(double.Parse(durationString, CultureInfo.InvariantCulture)); + string url = $"{m3u8BaseUrl}/{filename}"; + string location = Path.Combine(taskTemporaryDirectory, filename); + + chunks.Add(new TwitchVodChunk + { + Url = url, + Index = index, + Duration = chunkDuration, + Location = location + }); + + index++; + } + + token.ThrowIfCancellationRequested(); + + if (_settingsService.Data.Twitch.Vod.PassiveTrimming) + { + PassiveTrimming(chunks, ref trimStart, ref trimEnd, ref duration); + } + + token.ThrowIfCancellationRequested(); + + long downloadedCount = 0; + Action taskEnd = () => + { + downloadedCount++; + double progress = ((double)downloadedCount / chunks.Count) * 100; + onProgress.Report(progress); + }; + ActionBlock<TwitchVodChunk> block = new ActionBlock<TwitchVodChunk>(x => DownloadChunk(x, token, taskEnd), new ExecutionDataflowBlockOptions + { + MaxDegreeOfParallelism = _settingsService.Data.Twitch.Vod.MaxNumberOfParallelDownloads + }); + foreach (TwitchVodChunk chunk in chunks) + { + block.Post(chunk); + } + block.Complete(); + await block.Completion; + + token.ThrowIfCancellationRequested(); + + string file = Path.Combine(taskTemporaryDirectory, _configurationService.Twitch.Download.Vod.FileName); + MergeFiles(file, chunks.Select(x => x.Location), token, true); + + return new VideoStreamDownloadResult + { + File = file, + NewTrimStart = trimStart, + NewTrimEnd = trimEnd, + NewDuration = duration, + }; + } + + #endregion + + + + #region PRIVATE METHODS + + private void MergeFiles(string destinationPath, IEnumerable<string> sourceFiles, CancellationToken token, bool deleteSource = false) + { + using (FileStream outputStream = File.Create(destinationPath)) + { + foreach (string path in sourceFiles) + { + token.ThrowIfCancellationRequested(); + + using (FileStream inputStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + inputStream.CopyTo(outputStream); + } + } + } + foreach (string item in sourceFiles) + { + if (deleteSource) + { + File.Delete(item); + } + } + } + + private void PassiveTrimming(List<TwitchVodChunk> chunks, ref TimeSpan trimStart, ref TimeSpan trimEnd, ref TimeSpan duration) + { + while (chunks.First().Duration <= trimStart) + { + TwitchVodChunk chunk = chunks.First(); + TimeSpan chunkDuration = chunk.Duration; + trimStart -= chunkDuration; + trimEnd -= chunkDuration; + duration -= chunkDuration; + chunks.Remove(chunk); + } + + while (chunks.Last().Duration <= duration.Subtract(trimEnd)) + { + TwitchVodChunk chunk = chunks.Last(); + TimeSpan chunkDuration = chunk.Duration; + duration -= chunkDuration; + chunks.Remove(chunk); + } + } + + private async Task DownloadChunk(TwitchVodChunk chunk, CancellationToken token, Action onTaskEndSuccessfully) + { + int retriesCount = 0; + while (true) + { + token.ThrowIfCancellationRequested(); + try + { + byte[] data = await _httpClient.GetByteArrayAsync(chunk.Url, token); + await File.WriteAllBytesAsync(chunk.Location, data, token); + onTaskEndSuccessfully.Invoke(); + return; + } + catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException) + { + if (_settingsService.Data.Twitch.Vod.ChunkDownloadingError.Retry && retriesCount < _settingsService.Data.Twitch.Vod.ChunkDownloadingError.RetriesCount) + { + retriesCount++; + await Task.Delay(_settingsService.Data.Twitch.Vod.ChunkDownloadingError.RetryDelay); + } + else throw; + } + } + } + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/VDownload.Sources.Twitch.Models.csproj b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/VDownload.Sources.Twitch.Models.csproj new file mode 100644 index 0000000..6bde5aa --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Models/VDownload.Sources.Twitch.Models.csproj @@ -0,0 +1,19 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="SimpleToolkit.Extensions" Version="1.7.4" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" /> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Settings\VDownload.Services.Data.Settings.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/Models/ChunkDownloadingError.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/Models/ChunkDownloadingError.cs new file mode 100644 index 0000000..ec6ed6b --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/Models/ChunkDownloadingError.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Settings.Models +{ + public class ChunkDownloadingError + { + [JsonProperty("error_retry")] + public bool Retry { get; set; } = true; + + [JsonProperty("retries_count")] + public int RetriesCount { get; set; } = 10; + + [JsonProperty("retry_delay")] + public int RetryDelay { get; set; } = 5000; + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/Models/Vod.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/Models/Vod.cs new file mode 100644 index 0000000..31e7a29 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/Models/Vod.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Sources.Twitch.Settings.Models +{ + public class Vod + { + [JsonProperty("passive_trimming")] + public bool PassiveTrimming { get; set; } = true; + + [JsonProperty("chunk_downloading_error")] + public ChunkDownloadingError ChunkDownloadingError { get; set; } = new ChunkDownloadingError(); + + [JsonProperty("max_number_of_parallel_downloads")] + public int MaxNumberOfParallelDownloads { get; set; } = 100; + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/TwitchSettings.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/TwitchSettings.cs new file mode 100644 index 0000000..6133be4 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/TwitchSettings.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Sources.Twitch.Settings.Models; + +namespace VDownload.Sources.Twitch.Settings +{ + public class TwitchSettings + { + [JsonProperty("vod")] + public Vod Vod { get; set; } = new Vod(); + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/VDownload.Sources.Twitch.Settings.csproj b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/VDownload.Sources.Twitch.Settings.csproj new file mode 100644 index 0000000..3b5b170 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch.Settings/VDownload.Sources.Twitch.Settings.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchSearchService.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchSearchService.cs new file mode 100644 index 0000000..76deef7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchSearchService.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Sources.Common; +using VDownload.Sources.Twitch.Api; +using VDownload.Sources.Twitch.Api.GQL.GetClipToken.Response; +using VDownload.Sources.Twitch.Api.GQL.GetVideoToken.Response; +using VDownload.Sources.Twitch.Api.Helix.GetClips.Response; +using VDownload.Sources.Twitch.Api.Helix.GetUsers.Response; +using VDownload.Sources.Twitch.Api.Helix.GetVideos.Response; +using VDownload.Sources.Twitch.Authentication; +using VDownload.Sources.Twitch.Configuration.Models; +using VDownload.Sources.Twitch.Models; + +namespace VDownload.Sources.Twitch +{ + public interface ITwitchSearchService : ISourceSearchService + { + } + + + + public class TwitchSearchService : SourceSearchService, ITwitchSearchService + { + #region SERVICES + + protected readonly IConfigurationService _configurationService; + protected readonly ITwitchApiService _apiService; + protected readonly ITwitchAuthenticationService _twitchAuthenticationService; + protected readonly ITwitchVideoStreamFactoryService _videoStreamFactoryService; + + #endregion + + + + #region CONSTRUCTORS + + public TwitchSearchService(IConfigurationService configurationService, ITwitchApiService apiService, ITwitchAuthenticationService authenticationService, ITwitchVideoStreamFactoryService videoStreamFactoryService) + { + _configurationService = configurationService; + _apiService = apiService; + _twitchAuthenticationService = authenticationService; + _videoStreamFactoryService = videoStreamFactoryService; + } + + #endregion + + + + #region PRIVATE METHODS + + protected override IEnumerable<SearchRegexVideo> GetVideoRegexes() + { + return [ + .._configurationService.Twitch.Search.Vod.Regexes.Select(x => new SearchRegexVideo + { + Regex = new Regex(x), + SearchFunction = async (id) => await GetVod(id) + }), + .._configurationService.Twitch.Search.Clip.Regexes.Select(x => new SearchRegexVideo + { + Regex = new Regex(x), + SearchFunction = async (id) => await GetClip(id) + }), + ]; + } + + protected override IEnumerable<SearchRegexPlaylist> GetPlaylistRegexes() + { + return [ + .. _configurationService.Twitch.Search.Channel.Regexes.Select(x => new SearchRegexPlaylist + { + Regex = new Regex(x), + SearchFunction = async (id, maxVideoCount) => await GetChannel(id, maxVideoCount) + }), + ]; + } + + protected async Task<TwitchVod> GetVod(string id) + { + byte[] token = await GetToken(); + + GetVideosResponse info = await _apiService.HelixGetVideo(id, token); + + if (info.Data is null) + { + throw CreateExceptionVodNotFound(); + } + + Api.Helix.GetVideos.Response.Data vodResponse = info.Data[0]; + + TwitchVod vod = await ParseVod(vodResponse); + + return vod; + } + + protected async Task<TwitchClip> GetClip(string id) + { + byte[] token = await GetToken(); + + GetClipsResponse info = await _apiService.HelixGetClip(id, token); + + if (info.Data.Count == 0) + { + throw CreateExceptionClipNotFound(); + } + + Api.Helix.GetClips.Response.Data clipResponse = info.Data[0]; + + TwitchClip clip = await ParseClip(clipResponse); + + return clip; + } + + protected async Task<TwitchChannel> GetChannel(string id, int count) + { + byte[] token = await GetToken(); + + Api.Helix.GetUsers.Response.Data userResponse; + try + { + GetUsersResponse info = await _apiService.HelixGetUser(id, token); + if (info.Data.Count <= 0) + { + throw CreateExceptionChannelNotFound(); + } + userResponse = info.Data[0]; + } + catch (InvalidOperationException ex) + { + // TODO: Add logging + throw; + } + + TwitchChannel channel = new TwitchChannel + { + Id = userResponse.Id, + Name = userResponse.DisplayName, + Description = userResponse.Description, + Url = new Uri(string.Format(_configurationService.Twitch.Search.Channel.Url, id)), + }; + + List<Task<TwitchVod>> tasks = new List<Task<TwitchVod>>(); + string? cursor = null; + List<Api.Helix.GetVideos.Response.Data> videosList; + count = count == 0 ? int.MaxValue : count; + int videos = 0; + do + { + videos = count > 100 ? 100 : count; + GetVideosResponse videosResponse = await _apiService.HelixGetUserVideos(channel.Id, token, videos, cursor); + + if (!tasks.Any() && !videosResponse.Data.Any()) + { + throw CreateExceptionEmptyPlaylist(); + } + + videosList = videosResponse.Data; + cursor = videosResponse.Pagination.Cursor; + tasks.AddRange(videosList.Select(ParseVod)); + } + while (tasks.Count < count && videosList.Count == videos); + + await Task.WhenAll(tasks); + + channel.AddRange(tasks.Select(x => x.Result)); + + return channel; + } + + protected async Task<TwitchVod> ParseVod(Api.Helix.GetVideos.Response.Data data) + { + Task<IEnumerable<TwitchVodStream>> streamsTask = GetVodStreams(data.Id); + + Thumbnail thumbnailConfig = _configurationService.Twitch.Search.Vod.Thumbnail; + Regex liveThumbnailRegex = new Regex(_configurationService.Twitch.Search.Vod.LiveThumbnailUrlRegex); + Uri? thumbnail = null; + if (!liveThumbnailRegex.IsMatch(data.ThumbnailUrl)) + { + thumbnail = new Uri(data.ThumbnailUrl.Replace("%{width}", thumbnailConfig.Width.ToString()).Replace("%{height}", thumbnailConfig.Height.ToString())); + } + TwitchVod vod = new TwitchVod + { + Id = data.Id, + Title = data.Title, + Description = data.Description, + Author = data.UserName, + PublishDate = data.PublishedAt, + Duration = ParseVodDuration(data.Duration), + Views = data.ViewCount, + ThumbnailUrl = thumbnail, + Url = new Uri(data.Url), + }; + + await streamsTask; + foreach (TwitchVodStream stream in streamsTask.Result) + { + vod.Streams.Add(stream); + } + + return vod; + } + + protected async Task<TwitchClip> ParseClip(Api.Helix.GetClips.Response.Data data) + { + Task<IEnumerable<TwitchClipStream>> streamsTask = GetClipStreams(data.Id); + + TwitchClip clip = new TwitchClip + { + Id = data.Id, + Title = data.Title, + Author = data.BroadcasterName, + Creator = data.CreatorName, + PublishDate = data.CreatedAt, + Duration = TimeSpan.FromSeconds(Math.Round(data.Duration)), + Views = data.ViewCount, + ThumbnailUrl = new Uri(data.ThumbnailUrl), + Url = new Uri(data.Url), + }; + + await streamsTask; + foreach (TwitchClipStream stream in streamsTask.Result) + { + clip.Streams.Add(stream); + } + + return clip; + } + + protected async Task<IEnumerable<TwitchVodStream>> GetVodStreams(string id) + { + GetVideoTokenResponse videoToken = await _apiService.GQLGetVideoToken(id); + + string playlist = await _apiService.UsherGetVideoPlaylist(id, videoToken.Data.VideoPlaybackAccessToken.Value, videoToken.Data.VideoPlaybackAccessToken.Signature); + + Regex regex = new Regex(_configurationService.Twitch.Search.Vod.StreamPlaylistRegex); + MatchCollection matches = regex.Matches(playlist); + + List<TwitchVodStream> streams = new List<TwitchVodStream>(); + foreach (Match match in matches) + { + TwitchVodStream stream = _videoStreamFactoryService.CreateVodStream(); + stream.Name = match.Groups["id"].Value; + stream.VideoCodec = match.Groups["video_codec"].Value; + stream.AudioCodec = match.Groups["audio_codec"].Value; + stream.Width = int.Parse(match.Groups["width"].Value); + stream.Height = int.Parse(match.Groups["height"].Value); + stream.UrlM3U8 = match.Groups["url"].Value; + streams.Add(stream); + } + return streams; + } + + protected async Task<IEnumerable<TwitchClipStream>> GetClipStreams(string id) + { + GetClipTokenResponse clipToken = await _apiService.GQLGetClipToken(id); + + List<TwitchClipStream> streams = new List<TwitchClipStream>(); + foreach (GetClipTokenVideoQuality streamData in clipToken.Data.Clip.VideoQualities) + { + TwitchClipStream stream = _videoStreamFactoryService.CreateClipStream(); + stream.Name = $"{streamData.Quality}p{Math.Round(streamData.FrameRate)}"; + stream.Height = int.Parse(streamData.Quality); + stream.FrameRate = streamData.FrameRate; + stream.Url = new Uri(streamData.SourceURL); + stream.Signature = clipToken.Data.Clip.PlaybackAccessToken.Signature; + stream.Token = clipToken.Data.Clip.PlaybackAccessToken.Value; + streams.Add(stream); + } + return streams; + } + + protected TimeSpan ParseVodDuration(string duration) + { + IEnumerable<string> parts = duration.Split(['h', 'm', 's'])[..^1].Reverse(); + string? seconds = parts.ElementAtOrDefault(0); + string? minutes = parts.ElementAtOrDefault(1); + string? hours = parts.ElementAtOrDefault(2); + + TimeSpan timeSpan = TimeSpan.Zero; + if (!string.IsNullOrEmpty(seconds)) + { + int secondsInt = int.Parse(seconds); + timeSpan += TimeSpan.FromSeconds(secondsInt); + } + if (!string.IsNullOrEmpty(minutes)) + { + int minutesInt = int.Parse(minutes); + timeSpan += TimeSpan.FromMinutes(minutesInt); + } + if (!string.IsNullOrEmpty(hours)) + { + int hoursInt = int.Parse(hours); + timeSpan += TimeSpan.FromHours(hoursInt); + } + return timeSpan; + } + + protected async Task<byte[]> GetToken() + { + byte[]? token = await _twitchAuthenticationService.GetToken() ?? throw CreateExceptionNotAuthenticated(); + + TwitchValidationResult validation = await _twitchAuthenticationService.ValidateToken(token); + if (!validation.Success) + { + throw CreateExceptionTokenValidationUnsuccessful(); + } + return token; + } + + protected MediaSearchException CreateExceptionNotAuthenticated() => new MediaSearchException("TwitchNotAuthenticated"); + protected MediaSearchException CreateExceptionTokenValidationUnsuccessful() => new MediaSearchException("TwitchTokenValidationUnsuccessful"); + protected MediaSearchException CreateExceptionChannelNotFound() => new MediaSearchException("TwitchChannelNotFound"); + protected MediaSearchException CreateExceptionVodNotFound() => new MediaSearchException("TwitchVodNotFound"); + protected MediaSearchException CreateExceptionClipNotFound() => new MediaSearchException("TwitchClipNotFound"); + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchVideoStreamFactoryService.cs b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchVideoStreamFactoryService.cs new file mode 100644 index 0000000..a5821e7 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/TwitchVideoStreamFactoryService.cs @@ -0,0 +1,49 @@ +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Settings; +using VDownload.Sources.Twitch.Models; + +namespace VDownload.Sources.Twitch +{ + public interface ITwitchVideoStreamFactoryService + { + TwitchVodStream CreateVodStream(); + TwitchClipStream CreateClipStream(); + } + + + + public class TwitchVideoStreamFactoryService : ITwitchVideoStreamFactoryService + { + #region SERVICES + + protected readonly HttpClient _httpClient; + + protected readonly IConfigurationService _configurationService; + protected readonly ISettingsService _settingsService; + + #endregion + + + + #region CONSTRUCTORS + + public TwitchVideoStreamFactoryService(HttpClient httpClient, IConfigurationService configurationService, ISettingsService settingsService) + { + _httpClient = httpClient; + _configurationService = configurationService; + _settingsService = settingsService; + } + + #endregion + + + + #region PUBLIC METHODS + + public TwitchVodStream CreateVodStream() => new TwitchVodStream(_httpClient, _configurationService, _settingsService); + + public TwitchClipStream CreateClipStream() => new TwitchClipStream(_httpClient, _configurationService, _settingsService); + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/VDownload.Sources.Twitch.csproj b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/VDownload.Sources.Twitch.csproj new file mode 100644 index 0000000..04613bf --- /dev/null +++ b/VDownload.Sources/VDownload.Sources.Twitch/VDownload.Sources.Twitch/VDownload.Sources.Twitch.csproj @@ -0,0 +1,16 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + <ProjectReference Include="..\..\VDownload.Sources.Common\VDownload.Sources.Common.csproj" /> + <ProjectReference Include="..\VDownload.Sources.Twitch.Authentication\VDownload.Sources.Twitch.Authentication.csproj" /> + <ProjectReference Include="..\VDownload.Sources.Twitch.Models\VDownload.Sources.Twitch.Models.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.Sources/VDownload.Sources/SearchService.cs b/VDownload.Sources/VDownload.Sources/SearchService.cs new file mode 100644 index 0000000..e96165f --- /dev/null +++ b/VDownload.Sources/VDownload.Sources/SearchService.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using VDownload.Models; +using VDownload.Services.Data.Configuration; +using VDownload.Sources.Common; +using VDownload.Sources.Twitch; +using VDownload.Sources.Twitch.Configuration; +using VDownload.Sources.Twitch.Search; + +namespace VDownload.Sources +{ + public interface ISearchService + { + Task<Playlist> SearchPlaylist(string url, int maxVideoCount); + Task<Video> SearchVideo(string url); + } + + + + public class SearchService : ISearchService + { + #region FIELDS + + private readonly List<(Regex Regex, ISourceSearchService Service)> _urlMappings; + + #endregion + + + + #region CONSTRUCTORS + + public SearchService(IConfigurationService configurationService, ITwitchSearchService twitchSearchService) + { + _urlMappings = + [ + .. configurationService.Twitch.Search.GeneralRegexes.Select(x => (new Regex(x), twitchSearchService)), + ]; + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task<Video> SearchVideo(string url) + { + BaseUrlCheck(url); + + foreach ((Regex Regex, ISourceSearchService Service) mapping in _urlMappings) + { + if (mapping.Regex.IsMatch(url)) + { + return await mapping.Service.SearchVideo(url); + } + } + + throw CreateExceptionSourceNotSupported(); + } + + public async Task<Playlist> SearchPlaylist(string url, int maxVideoCount) + { + BaseUrlCheck(url); + + foreach ((Regex Regex, ISourceSearchService Service) mapping in _urlMappings) + { + if (mapping.Regex.IsMatch(url)) + { + return await mapping.Service.SearchPlaylist(url, maxVideoCount); + } + } + + throw CreateExceptionSourceNotSupported(); + } + + #endregion + + + + #region PRIVATE METHODS + + protected void BaseUrlCheck(string url) + { + if (string.IsNullOrWhiteSpace(url)) + { + throw CreateExceptionEmptyUrl(); + } + } + + protected MediaSearchException CreateExceptionSourceNotSupported() => new MediaSearchException("SourceNotSupported"); + protected MediaSearchException CreateExceptionEmptyUrl() => new MediaSearchException("EmptyUrl"); + + #endregion + } +} diff --git a/VDownload.Sources/VDownload.Sources/VDownload.Sources.csproj b/VDownload.Sources/VDownload.Sources/VDownload.Sources.csproj new file mode 100644 index 0000000..c27e6e9 --- /dev/null +++ b/VDownload.Sources/VDownload.Sources/VDownload.Sources.csproj @@ -0,0 +1,16 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + <ProjectReference Include="..\..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Subscriptions\VDownload.Services.Data.Subscriptions.csproj" /> + <ProjectReference Include="..\VDownload.Sources.Common\VDownload.Sources.Common.csproj" /> + <ProjectReference Include="..\VDownload.Sources.Twitch\VDownload.Sources.Twitch\VDownload.Sources.Twitch.csproj" /> + </ItemGroup> + +</Project> diff --git a/VDownload.sln b/VDownload.sln index 152e090..0261eef 100644 --- a/VDownload.sln +++ b/VDownload.sln @@ -1,86 +1,600 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VDownload", "VDownload\VDownload.csproj", "{324AB81A-F68D-424D-B90E-5402F5E44240}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload", "VDownload\VDownload.csproj", "{62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A087406C-5459-423E-BB0B-84BC93E5B38F}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VDownload.Sources", "VDownload.Sources", "{9A8EB967-1EAB-439E-8FDB-B9ACAC6499EC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VDownload.Core", "VDownload.Core\VDownload.Core.csproj", "{65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VDownload.Sources.Twitch", "VDownload.Sources.Twitch", "{8D94F264-4EE9-4C24-AB77-2CCA36858309}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VDownload.Services", "VDownload.Services", "{8D351DB0-74E6-4C1E-A123-34D6BBBD5585}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VDownload.Services.UI", "VDownload.Services.UI", "{4CC2DC7D-27D2-4F52-89DF-7D45B41BB40F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VDownload.Services.Data", "VDownload.Services.Data", "{05A45688-7EEF-4656-818A-2477625C3707}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VDownload.Services.Utility", "VDownload.Services.Utility", "{1020167A-4101-496E-82CF-41B65769DD28}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Data.Authentication", "VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Authentication\VDownload.Services.Data.Authentication.csproj", "{FB21E094-B723-4E7F-B7CB-434BF19B66E1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Data.Configuration", "VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj", "{A8292FE6-F29C-46EA-B9C4-788CF8C38C91}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Data.Settings", "VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Settings\VDownload.Services.Data.Settings.csproj", "{064C9E03-87C2-4C3C-B228-5C1EE93C719F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.UI.Notifications", "VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.Notifications\VDownload.Services.UI.Notifications.csproj", "{A561A492-6BA3-42B1-9881-14D844C499FB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.UI.Dialogs", "VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.Dialogs\VDownload.Services.UI.Dialogs.csproj", "{755DC010-214D-4975-8AAA-6B48CF367935}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.UI.StoragePicker", "VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.StoragePicker\VDownload.Services.UI.StoragePicker.csproj", "{F9ACBE26-EDB6-4E29-8511-DDAE48534D87}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.UI.WebView", "VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.WebView\VDownload.Services.UI.WebView.csproj", "{B9307732-16BB-4289-9D92-E69FEF36C052}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Utility.HttpClient", "VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.HttpClient\VDownload.Services.Utility.HttpClient.csproj", "{B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Utility.Encryption", "VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.Encryption\VDownload.Services.Utility.Encryption.csproj", "{B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources.Twitch", "VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch\VDownload.Sources.Twitch.csproj", "{9C76F18C-0689-4CE1-A426-EFCBC606D606}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources.Twitch.Configuration", "VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Configuration\VDownload.Sources.Twitch.Configuration.csproj", "{25637E29-4A69-4E36-B99B-33454506AD14}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources.Twitch.Authentication", "VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Authentication\VDownload.Sources.Twitch.Authentication.csproj", "{86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources.Twitch.Api", "VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Api\VDownload.Sources.Twitch.Api.csproj", "{5FA027FC-9973-4331-85F8-A3893823E73C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources.Twitch.Models", "VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Models\VDownload.Sources.Twitch.Models.csproj", "{711FA7BE-27DA-425D-9777-967F6E519CFF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources.Common", "VDownload.Sources\VDownload.Sources.Common\VDownload.Sources.Common.csproj", "{8E850249-E79D-40E7-B02D-393632DF8B8E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources", "VDownload.Sources\VDownload.Sources\VDownload.Sources.csproj", "{64A217AE-4E95-468E-85C3-67C27D689FF4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VDownload.Core", "VDownload.Core", "{8539067C-9968-4AEB-928C-FEDC43989A79}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Models", "VDownload.Models\VDownload.Models.csproj", "{16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.UI.DictionaryResources", "VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.DictionaryResources\VDownload.Services.UI.DictionaryResources.csproj", "{8DC55331-B9F3-4811-8474-348662963260}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Sources.Twitch.Settings", "VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Settings\VDownload.Sources.Twitch.Settings.csproj", "{A726FD43-B111-423B-BAF6-D65B4C0E37B5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Core.Strings", "VDownload.Core\VDownload.Core.Strings\VDownload.Core.Strings.csproj", "{E1D4352C-51AC-4572-8515-0B4E89A2442F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Core.ViewModels", "VDownload.Core\VDownload.Core.ViewModels\VDownload.Core.ViewModels.csproj", "{54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Core.Views", "VDownload.Core\VDownload.Core.Views\VDownload.Core.Views.csproj", "{E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Core.Tasks", "VDownload.Core\VDownload.Core.Tasks\VDownload.Core.Tasks.csproj", "{3BE998A3-1126-4496-BF60-80D0CEA4D24F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Utility.FFmpeg", "VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.FFmpeg\VDownload.Services.Utility.FFmpeg.csproj", "{A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Utility.Filename", "VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.Filename\VDownload.Services.Utility.Filename.csproj", "{4647EFB5-A206-4F47-976D-BAED11B52579}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Data.Application", "VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Application\VDownload.Services.Data.Application.csproj", "{4983E15B-3730-4646-A2BD-16B9ECC9E4FF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Data.Subscriptions", "VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Subscriptions\VDownload.Services.Data.Subscriptions.csproj", "{3193DABC-87F8-4256-9449-3CF42FEF7098}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VDownload.Services.Common", "VDownload.Services\VDownload.Services.Common\VDownload.Services.Common.csproj", "{267F5A31-1257-4820-9FE5-C11D26CC3C55}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|Any CPU.Build.0 = Debug|Any CPU - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|ARM.ActiveCfg = Debug|ARM - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|ARM.Build.0 = Debug|ARM - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|ARM.Deploy.0 = Debug|ARM - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|ARM64.Build.0 = Debug|ARM64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|ARM64.Deploy.0 = Debug|ARM64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|x64.ActiveCfg = Debug|x64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|x64.Build.0 = Debug|x64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|x64.Deploy.0 = Debug|x64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|x86.ActiveCfg = Debug|x86 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|x86.Build.0 = Debug|x86 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Debug|x86.Deploy.0 = Debug|x86 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|Any CPU.ActiveCfg = Release|Any CPU - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|Any CPU.Build.0 = Release|Any CPU - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|Any CPU.Deploy.0 = Release|Any CPU - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|ARM.ActiveCfg = Release|ARM - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|ARM.Build.0 = Release|ARM - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|ARM.Deploy.0 = Release|ARM - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|ARM64.ActiveCfg = Release|ARM64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|ARM64.Build.0 = Release|ARM64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|ARM64.Deploy.0 = Release|ARM64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|x64.ActiveCfg = Release|x64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|x64.Build.0 = Release|x64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|x64.Deploy.0 = Release|x64 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|x86.ActiveCfg = Release|x86 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|x86.Build.0 = Release|x86 - {324AB81A-F68D-424D-B90E-5402F5E44240}.Release|x86.Deploy.0 = Release|x86 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|ARM.ActiveCfg = Debug|ARM - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|ARM.Build.0 = Debug|ARM - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|ARM64.Build.0 = Debug|ARM64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|x64.ActiveCfg = Debug|x64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|x64.Build.0 = Debug|x64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|x86.ActiveCfg = Debug|x86 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Debug|x86.Build.0 = Debug|x86 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|Any CPU.Build.0 = Release|Any CPU - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|ARM.ActiveCfg = Release|ARM - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|ARM.Build.0 = Release|ARM - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|ARM64.ActiveCfg = Release|ARM64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|ARM64.Build.0 = Release|ARM64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|x64.ActiveCfg = Release|x64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|x64.Build.0 = Release|x64 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|x86.ActiveCfg = Release|x86 - {65C44D96-9C6C-47AD-A1F5-86BFAF2B15B8}.Release|x86.Build.0 = Release|x86 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|Any CPU.ActiveCfg = Debug|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|Any CPU.Build.0 = Debug|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|ARM64.Build.0 = Debug|ARM64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|x64.ActiveCfg = Debug|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|x64.Build.0 = Debug|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|x64.Deploy.0 = Debug|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|x86.ActiveCfg = Debug|x86 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|x86.Build.0 = Debug|x86 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Debug|x86.Deploy.0 = Debug|x86 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|Any CPU.ActiveCfg = Release|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|Any CPU.Build.0 = Release|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|ARM64.ActiveCfg = Release|ARM64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|ARM64.Build.0 = Release|ARM64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|ARM64.Deploy.0 = Release|ARM64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|x64.ActiveCfg = Release|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|x64.Build.0 = Release|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|x64.Deploy.0 = Release|x64 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|x86.ActiveCfg = Release|x86 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|x86.Build.0 = Release|x86 + {62A8EAD3-6D6A-40FC-9E1E-C57C02E27244}.Release|x86.Deploy.0 = Release|x86 + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|ARM64.Build.0 = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|x64.ActiveCfg = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|x64.Build.0 = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|x86.ActiveCfg = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Debug|x86.Build.0 = Debug|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|Any CPU.Build.0 = Release|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|ARM64.ActiveCfg = Release|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|ARM64.Build.0 = Release|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|x64.ActiveCfg = Release|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|x64.Build.0 = Release|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|x86.ActiveCfg = Release|Any CPU + {FB21E094-B723-4E7F-B7CB-434BF19B66E1}.Release|x86.Build.0 = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|ARM64.Build.0 = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|x64.Build.0 = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Debug|x86.Build.0 = Debug|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|Any CPU.Build.0 = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|ARM64.ActiveCfg = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|ARM64.Build.0 = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|x64.ActiveCfg = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|x64.Build.0 = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|x86.ActiveCfg = Release|Any CPU + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91}.Release|x86.Build.0 = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|ARM64.Build.0 = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|x64.ActiveCfg = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|x64.Build.0 = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|x86.ActiveCfg = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Debug|x86.Build.0 = Debug|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|Any CPU.Build.0 = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|ARM64.ActiveCfg = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|ARM64.Build.0 = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|x64.ActiveCfg = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|x64.Build.0 = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|x86.ActiveCfg = Release|Any CPU + {064C9E03-87C2-4C3C-B228-5C1EE93C719F}.Release|x86.Build.0 = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|ARM64.Build.0 = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|x64.ActiveCfg = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|x64.Build.0 = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|x86.ActiveCfg = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Debug|x86.Build.0 = Debug|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|Any CPU.Build.0 = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|ARM64.ActiveCfg = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|ARM64.Build.0 = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|x64.ActiveCfg = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|x64.Build.0 = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|x86.ActiveCfg = Release|Any CPU + {A561A492-6BA3-42B1-9881-14D844C499FB}.Release|x86.Build.0 = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|Any CPU.Build.0 = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|ARM64.Build.0 = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|x64.ActiveCfg = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|x64.Build.0 = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|x86.ActiveCfg = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Debug|x86.Build.0 = Debug|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|Any CPU.ActiveCfg = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|Any CPU.Build.0 = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|ARM64.ActiveCfg = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|ARM64.Build.0 = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|x64.ActiveCfg = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|x64.Build.0 = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|x86.ActiveCfg = Release|Any CPU + {755DC010-214D-4975-8AAA-6B48CF367935}.Release|x86.Build.0 = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|ARM64.Build.0 = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|x64.ActiveCfg = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|x64.Build.0 = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|x86.ActiveCfg = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Debug|x86.Build.0 = Debug|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|Any CPU.Build.0 = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|ARM64.ActiveCfg = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|ARM64.Build.0 = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|x64.ActiveCfg = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|x64.Build.0 = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|x86.ActiveCfg = Release|Any CPU + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87}.Release|x86.Build.0 = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|ARM64.Build.0 = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|x64.Build.0 = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Debug|x86.Build.0 = Debug|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|Any CPU.Build.0 = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|ARM64.ActiveCfg = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|ARM64.Build.0 = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|x64.ActiveCfg = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|x64.Build.0 = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|x86.ActiveCfg = Release|Any CPU + {B9307732-16BB-4289-9D92-E69FEF36C052}.Release|x86.Build.0 = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|ARM64.Build.0 = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|x64.ActiveCfg = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|x64.Build.0 = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|x86.ActiveCfg = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Debug|x86.Build.0 = Debug|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|Any CPU.Build.0 = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|ARM64.ActiveCfg = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|ARM64.Build.0 = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|x64.ActiveCfg = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|x64.Build.0 = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|x86.ActiveCfg = Release|Any CPU + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615}.Release|x86.Build.0 = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|ARM64.Build.0 = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|x64.Build.0 = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Debug|x86.Build.0 = Debug|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|Any CPU.Build.0 = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|ARM64.ActiveCfg = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|ARM64.Build.0 = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|x64.ActiveCfg = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|x64.Build.0 = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|x86.ActiveCfg = Release|Any CPU + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0}.Release|x86.Build.0 = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|ARM64.Build.0 = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|x64.ActiveCfg = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|x64.Build.0 = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|x86.ActiveCfg = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Debug|x86.Build.0 = Debug|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|Any CPU.Build.0 = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|ARM64.ActiveCfg = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|ARM64.Build.0 = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|x64.ActiveCfg = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|x64.Build.0 = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|x86.ActiveCfg = Release|Any CPU + {9C76F18C-0689-4CE1-A426-EFCBC606D606}.Release|x86.Build.0 = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|ARM64.Build.0 = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|x64.ActiveCfg = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|x64.Build.0 = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|x86.ActiveCfg = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Debug|x86.Build.0 = Debug|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|Any CPU.Build.0 = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|ARM64.ActiveCfg = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|ARM64.Build.0 = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|x64.ActiveCfg = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|x64.Build.0 = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|x86.ActiveCfg = Release|Any CPU + {25637E29-4A69-4E36-B99B-33454506AD14}.Release|x86.Build.0 = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|ARM64.Build.0 = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|x64.ActiveCfg = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|x64.Build.0 = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|x86.ActiveCfg = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Debug|x86.Build.0 = Debug|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|Any CPU.Build.0 = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|ARM64.ActiveCfg = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|ARM64.Build.0 = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|x64.ActiveCfg = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|x64.Build.0 = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|x86.ActiveCfg = Release|Any CPU + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C}.Release|x86.Build.0 = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|ARM64.Build.0 = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|x64.ActiveCfg = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|x64.Build.0 = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|x86.ActiveCfg = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Debug|x86.Build.0 = Debug|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|Any CPU.Build.0 = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|ARM64.ActiveCfg = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|ARM64.Build.0 = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|x64.ActiveCfg = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|x64.Build.0 = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|x86.ActiveCfg = Release|Any CPU + {5FA027FC-9973-4331-85F8-A3893823E73C}.Release|x86.Build.0 = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|ARM64.Build.0 = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|x64.ActiveCfg = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|x64.Build.0 = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|x86.ActiveCfg = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Debug|x86.Build.0 = Debug|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|Any CPU.Build.0 = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|ARM64.ActiveCfg = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|ARM64.Build.0 = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|x64.ActiveCfg = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|x64.Build.0 = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|x86.ActiveCfg = Release|Any CPU + {711FA7BE-27DA-425D-9777-967F6E519CFF}.Release|x86.Build.0 = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|ARM64.Build.0 = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|x64.Build.0 = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|x86.ActiveCfg = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Debug|x86.Build.0 = Debug|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|Any CPU.Build.0 = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|ARM64.ActiveCfg = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|ARM64.Build.0 = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|x64.ActiveCfg = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|x64.Build.0 = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|x86.ActiveCfg = Release|Any CPU + {8E850249-E79D-40E7-B02D-393632DF8B8E}.Release|x86.Build.0 = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|ARM64.Build.0 = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|x64.Build.0 = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Debug|x86.Build.0 = Debug|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|Any CPU.Build.0 = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|ARM64.ActiveCfg = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|ARM64.Build.0 = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|x64.ActiveCfg = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|x64.Build.0 = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|x86.ActiveCfg = Release|Any CPU + {64A217AE-4E95-468E-85C3-67C27D689FF4}.Release|x86.Build.0 = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|ARM64.Build.0 = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|x64.Build.0 = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|x86.ActiveCfg = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Debug|x86.Build.0 = Debug|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|Any CPU.Build.0 = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|ARM64.ActiveCfg = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|ARM64.Build.0 = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|x64.ActiveCfg = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|x64.Build.0 = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|x86.ActiveCfg = Release|Any CPU + {16B56EA9-C218-4A8E-B3DE-29F200EF2EE2}.Release|x86.Build.0 = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|ARM64.Build.0 = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|x64.ActiveCfg = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|x64.Build.0 = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|x86.ActiveCfg = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Debug|x86.Build.0 = Debug|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|Any CPU.Build.0 = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|ARM64.ActiveCfg = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|ARM64.Build.0 = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|x64.ActiveCfg = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|x64.Build.0 = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|x86.ActiveCfg = Release|Any CPU + {8DC55331-B9F3-4811-8474-348662963260}.Release|x86.Build.0 = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|ARM64.Build.0 = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|x64.ActiveCfg = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|x64.Build.0 = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|x86.ActiveCfg = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Debug|x86.Build.0 = Debug|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|Any CPU.Build.0 = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|ARM64.ActiveCfg = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|ARM64.Build.0 = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|x64.ActiveCfg = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|x64.Build.0 = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|x86.ActiveCfg = Release|Any CPU + {A726FD43-B111-423B-BAF6-D65B4C0E37B5}.Release|x86.Build.0 = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|ARM64.Build.0 = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|x64.ActiveCfg = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|x64.Build.0 = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|x86.ActiveCfg = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Debug|x86.Build.0 = Debug|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|Any CPU.Build.0 = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|ARM64.ActiveCfg = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|ARM64.Build.0 = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|x64.ActiveCfg = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|x64.Build.0 = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|x86.ActiveCfg = Release|Any CPU + {E1D4352C-51AC-4572-8515-0B4E89A2442F}.Release|x86.Build.0 = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|ARM64.Build.0 = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|x64.ActiveCfg = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|x64.Build.0 = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|x86.ActiveCfg = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Debug|x86.Build.0 = Debug|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|Any CPU.Build.0 = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|ARM64.ActiveCfg = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|ARM64.Build.0 = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|x64.ActiveCfg = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|x64.Build.0 = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|x86.ActiveCfg = Release|Any CPU + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC}.Release|x86.Build.0 = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|ARM64.Build.0 = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|x64.ActiveCfg = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|x64.Build.0 = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|x86.ActiveCfg = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Debug|x86.Build.0 = Debug|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|Any CPU.Build.0 = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|ARM64.ActiveCfg = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|ARM64.Build.0 = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|x64.ActiveCfg = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|x64.Build.0 = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|x86.ActiveCfg = Release|Any CPU + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80}.Release|x86.Build.0 = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|ARM64.Build.0 = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|x64.ActiveCfg = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|x64.Build.0 = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|x86.ActiveCfg = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Debug|x86.Build.0 = Debug|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|Any CPU.Build.0 = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|ARM64.ActiveCfg = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|ARM64.Build.0 = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|x64.ActiveCfg = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|x64.Build.0 = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|x86.ActiveCfg = Release|Any CPU + {3BE998A3-1126-4496-BF60-80D0CEA4D24F}.Release|x86.Build.0 = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|ARM64.Build.0 = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|x64.ActiveCfg = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|x64.Build.0 = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|x86.ActiveCfg = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Debug|x86.Build.0 = Debug|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|Any CPU.Build.0 = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|ARM64.ActiveCfg = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|ARM64.Build.0 = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|x64.ActiveCfg = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|x64.Build.0 = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|x86.ActiveCfg = Release|Any CPU + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B}.Release|x86.Build.0 = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|ARM64.Build.0 = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|x64.ActiveCfg = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|x64.Build.0 = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|x86.ActiveCfg = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Debug|x86.Build.0 = Debug|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|Any CPU.Build.0 = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|ARM64.ActiveCfg = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|ARM64.Build.0 = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|x64.ActiveCfg = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|x64.Build.0 = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|x86.ActiveCfg = Release|Any CPU + {4647EFB5-A206-4F47-976D-BAED11B52579}.Release|x86.Build.0 = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|ARM64.Build.0 = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|x64.Build.0 = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Debug|x86.Build.0 = Debug|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|Any CPU.Build.0 = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|ARM64.ActiveCfg = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|ARM64.Build.0 = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|x64.ActiveCfg = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|x64.Build.0 = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|x86.ActiveCfg = Release|Any CPU + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF}.Release|x86.Build.0 = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|ARM64.Build.0 = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|x64.ActiveCfg = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|x64.Build.0 = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|x86.ActiveCfg = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Debug|x86.Build.0 = Debug|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|Any CPU.Build.0 = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|ARM64.ActiveCfg = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|ARM64.Build.0 = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|x64.ActiveCfg = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|x64.Build.0 = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|x86.ActiveCfg = Release|Any CPU + {3193DABC-87F8-4256-9449-3CF42FEF7098}.Release|x86.Build.0 = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|ARM64.Build.0 = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|x64.ActiveCfg = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|x64.Build.0 = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|x86.ActiveCfg = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Debug|x86.Build.0 = Debug|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|Any CPU.Build.0 = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|ARM64.ActiveCfg = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|ARM64.Build.0 = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|x64.ActiveCfg = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|x64.Build.0 = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|x86.ActiveCfg = Release|Any CPU + {267F5A31-1257-4820-9FE5-C11D26CC3C55}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {8D94F264-4EE9-4C24-AB77-2CCA36858309} = {9A8EB967-1EAB-439E-8FDB-B9ACAC6499EC} + {4CC2DC7D-27D2-4F52-89DF-7D45B41BB40F} = {8D351DB0-74E6-4C1E-A123-34D6BBBD5585} + {05A45688-7EEF-4656-818A-2477625C3707} = {8D351DB0-74E6-4C1E-A123-34D6BBBD5585} + {1020167A-4101-496E-82CF-41B65769DD28} = {8D351DB0-74E6-4C1E-A123-34D6BBBD5585} + {FB21E094-B723-4E7F-B7CB-434BF19B66E1} = {05A45688-7EEF-4656-818A-2477625C3707} + {A8292FE6-F29C-46EA-B9C4-788CF8C38C91} = {05A45688-7EEF-4656-818A-2477625C3707} + {064C9E03-87C2-4C3C-B228-5C1EE93C719F} = {05A45688-7EEF-4656-818A-2477625C3707} + {A561A492-6BA3-42B1-9881-14D844C499FB} = {4CC2DC7D-27D2-4F52-89DF-7D45B41BB40F} + {755DC010-214D-4975-8AAA-6B48CF367935} = {4CC2DC7D-27D2-4F52-89DF-7D45B41BB40F} + {F9ACBE26-EDB6-4E29-8511-DDAE48534D87} = {4CC2DC7D-27D2-4F52-89DF-7D45B41BB40F} + {B9307732-16BB-4289-9D92-E69FEF36C052} = {4CC2DC7D-27D2-4F52-89DF-7D45B41BB40F} + {B08B1EBC-0BA6-478E-B3F4-C1F9D0983615} = {1020167A-4101-496E-82CF-41B65769DD28} + {B6E5B2A9-3EF5-47A4-BDC6-66173FDCFFC0} = {1020167A-4101-496E-82CF-41B65769DD28} + {9C76F18C-0689-4CE1-A426-EFCBC606D606} = {8D94F264-4EE9-4C24-AB77-2CCA36858309} + {25637E29-4A69-4E36-B99B-33454506AD14} = {8D94F264-4EE9-4C24-AB77-2CCA36858309} + {86707ECB-2DC9-4E1C-96F0-BD3C9C610A3C} = {8D94F264-4EE9-4C24-AB77-2CCA36858309} + {5FA027FC-9973-4331-85F8-A3893823E73C} = {8D94F264-4EE9-4C24-AB77-2CCA36858309} + {711FA7BE-27DA-425D-9777-967F6E519CFF} = {8D94F264-4EE9-4C24-AB77-2CCA36858309} + {8E850249-E79D-40E7-B02D-393632DF8B8E} = {9A8EB967-1EAB-439E-8FDB-B9ACAC6499EC} + {64A217AE-4E95-468E-85C3-67C27D689FF4} = {9A8EB967-1EAB-439E-8FDB-B9ACAC6499EC} + {8DC55331-B9F3-4811-8474-348662963260} = {4CC2DC7D-27D2-4F52-89DF-7D45B41BB40F} + {A726FD43-B111-423B-BAF6-D65B4C0E37B5} = {8D94F264-4EE9-4C24-AB77-2CCA36858309} + {E1D4352C-51AC-4572-8515-0B4E89A2442F} = {8539067C-9968-4AEB-928C-FEDC43989A79} + {54DCFBDC-7A2E-439A-92F5-E5A19A2D0DFC} = {8539067C-9968-4AEB-928C-FEDC43989A79} + {E470FCE2-DB0D-4771-8C9D-43D8AB85FB80} = {8539067C-9968-4AEB-928C-FEDC43989A79} + {3BE998A3-1126-4496-BF60-80D0CEA4D24F} = {8539067C-9968-4AEB-928C-FEDC43989A79} + {A3166F8A-ECAD-4D4B-9BE3-96FEC799B27B} = {1020167A-4101-496E-82CF-41B65769DD28} + {4647EFB5-A206-4F47-976D-BAED11B52579} = {1020167A-4101-496E-82CF-41B65769DD28} + {4983E15B-3730-4646-A2BD-16B9ECC9E4FF} = {05A45688-7EEF-4656-818A-2477625C3707} + {3193DABC-87F8-4256-9449-3CF42FEF7098} = {05A45688-7EEF-4656-818A-2477625C3707} + {267F5A31-1257-4820-9FE5-C11D26CC3C55} = {8D351DB0-74E6-4C1E-A123-34D6BBBD5585} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {878070E9-B074-4931-94AC-8BACD6906448} + SolutionGuid = {9FD7B842-C3E2-4FD0-AD8A-C8E619280AB7} EndGlobalSection EndGlobal diff --git a/VDownload/Activation/ActivationHandler.cs b/VDownload/Activation/ActivationHandler.cs new file mode 100644 index 0000000..1d6ef5f --- /dev/null +++ b/VDownload/Activation/ActivationHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Activation +{ + public interface IActivationHandler + { + bool CanHandle(object args); + Task HandleAsync(object args); + } + + + + public abstract class ActivationHandler<T> : IActivationHandler + where T : class + { + #region PUBLIC METHODS + + public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!); + + public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!); + + #endregion + + + + #region PRIVATE METHODS + + protected virtual bool CanHandleInternal(T args) => true; + + protected abstract Task HandleInternalAsync(T args); + + #endregion + } +} diff --git a/VDownload/Activation/ActivationService.cs b/VDownload/Activation/ActivationService.cs new file mode 100644 index 0000000..1cb9125 --- /dev/null +++ b/VDownload/Activation/ActivationService.cs @@ -0,0 +1,137 @@ +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VDownload.Core.Views; +using VDownload.Services.Common; +using VDownload.Services.Data.Application; +using VDownload.Services.Data.Authentication; +using VDownload.Services.Data.Settings; +using VDownload.Services.Data.Subscriptions; +using VDownload.Services.UI.Dialogs; +using VDownload.Services.UI.DictionaryResources; +using VDownload.Services.UI.Notifications; +using VDownload.Services.UI.StoragePicker; + +namespace VDownload.Activation +{ + public interface IActivationService + { + Task ActivateAsync(object activationArgs); + } + + + + public class ActivationService : IActivationService + { + #region SERVICES + protected readonly ActivationHandler<LaunchActivatedEventArgs> _defaultHandler; + protected readonly IEnumerable<IActivationHandler> _activationHandlers; + + #endregion + + + + #region FIELDS + + protected readonly ICollection<Func<Task>> _beforeActivationInitializations = new List<Func<Task>>(); + protected readonly ICollection<Func<Task>> _afterActivationInitializations = new List<Func<Task>>(); + protected readonly ICollection<Func<Task>> _afterWindowRootLoadedInitializations = new List<Func<Task>>(); + + protected BaseWindow _window; + + #endregion + + + + #region CONSTRUCTORS + + public ActivationService(ActivationHandler<LaunchActivatedEventArgs> defaultHandler, IEnumerable<IActivationHandler> activationHandlers, ISettingsService settingsService, IApplicationDataService applicationDataService, IAuthenticationDataService authenticationDataService, ISubscriptionsDataService subscriptionsDataService, IStoragePickerService storagePickerService, INotificationsService notificationsService, IDictionaryResourcesService dictionaryResourcesService, IDialogsService dialogsService) + { + _defaultHandler = defaultHandler; + _activationHandlers = activationHandlers; + + _beforeActivationInitializations.Add(() => dictionaryResourcesService.Initialize((App.Current as App).Resources)); + + _afterActivationInitializations.Add(settingsService.Initialize); + _afterActivationInitializations.Add(applicationDataService.Initialize); + _afterActivationInitializations.Add(authenticationDataService.Initialize); + _afterActivationInitializations.Add(subscriptionsDataService.Initialize); + _afterActivationInitializations.Add(() => storagePickerService.Initialize(_window)); + _afterActivationInitializations.Add(() => notificationsService.Initialize(_window)); + + _afterWindowRootLoadedInitializations.Add(() => dialogsService.Initialize(_window.XamlRoot)); + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task ActivateAsync(object activationArgs) + { + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", "testactivate\n"); + await InitializeAsync(); + ViewModelToViewConverter.Initialize((App.Current as App)!.Host.Services); + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", "test1\n"); + + await HandleActivationAsync(activationArgs); + + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", "test2\n"); + _window = App.GetService<BaseWindow>(); + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", "test3\n"); + _window.RootLoaded += Window_RootLoaded; + _window.Activate(); + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", "test4\n"); + + await StartupAsync(); + } + + #endregion + + + + #region PRIVATE METHODS + + #region EVENT HANDLERS + + protected async void Window_RootLoaded(object sender, EventArgs e) => await AfterWindowRootLoaded(); + + #endregion + + protected async Task InitializeAsync() + { + List<Task> tasks = new List<Task>(); + foreach (Func<Task> init in _beforeActivationInitializations) + { + tasks.Add(init.Invoke()); + } + await Task.WhenAll(tasks); + } + + protected async Task StartupAsync() => await Task.WhenAll(_afterActivationInitializations.Select(x => x.Invoke())); + + protected async Task AfterWindowRootLoaded() => await Task.WhenAll(_afterWindowRootLoadedInitializations.Select(x => x.Invoke())); + + protected async Task HandleActivationAsync(object activationArgs) + { + var activationHandler = _activationHandlers.FirstOrDefault(h => h.CanHandle(activationArgs)); + + if (activationHandler != null) + { + await activationHandler.HandleAsync(activationArgs); + } + + if (_defaultHandler.CanHandle(activationArgs)) + { + await _defaultHandler.HandleAsync(activationArgs); + } + } + + #endregion + } +} diff --git a/VDownload/Activation/DefaultActivationHandler.cs b/VDownload/Activation/DefaultActivationHandler.cs new file mode 100644 index 0000000..402ec4c --- /dev/null +++ b/VDownload/Activation/DefaultActivationHandler.cs @@ -0,0 +1,21 @@ +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VDownload.Activation +{ + public class DefaultActivationHandler : ActivationHandler<LaunchActivatedEventArgs> + { + #region PRIVATE METHODS + + protected override async Task HandleInternalAsync(LaunchActivatedEventArgs args) + { + await Task.CompletedTask; + } + + #endregion + } +} diff --git a/VDownload/App.xaml b/VDownload/App.xaml index e1d3280..24ae100 100644 --- a/VDownload/App.xaml +++ b/VDownload/App.xaml @@ -1,9 +1,25 @@ -<Application +<?xml version="1.0" encoding="utf-8"?> +<Application x:Class="VDownload.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:VDownload"> <Application.Resources> - <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"/> + <ResourceDictionary Source="Dictionaries/Converters.xaml"/> + <ResourceDictionary Source="Dictionaries/Colors.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesLogo.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesSources.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesOther.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesBaseView.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesHomeView.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesHomeDownloadsView.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesHomeVideoCollectionView.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesHomeVideoView.xaml"/> + <ResourceDictionary Source="Dictionaries/Images/ImagesSettingsView.xaml"/> + </ResourceDictionary.MergedDictionaries> + </ResourceDictionary> </Application.Resources> </Application> diff --git a/VDownload/App.xaml.cs b/VDownload/App.xaml.cs index 539eb27..22ce65d 100644 --- a/VDownload/App.xaml.cs +++ b/VDownload/App.xaml.cs @@ -1,95 +1,219 @@ -// Internal -using VDownload.Core.Services; - -// System +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Toolkit.Uwp.Notifications; +using Microsoft.UI; +using Microsoft.UI.Xaml; +using Microsoft.Windows.AppNotifications; using System; -using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; using System.Threading.Tasks; -using Windows.ApplicationModel; -using Windows.ApplicationModel.Activation; -using Windows.Storage; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Navigation; -using Windows.Storage.AccessCache; +using VDownload.Activation; +using VDownload.Core.Tasks; +using VDownload.Core.ViewModels; +using VDownload.Core.ViewModels.About; +using VDownload.Core.ViewModels.Authentication; +using VDownload.Core.ViewModels.Home; +using VDownload.Core.ViewModels.Settings; +using VDownload.Core.ViewModels.Subscriptions; +using VDownload.Core.Views; +using VDownload.Core.Views.About; +using VDownload.Core.Views.Authentication; +using VDownload.Core.Views.Home; +using VDownload.Core.Views.Settings; +using VDownload.Core.Views.Subscriptions; +using VDownload.Services.Data.Application; +using VDownload.Services.Data.Authentication; +using VDownload.Services.Data.Configuration; +using VDownload.Services.Data.Configuration.Models; +using VDownload.Services.Data.Settings; +using VDownload.Services.Data.Subscriptions; +using VDownload.Services.UI.Dialogs; +using VDownload.Services.UI.DictionaryResources; +using VDownload.Services.UI.Notifications; +using VDownload.Services.UI.StoragePicker; +using VDownload.Services.UI.WebView; +using VDownload.Services.Utility.Encryption; +using VDownload.Services.Utility.FFmpeg; +using VDownload.Services.Utility.Filename; +using VDownload.Services.Utility.HttpClient; +using VDownload.Sources; +using VDownload.Sources.Twitch; +using VDownload.Sources.Twitch.Api; +using VDownload.Sources.Twitch.Authentication; +using Windows.Graphics.Printing; +using Windows.UI.Notifications; namespace VDownload { - sealed partial class App : Application + public partial class App : Application { + #region PROPERTIES + + public static BaseWindow Window { get; protected set; } + + public static T GetService<T>() where T : class + { + if ((App.Current as App)!.Host.Services.GetService(typeof(T)) is not T service) + { + throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs."); + } + + return service; + } + + public IHost Host { get; set; } + + #endregion + + + #region CONSTRUCTORS public App() { - InitializeComponent(); - Suspending += OnSuspending; + this.InitializeComponent(); + + Host = Microsoft.Extensions.Hosting.Host + .CreateDefaultBuilder() + .UseContentRoot(AppContext.BaseDirectory) + .ConfigureAppConfiguration((builder) => + { + builder.Sources.Add(new JsonConfigurationSource + { + Path = "configuration.json" + }); + }) + .ConfigureLogging((builder) => + { + builder.AddConsole(); + }) + .ConfigureServices((context, services) => + { + BuildCore(services); + + BuildDataServices(services); + BuildUIServices(services); + BuildUtilityServices(services); + BuildSourcesServices(services); + + BuildTasksManager(services); + BuildPresentation(services); + BuildActivation(services); + }) + .Build(); + + UnhandledException += UnhandledExceptionCatched; } #endregion - #region EVENT HANDLERS VOIDS + #region PRIVATE METHODS - // ON LAUNCHED - protected override async void OnLaunched(LaunchActivatedEventArgs e) + #region EVENT HANDLERS + + protected override async void OnLaunched(LaunchActivatedEventArgs args) { - // Rebuild configuration file - Config.Rebuild(); + base.OnLaunched(args); - // Delete temp on start - if ((bool)Config.GetValue("delete_temp_on_start")) - { - IReadOnlyList<IStorageItem> tempItems; - if (StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_temp_location")) - tempItems = await (await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_temp_location")).GetItemsAsync(); - else - tempItems = await ApplicationData.Current.TemporaryFolder.GetItemsAsync(); + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", "testlaunched\n"); - List<Task> tasks = new List<Task>(); - foreach (IStorageItem item in tempItems) tasks.Add(item.DeleteAsync().AsTask()); - await Task.WhenAll(tasks); - } - - // Do not repeat app initialization when the Window already has content, - // just ensure that the window is active - if (!(Window.Current.Content is Frame rootFrame)) - { - // Create a Frame to act as the navigation context and navigate to the first page - rootFrame = new Frame(); - rootFrame.NavigationFailed += OnNavigationFailed; - - // Place the frame in the current Window - Window.Current.Content = rootFrame; - } - - if (e.PrelaunchActivated == false) - { - if (rootFrame.Content == null) - { - // When the navigation stack isn't restored navigate to the first page, - // configuring the new page by passing required information as a navigation - // parameter - rootFrame.Navigate(typeof(Views.MainPage), e.Arguments); - } - - // Ensure the current window is active - Window.Current.Activate(); - } + await GetService<IActivationService>().ActivateAsync(args); } - // ON NAVIGATION FAILED - private void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + protected void UnhandledExceptionCatched(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + File.AppendAllText("C:\\Users\\mateusz\\Desktop\\test.txt", $"test {e.Message} {e.Exception.StackTrace}\n"); + Environment.Exit(0); } - // ON SUSPENDING - private void OnSuspending(object sender, SuspendingEventArgs e) + #endregion + + protected void BuildCore(IServiceCollection services) { - var deferral = e.SuspendingOperation.GetDeferral(); - //TODO: Save application state and stop any background activity - deferral.Complete(); + services.AddSingleton<HttpClient>(); + } + + protected void BuildDataServices(IServiceCollection services) + { + services.AddSingleton<IConfigurationService, ConfigurationService>(); + services.AddSingleton<IAuthenticationDataService, AuthenticationDataService>(); + services.AddSingleton<ISettingsService, SettingsService>(); + services.AddSingleton<IApplicationDataService, ApplicationDataService>(); + services.AddSingleton<ISubscriptionsDataService, SubscriptionsDataService>(); + } + + protected void BuildUIServices(IServiceCollection services) + { + services.AddSingleton<IDictionaryResourcesService, DictionaryResourcesService>(); + services.AddSingleton<IWebViewService, WebViewService>(); + services.AddSingleton<IStoragePickerService, StoragePickerService>(); + services.AddSingleton<INotificationsService, NotificationsService>(); + services.AddSingleton<IDialogsService, DialogsService>(); + } + + protected void BuildUtilityServices(IServiceCollection services) + { + services.AddSingleton<IEncryptionService, EncryptionService>(); + services.AddSingleton<IHttpClientService, HttpClientService>(); + services.AddSingleton<IFFmpegService, FFmpegService>(); + services.AddSingleton<IFilenameService, FilenameService>(); + } + + protected void BuildSourcesServices(IServiceCollection services) + { + // Twitch + services.AddSingleton<ITwitchApiService, TwitchApiService>(); + services.AddSingleton<ITwitchAuthenticationService, TwitchAuthenticationService>(); + services.AddSingleton<ITwitchVideoStreamFactoryService, TwitchVideoStreamFactoryService>(); + services.AddSingleton<ITwitchSearchService, TwitchSearchService>(); + + // Base + services.AddSingleton<ISearchService, SearchService>(); + } + + protected void BuildTasksManager(IServiceCollection services) + { + services.AddSingleton<IDownloadTaskFactoryService, DownloadTaskFactoryService>(); + services.AddSingleton<IDownloadTaskManager, DownloadTaskManager>(); + } + + protected void BuildPresentation(IServiceCollection services) + { + // ViewModels + services.AddSingleton<AboutViewModel>(); + services.AddSingleton<AuthenticationViewModel>(); + services.AddSingleton<SettingsViewModel>(); + services.AddSingleton<HomeDownloadsViewModel>(); + services.AddSingleton<HomeVideoViewModel>(); + services.AddSingleton<HomeVideoCollectionViewModel>(); + services.AddSingleton<HomeViewModel>(); + services.AddSingleton<SubscriptionsViewModel>(); + services.AddSingleton<BaseViewModel>(); + + // Views + services.AddTransient<AboutView>(); + services.AddTransient<AuthenticationView>(); + services.AddTransient<SettingsView>(); + services.AddTransient<HomeDownloadsView>(); + services.AddTransient<HomeVideoView>(); + services.AddTransient<HomeVideoCollectionView>(); + services.AddTransient<HomeView>(); + services.AddTransient<SubscriptionsView>(); + services.AddTransient<BaseWindow>(); + } + + protected void BuildActivation(IServiceCollection services) + { + services.AddTransient<ActivationHandler<LaunchActivatedEventArgs>, DefaultActivationHandler>(); + + services.AddSingleton<IActivationService, ActivationService>(); } #endregion diff --git a/VDownload/Assets/BaseView/AboutDark.png b/VDownload/Assets/BaseView/AboutDark.png new file mode 100644 index 0000000..a3e8957 Binary files /dev/null and b/VDownload/Assets/BaseView/AboutDark.png differ diff --git a/VDownload/Assets/BaseView/AboutLight.png b/VDownload/Assets/BaseView/AboutLight.png new file mode 100644 index 0000000..5f5802e Binary files /dev/null and b/VDownload/Assets/BaseView/AboutLight.png differ diff --git a/VDownload/Assets/BaseView/AuthenticationDark.png b/VDownload/Assets/BaseView/AuthenticationDark.png new file mode 100644 index 0000000..1be4287 Binary files /dev/null and b/VDownload/Assets/BaseView/AuthenticationDark.png differ diff --git a/VDownload/Assets/BaseView/AuthenticationLight.png b/VDownload/Assets/BaseView/AuthenticationLight.png new file mode 100644 index 0000000..4d071bc Binary files /dev/null and b/VDownload/Assets/BaseView/AuthenticationLight.png differ diff --git a/VDownload/Assets/BaseView/HomeDark.png b/VDownload/Assets/BaseView/HomeDark.png new file mode 100644 index 0000000..1a88710 Binary files /dev/null and b/VDownload/Assets/BaseView/HomeDark.png differ diff --git a/VDownload/Assets/BaseView/HomeLight.png b/VDownload/Assets/BaseView/HomeLight.png new file mode 100644 index 0000000..b7b0a64 Binary files /dev/null and b/VDownload/Assets/BaseView/HomeLight.png differ diff --git a/VDownload/Assets/BaseView/SubscriptionsDark.png b/VDownload/Assets/BaseView/SubscriptionsDark.png new file mode 100644 index 0000000..1c34f4f Binary files /dev/null and b/VDownload/Assets/BaseView/SubscriptionsDark.png differ diff --git a/VDownload/Assets/BaseView/SubscriptionsLight.png b/VDownload/Assets/BaseView/SubscriptionsLight.png new file mode 100644 index 0000000..9f1e77d Binary files /dev/null and b/VDownload/Assets/BaseView/SubscriptionsLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/CancelledDark.png b/VDownload/Assets/HomeDownloadsView/CancelledDark.png new file mode 100644 index 0000000..7846b3e Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/CancelledDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/CancelledLight.png b/VDownload/Assets/HomeDownloadsView/CancelledLight.png new file mode 100644 index 0000000..c8f8d9f Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/CancelledLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/DoneDark.png b/VDownload/Assets/HomeDownloadsView/DoneDark.png new file mode 100644 index 0000000..e9c6302 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/DoneDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/DoneLight.png b/VDownload/Assets/HomeDownloadsView/DoneLight.png new file mode 100644 index 0000000..2b1fea0 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/DoneLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/DownloadingDark.png b/VDownload/Assets/HomeDownloadsView/DownloadingDark.png new file mode 100644 index 0000000..8ccaab3 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/DownloadingDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/DownloadingLight.png b/VDownload/Assets/HomeDownloadsView/DownloadingLight.png new file mode 100644 index 0000000..e7a1130 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/DownloadingLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/ErrorDark.png b/VDownload/Assets/HomeDownloadsView/ErrorDark.png new file mode 100644 index 0000000..d35ebe1 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/ErrorDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/ErrorLight.png b/VDownload/Assets/HomeDownloadsView/ErrorLight.png new file mode 100644 index 0000000..85e7a4c Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/ErrorLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/FileDark.png b/VDownload/Assets/HomeDownloadsView/FileDark.png new file mode 100644 index 0000000..0ef1391 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/FileDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/FileLight.png b/VDownload/Assets/HomeDownloadsView/FileLight.png new file mode 100644 index 0000000..27e0a2b Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/FileLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/FinalizingDark.png b/VDownload/Assets/HomeDownloadsView/FinalizingDark.png new file mode 100644 index 0000000..dcc56d2 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/FinalizingDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/FinalizingLight.png b/VDownload/Assets/HomeDownloadsView/FinalizingLight.png new file mode 100644 index 0000000..6e2018c Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/FinalizingLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/IdleDark.png b/VDownload/Assets/HomeDownloadsView/IdleDark.png new file mode 100644 index 0000000..2c940d5 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/IdleDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/IdleLight.png b/VDownload/Assets/HomeDownloadsView/IdleLight.png new file mode 100644 index 0000000..7a8b625 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/IdleLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/InitializingDark.png b/VDownload/Assets/HomeDownloadsView/InitializingDark.png new file mode 100644 index 0000000..8c5619c Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/InitializingDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/InitializingLight.png b/VDownload/Assets/HomeDownloadsView/InitializingLight.png new file mode 100644 index 0000000..042b0f9 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/InitializingLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/NoTasks.png b/VDownload/Assets/HomeDownloadsView/NoTasks.png new file mode 100644 index 0000000..095d9ef Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/NoTasks.png differ diff --git a/VDownload/Assets/HomeDownloadsView/ProcessingDark.png b/VDownload/Assets/HomeDownloadsView/ProcessingDark.png new file mode 100644 index 0000000..35fe5ef Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/ProcessingDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/ProcessingLight.png b/VDownload/Assets/HomeDownloadsView/ProcessingLight.png new file mode 100644 index 0000000..8c5cc38 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/ProcessingLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/QualityDark.png b/VDownload/Assets/HomeDownloadsView/QualityDark.png new file mode 100644 index 0000000..71fe363 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/QualityDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/QualityLight.png b/VDownload/Assets/HomeDownloadsView/QualityLight.png new file mode 100644 index 0000000..685ac0d Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/QualityLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/QueuedDark.png b/VDownload/Assets/HomeDownloadsView/QueuedDark.png new file mode 100644 index 0000000..a1b6bb7 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/QueuedDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/QueuedLight.png b/VDownload/Assets/HomeDownloadsView/QueuedLight.png new file mode 100644 index 0000000..be30883 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/QueuedLight.png differ diff --git a/VDownload/Assets/HomeDownloadsView/TimeDark.png b/VDownload/Assets/HomeDownloadsView/TimeDark.png new file mode 100644 index 0000000..a1b6bb7 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/TimeDark.png differ diff --git a/VDownload/Assets/HomeDownloadsView/TimeLight.png b/VDownload/Assets/HomeDownloadsView/TimeLight.png new file mode 100644 index 0000000..be30883 Binary files /dev/null and b/VDownload/Assets/HomeDownloadsView/TimeLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/AuthorDark.png b/VDownload/Assets/HomeVideoCollectionView/AuthorDark.png new file mode 100644 index 0000000..bce1cfd Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/AuthorDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/AuthorLight.png b/VDownload/Assets/HomeVideoCollectionView/AuthorLight.png new file mode 100644 index 0000000..48a0d5e Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/AuthorLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/DateDark.png b/VDownload/Assets/HomeVideoCollectionView/DateDark.png new file mode 100644 index 0000000..dab9efb Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/DateDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/DateLight.png b/VDownload/Assets/HomeVideoCollectionView/DateLight.png new file mode 100644 index 0000000..a36dc6c Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/DateLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/DirectoryDark.png b/VDownload/Assets/HomeVideoCollectionView/DirectoryDark.png new file mode 100644 index 0000000..731e15e Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/DirectoryDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/DirectoryLight.png b/VDownload/Assets/HomeVideoCollectionView/DirectoryLight.png new file mode 100644 index 0000000..b82f380 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/DirectoryLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/ExtensionDark.png b/VDownload/Assets/HomeVideoCollectionView/ExtensionDark.png new file mode 100644 index 0000000..0ef1391 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/ExtensionDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/ExtensionLight.png b/VDownload/Assets/HomeVideoCollectionView/ExtensionLight.png new file mode 100644 index 0000000..27e0a2b Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/ExtensionLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/FilenameDark.png b/VDownload/Assets/HomeVideoCollectionView/FilenameDark.png new file mode 100644 index 0000000..4a9b713 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/FilenameDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/FilenameLight.png b/VDownload/Assets/HomeVideoCollectionView/FilenameLight.png new file mode 100644 index 0000000..bd9da97 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/FilenameLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/MediaDark.png b/VDownload/Assets/HomeVideoCollectionView/MediaDark.png new file mode 100644 index 0000000..d7a3389 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/MediaDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/MediaLight.png b/VDownload/Assets/HomeVideoCollectionView/MediaLight.png new file mode 100644 index 0000000..3d8410b Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/MediaLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/QualityDark.png b/VDownload/Assets/HomeVideoCollectionView/QualityDark.png new file mode 100644 index 0000000..71fe363 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/QualityDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/QualityLight.png b/VDownload/Assets/HomeVideoCollectionView/QualityLight.png new file mode 100644 index 0000000..685ac0d Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/QualityLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/TimeDark.png b/VDownload/Assets/HomeVideoCollectionView/TimeDark.png new file mode 100644 index 0000000..a1b6bb7 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/TimeDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/TimeLight.png b/VDownload/Assets/HomeVideoCollectionView/TimeLight.png new file mode 100644 index 0000000..be30883 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/TimeLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/TrimDark.png b/VDownload/Assets/HomeVideoCollectionView/TrimDark.png new file mode 100644 index 0000000..a696333 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/TrimDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/TrimLight.png b/VDownload/Assets/HomeVideoCollectionView/TrimLight.png new file mode 100644 index 0000000..7d03273 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/TrimLight.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/ViewDark.png b/VDownload/Assets/HomeVideoCollectionView/ViewDark.png new file mode 100644 index 0000000..1f8b982 Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/ViewDark.png differ diff --git a/VDownload/Assets/HomeVideoCollectionView/ViewLight.png b/VDownload/Assets/HomeVideoCollectionView/ViewLight.png new file mode 100644 index 0000000..cd6ed5b Binary files /dev/null and b/VDownload/Assets/HomeVideoCollectionView/ViewLight.png differ diff --git a/VDownload/Assets/HomeVideoView/AuthorDark.png b/VDownload/Assets/HomeVideoView/AuthorDark.png new file mode 100644 index 0000000..bce1cfd Binary files /dev/null and b/VDownload/Assets/HomeVideoView/AuthorDark.png differ diff --git a/VDownload/Assets/HomeVideoView/AuthorLight.png b/VDownload/Assets/HomeVideoView/AuthorLight.png new file mode 100644 index 0000000..48a0d5e Binary files /dev/null and b/VDownload/Assets/HomeVideoView/AuthorLight.png differ diff --git a/VDownload/Assets/HomeVideoView/DateDark.png b/VDownload/Assets/HomeVideoView/DateDark.png new file mode 100644 index 0000000..dab9efb Binary files /dev/null and b/VDownload/Assets/HomeVideoView/DateDark.png differ diff --git a/VDownload/Assets/HomeVideoView/DateLight.png b/VDownload/Assets/HomeVideoView/DateLight.png new file mode 100644 index 0000000..a36dc6c Binary files /dev/null and b/VDownload/Assets/HomeVideoView/DateLight.png differ diff --git a/VDownload/Assets/HomeVideoView/DirectoryDark.png b/VDownload/Assets/HomeVideoView/DirectoryDark.png new file mode 100644 index 0000000..731e15e Binary files /dev/null and b/VDownload/Assets/HomeVideoView/DirectoryDark.png differ diff --git a/VDownload/Assets/HomeVideoView/DirectoryLight.png b/VDownload/Assets/HomeVideoView/DirectoryLight.png new file mode 100644 index 0000000..b82f380 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/DirectoryLight.png differ diff --git a/VDownload/Assets/HomeVideoView/ExtensionDark.png b/VDownload/Assets/HomeVideoView/ExtensionDark.png new file mode 100644 index 0000000..0ef1391 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/ExtensionDark.png differ diff --git a/VDownload/Assets/HomeVideoView/ExtensionLight.png b/VDownload/Assets/HomeVideoView/ExtensionLight.png new file mode 100644 index 0000000..27e0a2b Binary files /dev/null and b/VDownload/Assets/HomeVideoView/ExtensionLight.png differ diff --git a/VDownload/Assets/HomeVideoView/FilenameDark.png b/VDownload/Assets/HomeVideoView/FilenameDark.png new file mode 100644 index 0000000..4a9b713 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/FilenameDark.png differ diff --git a/VDownload/Assets/HomeVideoView/FilenameLight.png b/VDownload/Assets/HomeVideoView/FilenameLight.png new file mode 100644 index 0000000..bd9da97 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/FilenameLight.png differ diff --git a/VDownload/Assets/HomeVideoView/MediaDark.png b/VDownload/Assets/HomeVideoView/MediaDark.png new file mode 100644 index 0000000..d7a3389 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/MediaDark.png differ diff --git a/VDownload/Assets/HomeVideoView/MediaLight.png b/VDownload/Assets/HomeVideoView/MediaLight.png new file mode 100644 index 0000000..3d8410b Binary files /dev/null and b/VDownload/Assets/HomeVideoView/MediaLight.png differ diff --git a/VDownload/Assets/HomeVideoView/QualityDark.png b/VDownload/Assets/HomeVideoView/QualityDark.png new file mode 100644 index 0000000..71fe363 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/QualityDark.png differ diff --git a/VDownload/Assets/HomeVideoView/QualityLight.png b/VDownload/Assets/HomeVideoView/QualityLight.png new file mode 100644 index 0000000..685ac0d Binary files /dev/null and b/VDownload/Assets/HomeVideoView/QualityLight.png differ diff --git a/VDownload/Assets/HomeVideoView/TimeDark.png b/VDownload/Assets/HomeVideoView/TimeDark.png new file mode 100644 index 0000000..a1b6bb7 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/TimeDark.png differ diff --git a/VDownload/Assets/HomeVideoView/TimeLight.png b/VDownload/Assets/HomeVideoView/TimeLight.png new file mode 100644 index 0000000..be30883 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/TimeLight.png differ diff --git a/VDownload/Assets/HomeVideoView/TrimDark.png b/VDownload/Assets/HomeVideoView/TrimDark.png new file mode 100644 index 0000000..a696333 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/TrimDark.png differ diff --git a/VDownload/Assets/HomeVideoView/TrimLight.png b/VDownload/Assets/HomeVideoView/TrimLight.png new file mode 100644 index 0000000..7d03273 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/TrimLight.png differ diff --git a/VDownload/Assets/HomeVideoView/ViewDark.png b/VDownload/Assets/HomeVideoView/ViewDark.png new file mode 100644 index 0000000..1f8b982 Binary files /dev/null and b/VDownload/Assets/HomeVideoView/ViewDark.png differ diff --git a/VDownload/Assets/HomeVideoView/ViewLight.png b/VDownload/Assets/HomeVideoView/ViewLight.png new file mode 100644 index 0000000..cd6ed5b Binary files /dev/null and b/VDownload/Assets/HomeVideoView/ViewLight.png differ diff --git a/VDownload/Assets/HomeView/Error.png b/VDownload/Assets/HomeView/Error.png new file mode 100644 index 0000000..9cbf794 Binary files /dev/null and b/VDownload/Assets/HomeView/Error.png differ diff --git a/VDownload/Assets/Icons/ActiveTasksNumberDark.svg b/VDownload/Assets/Icons/ActiveTasksNumberDark.svg deleted file mode 100644 index 22a9127..0000000 --- a/VDownload/Assets/Icons/ActiveTasksNumberDark.svg +++ /dev/null @@ -1,12 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1470 1470"><g transform="matrix(1,0,0,1,-65,0)"> - <path d="M800,650L800,1116.67" style="fill:none;fill-rule:nonzero;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1,0,0,1,-65,0)"> - <path d="M583.333,900L800,1116.67L1016.67,900" style="fill:none;fill-rule:nonzero;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1,0,0,1,-65,0)"> - <path d="M683.333,250L416.667,250C361.433,250 316.667,294.767 316.667,350L316.667,1316.67C316.667,1371.9 361.433,1416.67 416.667,1416.67L1183.33,1416.67C1238.57,1416.67 1283.33,1371.9 1283.33,1316.67L1283.33,350C1283.33,294.767 1238.57,250 1183.33,250L916.667,250" style="fill:none;fill-rule:nonzero;stroke:white;stroke-width:100px;stroke-linecap:butt;"/> - </g> - <g transform="matrix(1,0,0,1,-65,0)"> - <path d="M916.667,350L683.333,350L683.333,166.667C683.333,102.233 735.567,50 800,50C864.433,50 916.667,102.233 916.667,166.667L916.667,350Z" style="fill:none;fill-rule:nonzero;stroke:white;stroke-width:100px;stroke-linecap:butt;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ActiveTasksNumberLight.svg b/VDownload/Assets/Icons/ActiveTasksNumberLight.svg deleted file mode 100644 index ea4876d..0000000 --- a/VDownload/Assets/Icons/ActiveTasksNumberLight.svg +++ /dev/null @@ -1,12 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1470 1470"><g transform="matrix(1,0,0,1,-65,0)"> - <path d="M800,650L800,1116.67" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1,0,0,1,-65,0)"> - <path d="M583.333,900L800,1116.67L1016.67,900" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1,0,0,1,-65,0)"> - <path d="M683.333,250L416.667,250C361.433,250 316.667,294.767 316.667,350L316.667,1316.67C316.667,1371.9 361.433,1416.67 416.667,1416.67L1183.33,1416.67C1238.57,1416.67 1283.33,1371.9 1283.33,1316.67L1283.33,350C1283.33,294.767 1238.57,250 1183.33,250L916.667,250" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:100px;stroke-linecap:butt;"/> - </g> - <g transform="matrix(1,0,0,1,-65,0)"> - <path d="M916.667,350L683.333,350L683.333,166.667C683.333,102.233 735.567,50 800,50C864.433,50 916.667,102.233 916.667,166.667L916.667,350Z" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:100px;stroke-linecap:butt;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ApplyToAllOptionsDark.svg b/VDownload/Assets/Icons/ApplyToAllOptionsDark.svg deleted file mode 100644 index 3ffc448..0000000 --- a/VDownload/Assets/Icons/ApplyToAllOptionsDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.198,0,0,1.198,-158.4,-158.4)"> - <path d="M800,133.333C747.437,133.333 696.755,139.981 648.372,151.237C627.745,156.052 612.314,173.474 610.026,194.531L599.414,291.276C595.943,322.979 577.63,351.028 550,366.992C522.425,382.924 488.954,384.689 459.766,371.875L459.701,371.875L370.833,332.747C351.454,324.219 328.665,328.882 314.193,344.336C245.11,417.986 192.089,507.169 161.979,606.706C155.847,626.96 163.207,649.011 180.273,661.523L259.115,719.336C284.86,738.264 300,768.093 300,800C300,831.924 284.86,861.778 259.115,880.664L180.273,938.411C163.207,950.923 155.847,972.975 161.979,993.229C192.086,1092.76 245.064,1182 314.193,1255.66C328.681,1271.1 351.468,1275.73 370.833,1267.19L459.701,1228.06C488.903,1215.22 522.406,1217.07 550,1233.01C577.63,1248.97 595.943,1277.02 599.414,1308.72L610.026,1405.47C612.331,1426.48 627.726,1443.87 648.307,1448.7C696.712,1460 747.437,1466.67 800,1466.67C852.563,1466.67 903.245,1460.02 951.628,1448.76C972.255,1443.95 987.686,1426.53 989.974,1405.47L1000.59,1308.72C1004.06,1277.02 1022.37,1248.97 1050,1233.01C1077.58,1217.08 1111.05,1215.25 1140.23,1228.06L1229.17,1267.19C1248.53,1275.73 1271.32,1271.1 1285.81,1255.66C1354.89,1182.01 1407.91,1092.77 1438.02,993.229C1444.15,972.975 1436.79,950.923 1419.73,938.411L1340.88,880.664C1315.14,861.778 1300,831.924 1300,800C1300,768.076 1315.14,738.222 1340.88,719.336L1419.73,661.589C1436.79,649.077 1444.15,627.025 1438.02,606.771C1407.91,507.234 1354.89,417.986 1285.81,344.336C1271.32,328.904 1248.53,324.268 1229.17,332.813L1140.23,371.94C1111.05,384.754 1077.58,382.924 1050,366.992C1022.37,351.028 1004.06,322.979 1000.59,291.276L989.974,194.531C987.669,173.516 972.274,156.133 951.693,151.302C903.288,140.005 852.563,133.333 800,133.333ZM800,233.333C832.477,233.333 863.664,239.163 894.922,244.661L901.172,302.148C908.101,365.446 944.897,421.745 1000,453.581C1055.14,485.438 1122.27,489.1 1180.53,463.477L1233.4,440.234C1273.99,488.98 1306.13,544.039 1328.58,604.362L1281.71,638.737C1230.38,676.384 1200,736.324 1200,800C1200,863.676 1230.38,923.616 1281.71,961.263L1328.58,995.638C1306.13,1055.96 1273.99,1111.02 1233.4,1159.77L1180.53,1136.52C1122.27,1110.9 1055.14,1114.56 1000,1146.42C944.897,1178.26 908.101,1234.55 901.172,1297.85L894.922,1355.34C863.667,1360.82 832.461,1366.67 800,1366.67C767.523,1366.67 736.336,1360.84 705.078,1355.34L698.828,1297.85C691.899,1234.55 655.103,1178.26 600,1146.42C544.861,1114.56 477.73,1110.9 419.466,1136.52L366.602,1159.77C325.999,1111.03 293.865,1055.96 271.419,995.638L318.294,961.263C369.615,923.616 400,863.676 400,800C400,736.324 369.591,676.345 318.294,638.672L271.419,604.297C293.878,543.951 326.048,488.928 366.667,440.169L419.466,463.411C477.73,489.035 544.861,485.438 600,453.581C655.103,421.745 691.899,365.446 698.828,302.148L705.078,244.661C736.333,239.178 767.539,233.333 800,233.333ZM800,533.333C653.316,533.333 533.333,653.316 533.333,800C533.333,946.684 653.316,1066.67 800,1066.67C946.684,1066.67 1066.67,946.684 1066.67,800C1066.67,653.316 946.684,533.333 800,533.333ZM800,633.333C892.64,633.333 966.667,707.36 966.667,800C966.667,892.64 892.64,966.667 800,966.667C707.36,966.667 633.333,892.64 633.333,800C633.333,707.36 707.36,633.333 800,633.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ApplyToAllOptionsLight.svg b/VDownload/Assets/Icons/ApplyToAllOptionsLight.svg deleted file mode 100644 index cad632c..0000000 --- a/VDownload/Assets/Icons/ApplyToAllOptionsLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.198,0,0,1.198,-158.4,-158.4)"> - <path d="M800,133.333C747.437,133.333 696.755,139.981 648.372,151.237C627.745,156.052 612.314,173.474 610.026,194.531L599.414,291.276C595.943,322.979 577.63,351.028 550,366.992C522.425,382.924 488.954,384.689 459.766,371.875L459.701,371.875L370.833,332.747C351.454,324.219 328.665,328.882 314.193,344.336C245.11,417.986 192.089,507.169 161.979,606.706C155.847,626.96 163.207,649.011 180.273,661.523L259.115,719.336C284.86,738.264 300,768.093 300,800C300,831.924 284.86,861.778 259.115,880.664L180.273,938.411C163.207,950.923 155.847,972.975 161.979,993.229C192.086,1092.76 245.064,1182 314.193,1255.66C328.681,1271.1 351.468,1275.73 370.833,1267.19L459.701,1228.06C488.903,1215.22 522.406,1217.07 550,1233.01C577.63,1248.97 595.943,1277.02 599.414,1308.72L610.026,1405.47C612.331,1426.48 627.726,1443.87 648.307,1448.7C696.712,1460 747.437,1466.67 800,1466.67C852.563,1466.67 903.245,1460.02 951.628,1448.76C972.255,1443.95 987.686,1426.53 989.974,1405.47L1000.59,1308.72C1004.06,1277.02 1022.37,1248.97 1050,1233.01C1077.58,1217.08 1111.05,1215.25 1140.23,1228.06L1229.17,1267.19C1248.53,1275.73 1271.32,1271.1 1285.81,1255.66C1354.89,1182.01 1407.91,1092.77 1438.02,993.229C1444.15,972.975 1436.79,950.923 1419.73,938.411L1340.88,880.664C1315.14,861.778 1300,831.924 1300,800C1300,768.076 1315.14,738.222 1340.88,719.336L1419.73,661.589C1436.79,649.077 1444.15,627.025 1438.02,606.771C1407.91,507.234 1354.89,417.986 1285.81,344.336C1271.32,328.904 1248.53,324.268 1229.17,332.813L1140.23,371.94C1111.05,384.754 1077.58,382.924 1050,366.992C1022.37,351.028 1004.06,322.979 1000.59,291.276L989.974,194.531C987.669,173.516 972.274,156.133 951.693,151.302C903.288,140.005 852.563,133.333 800,133.333ZM800,233.333C832.477,233.333 863.664,239.163 894.922,244.661L901.172,302.148C908.101,365.446 944.897,421.745 1000,453.581C1055.14,485.438 1122.27,489.1 1180.53,463.477L1233.4,440.234C1273.99,488.98 1306.13,544.039 1328.58,604.362L1281.71,638.737C1230.38,676.384 1200,736.324 1200,800C1200,863.676 1230.38,923.616 1281.71,961.263L1328.58,995.638C1306.13,1055.96 1273.99,1111.02 1233.4,1159.77L1180.53,1136.52C1122.27,1110.9 1055.14,1114.56 1000,1146.42C944.897,1178.26 908.101,1234.55 901.172,1297.85L894.922,1355.34C863.667,1360.82 832.461,1366.67 800,1366.67C767.523,1366.67 736.336,1360.84 705.078,1355.34L698.828,1297.85C691.899,1234.55 655.103,1178.26 600,1146.42C544.861,1114.56 477.73,1110.9 419.466,1136.52L366.602,1159.77C325.999,1111.03 293.865,1055.96 271.419,995.638L318.294,961.263C369.615,923.616 400,863.676 400,800C400,736.324 369.591,676.345 318.294,638.672L271.419,604.297C293.878,543.951 326.048,488.928 366.667,440.169L419.466,463.411C477.73,489.035 544.861,485.438 600,453.581C655.103,421.745 691.899,365.446 698.828,302.148L705.078,244.661C736.333,239.178 767.539,233.333 800,233.333ZM800,533.333C653.316,533.333 533.333,653.316 533.333,800C533.333,946.684 653.316,1066.67 800,1066.67C946.684,1066.67 1066.67,946.684 1066.67,800C1066.67,653.316 946.684,533.333 800,533.333ZM800,633.333C892.64,633.333 966.667,707.36 966.667,800C966.667,892.64 892.64,966.667 800,966.667C707.36,966.667 633.333,892.64 633.333,800C633.333,707.36 707.36,633.333 800,633.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/AuthorDark.svg b/VDownload/Assets/Icons/AuthorDark.svg deleted file mode 100644 index 8f72976..0000000 --- a/VDownload/Assets/Icons/AuthorDark.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"> - <g transform="matrix(1.1995,0,0,1.1995,-161.207,-161.6)"> - <path d="M800,133.333C616.497,133.333 466.667,283.164 466.667,466.667C466.667,650.169 616.497,800 800,800C983.503,800 1133.33,650.169 1133.33,466.667C1133.33,283.164 983.503,133.333 800,133.333ZM800,233.333C929.459,233.333 1033.33,337.208 1033.33,466.667C1033.33,596.125 929.459,700 800,700C670.541,700 566.667,596.125 566.667,466.667C566.667,337.208 670.541,233.333 800,233.333ZM399.284,933.333C326.623,933.333 266.667,993.29 266.667,1065.95L266.667,1116.67C266.667,1236.81 342.642,1329.74 443.555,1385.42C544.467,1441.09 672.27,1466.67 800,1466.67C927.73,1466.67 1055.53,1441.09 1156.44,1385.42C1242.49,1337.94 1307.25,1262.09 1325.39,1166.67L1333.4,1166.67L1333.4,1065.95C1333.4,993.29 1273.38,933.333 1200.72,933.333L399.284,933.333ZM399.284,1033.33L1200.72,1033.33C1219.32,1033.33 1233.4,1047.34 1233.4,1065.95L1233.4,1066.67L1233.33,1066.67L1233.33,1116.67C1233.33,1196.53 1188.48,1253.59 1108.14,1297.92C1027.8,1342.24 913.937,1366.67 800,1366.67C686.063,1366.67 572.2,1342.24 491.862,1297.92C411.524,1253.59 366.667,1196.53 366.667,1116.67L366.667,1065.95C366.667,1047.34 380.678,1033.33 399.284,1033.33Z"/> - </g> -</svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/AuthorLight.svg b/VDownload/Assets/Icons/AuthorLight.svg deleted file mode 100644 index dd2fba3..0000000 --- a/VDownload/Assets/Icons/AuthorLight.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"> - <g transform="matrix(1.1995,0,0,1.1995,-161.207,-161.6)"> - <path d="M800,133.333C616.497,133.333 466.667,283.164 466.667,466.667C466.667,650.169 616.497,800 800,800C983.503,800 1133.33,650.169 1133.33,466.667C1133.33,283.164 983.503,133.333 800,133.333ZM800,233.333C929.459,233.333 1033.33,337.208 1033.33,466.667C1033.33,596.125 929.459,700 800,700C670.541,700 566.667,596.125 566.667,466.667C566.667,337.208 670.541,233.333 800,233.333ZM399.284,933.333C326.623,933.333 266.667,993.29 266.667,1065.95L266.667,1116.67C266.667,1236.81 342.642,1329.74 443.555,1385.42C544.467,1441.09 672.27,1466.67 800,1466.67C927.73,1466.67 1055.53,1441.09 1156.44,1385.42C1242.49,1337.94 1307.25,1262.09 1325.39,1166.67L1333.4,1166.67L1333.4,1065.95C1333.4,993.29 1273.38,933.333 1200.72,933.333L399.284,933.333ZM399.284,1033.33L1200.72,1033.33C1219.32,1033.33 1233.4,1047.34 1233.4,1065.95L1233.4,1066.67L1233.33,1066.67L1233.33,1116.67C1233.33,1196.53 1188.48,1253.59 1108.14,1297.92C1027.8,1342.24 913.937,1366.67 800,1366.67C686.063,1366.67 572.2,1342.24 491.862,1297.92C411.524,1253.59 366.667,1196.53 366.667,1116.67L366.667,1065.95C366.667,1047.34 380.678,1033.33 399.284,1033.33Z"/> - </g> -</svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/CustomMediaLocationDark.svg b/VDownload/Assets/Icons/CustomMediaLocationDark.svg deleted file mode 100644 index 30bd99c..0000000 --- a/VDownload/Assets/Icons/CustomMediaLocationDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M283.333,266.667C201.083,266.667 133.333,334.417 133.333,416.667L133.333,1183.33C133.333,1265.58 201.083,1333.33 283.333,1333.33L1316.67,1333.33C1398.92,1333.33 1466.67,1265.58 1466.67,1183.33L1466.67,583.333C1466.67,501.083 1398.92,433.333 1316.67,433.333L801.432,433.333L652.474,309.18C652.452,309.18 652.431,309.18 652.409,309.18C619.47,281.737 577.972,266.667 535.091,266.667L283.333,266.667ZM283.333,366.667L535.091,366.667C554.61,366.667 573.419,373.512 588.411,386.003L751.302,521.745C760.292,529.236 771.632,533.338 783.333,533.333L1316.67,533.333C1344.88,533.333 1366.67,555.117 1366.67,583.333L1366.67,1183.33C1366.67,1211.55 1344.88,1233.33 1316.67,1233.33L283.333,1233.33C255.117,1233.33 233.333,1211.55 233.333,1183.33L233.333,416.667C233.333,388.45 255.117,366.667 283.333,366.667ZM952.279,600.911C905.24,600.314 857.963,617.555 821.94,652.734L800,674.219L778.06,652.799C742.034,617.64 694.808,600.392 647.786,600.977C600.765,601.561 553.952,619.964 518.815,655.99C448.51,728.026 449.972,844.933 522.005,915.234L765.104,1152.47C784.4,1171.28 815.6,1171.28 834.896,1152.47L1078.06,915.169C1150.07,844.889 1151.49,728.031 1081.25,655.99C1046.12,619.944 999.317,601.509 952.279,600.911ZM648.958,700.326C670.184,700.068 691.529,708.075 708.203,724.349L765.104,779.818C784.4,798.618 815.6,798.618 834.896,779.818L891.797,724.349C891.819,724.327 891.84,724.306 891.862,724.284C908.539,707.997 929.829,700.06 951.042,700.326C972.254,700.592 993.369,709.093 1009.63,725.781C1042.2,759.145 1041.57,811.055 1008.2,843.62L800,1046.81L591.797,843.62C558.43,811.055 557.803,759.21 590.365,725.846C590.365,725.825 590.365,725.803 590.365,725.781C606.627,709.107 627.733,700.583 648.958,700.326Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/CustomMediaLocationLight.svg b/VDownload/Assets/Icons/CustomMediaLocationLight.svg deleted file mode 100644 index f57e1b6..0000000 --- a/VDownload/Assets/Icons/CustomMediaLocationLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M283.333,266.667C201.083,266.667 133.333,334.417 133.333,416.667L133.333,1183.33C133.333,1265.58 201.083,1333.33 283.333,1333.33L1316.67,1333.33C1398.92,1333.33 1466.67,1265.58 1466.67,1183.33L1466.67,583.333C1466.67,501.083 1398.92,433.333 1316.67,433.333L801.432,433.333L652.474,309.18C652.452,309.18 652.431,309.18 652.409,309.18C619.47,281.737 577.972,266.667 535.091,266.667L283.333,266.667ZM283.333,366.667L535.091,366.667C554.61,366.667 573.419,373.512 588.411,386.003L751.302,521.745C760.292,529.236 771.632,533.338 783.333,533.333L1316.67,533.333C1344.88,533.333 1366.67,555.117 1366.67,583.333L1366.67,1183.33C1366.67,1211.55 1344.88,1233.33 1316.67,1233.33L283.333,1233.33C255.117,1233.33 233.333,1211.55 233.333,1183.33L233.333,416.667C233.333,388.45 255.117,366.667 283.333,366.667ZM952.279,600.911C905.24,600.314 857.963,617.555 821.94,652.734L800,674.219L778.06,652.799C742.034,617.64 694.808,600.392 647.786,600.977C600.765,601.561 553.952,619.964 518.815,655.99C448.51,728.026 449.972,844.933 522.005,915.234L765.104,1152.47C784.4,1171.28 815.6,1171.28 834.896,1152.47L1078.06,915.169C1150.07,844.889 1151.49,728.031 1081.25,655.99C1046.12,619.944 999.317,601.509 952.279,600.911ZM648.958,700.326C670.184,700.068 691.529,708.075 708.203,724.349L765.104,779.818C784.4,798.618 815.6,798.618 834.896,779.818L891.797,724.349C891.819,724.327 891.84,724.306 891.862,724.284C908.539,707.997 929.829,700.06 951.042,700.326C972.254,700.592 993.369,709.093 1009.63,725.781C1042.2,759.145 1041.57,811.055 1008.2,843.62L800,1046.81L591.797,843.62C558.43,811.055 557.803,759.21 590.365,725.846C590.365,725.825 590.365,725.803 590.365,725.781C606.627,709.107 627.733,700.583 648.958,700.326Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DateDark.svg b/VDownload/Assets/Icons/DateDark.svg deleted file mode 100644 index be74141..0000000 --- a/VDownload/Assets/Icons/DateDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.3325,0,0,1.3325,-266.5,-266.5)"> - <path d="M416.667,200C297.602,200 200,297.602 200,416.667L200,1183.33C200,1302.4 297.602,1400 416.667,1400L1183.33,1400C1302.4,1400 1400,1302.4 1400,1183.33L1400,416.667C1400,297.602 1302.4,200 1183.33,200L416.667,200ZM416.667,300L1183.33,300C1248.34,300 1300,351.665 1300,416.667L1300,466.667L300,466.667L300,416.667C300,351.665 351.665,300 416.667,300ZM300,566.667L1300,566.667L1300,1183.33C1300,1248.34 1248.34,1300 1183.33,1300L416.667,1300C351.665,1300 300,1248.34 300,1183.33L300,566.667ZM516.667,700C470.951,700 433.333,737.618 433.333,783.333C433.333,829.049 470.951,866.667 516.667,866.667C562.382,866.667 600,829.049 600,783.333C600,737.618 562.382,700 516.667,700ZM800,700C754.285,700 716.667,737.618 716.667,783.333C716.667,829.049 754.285,866.667 800,866.667C845.715,866.667 883.333,829.049 883.333,783.333C883.333,737.618 845.715,700 800,700ZM1083.33,700C1037.62,700 1000,737.618 1000,783.333C1000,829.049 1037.62,866.667 1083.33,866.667C1129.05,866.667 1166.67,829.049 1166.67,783.333C1166.67,737.618 1129.05,700 1083.33,700ZM516.667,1000C470.951,1000 433.333,1037.62 433.333,1083.33C433.333,1129.05 470.951,1166.67 516.667,1166.67C562.382,1166.67 600,1129.05 600,1083.33C600,1037.62 562.382,1000 516.667,1000ZM800,1000C754.285,1000 716.667,1037.62 716.667,1083.33C716.667,1129.05 754.285,1166.67 800,1166.67C845.715,1166.67 883.333,1129.05 883.333,1083.33C883.333,1037.62 845.715,1000 800,1000Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DateLight.svg b/VDownload/Assets/Icons/DateLight.svg deleted file mode 100644 index a8a198c..0000000 --- a/VDownload/Assets/Icons/DateLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.3325,0,0,1.3325,-266.5,-266.5)"> - <path d="M416.667,200C297.602,200 200,297.602 200,416.667L200,1183.33C200,1302.4 297.602,1400 416.667,1400L1183.33,1400C1302.4,1400 1400,1302.4 1400,1183.33L1400,416.667C1400,297.602 1302.4,200 1183.33,200L416.667,200ZM416.667,300L1183.33,300C1248.34,300 1300,351.665 1300,416.667L1300,466.667L300,466.667L300,416.667C300,351.665 351.665,300 416.667,300ZM300,566.667L1300,566.667L1300,1183.33C1300,1248.34 1248.34,1300 1183.33,1300L416.667,1300C351.665,1300 300,1248.34 300,1183.33L300,566.667ZM516.667,700C470.951,700 433.333,737.618 433.333,783.333C433.333,829.049 470.951,866.667 516.667,866.667C562.382,866.667 600,829.049 600,783.333C600,737.618 562.382,700 516.667,700ZM800,700C754.285,700 716.667,737.618 716.667,783.333C716.667,829.049 754.285,866.667 800,866.667C845.715,866.667 883.333,829.049 883.333,783.333C883.333,737.618 845.715,700 800,700ZM1083.33,700C1037.62,700 1000,737.618 1000,783.333C1000,829.049 1037.62,866.667 1083.33,866.667C1129.05,866.667 1166.67,829.049 1166.67,783.333C1166.67,737.618 1129.05,700 1083.33,700ZM516.667,1000C470.951,1000 433.333,1037.62 433.333,1083.33C433.333,1129.05 470.951,1166.67 516.667,1166.67C562.382,1166.67 600,1129.05 600,1083.33C600,1037.62 562.382,1000 516.667,1000ZM800,1000C754.285,1000 716.667,1037.62 716.667,1083.33C716.667,1129.05 754.285,1166.67 800,1166.67C845.715,1166.67 883.333,1129.05 883.333,1083.33C883.333,1037.62 845.715,1000 800,1000Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DefaultAudioExtensionDark.svg b/VDownload/Assets/Icons/DefaultAudioExtensionDark.svg deleted file mode 100644 index 9a429fe..0000000 --- a/VDownload/Assets/Icons/DefaultAudioExtensionDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036ZM829.753,766.862C824.415,767.445 819.232,769.313 814.648,772.396C805.515,778.596 800,788.933 800,800L800,1080.53C780.333,1071.93 757.733,1066.67 733.333,1066.67C659.7,1066.67 600,1111.43 600,1166.67C600,1221.9 659.7,1266.67 733.333,1266.67C806.967,1266.67 866.667,1221.9 866.667,1166.67L866.667,949.219L987.63,997.656C997.93,1001.79 1009.55,1000.47 1018.69,994.271C1027.85,988.071 1033.33,977.733 1033.33,966.667L1033.33,911.784C1033.33,870.651 1008.64,834.245 970.443,818.945L845.703,769.01C840.57,766.96 835.09,766.279 829.753,766.862Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DefaultAudioExtensionLight.svg b/VDownload/Assets/Icons/DefaultAudioExtensionLight.svg deleted file mode 100644 index 522d165..0000000 --- a/VDownload/Assets/Icons/DefaultAudioExtensionLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036ZM829.753,766.862C824.415,767.445 819.232,769.313 814.648,772.396C805.515,778.596 800,788.933 800,800L800,1080.53C780.333,1071.93 757.733,1066.67 733.333,1066.67C659.7,1066.67 600,1111.43 600,1166.67C600,1221.9 659.7,1266.67 733.333,1266.67C806.967,1266.67 866.667,1221.9 866.667,1166.67L866.667,949.219L987.63,997.656C997.93,1001.79 1009.55,1000.47 1018.69,994.271C1027.85,988.071 1033.33,977.733 1033.33,966.667L1033.33,911.784C1033.33,870.651 1008.64,834.245 970.443,818.945L845.703,769.01C840.57,766.96 835.09,766.279 829.753,766.862Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DefaultMediaTypeDark.svg b/VDownload/Assets/Icons/DefaultMediaTypeDark.svg deleted file mode 100644 index b711faa..0000000 --- a/VDownload/Assets/Icons/DefaultMediaTypeDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.23077,0,0,1.23077,-204.462,-187.385)"> - <path d="M350,200C249.341,200 166.667,282.674 166.667,383.333L166.667,1216.67C166.667,1317.33 249.341,1400 350,1400L1283.33,1400C1383.99,1400 1466.67,1317.33 1466.67,1216.67L1466.67,383.333C1466.67,282.674 1383.99,200 1283.33,200L350,200ZM350,300L1283.33,300C1329.94,300 1366.67,336.726 1366.67,383.333L1366.67,1216.67C1366.67,1263.27 1329.94,1300 1283.33,1300L350,1300C303.392,1300 266.667,1263.27 266.667,1216.67L266.667,383.333C266.667,336.726 303.392,300 350,300ZM366.667,350C348.267,350 333.333,364.933 333.333,383.333L333.333,416.667C333.333,435.067 348.267,450 366.667,450L400,450C418.4,450 433.333,435.067 433.333,416.667L433.333,383.333C433.333,364.933 418.4,350 400,350L366.667,350ZM1233.33,350C1214.93,350 1200,364.933 1200,383.333L1200,416.667C1200,435.067 1214.93,450 1233.33,450L1266.67,450C1285.07,450 1300,435.067 1300,416.667L1300,383.333C1300,364.933 1285.07,350 1266.67,350L1233.33,350ZM683.333,533.398C668.77,533.398 654.043,537.201 640.951,544.922C615.478,559.97 600,587.195 600,616.732L600,983.398C600,1012.87 615.401,1039.96 640.885,1055.01C666.823,1070.43 697.284,1070.78 723.503,1056.38L1056.84,873.047L1056.9,872.982C1083.33,858.401 1100,830.405 1100,800.065C1100,769.725 1083.33,741.665 1056.9,727.083L1056.84,727.018L723.438,543.685L723.372,543.62C710.907,536.807 697.1,533.398 683.333,533.398ZM366.667,550C348.267,550 333.333,564.933 333.333,583.333L333.333,616.667C333.333,635.067 348.267,650 366.667,650L400,650C418.4,650 433.333,635.067 433.333,616.667L433.333,583.333C433.333,564.933 418.4,550 400,550L366.667,550ZM1233.33,550C1214.93,550 1200,564.933 1200,583.333L1200,616.667C1200,635.067 1214.93,650 1233.33,650L1266.67,650C1285.07,650 1300,635.067 1300,616.667L1300,583.333C1300,564.933 1285.07,550 1266.67,550L1233.33,550ZM700,644.857L982.096,800L700,955.208L700,644.857ZM366.667,750C348.267,750 333.333,764.933 333.333,783.333L333.333,816.667C333.333,835.067 348.267,850 366.667,850L400,850C418.4,850 433.333,835.067 433.333,816.667L433.333,783.333C433.333,764.933 418.4,750 400,750L366.667,750ZM1233.33,750C1214.93,750 1200,764.933 1200,783.333L1200,816.667C1200,835.067 1214.93,850 1233.33,850L1266.67,850C1285.07,850 1300,835.067 1300,816.667L1300,783.333C1300,764.933 1285.07,750 1266.67,750L1233.33,750ZM366.667,950C348.267,950 333.333,964.933 333.333,983.333L333.333,1016.67C333.333,1035.07 348.267,1050 366.667,1050L400,1050C418.4,1050 433.333,1035.07 433.333,1016.67L433.333,983.333C433.333,964.933 418.4,950 400,950L366.667,950ZM1233.33,950C1214.93,950 1200,964.933 1200,983.333L1200,1016.67C1200,1035.07 1214.93,1050 1233.33,1050L1266.67,1050C1285.07,1050 1300,1035.07 1300,1016.67L1300,983.333C1300,964.933 1285.07,950 1266.67,950L1233.33,950ZM366.667,1150C348.267,1150 333.333,1164.93 333.333,1183.33L333.333,1216.67C333.333,1235.07 348.267,1250 366.667,1250L400,1250C418.4,1250 433.333,1235.07 433.333,1216.67L433.333,1183.33C433.333,1164.93 418.4,1150 400,1150L366.667,1150ZM1233.33,1150C1214.93,1150 1200,1164.93 1200,1183.33L1200,1216.67C1200,1235.07 1214.93,1250 1233.33,1250L1266.67,1250C1285.07,1250 1300,1235.07 1300,1216.67L1300,1183.33C1300,1164.93 1285.07,1150 1266.67,1150L1233.33,1150Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DefaultMediaTypeLight.svg b/VDownload/Assets/Icons/DefaultMediaTypeLight.svg deleted file mode 100644 index 956bd74..0000000 --- a/VDownload/Assets/Icons/DefaultMediaTypeLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.23077,0,0,1.23077,-204.462,-187.385)"> - <path d="M350,200C249.341,200 166.667,282.674 166.667,383.333L166.667,1216.67C166.667,1317.33 249.341,1400 350,1400L1283.33,1400C1383.99,1400 1466.67,1317.33 1466.67,1216.67L1466.67,383.333C1466.67,282.674 1383.99,200 1283.33,200L350,200ZM350,300L1283.33,300C1329.94,300 1366.67,336.726 1366.67,383.333L1366.67,1216.67C1366.67,1263.27 1329.94,1300 1283.33,1300L350,1300C303.392,1300 266.667,1263.27 266.667,1216.67L266.667,383.333C266.667,336.726 303.392,300 350,300ZM366.667,350C348.267,350 333.333,364.933 333.333,383.333L333.333,416.667C333.333,435.067 348.267,450 366.667,450L400,450C418.4,450 433.333,435.067 433.333,416.667L433.333,383.333C433.333,364.933 418.4,350 400,350L366.667,350ZM1233.33,350C1214.93,350 1200,364.933 1200,383.333L1200,416.667C1200,435.067 1214.93,450 1233.33,450L1266.67,450C1285.07,450 1300,435.067 1300,416.667L1300,383.333C1300,364.933 1285.07,350 1266.67,350L1233.33,350ZM683.333,533.398C668.77,533.398 654.043,537.201 640.951,544.922C615.478,559.97 600,587.195 600,616.732L600,983.398C600,1012.87 615.401,1039.96 640.885,1055.01C666.823,1070.43 697.284,1070.78 723.503,1056.38L1056.84,873.047L1056.9,872.982C1083.33,858.401 1100,830.405 1100,800.065C1100,769.725 1083.33,741.665 1056.9,727.083L1056.84,727.018L723.438,543.685L723.372,543.62C710.907,536.807 697.1,533.398 683.333,533.398ZM366.667,550C348.267,550 333.333,564.933 333.333,583.333L333.333,616.667C333.333,635.067 348.267,650 366.667,650L400,650C418.4,650 433.333,635.067 433.333,616.667L433.333,583.333C433.333,564.933 418.4,550 400,550L366.667,550ZM1233.33,550C1214.93,550 1200,564.933 1200,583.333L1200,616.667C1200,635.067 1214.93,650 1233.33,650L1266.67,650C1285.07,650 1300,635.067 1300,616.667L1300,583.333C1300,564.933 1285.07,550 1266.67,550L1233.33,550ZM700,644.857L982.096,800L700,955.208L700,644.857ZM366.667,750C348.267,750 333.333,764.933 333.333,783.333L333.333,816.667C333.333,835.067 348.267,850 366.667,850L400,850C418.4,850 433.333,835.067 433.333,816.667L433.333,783.333C433.333,764.933 418.4,750 400,750L366.667,750ZM1233.33,750C1214.93,750 1200,764.933 1200,783.333L1200,816.667C1200,835.067 1214.93,850 1233.33,850L1266.67,850C1285.07,850 1300,835.067 1300,816.667L1300,783.333C1300,764.933 1285.07,750 1266.67,750L1233.33,750ZM366.667,950C348.267,950 333.333,964.933 333.333,983.333L333.333,1016.67C333.333,1035.07 348.267,1050 366.667,1050L400,1050C418.4,1050 433.333,1035.07 433.333,1016.67L433.333,983.333C433.333,964.933 418.4,950 400,950L366.667,950ZM1233.33,950C1214.93,950 1200,964.933 1200,983.333L1200,1016.67C1200,1035.07 1214.93,1050 1233.33,1050L1266.67,1050C1285.07,1050 1300,1035.07 1300,1016.67L1300,983.333C1300,964.933 1285.07,950 1266.67,950L1233.33,950ZM366.667,1150C348.267,1150 333.333,1164.93 333.333,1183.33L333.333,1216.67C333.333,1235.07 348.267,1250 366.667,1250L400,1250C418.4,1250 433.333,1235.07 433.333,1216.67L433.333,1183.33C433.333,1164.93 418.4,1150 400,1150L366.667,1150ZM1233.33,1150C1214.93,1150 1200,1164.93 1200,1183.33L1200,1216.67C1200,1235.07 1214.93,1250 1233.33,1250L1266.67,1250C1285.07,1250 1300,1235.07 1300,1216.67L1300,1183.33C1300,1164.93 1285.07,1150 1266.67,1150L1233.33,1150Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DefaultVideoExtensionDark.svg b/VDownload/Assets/Icons/DefaultVideoExtensionDark.svg deleted file mode 100644 index 56ab229..0000000 --- a/VDownload/Assets/Icons/DefaultVideoExtensionDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036ZM650,733.398C635.437,733.398 620.71,737.201 607.617,744.922C582.145,759.97 566.667,787.13 566.667,816.667L566.667,1183.33C566.667,1212.77 582.052,1239.89 607.487,1254.95C633.437,1270.4 663.932,1270.79 690.169,1256.38L1023.5,1072.98L1023.57,1072.98C1050,1058.4 1066.67,1030.34 1066.67,1000C1066.67,969.66 1050,941.6 1023.57,927.018L1023.5,927.018L690.104,743.62L690.039,743.62C677.572,736.804 663.766,733.398 650,733.398ZM666.667,844.792L948.828,1000L666.667,1155.21L666.667,844.792ZM641.992,1168.68C642.039,1168.66 642.076,1168.71 642.122,1168.68L641.992,1168.75L641.992,1168.68Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DefaultVideoExtensionLight.svg b/VDownload/Assets/Icons/DefaultVideoExtensionLight.svg deleted file mode 100644 index 48afad2..0000000 --- a/VDownload/Assets/Icons/DefaultVideoExtensionLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036ZM650,733.398C635.437,733.398 620.71,737.201 607.617,744.922C582.145,759.97 566.667,787.13 566.667,816.667L566.667,1183.33C566.667,1212.77 582.052,1239.89 607.487,1254.95C633.437,1270.4 663.932,1270.79 690.169,1256.38L1023.5,1072.98L1023.57,1072.98C1050,1058.4 1066.67,1030.34 1066.67,1000C1066.67,969.66 1050,941.6 1023.57,927.018L1023.5,927.018L690.104,743.62L690.039,743.62C677.572,736.804 663.766,733.398 650,733.398ZM666.667,844.792L948.828,1000L666.667,1155.21L666.667,844.792ZM641.992,1168.68C642.039,1168.66 642.076,1168.71 642.122,1168.68L641.992,1168.75L641.992,1168.68Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorDark.svg b/VDownload/Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorDark.svg deleted file mode 100644 index 09f6159..0000000 --- a/VDownload/Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1405 1405"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M800,133.333C690.138,133.333 600,223.471 600,333.333L250,333.333C249.764,333.33 249.529,333.328 249.293,333.328C221.861,333.328 199.288,355.901 199.288,383.333C199.288,410.765 221.861,433.338 249.293,433.338C249.529,433.338 249.764,433.337 250,433.333L333.333,433.333L333.333,1283.33C333.333,1384.43 415.567,1466.67 516.667,1466.67L854.427,1466.67C825.927,1437.03 801.687,1403.43 782.487,1366.67L516.667,1366.67C470.733,1366.67 433.333,1329.27 433.333,1283.33L433.333,433.333L1166.67,433.333L1166.67,733.333C1201.13,733.333 1234.5,737.778 1266.67,745.378L1266.67,433.333L1350,433.333C1350.24,433.337 1350.47,433.338 1350.71,433.338C1378.14,433.338 1400.71,410.765 1400.71,383.333C1400.71,355.901 1378.14,333.328 1350.71,333.328C1350.47,333.328 1350.24,333.33 1350,333.333L1000,333.333C1000,223.471 909.862,133.333 800,133.333ZM800,233.333C855.805,233.333 900,277.529 900,333.333L700,333.333C700,277.529 744.196,233.333 800,233.333ZM682.552,599.284C655.148,599.712 632.94,622.595 633.333,650L633.333,1150C633.33,1150.24 633.328,1150.47 633.328,1150.71C633.328,1178.14 655.901,1200.71 683.333,1200.71C710.765,1200.71 733.338,1178.14 733.338,1150.71C733.338,1150.47 733.337,1150.24 733.333,1150L733.333,650C733.337,649.761 733.338,649.522 733.338,649.283C733.338,621.851 710.765,599.278 683.333,599.278C683.073,599.278 682.813,599.28 682.552,599.284ZM916.667,600C889.033,600 866.667,622.367 866.667,650L866.667,854.427C896.3,825.927 929.9,801.687 966.667,782.487L966.667,650C966.667,622.367 944.3,600 916.667,600ZM1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1000,966.667C1008.52,966.667 1017.05,969.916 1023.57,976.432L1166.67,1119.53L1309.77,976.432C1322.8,963.399 1343.87,963.399 1356.9,976.432C1369.93,989.466 1369.93,1010.53 1356.9,1023.57L1213.8,1166.67L1356.9,1309.77C1369.93,1322.8 1369.93,1343.87 1356.9,1356.9C1350.4,1363.4 1341.87,1366.67 1333.33,1366.67C1324.8,1366.67 1316.27,1363.4 1309.77,1356.9L1166.67,1213.8L1023.57,1356.9C1017.07,1363.4 1008.53,1366.67 1000,1366.67C991.467,1366.67 982.932,1363.4 976.432,1356.9C963.399,1343.87 963.399,1322.8 976.432,1309.77L1119.53,1166.67L976.432,1023.57C963.399,1010.53 963.399,989.466 976.432,976.432C982.949,969.916 991.475,966.667 1000,966.667Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorLight.svg b/VDownload/Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorLight.svg deleted file mode 100644 index 37dad85..0000000 --- a/VDownload/Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1405 1405"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M800,133.333C690.138,133.333 600,223.471 600,333.333L250,333.333C249.764,333.33 249.529,333.328 249.293,333.328C221.861,333.328 199.288,355.901 199.288,383.333C199.288,410.765 221.861,433.338 249.293,433.338C249.529,433.338 249.764,433.337 250,433.333L333.333,433.333L333.333,1283.33C333.333,1384.43 415.567,1466.67 516.667,1466.67L854.427,1466.67C825.927,1437.03 801.687,1403.43 782.487,1366.67L516.667,1366.67C470.733,1366.67 433.333,1329.27 433.333,1283.33L433.333,433.333L1166.67,433.333L1166.67,733.333C1201.13,733.333 1234.5,737.778 1266.67,745.378L1266.67,433.333L1350,433.333C1350.24,433.337 1350.47,433.338 1350.71,433.338C1378.14,433.338 1400.71,410.765 1400.71,383.333C1400.71,355.901 1378.14,333.328 1350.71,333.328C1350.47,333.328 1350.24,333.33 1350,333.333L1000,333.333C1000,223.471 909.862,133.333 800,133.333ZM800,233.333C855.805,233.333 900,277.529 900,333.333L700,333.333C700,277.529 744.196,233.333 800,233.333ZM682.552,599.284C655.148,599.712 632.94,622.595 633.333,650L633.333,1150C633.33,1150.24 633.328,1150.47 633.328,1150.71C633.328,1178.14 655.901,1200.71 683.333,1200.71C710.765,1200.71 733.338,1178.14 733.338,1150.71C733.338,1150.47 733.337,1150.24 733.333,1150L733.333,650C733.337,649.761 733.338,649.522 733.338,649.283C733.338,621.851 710.765,599.278 683.333,599.278C683.073,599.278 682.813,599.28 682.552,599.284ZM916.667,600C889.033,600 866.667,622.367 866.667,650L866.667,854.427C896.3,825.927 929.9,801.687 966.667,782.487L966.667,650C966.667,622.367 944.3,600 916.667,600ZM1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1000,966.667C1008.52,966.667 1017.05,969.916 1023.57,976.432L1166.67,1119.53L1309.77,976.432C1322.8,963.399 1343.87,963.399 1356.9,976.432C1369.93,989.466 1369.93,1010.53 1356.9,1023.57L1213.8,1166.67L1356.9,1309.77C1369.93,1322.8 1369.93,1343.87 1356.9,1356.9C1350.4,1363.4 1341.87,1366.67 1333.33,1366.67C1324.8,1366.67 1316.27,1363.4 1309.77,1356.9L1166.67,1213.8L1023.57,1356.9C1017.07,1363.4 1008.53,1366.67 1000,1366.67C991.467,1366.67 982.932,1363.4 976.432,1356.9C963.399,1343.87 963.399,1322.8 976.432,1309.77L1119.53,1166.67L976.432,1023.57C963.399,1010.53 963.399,989.466 976.432,976.432C982.949,969.916 991.475,966.667 1000,966.667Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DeleteTemporaryFilesOnStartDark.svg b/VDownload/Assets/Icons/DeleteTemporaryFilesOnStartDark.svg deleted file mode 100644 index 60e3d88..0000000 --- a/VDownload/Assets/Icons/DeleteTemporaryFilesOnStartDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1330 1330"> <g transform="matrix(1,0,0,1,-135,-135)"> - <path d="M800,133.333C683.056,133.333 585.68,220.714 569.336,333.333L341.276,333.333C338.437,332.848 335.562,332.608 332.682,332.617C330.194,332.671 327.713,332.91 325.26,333.333L216.667,333.333C216.431,333.33 216.195,333.328 215.96,333.328C188.528,333.328 165.955,355.901 165.955,383.333C165.955,410.765 188.528,433.338 215.96,433.338C216.195,433.338 216.431,433.337 216.667,433.333L287.956,433.333L371.875,1300.98C380.911,1394.53 460.393,1466.67 554.362,1466.67L1045.57,1466.67C1139.55,1466.67 1219.03,1394.54 1228.06,1300.98L1312.04,433.333L1383.33,433.333C1383.57,433.337 1383.81,433.338 1384.04,433.338C1411.47,433.338 1434.05,410.765 1434.05,383.333C1434.05,355.901 1411.47,333.328 1384.04,333.328C1383.81,333.328 1383.57,333.33 1383.33,333.333L1274.81,333.333C1269.5,332.473 1264.09,332.473 1258.79,333.333L1030.66,333.333C1014.32,220.714 916.944,133.333 800,133.333ZM800,233.333C862.639,233.333 914.026,275.605 928.711,333.333L671.289,333.333C685.974,275.605 737.361,233.333 800,233.333ZM388.346,433.333L1211.59,433.333L1128.52,1291.34C1124.35,1334.51 1088.93,1366.67 1045.57,1366.67L554.362,1366.67C511.065,1366.67 475.583,1334.45 471.419,1291.34L388.346,433.333ZM682.552,599.284C655.148,599.712 632.94,622.595 633.333,650L633.333,1150C633.33,1150.24 633.328,1150.47 633.328,1150.71C633.328,1178.14 655.901,1200.71 683.333,1200.71C710.765,1200.71 733.338,1178.14 733.338,1150.71C733.338,1150.47 733.337,1150.24 733.333,1150L733.333,650C733.337,649.761 733.338,649.522 733.338,649.283C733.338,621.851 710.765,599.278 683.333,599.278C683.073,599.278 682.813,599.28 682.552,599.284ZM915.885,599.284C888.481,599.712 866.274,622.595 866.667,650L866.667,1150C866.663,1150.24 866.662,1150.47 866.662,1150.71C866.662,1178.14 889.235,1200.71 916.667,1200.71C944.099,1200.71 966.672,1178.14 966.672,1150.71C966.672,1150.47 966.67,1150.24 966.667,1150L966.667,650C966.67,649.761 966.672,649.522 966.672,649.283C966.672,621.851 944.099,599.278 916.667,599.278C916.406,599.278 916.146,599.28 915.885,599.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DeleteTemporaryFilesOnStartLight.svg b/VDownload/Assets/Icons/DeleteTemporaryFilesOnStartLight.svg deleted file mode 100644 index 68bfbb3..0000000 --- a/VDownload/Assets/Icons/DeleteTemporaryFilesOnStartLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1330 1330"> <g transform="matrix(1,0,0,1,-135,-135)"> - <path d="M800,133.333C683.056,133.333 585.68,220.714 569.336,333.333L341.276,333.333C338.437,332.848 335.562,332.608 332.682,332.617C330.194,332.671 327.713,332.91 325.26,333.333L216.667,333.333C216.431,333.33 216.195,333.328 215.96,333.328C188.528,333.328 165.955,355.901 165.955,383.333C165.955,410.765 188.528,433.338 215.96,433.338C216.195,433.338 216.431,433.337 216.667,433.333L287.956,433.333L371.875,1300.98C380.911,1394.53 460.393,1466.67 554.362,1466.67L1045.57,1466.67C1139.55,1466.67 1219.03,1394.54 1228.06,1300.98L1312.04,433.333L1383.33,433.333C1383.57,433.337 1383.81,433.338 1384.04,433.338C1411.47,433.338 1434.05,410.765 1434.05,383.333C1434.05,355.901 1411.47,333.328 1384.04,333.328C1383.81,333.328 1383.57,333.33 1383.33,333.333L1274.81,333.333C1269.5,332.473 1264.09,332.473 1258.79,333.333L1030.66,333.333C1014.32,220.714 916.944,133.333 800,133.333ZM800,233.333C862.639,233.333 914.026,275.605 928.711,333.333L671.289,333.333C685.974,275.605 737.361,233.333 800,233.333ZM388.346,433.333L1211.59,433.333L1128.52,1291.34C1124.35,1334.51 1088.93,1366.67 1045.57,1366.67L554.362,1366.67C511.065,1366.67 475.583,1334.45 471.419,1291.34L388.346,433.333ZM682.552,599.284C655.148,599.712 632.94,622.595 633.333,650L633.333,1150C633.33,1150.24 633.328,1150.47 633.328,1150.71C633.328,1178.14 655.901,1200.71 683.333,1200.71C710.765,1200.71 733.338,1178.14 733.338,1150.71C733.338,1150.47 733.337,1150.24 733.333,1150L733.333,650C733.337,649.761 733.338,649.522 733.338,649.283C733.338,621.851 710.765,599.278 683.333,599.278C683.073,599.278 682.813,599.28 682.552,599.284ZM915.885,599.284C888.481,599.712 866.274,622.595 866.667,650L866.667,1150C866.663,1150.24 866.662,1150.47 866.662,1150.71C866.662,1178.14 889.235,1200.71 916.667,1200.71C944.099,1200.71 966.672,1178.14 966.672,1150.71C966.672,1150.47 966.67,1150.24 966.667,1150L966.667,650C966.67,649.761 966.672,649.522 966.672,649.283C966.672,621.851 944.099,599.278 916.667,599.278C916.406,599.278 916.146,599.28 915.885,599.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DurationDark.svg b/VDownload/Assets/Icons/DurationDark.svg deleted file mode 100644 index daf5f90..0000000 --- a/VDownload/Assets/Icons/DurationDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-163.7,-163.7)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM782.552,399.284C755.148,399.712 732.94,422.595 733.333,450L733.333,850C733.336,877.428 755.905,899.997 783.333,900L1050,900C1050.24,900.003 1050.47,900.005 1050.71,900.005C1078.14,900.005 1100.71,877.432 1100.71,850C1100.71,822.568 1078.14,799.995 1050.71,799.995C1050.47,799.995 1050.24,799.997 1050,800L833.333,800L833.333,450C833.337,449.761 833.338,449.522 833.338,449.283C833.338,421.851 810.765,399.278 783.333,399.278C783.073,399.278 782.813,399.28 782.552,399.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/DurationLight.svg b/VDownload/Assets/Icons/DurationLight.svg deleted file mode 100644 index a94f5d6..0000000 --- a/VDownload/Assets/Icons/DurationLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-163.7,-163.7)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM782.552,399.284C755.148,399.712 732.94,422.595 733.333,450L733.333,850C733.336,877.428 755.905,899.997 783.333,900L1050,900C1050.24,900.003 1050.47,900.005 1050.71,900.005C1078.14,900.005 1100.71,877.432 1100.71,850C1100.71,822.568 1078.14,799.995 1050.71,799.995C1050.47,799.995 1050.24,799.997 1050,800L833.333,800L833.333,450C833.337,449.761 833.338,449.522 833.338,449.283C833.338,421.851 810.765,399.278 783.333,399.278C783.073,399.278 782.813,399.28 782.552,399.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/EditingAlgorithmDark.svg b/VDownload/Assets/Icons/EditingAlgorithmDark.svg deleted file mode 100644 index 6b7142c..0000000 --- a/VDownload/Assets/Icons/EditingAlgorithmDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.11651,0,0,1.11651,-114.651,-186.605)"> - <path d="M283.333,233.333C182.233,233.333 100,315.6 100,416.667L100,1183.33C100,1284.4 182.233,1366.67 283.333,1366.67L733.333,1366.67C733.333,1330.87 741.718,1297.03 756.185,1266.67L283.333,1266.67C237.4,1266.67 200,1229.3 200,1183.33L200,416.667C200,370.7 237.4,333.333 283.333,333.333L1316.67,333.333C1362.6,333.333 1400,370.7 1400,416.667L1400,701.693C1418.73,704.393 1437.12,711.141 1453.32,723.307C1479.05,742.607 1494.56,770.5 1498.83,800L1500,800L1500,416.667C1500,315.6 1417.77,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1386.2,765.625C1369.3,764.93 1353.15,772.857 1343.36,786.654L1158.98,1032.42L955.273,784.896C945.741,772.99 931.267,766.078 916.016,766.146C888.67,766.271 866.239,788.805 866.239,816.15C866.239,827.969 870.429,839.413 878.06,848.438L1097.2,1114.78L1025.72,1210.03C1007.54,1203.65 987.694,1200 966.667,1200C915.278,1200 870.405,1221.07 841.797,1253.26C813.189,1285.44 800,1326.39 800,1366.67C800,1406.94 813.189,1447.89 841.797,1480.08C870.405,1512.26 915.278,1533.33 966.667,1533.33C1018.06,1533.33 1062.93,1512.26 1091.54,1480.08C1120.14,1447.89 1133.33,1406.94 1133.33,1366.67C1133.33,1333.5 1123.82,1300.16 1104.69,1271.35L1162.57,1194.21L1227.41,1273.05C1209.06,1301.47 1200,1334.16 1200,1366.67C1200,1406.94 1213.19,1447.89 1241.8,1480.08C1270.4,1512.26 1315.28,1533.33 1366.67,1533.33C1418.06,1533.33 1462.93,1512.26 1491.54,1480.08C1520.14,1447.89 1533.33,1406.94 1533.33,1366.67C1533.33,1326.39 1520.14,1285.44 1491.54,1253.26C1462.93,1221.07 1418.06,1200 1366.67,1200C1344.9,1200 1324.42,1203.94 1305.73,1210.74L1224.35,1111.91L1423.31,846.68C1430.33,837.839 1434.15,826.876 1434.15,815.588C1434.15,788.931 1412.83,766.72 1386.2,765.625ZM283.333,1100C264.933,1100 250,1114.93 250,1133.33L250,1166.67C250,1185.07 264.933,1200 283.333,1200L316.667,1200C335.067,1200 350,1185.07 350,1166.67L350,1133.33C350,1114.93 335.067,1100 316.667,1100L283.333,1100ZM483.333,1100C464.933,1100 450,1114.93 450,1133.33L450,1166.67C450,1185.07 464.933,1200 483.333,1200L516.667,1200C535.067,1200 550,1185.07 550,1166.67L550,1133.33C550,1114.93 535.067,1100 516.667,1100L483.333,1100ZM683.333,1100C664.933,1100 650,1114.93 650,1133.33L650,1166.67C650,1185.07 664.933,1200 683.333,1200L716.667,1200C735.067,1200 750,1185.07 750,1166.67L750,1133.33C750,1114.93 735.067,1100 716.667,1100L683.333,1100ZM966.667,1300C979.861,1300 989.903,1302.06 997.852,1305.53C1003.13,1312.89 1010.31,1318.67 1018.62,1322.27C1027.7,1333.76 1033.33,1349.82 1033.33,1366.67C1033.33,1384.72 1027.08,1402.11 1016.8,1413.67C1006.52,1425.24 993.056,1433.33 966.667,1433.33C940.278,1433.33 926.817,1425.24 916.536,1413.67C906.256,1402.11 900,1384.72 900,1366.67C900,1348.61 906.256,1331.23 916.536,1319.66C926.817,1308.1 940.278,1300 966.667,1300ZM1366.67,1300C1393.06,1300 1406.52,1308.1 1416.8,1319.66C1427.08,1331.23 1433.33,1348.61 1433.33,1366.67C1433.33,1384.72 1427.08,1402.11 1416.8,1413.67C1406.52,1425.24 1393.06,1433.33 1366.67,1433.33C1340.28,1433.33 1326.82,1425.24 1316.54,1413.67C1306.26,1402.11 1300,1384.72 1300,1366.67C1300,1349.41 1305.81,1332.89 1315.3,1321.35C1322.81,1317.95 1329.37,1312.74 1334.38,1306.18C1342.54,1302.35 1352.73,1300 1366.67,1300Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/EditingAlgorithmLight.svg b/VDownload/Assets/Icons/EditingAlgorithmLight.svg deleted file mode 100644 index 0b7d608..0000000 --- a/VDownload/Assets/Icons/EditingAlgorithmLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.11651,0,0,1.11651,-114.651,-186.605)"> - <path d="M283.333,233.333C182.233,233.333 100,315.6 100,416.667L100,1183.33C100,1284.4 182.233,1366.67 283.333,1366.67L733.333,1366.67C733.333,1330.87 741.718,1297.03 756.185,1266.67L283.333,1266.67C237.4,1266.67 200,1229.3 200,1183.33L200,416.667C200,370.7 237.4,333.333 283.333,333.333L1316.67,333.333C1362.6,333.333 1400,370.7 1400,416.667L1400,701.693C1418.73,704.393 1437.12,711.141 1453.32,723.307C1479.05,742.607 1494.56,770.5 1498.83,800L1500,800L1500,416.667C1500,315.6 1417.77,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1386.2,765.625C1369.3,764.93 1353.15,772.857 1343.36,786.654L1158.98,1032.42L955.273,784.896C945.741,772.99 931.267,766.078 916.016,766.146C888.67,766.271 866.239,788.805 866.239,816.15C866.239,827.969 870.429,839.413 878.06,848.438L1097.2,1114.78L1025.72,1210.03C1007.54,1203.65 987.694,1200 966.667,1200C915.278,1200 870.405,1221.07 841.797,1253.26C813.189,1285.44 800,1326.39 800,1366.67C800,1406.94 813.189,1447.89 841.797,1480.08C870.405,1512.26 915.278,1533.33 966.667,1533.33C1018.06,1533.33 1062.93,1512.26 1091.54,1480.08C1120.14,1447.89 1133.33,1406.94 1133.33,1366.67C1133.33,1333.5 1123.82,1300.16 1104.69,1271.35L1162.57,1194.21L1227.41,1273.05C1209.06,1301.47 1200,1334.16 1200,1366.67C1200,1406.94 1213.19,1447.89 1241.8,1480.08C1270.4,1512.26 1315.28,1533.33 1366.67,1533.33C1418.06,1533.33 1462.93,1512.26 1491.54,1480.08C1520.14,1447.89 1533.33,1406.94 1533.33,1366.67C1533.33,1326.39 1520.14,1285.44 1491.54,1253.26C1462.93,1221.07 1418.06,1200 1366.67,1200C1344.9,1200 1324.42,1203.94 1305.73,1210.74L1224.35,1111.91L1423.31,846.68C1430.33,837.839 1434.15,826.876 1434.15,815.588C1434.15,788.931 1412.83,766.72 1386.2,765.625ZM283.333,1100C264.933,1100 250,1114.93 250,1133.33L250,1166.67C250,1185.07 264.933,1200 283.333,1200L316.667,1200C335.067,1200 350,1185.07 350,1166.67L350,1133.33C350,1114.93 335.067,1100 316.667,1100L283.333,1100ZM483.333,1100C464.933,1100 450,1114.93 450,1133.33L450,1166.67C450,1185.07 464.933,1200 483.333,1200L516.667,1200C535.067,1200 550,1185.07 550,1166.67L550,1133.33C550,1114.93 535.067,1100 516.667,1100L483.333,1100ZM683.333,1100C664.933,1100 650,1114.93 650,1133.33L650,1166.67C650,1185.07 664.933,1200 683.333,1200L716.667,1200C735.067,1200 750,1185.07 750,1166.67L750,1133.33C750,1114.93 735.067,1100 716.667,1100L683.333,1100ZM966.667,1300C979.861,1300 989.903,1302.06 997.852,1305.53C1003.13,1312.89 1010.31,1318.67 1018.62,1322.27C1027.7,1333.76 1033.33,1349.82 1033.33,1366.67C1033.33,1384.72 1027.08,1402.11 1016.8,1413.67C1006.52,1425.24 993.056,1433.33 966.667,1433.33C940.278,1433.33 926.817,1425.24 916.536,1413.67C906.256,1402.11 900,1384.72 900,1366.67C900,1348.61 906.256,1331.23 916.536,1319.66C926.817,1308.1 940.278,1300 966.667,1300ZM1366.67,1300C1393.06,1300 1406.52,1308.1 1416.8,1319.66C1427.08,1331.23 1433.33,1348.61 1433.33,1366.67C1433.33,1384.72 1427.08,1402.11 1416.8,1413.67C1406.52,1425.24 1393.06,1433.33 1366.67,1433.33C1340.28,1433.33 1326.82,1425.24 1316.54,1413.67C1306.26,1402.11 1300,1384.72 1300,1366.67C1300,1349.41 1305.81,1332.89 1315.3,1321.35C1322.81,1317.95 1329.37,1312.74 1334.38,1306.18C1342.54,1302.35 1352.73,1300 1366.67,1300Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/Error.svg b/VDownload/Assets/Icons/Error.svg deleted file mode 100644 index ea72c79..0000000 --- a/VDownload/Assets/Icons/Error.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="#f44336"><path d="m20.132 17.303-13.435-13.435c-.586-.586-1.536-.586-2.121 0l-.708.707c-.586.586-.586 1.536 0 2.121l13.435 13.435c.586.586 1.536.586 2.121 0l.707-.707c.587-.585.587-1.535.001-2.121z"/><path d="m17.303 3.868-13.435 13.435c-.586.586-.586 1.536 0 2.121l.707.707c.586.586 1.536.586 2.121 0l13.436-13.434c.586-.586.586-1.536 0-2.121l-.707-.707c-.586-.587-1.536-.587-2.122-.001z"/></g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/FileDark.svg b/VDownload/Assets/Icons/FileDark.svg deleted file mode 100644 index a302ca9..0000000 --- a/VDownload/Assets/Icons/FileDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-160.6,-160.7)"> - <path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/FileLight.svg b/VDownload/Assets/Icons/FileLight.svg deleted file mode 100644 index 8c80f0f..0000000 --- a/VDownload/Assets/Icons/FileLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-160.6,-160.7)"> - <path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/FilenameTemplateDark.svg b/VDownload/Assets/Icons/FilenameTemplateDark.svg deleted file mode 100644 index 33926df..0000000 --- a/VDownload/Assets/Icons/FilenameTemplateDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-114,-130)"> - <path d="M650,133.333C649.764,133.33 649.529,133.328 649.293,133.328C621.861,133.328 599.288,155.901 599.288,183.333C599.288,210.765 621.861,233.338 649.293,233.338C649.529,233.338 649.764,233.337 650,233.333L733.333,233.333L733.333,1366.67L650,1366.67C649.764,1366.66 649.529,1366.66 649.293,1366.66C621.861,1366.66 599.288,1389.24 599.288,1416.67C599.288,1444.1 621.861,1466.67 649.293,1466.67C649.529,1466.67 649.764,1466.67 650,1466.67L775.13,1466.67C780.498,1467.55 785.974,1467.55 791.341,1466.67L916.667,1466.67C916.902,1466.67 917.138,1466.67 917.374,1466.67C944.806,1466.67 967.379,1444.1 967.379,1416.67C967.379,1389.24 944.806,1366.66 917.374,1366.66C917.138,1366.66 916.902,1366.66 916.667,1366.67L833.333,1366.67L833.333,233.333L916.667,233.333C916.902,233.337 917.138,233.338 917.374,233.338C944.806,233.338 967.379,210.765 967.379,183.333C967.379,155.901 944.806,133.328 917.374,133.328C917.138,133.328 916.902,133.33 916.667,133.333L650,133.333ZM350,333.333C230.935,333.333 133.333,430.935 133.333,550L133.333,1050C133.333,1169.07 230.935,1266.67 350,1266.67L666.667,1266.67L666.667,1166.67L350,1166.67C284.998,1166.67 233.333,1115 233.333,1050L233.333,550C233.333,484.998 284.998,433.333 350,433.333L666.667,433.333L666.667,333.333L350,333.333ZM900,333.333L900,433.333L1216.67,433.333C1281.67,433.333 1333.33,484.998 1333.33,550L1333.33,1050C1333.33,1115 1281.67,1166.67 1216.67,1166.67L900,1166.67L900,1266.67L1216.67,1266.67C1335.73,1266.67 1433.33,1169.07 1433.33,1050L1433.33,550C1433.33,430.935 1335.73,333.333 1216.67,333.333L900,333.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/FilenameTemplateLight.svg b/VDownload/Assets/Icons/FilenameTemplateLight.svg deleted file mode 100644 index 3a9a9a9..0000000 --- a/VDownload/Assets/Icons/FilenameTemplateLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-114,-130)"> - <path d="M650,133.333C649.764,133.33 649.529,133.328 649.293,133.328C621.861,133.328 599.288,155.901 599.288,183.333C599.288,210.765 621.861,233.338 649.293,233.338C649.529,233.338 649.764,233.337 650,233.333L733.333,233.333L733.333,1366.67L650,1366.67C649.764,1366.66 649.529,1366.66 649.293,1366.66C621.861,1366.66 599.288,1389.24 599.288,1416.67C599.288,1444.1 621.861,1466.67 649.293,1466.67C649.529,1466.67 649.764,1466.67 650,1466.67L775.13,1466.67C780.498,1467.55 785.974,1467.55 791.341,1466.67L916.667,1466.67C916.902,1466.67 917.138,1466.67 917.374,1466.67C944.806,1466.67 967.379,1444.1 967.379,1416.67C967.379,1389.24 944.806,1366.66 917.374,1366.66C917.138,1366.66 916.902,1366.66 916.667,1366.67L833.333,1366.67L833.333,233.333L916.667,233.333C916.902,233.337 917.138,233.338 917.374,233.338C944.806,233.338 967.379,210.765 967.379,183.333C967.379,155.901 944.806,133.328 917.374,133.328C917.138,133.328 916.902,133.33 916.667,133.333L650,133.333ZM350,333.333C230.935,333.333 133.333,430.935 133.333,550L133.333,1050C133.333,1169.07 230.935,1266.67 350,1266.67L666.667,1266.67L666.667,1166.67L350,1166.67C284.998,1166.67 233.333,1115 233.333,1050L233.333,550C233.333,484.998 284.998,433.333 350,433.333L666.667,433.333L666.667,333.333L350,333.333ZM900,333.333L900,433.333L1216.67,433.333C1281.67,433.333 1333.33,484.998 1333.33,550L1333.33,1050C1333.33,1115 1281.67,1166.67 1216.67,1166.67L900,1166.67L900,1266.67L1216.67,1266.67C1335.73,1266.67 1433.33,1169.07 1433.33,1050L1433.33,550C1433.33,430.935 1335.73,333.333 1216.67,333.333L900,333.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/FilterDark.svg b/VDownload/Assets/Icons/FilterDark.svg deleted file mode 100644 index 86669a0..0000000 --- a/VDownload/Assets/Icons/FilterDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.33333,0,0,1.33333,-266.667,-266.663)"> - <path d="M283.333,200C237.88,200 200,237.88 200,283.333L200,386.328C200,462.883 235.183,535.295 295.313,582.682L633.333,849.023L633.333,1350C633.347,1377.42 655.916,1399.98 683.338,1399.98C693.745,1399.98 703.897,1396.73 712.37,1390.69L945.703,1224.02C958.842,1214.65 966.661,1199.47 966.667,1183.33L966.667,848.958L1304.69,582.682C1364.82,535.295 1400,462.883 1400,386.328L1400,283.333C1400,237.88 1362.12,200 1316.67,200L283.333,200ZM300,300L1300,300L1300,386.328C1300,432.306 1278.98,475.623 1242.84,504.102L909.57,766.667L690.43,766.667L357.161,504.102C321.025,475.623 300,432.306 300,386.328L300,300ZM733.333,866.667L866.667,866.667L866.667,1157.62L733.333,1252.87L733.333,866.667Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/FilterLight.svg b/VDownload/Assets/Icons/FilterLight.svg deleted file mode 100644 index 420051c..0000000 --- a/VDownload/Assets/Icons/FilterLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.33333,0,0,1.33333,-266.667,-266.663)"> - <path d="M283.333,200C237.88,200 200,237.88 200,283.333L200,386.328C200,462.883 235.183,535.295 295.313,582.682L633.333,849.023L633.333,1350C633.347,1377.42 655.916,1399.98 683.338,1399.98C693.745,1399.98 703.897,1396.73 712.37,1390.69L945.703,1224.02C958.842,1214.65 966.661,1199.47 966.667,1183.33L966.667,848.958L1304.69,582.682C1364.82,535.295 1400,462.883 1400,386.328L1400,283.333C1400,237.88 1362.12,200 1316.67,200L283.333,200ZM300,300L1300,300L1300,386.328C1300,432.306 1278.98,475.623 1242.84,504.102L909.57,766.667L690.43,766.667L357.161,504.102C321.025,475.623 300,432.306 300,386.328L300,300ZM733.333,866.667L866.667,866.667L866.667,1157.62L733.333,1252.87L733.333,866.667Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/LastMediaLocationDark.svg b/VDownload/Assets/Icons/LastMediaLocationDark.svg deleted file mode 100644 index 26f50d6..0000000 --- a/VDownload/Assets/Icons/LastMediaLocationDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M283.333,266.667C201.083,266.667 133.333,334.417 133.333,416.667L133.333,1183.33C133.333,1265.58 201.083,1333.33 283.333,1333.33L1316.67,1333.33C1398.92,1333.33 1466.67,1265.58 1466.67,1183.33L1466.67,583.333C1466.67,501.083 1398.92,433.333 1316.67,433.333L801.432,433.333L652.474,309.18C652.452,309.18 652.431,309.18 652.409,309.18C619.47,281.737 577.972,266.667 535.091,266.667L283.333,266.667ZM283.333,366.667L535.091,366.667C554.61,366.667 573.419,373.512 588.411,386.003L751.302,521.745C760.292,529.236 771.632,533.338 783.333,533.333L1316.67,533.333C1344.88,533.333 1366.67,555.117 1366.67,583.333L1366.67,1183.33C1366.67,1211.55 1344.88,1233.33 1316.67,1233.33L283.333,1233.33C255.117,1233.33 233.333,1211.55 233.333,1183.33L233.333,416.667C233.333,388.45 255.117,366.667 283.333,366.667ZM815.885,632.617C788.481,633.045 766.274,655.928 766.667,683.333L766.667,762.63L710.612,706.576C701.2,696.883 688.25,691.407 674.74,691.406C647.312,691.413 624.747,713.984 624.747,741.411C624.747,754.919 630.22,767.867 639.909,777.279L695.964,833.333L616.667,833.333C616.431,833.33 616.195,833.328 615.96,833.328C588.528,833.328 565.955,855.901 565.955,883.333C565.955,910.765 588.528,933.338 615.96,933.338C616.195,933.338 616.431,933.337 616.667,933.333L695.964,933.333L639.909,989.388C630.092,998.813 624.536,1011.85 624.536,1025.46C624.536,1052.89 647.109,1075.46 674.541,1075.46C688.15,1075.46 701.187,1069.91 710.612,1060.09L766.667,1004.04L766.667,1083.33C766.663,1083.57 766.662,1083.81 766.662,1084.04C766.662,1111.47 789.235,1134.05 816.667,1134.05C844.099,1134.05 866.672,1111.47 866.672,1084.04C866.672,1083.81 866.67,1083.57 866.667,1083.33L866.667,1004.04L922.721,1060.09C932.147,1069.91 945.183,1075.46 958.793,1075.46C986.225,1075.46 1008.8,1052.89 1008.8,1025.46C1008.8,1011.85 1003.24,998.813 993.424,989.388L937.37,933.333L1016.67,933.333C1016.9,933.337 1017.14,933.338 1017.37,933.338C1044.81,933.338 1067.38,910.765 1067.38,883.333C1067.38,855.901 1044.81,833.328 1017.37,833.328C1017.14,833.328 1016.9,833.33 1016.67,833.333L937.37,833.333L993.424,777.279C1003.24,767.853 1008.8,754.817 1008.8,741.207C1008.8,713.775 986.225,691.202 958.793,691.202C945.183,691.202 932.147,696.758 922.721,706.576L866.667,762.63L866.667,683.333C866.67,683.094 866.672,682.855 866.672,682.616C866.672,655.184 844.099,632.611 816.667,632.611C816.406,632.611 816.146,632.613 815.885,632.617Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/LastMediaLocationLight.svg b/VDownload/Assets/Icons/LastMediaLocationLight.svg deleted file mode 100644 index 5917c91..0000000 --- a/VDownload/Assets/Icons/LastMediaLocationLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1340 1340"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M283.333,266.667C201.083,266.667 133.333,334.417 133.333,416.667L133.333,1183.33C133.333,1265.58 201.083,1333.33 283.333,1333.33L1316.67,1333.33C1398.92,1333.33 1466.67,1265.58 1466.67,1183.33L1466.67,583.333C1466.67,501.083 1398.92,433.333 1316.67,433.333L801.432,433.333L652.474,309.18C652.452,309.18 652.431,309.18 652.409,309.18C619.47,281.737 577.972,266.667 535.091,266.667L283.333,266.667ZM283.333,366.667L535.091,366.667C554.61,366.667 573.419,373.512 588.411,386.003L751.302,521.745C760.292,529.236 771.632,533.338 783.333,533.333L1316.67,533.333C1344.88,533.333 1366.67,555.117 1366.67,583.333L1366.67,1183.33C1366.67,1211.55 1344.88,1233.33 1316.67,1233.33L283.333,1233.33C255.117,1233.33 233.333,1211.55 233.333,1183.33L233.333,416.667C233.333,388.45 255.117,366.667 283.333,366.667ZM815.885,632.617C788.481,633.045 766.274,655.928 766.667,683.333L766.667,762.63L710.612,706.576C701.2,696.883 688.25,691.407 674.74,691.406C647.312,691.413 624.747,713.984 624.747,741.411C624.747,754.919 630.22,767.867 639.909,777.279L695.964,833.333L616.667,833.333C616.431,833.33 616.195,833.328 615.96,833.328C588.528,833.328 565.955,855.901 565.955,883.333C565.955,910.765 588.528,933.338 615.96,933.338C616.195,933.338 616.431,933.337 616.667,933.333L695.964,933.333L639.909,989.388C630.092,998.813 624.536,1011.85 624.536,1025.46C624.536,1052.89 647.109,1075.46 674.541,1075.46C688.15,1075.46 701.187,1069.91 710.612,1060.09L766.667,1004.04L766.667,1083.33C766.663,1083.57 766.662,1083.81 766.662,1084.04C766.662,1111.47 789.235,1134.05 816.667,1134.05C844.099,1134.05 866.672,1111.47 866.672,1084.04C866.672,1083.81 866.67,1083.57 866.667,1083.33L866.667,1004.04L922.721,1060.09C932.147,1069.91 945.183,1075.46 958.793,1075.46C986.225,1075.46 1008.8,1052.89 1008.8,1025.46C1008.8,1011.85 1003.24,998.813 993.424,989.388L937.37,933.333L1016.67,933.333C1016.9,933.337 1017.14,933.338 1017.37,933.338C1044.81,933.338 1067.38,910.765 1067.38,883.333C1067.38,855.901 1044.81,833.328 1017.37,833.328C1017.14,833.328 1016.9,833.33 1016.67,833.333L937.37,833.333L993.424,777.279C1003.24,767.853 1008.8,754.817 1008.8,741.207C1008.8,713.775 986.225,691.202 958.793,691.202C945.183,691.202 932.147,696.758 922.721,706.576L866.667,762.63L866.667,683.333C866.67,683.094 866.672,682.855 866.672,682.616C866.672,655.184 844.099,632.611 816.667,632.611C816.406,632.611 816.146,632.613 815.885,632.617Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/LocationDark.svg b/VDownload/Assets/Icons/LocationDark.svg deleted file mode 100644 index 9a2a55a..0000000 --- a/VDownload/Assets/Icons/LocationDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.19956,0,0,1.19956,-159.645,-159.608)"> - <path d="M800,133.333C487.642,133.333 233.333,387.642 233.333,700C233.333,840.226 284.777,968.828 369.271,1067.71L369.531,1067.97L369.727,1068.23C369.727,1068.23 610.791,1343.3 696.615,1425.19C754.086,1479.99 845.849,1479.99 903.32,1425.19C1001.15,1331.89 1230.34,1068.1 1230.34,1068.1L1230.47,1067.9L1230.66,1067.71C1315.23,968.827 1366.67,840.226 1366.67,700C1366.67,387.642 1112.36,133.333 800,133.333ZM800,233.333C1058.31,233.333 1266.67,441.692 1266.67,700C1266.67,815.84 1224.45,921.086 1154.62,1002.73C1154.17,1003.25 919.668,1271.45 834.31,1352.87C814.514,1371.74 785.421,1371.74 765.625,1352.87C694.286,1284.79 446.037,1003.56 445.313,1002.73L445.247,1002.67C375.511,921.028 333.333,815.808 333.333,700C333.333,441.692 541.692,233.333 800,233.333ZM800,500C737.5,500 684.294,525.238 650.13,563.672C615.967,602.106 600,651.389 600,700C600,748.611 615.967,797.894 650.13,836.328C684.294,874.762 737.5,900 800,900C862.5,900 915.706,874.762 949.87,836.328C984.033,797.894 1000,748.611 1000,700C1000,651.389 984.033,602.106 949.87,563.672C915.706,525.238 862.5,500 800,500ZM800,600C837.5,600 859.294,612.262 875.13,630.078C890.967,647.894 900,673.611 900,700C900,726.389 890.967,752.106 875.13,769.922C859.294,787.738 837.5,800 800,800C762.5,800 740.706,787.738 724.87,769.922C709.033,752.106 700,726.389 700,700C700,673.611 709.033,647.894 724.87,630.078C740.706,612.262 762.5,600 800,600Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/LocationLight.svg b/VDownload/Assets/Icons/LocationLight.svg deleted file mode 100644 index b4d0831..0000000 --- a/VDownload/Assets/Icons/LocationLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.19956,0,0,1.19956,-159.645,-159.608)"> - <path d="M800,133.333C487.642,133.333 233.333,387.642 233.333,700C233.333,840.226 284.777,968.828 369.271,1067.71L369.531,1067.97L369.727,1068.23C369.727,1068.23 610.791,1343.3 696.615,1425.19C754.086,1479.99 845.849,1479.99 903.32,1425.19C1001.15,1331.89 1230.34,1068.1 1230.34,1068.1L1230.47,1067.9L1230.66,1067.71C1315.23,968.827 1366.67,840.226 1366.67,700C1366.67,387.642 1112.36,133.333 800,133.333ZM800,233.333C1058.31,233.333 1266.67,441.692 1266.67,700C1266.67,815.84 1224.45,921.086 1154.62,1002.73C1154.17,1003.25 919.668,1271.45 834.31,1352.87C814.514,1371.74 785.421,1371.74 765.625,1352.87C694.286,1284.79 446.037,1003.56 445.313,1002.73L445.247,1002.67C375.511,921.028 333.333,815.808 333.333,700C333.333,441.692 541.692,233.333 800,233.333ZM800,500C737.5,500 684.294,525.238 650.13,563.672C615.967,602.106 600,651.389 600,700C600,748.611 615.967,797.894 650.13,836.328C684.294,874.762 737.5,900 800,900C862.5,900 915.706,874.762 949.87,836.328C984.033,797.894 1000,748.611 1000,700C1000,651.389 984.033,602.106 949.87,563.672C915.706,525.238 862.5,500 800,500ZM800,600C837.5,600 859.294,612.262 875.13,630.078C890.967,647.894 900,673.611 900,700C900,726.389 890.967,752.106 875.13,769.922C859.294,787.738 837.5,800 800,800C762.5,800 740.706,787.738 724.87,769.922C709.033,752.106 700,726.389 700,700C700,673.611 709.033,647.894 724.87,630.078C740.706,612.262 762.5,600 800,600Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/MediaTypeDark.svg b/VDownload/Assets/Icons/MediaTypeDark.svg deleted file mode 100644 index b711faa..0000000 --- a/VDownload/Assets/Icons/MediaTypeDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.23077,0,0,1.23077,-204.462,-187.385)"> - <path d="M350,200C249.341,200 166.667,282.674 166.667,383.333L166.667,1216.67C166.667,1317.33 249.341,1400 350,1400L1283.33,1400C1383.99,1400 1466.67,1317.33 1466.67,1216.67L1466.67,383.333C1466.67,282.674 1383.99,200 1283.33,200L350,200ZM350,300L1283.33,300C1329.94,300 1366.67,336.726 1366.67,383.333L1366.67,1216.67C1366.67,1263.27 1329.94,1300 1283.33,1300L350,1300C303.392,1300 266.667,1263.27 266.667,1216.67L266.667,383.333C266.667,336.726 303.392,300 350,300ZM366.667,350C348.267,350 333.333,364.933 333.333,383.333L333.333,416.667C333.333,435.067 348.267,450 366.667,450L400,450C418.4,450 433.333,435.067 433.333,416.667L433.333,383.333C433.333,364.933 418.4,350 400,350L366.667,350ZM1233.33,350C1214.93,350 1200,364.933 1200,383.333L1200,416.667C1200,435.067 1214.93,450 1233.33,450L1266.67,450C1285.07,450 1300,435.067 1300,416.667L1300,383.333C1300,364.933 1285.07,350 1266.67,350L1233.33,350ZM683.333,533.398C668.77,533.398 654.043,537.201 640.951,544.922C615.478,559.97 600,587.195 600,616.732L600,983.398C600,1012.87 615.401,1039.96 640.885,1055.01C666.823,1070.43 697.284,1070.78 723.503,1056.38L1056.84,873.047L1056.9,872.982C1083.33,858.401 1100,830.405 1100,800.065C1100,769.725 1083.33,741.665 1056.9,727.083L1056.84,727.018L723.438,543.685L723.372,543.62C710.907,536.807 697.1,533.398 683.333,533.398ZM366.667,550C348.267,550 333.333,564.933 333.333,583.333L333.333,616.667C333.333,635.067 348.267,650 366.667,650L400,650C418.4,650 433.333,635.067 433.333,616.667L433.333,583.333C433.333,564.933 418.4,550 400,550L366.667,550ZM1233.33,550C1214.93,550 1200,564.933 1200,583.333L1200,616.667C1200,635.067 1214.93,650 1233.33,650L1266.67,650C1285.07,650 1300,635.067 1300,616.667L1300,583.333C1300,564.933 1285.07,550 1266.67,550L1233.33,550ZM700,644.857L982.096,800L700,955.208L700,644.857ZM366.667,750C348.267,750 333.333,764.933 333.333,783.333L333.333,816.667C333.333,835.067 348.267,850 366.667,850L400,850C418.4,850 433.333,835.067 433.333,816.667L433.333,783.333C433.333,764.933 418.4,750 400,750L366.667,750ZM1233.33,750C1214.93,750 1200,764.933 1200,783.333L1200,816.667C1200,835.067 1214.93,850 1233.33,850L1266.67,850C1285.07,850 1300,835.067 1300,816.667L1300,783.333C1300,764.933 1285.07,750 1266.67,750L1233.33,750ZM366.667,950C348.267,950 333.333,964.933 333.333,983.333L333.333,1016.67C333.333,1035.07 348.267,1050 366.667,1050L400,1050C418.4,1050 433.333,1035.07 433.333,1016.67L433.333,983.333C433.333,964.933 418.4,950 400,950L366.667,950ZM1233.33,950C1214.93,950 1200,964.933 1200,983.333L1200,1016.67C1200,1035.07 1214.93,1050 1233.33,1050L1266.67,1050C1285.07,1050 1300,1035.07 1300,1016.67L1300,983.333C1300,964.933 1285.07,950 1266.67,950L1233.33,950ZM366.667,1150C348.267,1150 333.333,1164.93 333.333,1183.33L333.333,1216.67C333.333,1235.07 348.267,1250 366.667,1250L400,1250C418.4,1250 433.333,1235.07 433.333,1216.67L433.333,1183.33C433.333,1164.93 418.4,1150 400,1150L366.667,1150ZM1233.33,1150C1214.93,1150 1200,1164.93 1200,1183.33L1200,1216.67C1200,1235.07 1214.93,1250 1233.33,1250L1266.67,1250C1285.07,1250 1300,1235.07 1300,1216.67L1300,1183.33C1300,1164.93 1285.07,1150 1266.67,1150L1233.33,1150Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/MediaTypeLight.svg b/VDownload/Assets/Icons/MediaTypeLight.svg deleted file mode 100644 index 956bd74..0000000 --- a/VDownload/Assets/Icons/MediaTypeLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.23077,0,0,1.23077,-204.462,-187.385)"> - <path d="M350,200C249.341,200 166.667,282.674 166.667,383.333L166.667,1216.67C166.667,1317.33 249.341,1400 350,1400L1283.33,1400C1383.99,1400 1466.67,1317.33 1466.67,1216.67L1466.67,383.333C1466.67,282.674 1383.99,200 1283.33,200L350,200ZM350,300L1283.33,300C1329.94,300 1366.67,336.726 1366.67,383.333L1366.67,1216.67C1366.67,1263.27 1329.94,1300 1283.33,1300L350,1300C303.392,1300 266.667,1263.27 266.667,1216.67L266.667,383.333C266.667,336.726 303.392,300 350,300ZM366.667,350C348.267,350 333.333,364.933 333.333,383.333L333.333,416.667C333.333,435.067 348.267,450 366.667,450L400,450C418.4,450 433.333,435.067 433.333,416.667L433.333,383.333C433.333,364.933 418.4,350 400,350L366.667,350ZM1233.33,350C1214.93,350 1200,364.933 1200,383.333L1200,416.667C1200,435.067 1214.93,450 1233.33,450L1266.67,450C1285.07,450 1300,435.067 1300,416.667L1300,383.333C1300,364.933 1285.07,350 1266.67,350L1233.33,350ZM683.333,533.398C668.77,533.398 654.043,537.201 640.951,544.922C615.478,559.97 600,587.195 600,616.732L600,983.398C600,1012.87 615.401,1039.96 640.885,1055.01C666.823,1070.43 697.284,1070.78 723.503,1056.38L1056.84,873.047L1056.9,872.982C1083.33,858.401 1100,830.405 1100,800.065C1100,769.725 1083.33,741.665 1056.9,727.083L1056.84,727.018L723.438,543.685L723.372,543.62C710.907,536.807 697.1,533.398 683.333,533.398ZM366.667,550C348.267,550 333.333,564.933 333.333,583.333L333.333,616.667C333.333,635.067 348.267,650 366.667,650L400,650C418.4,650 433.333,635.067 433.333,616.667L433.333,583.333C433.333,564.933 418.4,550 400,550L366.667,550ZM1233.33,550C1214.93,550 1200,564.933 1200,583.333L1200,616.667C1200,635.067 1214.93,650 1233.33,650L1266.67,650C1285.07,650 1300,635.067 1300,616.667L1300,583.333C1300,564.933 1285.07,550 1266.67,550L1233.33,550ZM700,644.857L982.096,800L700,955.208L700,644.857ZM366.667,750C348.267,750 333.333,764.933 333.333,783.333L333.333,816.667C333.333,835.067 348.267,850 366.667,850L400,850C418.4,850 433.333,835.067 433.333,816.667L433.333,783.333C433.333,764.933 418.4,750 400,750L366.667,750ZM1233.33,750C1214.93,750 1200,764.933 1200,783.333L1200,816.667C1200,835.067 1214.93,850 1233.33,850L1266.67,850C1285.07,850 1300,835.067 1300,816.667L1300,783.333C1300,764.933 1285.07,750 1266.67,750L1233.33,750ZM366.667,950C348.267,950 333.333,964.933 333.333,983.333L333.333,1016.67C333.333,1035.07 348.267,1050 366.667,1050L400,1050C418.4,1050 433.333,1035.07 433.333,1016.67L433.333,983.333C433.333,964.933 418.4,950 400,950L366.667,950ZM1233.33,950C1214.93,950 1200,964.933 1200,983.333L1200,1016.67C1200,1035.07 1214.93,1050 1233.33,1050L1266.67,1050C1285.07,1050 1300,1035.07 1300,1016.67L1300,983.333C1300,964.933 1285.07,950 1266.67,950L1233.33,950ZM366.667,1150C348.267,1150 333.333,1164.93 333.333,1183.33L333.333,1216.67C333.333,1235.07 348.267,1250 366.667,1250L400,1250C418.4,1250 433.333,1235.07 433.333,1216.67L433.333,1183.33C433.333,1164.93 418.4,1150 400,1150L366.667,1150ZM1233.33,1150C1214.93,1150 1200,1164.93 1200,1183.33L1200,1216.67C1200,1235.07 1214.93,1250 1233.33,1250L1266.67,1250C1285.07,1250 1300,1235.07 1300,1216.67L1300,1183.33C1300,1164.93 1285.07,1150 1266.67,1150L1233.33,1150Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/MeteredConnectionDelayDark.svg b/VDownload/Assets/Icons/MeteredConnectionDelayDark.svg deleted file mode 100644 index b2fcf09..0000000 --- a/VDownload/Assets/Icons/MeteredConnectionDelayDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1405 1405"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M800,133.333C432.4,133.333 133.333,432.4 133.333,800C133.333,1167.6 432.4,1466.67 800,1466.67C817.567,1466.67 834.785,1465.4 852.018,1464.06C823.985,1434.4 800.148,1400.89 781.315,1364.26C709.282,1347.56 635.268,1236.47 595.768,1066.67L745.378,1066.67C753.711,1031.5 766.187,997.933 782.487,966.667L577.604,966.667C570.671,914.7 566.667,859.033 566.667,800C566.667,740.968 570.669,685.311 577.604,633.333L1022.13,633.333C1027.27,671.863 1030.85,712.315 1032.29,754.818C1063.92,744.518 1097.14,737.858 1131.51,735.091C1129.88,700.108 1126.52,666.392 1122.4,633.333L1340.82,633.333C1355.43,680.638 1364.4,730.476 1366.02,782.161C1401.98,800.861 1434.86,824.418 1464.06,852.018C1465.36,834.785 1466.67,817.567 1466.67,800C1466.67,432.4 1167.6,133.333 800,133.333ZM800,233.333C877.6,233.333 961.182,348.791 1004.04,533.333L595.768,533.333C638.685,348.784 722.362,233.333 800,233.333ZM594.336,272.526C550.644,340.228 516.359,429.907 494.206,533.333L300.716,533.333C364.249,415.192 468.24,321.877 594.336,272.526ZM1005.73,272.526C1131.83,321.882 1235.81,415.152 1299.22,533.333L1106.12,533.333C1084.07,429.686 1049.46,340.302 1005.73,272.526ZM258.594,633.333L477.083,633.333C470.442,686.473 466.667,742.143 466.667,800C466.667,857.867 470.548,913.467 477.148,966.667L258.464,966.667C242.197,913.933 233.333,858 233.333,800C233.333,741.97 242.293,686.045 258.594,633.333ZM1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1133.33,933.333C1151.73,933.333 1166.67,948.267 1166.67,966.667L1166.67,1166.67L1333.33,1166.67C1351.73,1166.67 1366.67,1181.6 1366.67,1200C1366.67,1218.4 1351.73,1233.33 1333.33,1233.33L1133.33,1233.33C1114.93,1233.33 1100,1218.4 1100,1200L1100,966.667C1100,948.267 1114.93,933.333 1133.33,933.333ZM300.26,1066.67L493.62,1066.67C515.72,1170.5 550.569,1259.61 594.336,1327.47C468.136,1278.07 363.727,1185.1 300.26,1066.67Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/MeteredConnectionDelayLight.svg b/VDownload/Assets/Icons/MeteredConnectionDelayLight.svg deleted file mode 100644 index 960e876..0000000 --- a/VDownload/Assets/Icons/MeteredConnectionDelayLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1405 1405"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M800,133.333C432.4,133.333 133.333,432.4 133.333,800C133.333,1167.6 432.4,1466.67 800,1466.67C817.567,1466.67 834.785,1465.4 852.018,1464.06C823.985,1434.4 800.148,1400.89 781.315,1364.26C709.282,1347.56 635.268,1236.47 595.768,1066.67L745.378,1066.67C753.711,1031.5 766.187,997.933 782.487,966.667L577.604,966.667C570.671,914.7 566.667,859.033 566.667,800C566.667,740.968 570.669,685.311 577.604,633.333L1022.13,633.333C1027.27,671.863 1030.85,712.315 1032.29,754.818C1063.92,744.518 1097.14,737.858 1131.51,735.091C1129.88,700.108 1126.52,666.392 1122.4,633.333L1340.82,633.333C1355.43,680.638 1364.4,730.476 1366.02,782.161C1401.98,800.861 1434.86,824.418 1464.06,852.018C1465.36,834.785 1466.67,817.567 1466.67,800C1466.67,432.4 1167.6,133.333 800,133.333ZM800,233.333C877.6,233.333 961.182,348.791 1004.04,533.333L595.768,533.333C638.685,348.784 722.362,233.333 800,233.333ZM594.336,272.526C550.644,340.228 516.359,429.907 494.206,533.333L300.716,533.333C364.249,415.192 468.24,321.877 594.336,272.526ZM1005.73,272.526C1131.83,321.882 1235.81,415.152 1299.22,533.333L1106.12,533.333C1084.07,429.686 1049.46,340.302 1005.73,272.526ZM258.594,633.333L477.083,633.333C470.442,686.473 466.667,742.143 466.667,800C466.667,857.867 470.548,913.467 477.148,966.667L258.464,966.667C242.197,913.933 233.333,858 233.333,800C233.333,741.97 242.293,686.045 258.594,633.333ZM1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1133.33,933.333C1151.73,933.333 1166.67,948.267 1166.67,966.667L1166.67,1166.67L1333.33,1166.67C1351.73,1166.67 1366.67,1181.6 1366.67,1200C1366.67,1218.4 1351.73,1233.33 1333.33,1233.33L1133.33,1233.33C1114.93,1233.33 1100,1218.4 1100,1200L1100,966.667C1100,948.267 1114.93,933.333 1133.33,933.333ZM300.26,1066.67L493.62,1066.67C515.72,1170.5 550.569,1259.61 594.336,1327.47C468.136,1278.07 363.727,1185.1 300.26,1066.67Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/MeteredConnectionWarningDark.svg b/VDownload/Assets/Icons/MeteredConnectionWarningDark.svg deleted file mode 100644 index c3547af..0000000 --- a/VDownload/Assets/Icons/MeteredConnectionWarningDark.svg +++ /dev/null @@ -1,9 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1404 1404"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M233.333,583.333L1366.67,583.333" style="fill:none;fill-rule:nonzero;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M781.3,1364.23C709.267,1347.53 635.267,1236.47 595.767,1066.67L745.4,1066.67C753.733,1031.5 766.167,997.933 782.467,966.667L577.6,966.667C570.667,914.7 566.667,859.033 566.667,800C566.667,466.033 689.633,233.333 800,233.333C905.267,233.333 1021.8,445.367 1032.3,754.833C1063.93,744.533 1097.13,737.867 1131.5,735.1C1122.67,545.4 1075.83,381.233 1005.7,272.533C1211.37,353.033 1358.73,550.033 1366,782.133C1401.97,800.833 1434.87,824.433 1464.07,852.033C1465.37,834.8 1466.67,817.567 1466.67,800C1466.67,432.4 1167.6,133.333 800,133.333C432.4,133.333 133.333,432.4 133.333,800C133.333,1167.6 432.4,1466.67 800,1466.67C817.567,1466.67 834.8,1465.37 852.033,1464.03C824,1434.37 800.133,1400.87 781.3,1364.23ZM233.333,800C233.333,560.133 383.367,355.1 594.333,272.533C516.267,393.5 466.667,582.833 466.667,800C466.667,857.867 470.567,913.467 477.167,966.667L258.433,966.667C242.167,913.933 233.333,858 233.333,800ZM300.267,1066.67L493.6,1066.67C515.7,1170.5 550.567,1259.6 594.333,1327.47C468.133,1278.07 363.733,1185.1 300.267,1066.67Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1133.33,966.667C1133.33,948.267 1148.23,933.333 1166.67,933.333C1185.1,933.333 1200,948.267 1200,966.667L1200,1233.33C1200,1251.73 1185.1,1266.67 1166.67,1266.67C1148.23,1266.67 1133.33,1251.73 1133.33,1233.33L1133.33,966.667ZM1166.67,1408.33C1143.67,1408.33 1125,1389.67 1125,1366.67C1125,1343.67 1143.67,1325 1166.67,1325C1189.67,1325 1208.33,1343.67 1208.33,1366.67C1208.33,1389.67 1189.67,1408.33 1166.67,1408.33Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/MeteredConnectionWarningLight.svg b/VDownload/Assets/Icons/MeteredConnectionWarningLight.svg deleted file mode 100644 index afbe339..0000000 --- a/VDownload/Assets/Icons/MeteredConnectionWarningLight.svg +++ /dev/null @@ -1,9 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1404 1404"><g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M233.333,583.333L1366.67,583.333" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M781.3,1364.23C709.267,1347.53 635.267,1236.47 595.767,1066.67L745.4,1066.67C753.733,1031.5 766.167,997.933 782.467,966.667L577.6,966.667C570.667,914.7 566.667,859.033 566.667,800C566.667,466.033 689.633,233.333 800,233.333C905.267,233.333 1021.8,445.367 1032.3,754.833C1063.93,744.533 1097.13,737.867 1131.5,735.1C1122.67,545.4 1075.83,381.233 1005.7,272.533C1211.37,353.033 1358.73,550.033 1366,782.133C1401.97,800.833 1434.87,824.433 1464.07,852.033C1465.37,834.8 1466.67,817.567 1466.67,800C1466.67,432.4 1167.6,133.333 800,133.333C432.4,133.333 133.333,432.4 133.333,800C133.333,1167.6 432.4,1466.67 800,1466.67C817.567,1466.67 834.8,1465.37 852.033,1464.03C824,1434.37 800.133,1400.87 781.3,1364.23ZM233.333,800C233.333,560.133 383.367,355.1 594.333,272.533C516.267,393.5 466.667,582.833 466.667,800C466.667,857.867 470.567,913.467 477.167,966.667L258.433,966.667C242.167,913.933 233.333,858 233.333,800ZM300.267,1066.67L493.6,1066.67C515.7,1170.5 550.567,1259.6 594.333,1327.47C468.133,1278.07 363.733,1185.1 300.267,1066.67Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1,0,0,1,-130,-130)"> - <path d="M1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1133.33,966.667C1133.33,948.267 1148.23,933.333 1166.67,933.333C1185.1,933.333 1200,948.267 1200,966.667L1200,1233.33C1200,1251.73 1185.1,1266.67 1166.67,1266.67C1148.23,1266.67 1133.33,1251.73 1133.33,1233.33L1133.33,966.667ZM1166.67,1408.33C1143.67,1408.33 1125,1389.67 1125,1366.67C1125,1343.67 1143.67,1325 1166.67,1325C1189.67,1325 1208.33,1343.67 1208.33,1366.67C1208.33,1389.67 1189.67,1408.33 1166.67,1408.33Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/QualityDark.svg b/VDownload/Assets/Icons/QualityDark.svg deleted file mode 100644 index 7e8c7a9..0000000 --- a/VDownload/Assets/Icons/QualityDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.14286,0,0,1.14286,-114.286,-114.286)"> - <path d="M283.333,233.333C182.674,233.333 100,316.008 100,416.667L100,1183.33C100,1283.99 182.674,1366.67 283.333,1366.67L1316.67,1366.67C1417.33,1366.67 1500,1283.99 1500,1183.33L1500,416.667C1500,316.008 1417.33,233.333 1316.67,233.333L283.333,233.333ZM283.333,333.333L716.667,333.333C763.274,333.333 800,370.059 800,416.667L800,716.667C800,763.274 763.274,800 716.667,800L283.333,800C236.726,800 200,763.274 200,716.667L200,416.667C200,410.841 200.597,405.139 201.693,399.674C209.359,361.422 242.552,333.333 283.333,333.333ZM879.557,333.333L1016.67,333.333C1063.27,333.333 1100,370.059 1100,416.667L1100,950C1100,996.608 1063.27,1033.33 1016.67,1033.33L283.333,1033.33C236.726,1033.33 200,996.608 200,950L200,879.557C225.076,892.51 253.36,900 283.333,900L716.667,900C817.326,900 900,817.326 900,716.667L900,416.667C900,386.693 892.51,358.409 879.557,333.333ZM1179.56,333.333L1316.67,333.333C1363.27,333.333 1400,370.059 1400,416.667L1400,1183.33C1400,1229.94 1363.27,1266.67 1316.67,1266.67L283.333,1266.67C236.726,1266.67 200,1229.94 200,1183.33L200,1112.89C225.076,1125.84 253.36,1133.33 283.333,1133.33L1016.67,1133.33C1117.33,1133.33 1200,1050.66 1200,950L1200,416.667C1200,386.693 1192.51,358.409 1179.56,333.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/QualityLight.svg b/VDownload/Assets/Icons/QualityLight.svg deleted file mode 100644 index b16c355..0000000 --- a/VDownload/Assets/Icons/QualityLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.14286,0,0,1.14286,-114.286,-114.286)"> - <path d="M283.333,233.333C182.674,233.333 100,316.008 100,416.667L100,1183.33C100,1283.99 182.674,1366.67 283.333,1366.67L1316.67,1366.67C1417.33,1366.67 1500,1283.99 1500,1183.33L1500,416.667C1500,316.008 1417.33,233.333 1316.67,233.333L283.333,233.333ZM283.333,333.333L716.667,333.333C763.274,333.333 800,370.059 800,416.667L800,716.667C800,763.274 763.274,800 716.667,800L283.333,800C236.726,800 200,763.274 200,716.667L200,416.667C200,410.841 200.597,405.139 201.693,399.674C209.359,361.422 242.552,333.333 283.333,333.333ZM879.557,333.333L1016.67,333.333C1063.27,333.333 1100,370.059 1100,416.667L1100,950C1100,996.608 1063.27,1033.33 1016.67,1033.33L283.333,1033.33C236.726,1033.33 200,996.608 200,950L200,879.557C225.076,892.51 253.36,900 283.333,900L716.667,900C817.326,900 900,817.326 900,716.667L900,416.667C900,386.693 892.51,358.409 879.557,333.333ZM1179.56,333.333L1316.67,333.333C1363.27,333.333 1400,370.059 1400,416.667L1400,1183.33C1400,1229.94 1363.27,1266.67 1316.67,1266.67L283.333,1266.67C236.726,1266.67 200,1229.94 200,1183.33L200,1112.89C225.076,1125.84 253.36,1133.33 283.333,1133.33L1016.67,1133.33C1117.33,1133.33 1200,1050.66 1200,950L1200,416.667C1200,386.693 1192.51,358.409 1179.56,333.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/RemoveSuccessfullyEndedTaskDark.svg b/VDownload/Assets/Icons/RemoveSuccessfullyEndedTaskDark.svg deleted file mode 100644 index c4ec375..0000000 --- a/VDownload/Assets/Icons/RemoveSuccessfullyEndedTaskDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1466 1466"><g transform="matrix(1,0,0,1,-67,0)"> - <path d="M800,0C708.545,0 633.333,75.211 633.333,166.667L633.333,200L416.667,200C334.417,200 266.667,267.75 266.667,350L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,350C1333.33,267.75 1265.58,200 1183.33,200L966.667,200L966.667,166.667C966.667,75.211 891.455,0 800,0ZM800,100C837.411,100 866.667,129.255 866.667,166.667L866.667,300L733.333,300L733.333,166.667C733.333,129.255 762.589,100 800,100ZM416.667,300L633.333,300L633.333,350C633.336,377.428 655.905,399.997 683.333,400L916.667,400C944.095,399.997 966.664,377.428 966.667,350L966.667,300L1183.33,300C1211.55,300 1233.33,321.783 1233.33,350L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,350C366.667,321.783 388.45,300 416.667,300ZM1065.69,632.878C1052.71,633.256 1040.37,638.676 1031.32,647.982L733.333,945.964L568.685,781.315C559.259,771.498 546.224,765.943 532.614,765.943C505.183,765.943 482.609,788.516 482.609,815.948C482.609,829.557 488.165,842.593 497.982,852.018L697.982,1052.02C717.378,1071.41 749.289,1071.41 768.685,1052.02L1102.02,718.685C1111.68,709.276 1117.13,696.348 1117.13,682.862C1117.13,655.43 1094.56,632.857 1067.13,632.857C1066.65,632.857 1066.17,632.864 1065.69,632.878Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/RemoveSuccessfullyEndedTaskLight.svg b/VDownload/Assets/Icons/RemoveSuccessfullyEndedTaskLight.svg deleted file mode 100644 index ad6d56e..0000000 --- a/VDownload/Assets/Icons/RemoveSuccessfullyEndedTaskLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1466 1466"><g transform="matrix(1,0,0,1,-67,0)"> - <path d="M800,0C708.545,0 633.333,75.211 633.333,166.667L633.333,200L416.667,200C334.417,200 266.667,267.75 266.667,350L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,350C1333.33,267.75 1265.58,200 1183.33,200L966.667,200L966.667,166.667C966.667,75.211 891.455,0 800,0ZM800,100C837.411,100 866.667,129.255 866.667,166.667L866.667,300L733.333,300L733.333,166.667C733.333,129.255 762.589,100 800,100ZM416.667,300L633.333,300L633.333,350C633.336,377.428 655.905,399.997 683.333,400L916.667,400C944.095,399.997 966.664,377.428 966.667,350L966.667,300L1183.33,300C1211.55,300 1233.33,321.783 1233.33,350L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,350C366.667,321.783 388.45,300 416.667,300ZM1065.69,632.878C1052.71,633.256 1040.37,638.676 1031.32,647.982L733.333,945.964L568.685,781.315C559.259,771.498 546.224,765.943 532.614,765.943C505.183,765.943 482.609,788.516 482.609,815.948C482.609,829.557 488.165,842.593 497.982,852.018L697.982,1052.02C717.378,1071.41 749.289,1071.41 768.685,1052.02L1102.02,718.685C1111.68,709.276 1117.13,696.348 1117.13,682.862C1117.13,655.43 1094.56,632.857 1067.13,632.857C1066.65,632.857 1066.17,632.864 1065.69,632.878Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ReplaceFileDark.svg b/VDownload/Assets/Icons/ReplaceFileDark.svg deleted file mode 100644 index be40b2d..0000000 --- a/VDownload/Assets/Icons/ReplaceFileDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1300 1300"><g transform="matrix(1,0,0,1,-149,-135)"> - <path d="M849.479,132.813C847.257,132.838 845.038,133.012 842.839,133.333L416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,916.667C266.663,916.902 266.662,917.138 266.662,917.374C266.662,944.806 289.235,967.379 316.667,967.379C344.099,967.379 366.672,944.806 366.672,917.374C366.672,917.138 366.67,916.902 366.667,916.667L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,883.333C1233.33,883.569 1233.33,883.805 1233.33,884.04C1233.33,911.472 1255.9,934.045 1283.33,934.045C1310.77,934.045 1333.34,911.472 1333.34,884.04C1333.34,883.805 1333.34,883.569 1333.33,883.333L1333.33,624.023C1335.29,609.755 1330.99,595.315 1321.55,584.44C1320.64,583.358 1319.68,582.316 1318.68,581.315L885.352,147.982C885.265,147.895 885.178,147.808 885.091,147.721C885.005,147.634 884.918,147.547 884.831,147.461C884.573,147.241 884.312,147.024 884.049,146.81C883.087,145.88 882.088,144.99 881.055,144.141C872.15,136.843 860.992,132.84 849.479,132.813ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036ZM1083.33,1000C1083.1,999.997 1082.86,999.995 1082.63,999.995C1055.19,999.995 1032.62,1022.57 1032.62,1050C1032.62,1077.43 1055.19,1100.01 1082.63,1100.01C1082.86,1100.01 1083.1,1100 1083.33,1100L1257.94,1100C1191.74,1177.95 1026.31,1333.33 759.049,1333.33C402.337,1333.33 260.807,1059.83 260.807,1059.83C252.224,1043.13 234.925,1032.61 216.146,1032.68C188.777,1032.77 166.308,1055.32 166.308,1082.69C166.308,1091.13 168.448,1099.44 172.526,1106.84C172.526,1106.84 350.095,1433.33 759.049,1433.33C1061.88,1433.33 1253.9,1256.34 1333.33,1164.39L1333.33,1350C1333.33,1350.24 1333.33,1350.47 1333.33,1350.71C1333.33,1378.14 1355.9,1400.71 1383.33,1400.71C1410.77,1400.71 1433.34,1378.14 1433.34,1350.71C1433.34,1350.47 1433.34,1350.24 1433.33,1350L1433.33,1050C1433.33,1022.57 1410.76,1000 1383.33,1000L1083.33,1000Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ReplaceFileLight.svg b/VDownload/Assets/Icons/ReplaceFileLight.svg deleted file mode 100644 index e16aab7..0000000 --- a/VDownload/Assets/Icons/ReplaceFileLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1300 1300"><g transform="matrix(1,0,0,1,-149,-135)"> - <path d="M849.479,132.813C847.257,132.838 845.038,133.012 842.839,133.333L416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,916.667C266.663,916.902 266.662,917.138 266.662,917.374C266.662,944.806 289.235,967.379 316.667,967.379C344.099,967.379 366.672,944.806 366.672,917.374C366.672,917.138 366.67,916.902 366.667,916.667L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,883.333C1233.33,883.569 1233.33,883.805 1233.33,884.04C1233.33,911.472 1255.9,934.045 1283.33,934.045C1310.77,934.045 1333.34,911.472 1333.34,884.04C1333.34,883.805 1333.34,883.569 1333.33,883.333L1333.33,624.023C1335.29,609.755 1330.99,595.315 1321.55,584.44C1320.64,583.358 1319.68,582.316 1318.68,581.315L885.352,147.982C885.265,147.895 885.178,147.808 885.091,147.721C885.005,147.634 884.918,147.547 884.831,147.461C884.573,147.241 884.312,147.024 884.049,146.81C883.087,145.88 882.088,144.99 881.055,144.141C872.15,136.843 860.992,132.84 849.479,132.813ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036ZM1083.33,1000C1083.1,999.997 1082.86,999.995 1082.63,999.995C1055.19,999.995 1032.62,1022.57 1032.62,1050C1032.62,1077.43 1055.19,1100.01 1082.63,1100.01C1082.86,1100.01 1083.1,1100 1083.33,1100L1257.94,1100C1191.74,1177.95 1026.31,1333.33 759.049,1333.33C402.337,1333.33 260.807,1059.83 260.807,1059.83C252.224,1043.13 234.925,1032.61 216.146,1032.68C188.777,1032.77 166.308,1055.32 166.308,1082.69C166.308,1091.13 168.448,1099.44 172.526,1106.84C172.526,1106.84 350.095,1433.33 759.049,1433.33C1061.88,1433.33 1253.9,1256.34 1333.33,1164.39L1333.33,1350C1333.33,1350.24 1333.33,1350.47 1333.33,1350.71C1333.33,1378.14 1355.9,1400.71 1383.33,1400.71C1410.77,1400.71 1433.34,1378.14 1433.34,1350.71C1433.34,1350.47 1433.34,1350.24 1433.33,1350L1433.33,1050C1433.33,1022.57 1410.76,1000 1383.33,1000L1083.33,1000Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ScheduleDark.svg b/VDownload/Assets/Icons/ScheduleDark.svg deleted file mode 100644 index b7cb061..0000000 --- a/VDownload/Assets/Icons/ScheduleDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.14286,0,0,1.14286,0,0)"> - <path d="M383.333,0C172.216,0 0,172.216 0,383.333C0,594.45 172.216,766.667 383.333,766.667C594.45,766.667 766.667,594.45 766.667,383.333C766.667,172.216 594.45,0 383.333,0ZM383.333,100C540.406,100 666.667,226.26 666.667,383.333C666.667,540.406 540.406,666.667 383.333,666.667C226.26,666.667 100,540.406 100,383.333C100,226.26 226.26,100 383.333,100ZM382.552,165.951C355.148,166.379 332.94,189.262 333.333,216.667L333.333,383.333C333.336,396.588 338.609,409.313 347.982,418.685L414.648,485.352C424.074,495.168 437.11,500.724 450.719,500.724C478.151,500.724 500.724,478.151 500.724,450.719C500.724,437.11 495.168,424.074 485.352,414.648L433.333,362.63L433.333,216.667C433.337,216.428 433.338,216.189 433.338,215.949C433.338,188.517 410.765,165.944 383.333,165.944C383.073,165.944 382.813,165.946 382.552,165.951ZM794.01,200C808.144,231.6 818.792,265.067 825.326,300L1183.33,300C1247.67,300 1300,352.333 1300,416.667L1300,466.667L825.326,466.667C818.759,501.6 808.11,535.067 794.01,566.667L1300,566.667L1300,1183.33C1300,1247.67 1247.67,1300 1183.33,1300L416.667,1300C352.333,1300 300,1247.67 300,1183.33L300,825.326C265.067,818.759 231.6,808.11 200,794.01L200,1183.33C200,1302.8 297.2,1400 416.667,1400L1183.33,1400C1302.8,1400 1400,1302.8 1400,1183.33L1400,416.667C1400,297.2 1302.8,200 1183.33,200L794.01,200ZM800,700C754.285,700 716.667,737.618 716.667,783.333C716.667,829.049 754.285,866.667 800,866.667C845.715,866.667 883.333,829.049 883.333,783.333C883.333,737.618 845.715,700 800,700ZM1083.33,700C1037.62,700 1000,737.618 1000,783.333C1000,829.049 1037.62,866.667 1083.33,866.667C1129.05,866.667 1166.67,829.049 1166.67,783.333C1166.67,737.618 1129.05,700 1083.33,700ZM516.667,1000C470.951,1000 433.333,1037.62 433.333,1083.33C433.333,1129.05 470.951,1166.67 516.667,1166.67C562.382,1166.67 600,1129.05 600,1083.33C600,1037.62 562.382,1000 516.667,1000ZM800,1000C754.285,1000 716.667,1037.62 716.667,1083.33C716.667,1129.05 754.285,1166.67 800,1166.67C845.715,1166.67 883.333,1129.05 883.333,1083.33C883.333,1037.62 845.715,1000 800,1000Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ScheduleLight.svg b/VDownload/Assets/Icons/ScheduleLight.svg deleted file mode 100644 index 8c368ba..0000000 --- a/VDownload/Assets/Icons/ScheduleLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.14286,0,0,1.14286,0,0)"> - <path d="M383.333,0C172.216,0 0,172.216 0,383.333C0,594.45 172.216,766.667 383.333,766.667C594.45,766.667 766.667,594.45 766.667,383.333C766.667,172.216 594.45,0 383.333,0ZM383.333,100C540.406,100 666.667,226.26 666.667,383.333C666.667,540.406 540.406,666.667 383.333,666.667C226.26,666.667 100,540.406 100,383.333C100,226.26 226.26,100 383.333,100ZM382.552,165.951C355.148,166.379 332.94,189.262 333.333,216.667L333.333,383.333C333.336,396.588 338.609,409.313 347.982,418.685L414.648,485.352C424.074,495.168 437.11,500.724 450.719,500.724C478.151,500.724 500.724,478.151 500.724,450.719C500.724,437.11 495.168,424.074 485.352,414.648L433.333,362.63L433.333,216.667C433.337,216.428 433.338,216.189 433.338,215.949C433.338,188.517 410.765,165.944 383.333,165.944C383.073,165.944 382.813,165.946 382.552,165.951ZM794.01,200C808.144,231.6 818.792,265.067 825.326,300L1183.33,300C1247.67,300 1300,352.333 1300,416.667L1300,466.667L825.326,466.667C818.759,501.6 808.11,535.067 794.01,566.667L1300,566.667L1300,1183.33C1300,1247.67 1247.67,1300 1183.33,1300L416.667,1300C352.333,1300 300,1247.67 300,1183.33L300,825.326C265.067,818.759 231.6,808.11 200,794.01L200,1183.33C200,1302.8 297.2,1400 416.667,1400L1183.33,1400C1302.8,1400 1400,1302.8 1400,1183.33L1400,416.667C1400,297.2 1302.8,200 1183.33,200L794.01,200ZM800,700C754.285,700 716.667,737.618 716.667,783.333C716.667,829.049 754.285,866.667 800,866.667C845.715,866.667 883.333,829.049 883.333,783.333C883.333,737.618 845.715,700 800,700ZM1083.33,700C1037.62,700 1000,737.618 1000,783.333C1000,829.049 1037.62,866.667 1083.33,866.667C1129.05,866.667 1166.67,829.049 1166.67,783.333C1166.67,737.618 1129.05,700 1083.33,700ZM516.667,1000C470.951,1000 433.333,1037.62 433.333,1083.33C433.333,1129.05 470.951,1166.67 516.667,1166.67C562.382,1166.67 600,1129.05 600,1083.33C600,1037.62 562.382,1000 516.667,1000ZM800,1000C754.285,1000 716.667,1037.62 716.667,1083.33C716.667,1129.05 754.285,1166.67 800,1166.67C845.715,1166.67 883.333,1129.05 883.333,1083.33C883.333,1037.62 845.715,1000 800,1000Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ShowNotifcationWhenSuccessfulDark.svg b/VDownload/Assets/Icons/ShowNotifcationWhenSuccessfulDark.svg deleted file mode 100644 index 749f2be..0000000 --- a/VDownload/Assets/Icons/ShowNotifcationWhenSuccessfulDark.svg +++ /dev/null @@ -1,6 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1435 1435"><g transform="matrix(1,0,0,1,-100,-65)"> - <path d="M1366.67,817.667L1366.67,1016.67C1366.67,1081 1314.33,1133.33 1250,1133.33L850,1133.33C839.333,1133.33 828.667,1137 820,1143.33L500,1383.33L500,1183.33C500,1155.67 477.667,1133.33 450,1133.33L350,1133.33C285.667,1133.33 233.333,1081 233.333,1016.67L233.333,450C233.333,385.667 285.667,333.333 350,333.333L745,333.333C753.333,298 766,264.667 782.333,233.333L350,233.333C230.667,233.333 133.333,330.667 133.333,450L133.333,1016.67C133.333,1136 230.667,1233.33 350,1233.33L400,1233.33L400,1416.67C400,1448.33 417.667,1477 446,1491.33C458,1497 470.667,1500 483.333,1500C501,1500 518.667,1494.33 533.333,1483.33L866.667,1233.33L1250,1233.33C1369.33,1233.33 1466.67,1136 1466.67,1016.67L1466.67,745.667C1437,774.333 1403.33,798.667 1366.67,817.667Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1,0,0,1,-100,-65)"> - <path d="M1166.67,66.667C1369.17,66.667 1533.33,230.833 1533.33,433.333C1533.33,635.833 1369.17,800 1166.67,800C964.167,800 800,635.833 800,433.333C800,230.833 964.167,66.667 1166.67,66.667ZM1123.57,590.233L1390.23,323.567C1403.27,310.533 1403.27,289.467 1390.23,276.433C1377.2,263.4 1356.13,263.4 1343.1,276.433L1100,519.533L990.233,409.767C977.2,396.733 956.133,396.733 943.1,409.767C930.067,422.8 930.067,443.867 943.1,456.9L1076.43,590.233C1082.93,596.733 1091.47,600 1100,600C1108.53,600 1117.07,596.733 1123.57,590.233Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ShowNotifcationWhenSuccessfulLight.svg b/VDownload/Assets/Icons/ShowNotifcationWhenSuccessfulLight.svg deleted file mode 100644 index 5b32503..0000000 --- a/VDownload/Assets/Icons/ShowNotifcationWhenSuccessfulLight.svg +++ /dev/null @@ -1,6 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1435 1435"><g transform="matrix(1,0,0,1,-100,-65)"> - <path d="M1366.67,817.667L1366.67,1016.67C1366.67,1081 1314.33,1133.33 1250,1133.33L850,1133.33C839.333,1133.33 828.667,1137 820,1143.33L500,1383.33L500,1183.33C500,1155.67 477.667,1133.33 450,1133.33L350,1133.33C285.667,1133.33 233.333,1081 233.333,1016.67L233.333,450C233.333,385.667 285.667,333.333 350,333.333L745,333.333C753.333,298 766,264.667 782.333,233.333L350,233.333C230.667,233.333 133.333,330.667 133.333,450L133.333,1016.67C133.333,1136 230.667,1233.33 350,1233.33L400,1233.33L400,1416.67C400,1448.33 417.667,1477 446,1491.33C458,1497 470.667,1500 483.333,1500C501,1500 518.667,1494.33 533.333,1483.33L866.667,1233.33L1250,1233.33C1369.33,1233.33 1466.67,1136 1466.67,1016.67L1466.67,745.667C1437,774.333 1403.33,798.667 1366.67,817.667Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1,0,0,1,-100,-65)"> - <path d="M1166.67,66.667C1369.17,66.667 1533.33,230.833 1533.33,433.333C1533.33,635.833 1369.17,800 1166.67,800C964.167,800 800,635.833 800,433.333C800,230.833 964.167,66.667 1166.67,66.667ZM1123.57,590.233L1390.23,323.567C1403.27,310.533 1403.27,289.467 1390.23,276.433C1377.2,263.4 1356.13,263.4 1343.1,276.433L1100,519.533L990.233,409.767C977.2,396.733 956.133,396.733 943.1,409.767C930.067,422.8 930.067,443.867 943.1,456.9L1076.43,590.233C1082.93,596.733 1091.47,600 1100,600C1108.53,600 1117.07,596.733 1123.57,590.233Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ShowNotifcationWhenUnsuccessfulDark.svg b/VDownload/Assets/Icons/ShowNotifcationWhenUnsuccessfulDark.svg deleted file mode 100644 index f7722c3..0000000 --- a/VDownload/Assets/Icons/ShowNotifcationWhenUnsuccessfulDark.svg +++ /dev/null @@ -1,6 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1435 1435"><g transform="matrix(1,0,0,1,-100,-65)"> - <path d="M1366.67,817.667L1366.67,1016.67C1366.67,1081 1314.33,1133.33 1250,1133.33L850,1133.33C839.333,1133.33 828.667,1137 820,1143.33L500,1383.33L500,1183.33C500,1155.67 477.667,1133.33 450,1133.33L350,1133.33C285.667,1133.33 233.333,1081 233.333,1016.67L233.333,450C233.333,385.667 285.667,333.333 350,333.333L745,333.333C753.333,298 766,264.667 782.333,233.333L350,233.333C230.667,233.333 133.333,330.667 133.333,450L133.333,1016.67C133.333,1136 230.667,1233.33 350,1233.33L400,1233.33L400,1416.67C400,1448.33 417.667,1477 446,1491.33C458,1497 470.667,1500 483.333,1500C501,1500 518.667,1494.33 533.333,1483.33L866.667,1233.33L1250,1233.33C1369.33,1233.33 1466.67,1136 1466.67,1016.67L1466.67,745.667C1437,774.333 1403.33,798.667 1366.67,817.667Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1,0,0,1,-99,-798)"> - <path d="M1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1133.33,966.667C1133.33,948.267 1148.23,933.333 1166.67,933.333C1185.1,933.333 1200,948.267 1200,966.667L1200,1233.33C1200,1251.73 1185.1,1266.67 1166.67,1266.67C1148.23,1266.67 1133.33,1251.73 1133.33,1233.33L1133.33,966.667ZM1166.67,1408.33C1143.67,1408.33 1125,1389.67 1125,1366.67C1125,1343.67 1143.67,1325 1166.67,1325C1189.67,1325 1208.33,1343.67 1208.33,1366.67C1208.33,1389.67 1189.67,1408.33 1166.67,1408.33Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ShowNotifcationWhenUnsuccessfulLight.svg b/VDownload/Assets/Icons/ShowNotifcationWhenUnsuccessfulLight.svg deleted file mode 100644 index 4999e02..0000000 --- a/VDownload/Assets/Icons/ShowNotifcationWhenUnsuccessfulLight.svg +++ /dev/null @@ -1,6 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1435 1435"><g transform="matrix(1,0,0,1,-100,-65)"> - <path d="M1366.67,817.667L1366.67,1016.67C1366.67,1081 1314.33,1133.33 1250,1133.33L850,1133.33C839.333,1133.33 828.667,1137 820,1143.33L500,1383.33L500,1183.33C500,1155.67 477.667,1133.33 450,1133.33L350,1133.33C285.667,1133.33 233.333,1081 233.333,1016.67L233.333,450C233.333,385.667 285.667,333.333 350,333.333L745,333.333C753.333,298 766,264.667 782.333,233.333L350,233.333C230.667,233.333 133.333,330.667 133.333,450L133.333,1016.67C133.333,1136 230.667,1233.33 350,1233.33L400,1233.33L400,1416.67C400,1448.33 417.667,1477 446,1491.33C458,1497 470.667,1500 483.333,1500C501,1500 518.667,1494.33 533.333,1483.33L866.667,1233.33L1250,1233.33C1369.33,1233.33 1466.67,1136 1466.67,1016.67L1466.67,745.667C1437,774.333 1403.33,798.667 1366.67,817.667Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1,0,0,1,-99,-798)"> - <path d="M1166.67,800C964.167,800 800,964.167 800,1166.67C800,1369.17 964.167,1533.33 1166.67,1533.33C1369.17,1533.33 1533.33,1369.17 1533.33,1166.67C1533.33,964.167 1369.17,800 1166.67,800ZM1133.33,966.667C1133.33,948.267 1148.23,933.333 1166.67,933.333C1185.1,933.333 1200,948.267 1200,966.667L1200,1233.33C1200,1251.73 1185.1,1266.67 1166.67,1266.67C1148.23,1266.67 1133.33,1251.73 1133.33,1233.33L1133.33,966.667ZM1166.67,1408.33C1143.67,1408.33 1125,1389.67 1125,1366.67C1125,1343.67 1143.67,1325 1166.67,1325C1189.67,1325 1208.33,1343.67 1208.33,1366.67C1208.33,1389.67 1189.67,1408.33 1166.67,1408.33Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateCancelledDark.svg b/VDownload/Assets/Icons/StateCancelledDark.svg deleted file mode 100644 index 5dd4643..0000000 --- a/VDownload/Assets/Icons/StateCancelledDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM1016.21,532.617C1003.01,532.929 990.458,538.456 981.315,547.982L800,729.297L618.685,547.982C609.273,538.289 596.323,532.813 582.812,532.813C555.385,532.819 532.82,555.39 532.82,582.817C532.82,596.325 538.293,609.273 547.982,618.685L729.297,800L547.982,981.315C538.165,990.741 532.609,1003.78 532.609,1017.39C532.609,1044.82 555.183,1067.39 582.614,1067.39C596.224,1067.39 609.259,1061.84 618.685,1052.02L800,870.703L981.315,1052.02C990.741,1061.84 1003.78,1067.39 1017.39,1067.39C1044.82,1067.39 1067.39,1044.82 1067.39,1017.39C1067.39,1003.78 1061.84,990.741 1052.02,981.315L870.703,800L1052.02,618.685C1061.84,609.259 1067.4,596.22 1067.4,582.608C1067.4,555.176 1044.82,532.603 1017.39,532.603C1017,532.603 1016.61,532.608 1016.21,532.617Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateCancelledLight.svg b/VDownload/Assets/Icons/StateCancelledLight.svg deleted file mode 100644 index 99c2da9..0000000 --- a/VDownload/Assets/Icons/StateCancelledLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM1016.21,532.617C1003.01,532.929 990.458,538.456 981.315,547.982L800,729.297L618.685,547.982C609.273,538.289 596.323,532.813 582.812,532.813C555.385,532.819 532.82,555.39 532.82,582.817C532.82,596.325 538.293,609.273 547.982,618.685L729.297,800L547.982,981.315C538.165,990.741 532.609,1003.78 532.609,1017.39C532.609,1044.82 555.183,1067.39 582.614,1067.39C596.224,1067.39 609.259,1061.84 618.685,1052.02L800,870.703L981.315,1052.02C990.741,1061.84 1003.78,1067.39 1017.39,1067.39C1044.82,1067.39 1067.39,1044.82 1067.39,1017.39C1067.39,1003.78 1061.84,990.741 1052.02,981.315L870.703,800L1052.02,618.685C1061.84,609.259 1067.4,596.22 1067.4,582.608C1067.4,555.176 1044.82,532.603 1017.39,532.603C1017,532.603 1016.61,532.608 1016.21,532.617Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateDoneDark.svg b/VDownload/Assets/Icons/StateDoneDark.svg deleted file mode 100644 index 3ef0866..0000000 --- a/VDownload/Assets/Icons/StateDoneDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM1049.02,599.544C1036.04,599.923 1023.71,605.343 1014.65,614.648L716.667,912.63L585.352,781.315C575.926,771.498 562.89,765.943 549.281,765.943C521.849,765.943 499.276,788.516 499.276,815.948C499.276,829.557 504.832,842.593 514.648,852.018L681.315,1018.69C700.711,1038.07 732.622,1038.07 752.018,1018.69L1085.35,685.352C1095.01,675.943 1100.47,663.014 1100.47,649.529C1100.47,622.097 1077.89,599.524 1050.46,599.524C1049.98,599.524 1049.5,599.53 1049.02,599.544Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateDoneLight.svg b/VDownload/Assets/Icons/StateDoneLight.svg deleted file mode 100644 index 334e7dd..0000000 --- a/VDownload/Assets/Icons/StateDoneLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM1049.02,599.544C1036.04,599.923 1023.71,605.343 1014.65,614.648L716.667,912.63L585.352,781.315C575.926,771.498 562.89,765.943 549.281,765.943C521.849,765.943 499.276,788.516 499.276,815.948C499.276,829.557 504.832,842.593 514.648,852.018L681.315,1018.69C700.711,1038.07 732.622,1038.07 752.018,1018.69L1085.35,685.352C1095.01,675.943 1100.47,663.014 1100.47,649.529C1100.47,622.097 1077.89,599.524 1050.46,599.524C1049.98,599.524 1049.5,599.53 1049.02,599.544Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateDownloadingDark.svg b/VDownload/Assets/Icons/StateDownloadingDark.svg deleted file mode 100644 index 77215f8..0000000 --- a/VDownload/Assets/Icons/StateDownloadingDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM799.219,499.284C771.814,499.712 749.607,522.595 750,550L750,929.297L635.352,814.648C625.926,804.832 612.89,799.276 599.281,799.276C571.849,799.276 549.276,821.849 549.276,849.281C549.276,862.89 554.832,875.926 564.648,885.352L764.648,1085.35C784.044,1104.74 815.956,1104.74 835.352,1085.35L1035.35,885.352C1045.17,875.926 1050.72,862.89 1050.72,849.281C1050.72,821.849 1028.15,799.276 1000.72,799.276C987.11,799.276 974.074,804.832 964.648,814.648L850,929.297L850,550C850.003,549.761 850.005,549.522 850.005,549.283C850.005,521.851 827.432,499.278 800,499.278C799.74,499.278 799.479,499.28 799.219,499.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateDownloadingLight.svg b/VDownload/Assets/Icons/StateDownloadingLight.svg deleted file mode 100644 index 1af4bfd..0000000 --- a/VDownload/Assets/Icons/StateDownloadingLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM799.219,499.284C771.814,499.712 749.607,522.595 750,550L750,929.297L635.352,814.648C625.926,804.832 612.89,799.276 599.281,799.276C571.849,799.276 549.276,821.849 549.276,849.281C549.276,862.89 554.832,875.926 564.648,885.352L764.648,1085.35C784.044,1104.74 815.956,1104.74 835.352,1085.35L1035.35,885.352C1045.17,875.926 1050.72,862.89 1050.72,849.281C1050.72,821.849 1028.15,799.276 1000.72,799.276C987.11,799.276 974.074,804.832 964.648,814.648L850,929.297L850,550C850.003,549.761 850.005,549.522 850.005,549.283C850.005,521.851 827.432,499.278 800,499.278C799.74,499.278 799.479,499.28 799.219,499.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateErrorDark.svg b/VDownload/Assets/Icons/StateErrorDark.svg deleted file mode 100644 index f19f115..0000000 --- a/VDownload/Assets/Icons/StateErrorDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM799.219,432.617C771.814,433.045 749.607,455.928 750,483.333L750,883.333C749.997,883.569 749.995,883.805 749.995,884.04C749.995,911.472 772.568,934.045 800,934.045C827.432,934.045 850.005,911.472 850.005,884.04C850.005,883.805 850.003,883.569 850,883.333L850,483.333C850.003,483.094 850.005,482.855 850.005,482.616C850.005,455.184 827.432,432.611 800,432.611C799.74,432.611 799.479,432.613 799.219,432.617ZM800,1033.33C763.428,1033.33 733.333,1063.43 733.333,1100C733.333,1136.57 763.428,1166.67 800,1166.67C836.572,1166.67 866.667,1136.57 866.667,1100C866.667,1063.43 836.572,1033.33 800,1033.33Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateErrorLight.svg b/VDownload/Assets/Icons/StateErrorLight.svg deleted file mode 100644 index f17e35f..0000000 --- a/VDownload/Assets/Icons/StateErrorLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM799.219,432.617C771.814,433.045 749.607,455.928 750,483.333L750,883.333C749.997,883.569 749.995,883.805 749.995,884.04C749.995,911.472 772.568,934.045 800,934.045C827.432,934.045 850.005,911.472 850.005,884.04C850.005,883.805 850.003,883.569 850,883.333L850,483.333C850.003,483.094 850.005,482.855 850.005,482.616C850.005,455.184 827.432,432.611 800,432.611C799.74,432.611 799.479,432.613 799.219,432.617ZM800,1033.33C763.428,1033.33 733.333,1063.43 733.333,1100C733.333,1136.57 763.428,1166.67 800,1166.67C836.572,1166.67 866.667,1136.57 866.667,1100C866.667,1063.43 836.572,1033.33 800,1033.33Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateFinalizingDark.svg b/VDownload/Assets/Icons/StateFinalizingDark.svg deleted file mode 100644 index 4fdb586..0000000 --- a/VDownload/Assets/Icons/StateFinalizingDark.svg +++ /dev/null @@ -1,42 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="700" y="300" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="900" y="500" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="700" y="700" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="500" y="500" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="1100" y="700" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="300" y="700" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="500" y="900" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="900" y="900" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="700" y="1100" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <path d="M500,1100L339.35,1100C381.15,1164.1 435.9,1218.8 500,1260.65L500,1100Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <path d="M1100,339.35L1100,500L1260.65,500C1218.8,435.9 1164.1,381.2 1100,339.35Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <path d="M339.35,500L500,500L500,339.35C435.9,381.2 381.2,435.9 339.35,500Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(-8.38883e-17,-1.37,-1.37,8.38883e-17,1895.11,1895.11)"> - <path d="M339.35,500L500,500L500,339.35C435.9,381.2 381.2,435.9 339.35,500Z" style="fill-rule:nonzero;"/> - </g> - <g transform="matrix(1.09602,0,0,1.08475,-153.251,-135.424)"> - <path d="M867,123C1269.35,123 1596,453.463 1596,860.5C1596,1267.54 1269.35,1598 867,1598C464.654,1598 138,1267.54 138,860.5C138,453.463 464.654,123 867,123ZM867,233.625C1208.99,233.625 1486.65,514.518 1486.65,860.5C1486.65,1206.48 1208.99,1487.38 867,1487.38C525.006,1487.38 247.35,1206.48 247.35,860.5C247.35,514.518 525.006,233.625 867,233.625Z"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateFinalizingLight.svg b/VDownload/Assets/Icons/StateFinalizingLight.svg deleted file mode 100644 index ecd2936..0000000 --- a/VDownload/Assets/Icons/StateFinalizingLight.svg +++ /dev/null @@ -1,42 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="700" y="300" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="900" y="500" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="700" y="700" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="500" y="500" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="1100" y="700" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="300" y="700" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="500" y="900" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="900" y="900" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <rect x="700" y="1100" width="200" height="200"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <path d="M500,1100L339.35,1100C381.15,1164.1 435.9,1218.8 500,1260.65L500,1100Z"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <path d="M1100,339.35L1100,500L1260.65,500C1218.8,435.9 1164.1,381.2 1100,339.35Z"/> - </g> - <g transform="matrix(1.37,0,0,1.37,-296,-296)"> - <path d="M339.35,500L500,500L500,339.35C435.9,381.2 381.2,435.9 339.35,500Z"/> - </g> - <g transform="matrix(-8.38883e-17,-1.37,-1.37,8.38883e-17,1895.11,1895.11)"> - <path d="M339.35,500L500,500L500,339.35C435.9,381.2 381.2,435.9 339.35,500Z"/> - </g> - <g transform="matrix(1.09602,0,0,1.08475,-153.251,-135.424)"> - <path d="M867,123C1269.35,123 1596,453.463 1596,860.5C1596,1267.54 1269.35,1598 867,1598C464.654,1598 138,1267.54 138,860.5C138,453.463 464.654,123 867,123ZM867,233.625C1208.99,233.625 1486.65,514.518 1486.65,860.5C1486.65,1206.48 1208.99,1487.38 867,1487.38C525.006,1487.38 247.35,1206.48 247.35,860.5C247.35,514.518 525.006,233.625 867,233.625Z"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateIdleDark.svg b/VDownload/Assets/Icons/StateIdleDark.svg deleted file mode 100644 index 411df29..0000000 --- a/VDownload/Assets/Icons/StateIdleDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateIdleLight.svg b/VDownload/Assets/Icons/StateIdleLight.svg deleted file mode 100644 index a20a1c4..0000000 --- a/VDownload/Assets/Icons/StateIdleLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateProcessingDark.svg b/VDownload/Assets/Icons/StateProcessingDark.svg deleted file mode 100644 index 5d97b33..0000000 --- a/VDownload/Assets/Icons/StateProcessingDark.svg +++ /dev/null @@ -1,18 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="466.667" r="116.667" style="fill:none;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="466.667" cy="800" r="116.667" style="fill:none;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="1133.33" r="116.667" style="fill:none;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="1133.33" cy="800" r="116.667" style="fill:none;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="800" r="616.667" style="fill:none;stroke:white;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="800" r="66.667"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateProcessingLight.svg b/VDownload/Assets/Icons/StateProcessingLight.svg deleted file mode 100644 index 03b9073..0000000 --- a/VDownload/Assets/Icons/StateProcessingLight.svg +++ /dev/null @@ -1,18 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="466.667" r="116.667" style="fill:none;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="466.667" cy="800" r="116.667" style="fill:none;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="1133.33" r="116.667" style="fill:none;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="1133.33" cy="800" r="116.667" style="fill:none;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="800" r="616.667" style="fill:none;stroke:black;stroke-width:100px;"/> - </g> - <g transform="matrix(1.20108,0,0,1.20108,-160.865,-160.865)"> - <circle cx="800" cy="800" r="66.667"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateQueuedDark.svg b/VDownload/Assets/Icons/StateQueuedDark.svg deleted file mode 100644 index 72b2229..0000000 --- a/VDownload/Assets/Icons/StateQueuedDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM782.552,399.284C755.148,399.712 732.94,422.595 733.333,450L733.333,850C733.336,877.428 755.905,899.997 783.333,900L1050,900C1050.24,900.003 1050.47,900.005 1050.71,900.005C1078.14,900.005 1100.71,877.432 1100.71,850C1100.71,822.568 1078.14,799.995 1050.71,799.995C1050.47,799.995 1050.24,799.997 1050,800L833.333,800L833.333,450C833.337,449.761 833.338,449.522 833.338,449.283C833.338,421.851 810.765,399.278 783.333,399.278C783.073,399.278 782.813,399.28 782.552,399.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateQueuedLight.svg b/VDownload/Assets/Icons/StateQueuedLight.svg deleted file mode 100644 index 316589d..0000000 --- a/VDownload/Assets/Icons/StateQueuedLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.201,0,0,1.201,-160.8,-160.8)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM782.552,399.284C755.148,399.712 732.94,422.595 733.333,450L733.333,850C733.336,877.428 755.905,899.997 783.333,900L1050,900C1050.24,900.003 1050.47,900.005 1050.71,900.005C1078.14,900.005 1100.71,877.432 1100.71,850C1100.71,822.568 1078.14,799.995 1050.71,799.995C1050.47,799.995 1050.24,799.997 1050,800L833.333,800L833.333,450C833.337,449.761 833.338,449.522 833.338,449.283C833.338,421.851 810.765,399.278 783.333,399.278C783.073,399.278 782.813,399.28 782.552,399.284Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateScheduledDark.svg b/VDownload/Assets/Icons/StateScheduledDark.svg deleted file mode 100644 index f3479a8..0000000 --- a/VDownload/Assets/Icons/StateScheduledDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.1995,0,0,1.1995,-159.6,-159.6)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM849.479,549.479C822.052,549.486 799.486,572.057 799.486,599.484C799.486,612.992 804.96,625.939 814.648,635.352L929.297,750L550,750C549.764,749.997 549.529,749.995 549.293,749.995C521.861,749.995 499.288,772.568 499.288,800C499.288,827.432 521.861,850.005 549.293,850.005C549.529,850.005 549.764,850.003 550,850L929.297,850L814.648,964.648C804.832,974.074 799.276,987.11 799.276,1000.72C799.276,1028.15 821.849,1050.72 849.281,1050.72C862.89,1050.72 875.926,1045.17 885.352,1035.35L1085.35,835.352C1104.74,815.956 1104.74,784.044 1085.35,764.648L885.352,564.648C875.939,554.956 862.99,549.48 849.479,549.479Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/StateScheduledLight.svg b/VDownload/Assets/Icons/StateScheduledLight.svg deleted file mode 100644 index 8d28925..0000000 --- a/VDownload/Assets/Icons/StateScheduledLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.1995,0,0,1.1995,-159.6,-159.6)"> - <path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM849.479,549.479C822.052,549.486 799.486,572.057 799.486,599.484C799.486,612.992 804.96,625.939 814.648,635.352L929.297,750L550,750C549.764,749.997 549.529,749.995 549.293,749.995C521.861,749.995 499.288,772.568 499.288,800C499.288,827.432 521.861,850.005 549.293,850.005C549.529,850.005 549.764,850.003 550,850L929.297,850L814.648,964.648C804.832,974.074 799.276,987.11 799.276,1000.72C799.276,1028.15 821.849,1050.72 849.281,1050.72C862.89,1050.72 875.926,1045.17 885.352,1035.35L1085.35,835.352C1104.74,815.956 1104.74,784.044 1085.35,764.648L885.352,564.648C875.939,554.956 862.99,549.48 849.479,549.479Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/TemporaryFilesLocationDark.svg b/VDownload/Assets/Icons/TemporaryFilesLocationDark.svg deleted file mode 100644 index 443feae..0000000 --- a/VDownload/Assets/Icons/TemporaryFilesLocationDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1333 1333"><g transform="matrix(1,0,0,1,-132,-132)"> - <path d="M283.333,266.667C201.083,266.667 133.333,334.417 133.333,416.667L133.333,1183.33C133.333,1265.58 201.083,1333.33 283.333,1333.33L1316.67,1333.33C1398.92,1333.33 1466.67,1265.58 1466.67,1183.33L1466.67,583.333C1466.67,501.083 1398.92,433.333 1316.67,433.333L801.432,433.333L652.409,309.18C619.469,281.737 577.972,266.667 535.091,266.667L283.333,266.667ZM283.333,366.667L535.091,366.667C554.61,366.667 573.419,373.512 588.411,386.003L705.208,483.333L588.411,580.664C573.419,593.155 554.61,600 535.091,600L233.333,600L233.333,416.667C233.333,388.45 255.117,366.667 283.333,366.667ZM801.432,533.333L1316.67,533.333C1344.88,533.333 1366.67,555.117 1366.67,583.333L1366.67,1183.33C1366.67,1211.55 1344.88,1233.33 1316.67,1233.33L283.333,1233.33C255.117,1233.33 233.333,1211.55 233.333,1183.33L233.333,700L535.091,700C577.972,700 619.469,684.93 652.409,657.487L801.432,533.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/TemporaryFilesLocationLight.svg b/VDownload/Assets/Icons/TemporaryFilesLocationLight.svg deleted file mode 100644 index 0d8d098..0000000 --- a/VDownload/Assets/Icons/TemporaryFilesLocationLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1333 1333"><g transform="matrix(1,0,0,1,-132,-132)"> - <path d="M283.333,266.667C201.083,266.667 133.333,334.417 133.333,416.667L133.333,1183.33C133.333,1265.58 201.083,1333.33 283.333,1333.33L1316.67,1333.33C1398.92,1333.33 1466.67,1265.58 1466.67,1183.33L1466.67,583.333C1466.67,501.083 1398.92,433.333 1316.67,433.333L801.432,433.333L652.409,309.18C619.469,281.737 577.972,266.667 535.091,266.667L283.333,266.667ZM283.333,366.667L535.091,366.667C554.61,366.667 573.419,373.512 588.411,386.003L705.208,483.333L588.411,580.664C573.419,593.155 554.61,600 535.091,600L233.333,600L233.333,416.667C233.333,388.45 255.117,366.667 283.333,366.667ZM801.432,533.333L1316.67,533.333C1344.88,533.333 1366.67,555.117 1366.67,583.333L1366.67,1183.33C1366.67,1211.55 1344.88,1233.33 1316.67,1233.33L283.333,1233.33C255.117,1233.33 233.333,1211.55 233.333,1183.33L233.333,700L535.091,700C577.972,700 619.469,684.93 652.409,657.487L801.432,533.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/TranscodingAlgorithmDark.svg b/VDownload/Assets/Icons/TranscodingAlgorithmDark.svg deleted file mode 100644 index 47667fe..0000000 --- a/VDownload/Assets/Icons/TranscodingAlgorithmDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1430 1430"><g transform="matrix(1,0,0,1,-100,-104)"> - <path d="M283.333,233.333C182.333,233.333 100,315.667 100,416.667L100,1183.33C100,1284.33 182.333,1366.67 283.333,1366.67L782.357,1366.67C766.023,1335.33 753.32,1302 744.987,1266.67L283.333,1266.67C237.333,1266.67 200,1229.33 200,1183.33L200,416.667C200,370.667 237.333,333.333 283.333,333.333L1316.67,333.333C1362.67,333.333 1400,370.667 1400,416.667L1400,801.693C1438,825.693 1471.67,855.641 1500,889.974L1500,416.667C1500,315.667 1417.67,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1054.82,817.122C1031.23,819.934 1009.76,836.396 1002.93,861.979L991.927,903.06C985.294,927.827 963.848,945.814 938.281,948.047L895.964,951.758C853.73,955.424 828.03,999.881 845.964,1038.28L863.932,1076.82C874.766,1100.09 869.924,1127.63 851.758,1145.77L821.68,1175.85C791.713,1205.81 800.663,1256.45 839.063,1274.35L877.604,1292.32C900.871,1303.15 914.864,1327.36 912.63,1352.93L908.854,1395.31C905.154,1437.51 944.582,1470.54 985.482,1459.57L1026.56,1448.5C1051.36,1441.84 1077.62,1451.43 1092.32,1472.46L1116.73,1507.29C1141,1541.99 1192.37,1541.99 1216.67,1507.29L1241.02,1472.46C1255.72,1451.46 1282.04,1441.84 1306.84,1448.5L1347.92,1459.57C1388.85,1470.54 1428.18,1437.51 1424.48,1395.31L1420.77,1352.93C1418.5,1327.36 1432.53,1303.15 1455.79,1292.32L1494.27,1274.35C1532.67,1256.45 1541.62,1205.78 1511.65,1175.85L1481.58,1145.77C1463.41,1127.63 1458.53,1100.09 1469.4,1076.82L1487.43,1038.28C1505.34,999.915 1479.63,955.424 1437.43,951.758L1395.05,948.047C1369.49,945.814 1348.04,927.86 1341.41,903.06L1330.4,861.979C1319.47,821.046 1271.22,803.434 1236.52,827.734L1201.69,852.148C1180.69,866.882 1152.67,866.882 1131.71,852.148L1096.88,827.734C1083.88,818.622 1068.97,815.435 1054.82,817.122ZM283.333,1100C265,1100 250,1115 250,1133.33L250,1166.67C250,1185 265,1200 283.333,1200L316.667,1200C335,1200 350,1185 350,1166.67L350,1133.33C350,1115 335,1100 316.667,1100L283.333,1100ZM483.333,1100C465,1100 450,1115 450,1133.33L450,1166.67C450,1185 465,1200 483.333,1200L516.667,1200C535,1200 550,1185 550,1166.67L550,1133.33C550,1115 535,1100 516.667,1100L483.333,1100ZM683.333,1100C665,1100 650,1115 650,1133.33L650,1166.67C650,1185 665,1200 683.333,1200L716.667,1200C723,1200 729.31,1198 734.31,1194.66L734.31,1193.68L737.695,1107.36C732.029,1102.69 724.667,1100 716.667,1100L683.333,1100Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/TranscodingAlgorithmLight.svg b/VDownload/Assets/Icons/TranscodingAlgorithmLight.svg deleted file mode 100644 index ddb5555..0000000 --- a/VDownload/Assets/Icons/TranscodingAlgorithmLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1430 1430"><g transform="matrix(1,0,0,1,-100,-104)"> - <path d="M283.333,233.333C182.333,233.333 100,315.667 100,416.667L100,1183.33C100,1284.33 182.333,1366.67 283.333,1366.67L782.357,1366.67C766.023,1335.33 753.32,1302 744.987,1266.67L283.333,1266.67C237.333,1266.67 200,1229.33 200,1183.33L200,416.667C200,370.667 237.333,333.333 283.333,333.333L1316.67,333.333C1362.67,333.333 1400,370.667 1400,416.667L1400,801.693C1438,825.693 1471.67,855.641 1500,889.974L1500,416.667C1500,315.667 1417.67,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1054.82,817.122C1031.23,819.934 1009.76,836.396 1002.93,861.979L991.927,903.06C985.294,927.827 963.848,945.814 938.281,948.047L895.964,951.758C853.73,955.424 828.03,999.881 845.964,1038.28L863.932,1076.82C874.766,1100.09 869.924,1127.63 851.758,1145.77L821.68,1175.85C791.713,1205.81 800.663,1256.45 839.063,1274.35L877.604,1292.32C900.871,1303.15 914.864,1327.36 912.63,1352.93L908.854,1395.31C905.154,1437.51 944.582,1470.54 985.482,1459.57L1026.56,1448.5C1051.36,1441.84 1077.62,1451.43 1092.32,1472.46L1116.73,1507.29C1141,1541.99 1192.37,1541.99 1216.67,1507.29L1241.02,1472.46C1255.72,1451.46 1282.04,1441.84 1306.84,1448.5L1347.92,1459.57C1388.85,1470.54 1428.18,1437.51 1424.48,1395.31L1420.77,1352.93C1418.5,1327.36 1432.53,1303.15 1455.79,1292.32L1494.27,1274.35C1532.67,1256.45 1541.62,1205.78 1511.65,1175.85L1481.58,1145.77C1463.41,1127.63 1458.53,1100.09 1469.4,1076.82L1487.43,1038.28C1505.34,999.915 1479.63,955.424 1437.43,951.758L1395.05,948.047C1369.49,945.814 1348.04,927.86 1341.41,903.06L1330.4,861.979C1319.47,821.046 1271.22,803.434 1236.52,827.734L1201.69,852.148C1180.69,866.882 1152.67,866.882 1131.71,852.148L1096.88,827.734C1083.88,818.622 1068.97,815.435 1054.82,817.122ZM283.333,1100C265,1100 250,1115 250,1133.33L250,1166.67C250,1185 265,1200 283.333,1200L316.667,1200C335,1200 350,1185 350,1166.67L350,1133.33C350,1115 335,1100 316.667,1100L283.333,1100ZM483.333,1100C465,1100 450,1115 450,1133.33L450,1166.67C450,1185 465,1200 483.333,1200L516.667,1200C535,1200 550,1185 550,1166.67L550,1133.33C550,1115 535,1100 516.667,1100L483.333,1100ZM683.333,1100C665,1100 650,1115 650,1133.33L650,1166.67C650,1185 665,1200 683.333,1200L716.667,1200C723,1200 729.31,1198 734.31,1194.66L734.31,1193.68L737.695,1107.36C732.029,1102.69 724.667,1100 716.667,1100L683.333,1100Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/TrimDark.svg b/VDownload/Assets/Icons/TrimDark.svg deleted file mode 100644 index 6b7142c..0000000 --- a/VDownload/Assets/Icons/TrimDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.11651,0,0,1.11651,-114.651,-186.605)"> - <path d="M283.333,233.333C182.233,233.333 100,315.6 100,416.667L100,1183.33C100,1284.4 182.233,1366.67 283.333,1366.67L733.333,1366.67C733.333,1330.87 741.718,1297.03 756.185,1266.67L283.333,1266.67C237.4,1266.67 200,1229.3 200,1183.33L200,416.667C200,370.7 237.4,333.333 283.333,333.333L1316.67,333.333C1362.6,333.333 1400,370.7 1400,416.667L1400,701.693C1418.73,704.393 1437.12,711.141 1453.32,723.307C1479.05,742.607 1494.56,770.5 1498.83,800L1500,800L1500,416.667C1500,315.6 1417.77,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1386.2,765.625C1369.3,764.93 1353.15,772.857 1343.36,786.654L1158.98,1032.42L955.273,784.896C945.741,772.99 931.267,766.078 916.016,766.146C888.67,766.271 866.239,788.805 866.239,816.15C866.239,827.969 870.429,839.413 878.06,848.438L1097.2,1114.78L1025.72,1210.03C1007.54,1203.65 987.694,1200 966.667,1200C915.278,1200 870.405,1221.07 841.797,1253.26C813.189,1285.44 800,1326.39 800,1366.67C800,1406.94 813.189,1447.89 841.797,1480.08C870.405,1512.26 915.278,1533.33 966.667,1533.33C1018.06,1533.33 1062.93,1512.26 1091.54,1480.08C1120.14,1447.89 1133.33,1406.94 1133.33,1366.67C1133.33,1333.5 1123.82,1300.16 1104.69,1271.35L1162.57,1194.21L1227.41,1273.05C1209.06,1301.47 1200,1334.16 1200,1366.67C1200,1406.94 1213.19,1447.89 1241.8,1480.08C1270.4,1512.26 1315.28,1533.33 1366.67,1533.33C1418.06,1533.33 1462.93,1512.26 1491.54,1480.08C1520.14,1447.89 1533.33,1406.94 1533.33,1366.67C1533.33,1326.39 1520.14,1285.44 1491.54,1253.26C1462.93,1221.07 1418.06,1200 1366.67,1200C1344.9,1200 1324.42,1203.94 1305.73,1210.74L1224.35,1111.91L1423.31,846.68C1430.33,837.839 1434.15,826.876 1434.15,815.588C1434.15,788.931 1412.83,766.72 1386.2,765.625ZM283.333,1100C264.933,1100 250,1114.93 250,1133.33L250,1166.67C250,1185.07 264.933,1200 283.333,1200L316.667,1200C335.067,1200 350,1185.07 350,1166.67L350,1133.33C350,1114.93 335.067,1100 316.667,1100L283.333,1100ZM483.333,1100C464.933,1100 450,1114.93 450,1133.33L450,1166.67C450,1185.07 464.933,1200 483.333,1200L516.667,1200C535.067,1200 550,1185.07 550,1166.67L550,1133.33C550,1114.93 535.067,1100 516.667,1100L483.333,1100ZM683.333,1100C664.933,1100 650,1114.93 650,1133.33L650,1166.67C650,1185.07 664.933,1200 683.333,1200L716.667,1200C735.067,1200 750,1185.07 750,1166.67L750,1133.33C750,1114.93 735.067,1100 716.667,1100L683.333,1100ZM966.667,1300C979.861,1300 989.903,1302.06 997.852,1305.53C1003.13,1312.89 1010.31,1318.67 1018.62,1322.27C1027.7,1333.76 1033.33,1349.82 1033.33,1366.67C1033.33,1384.72 1027.08,1402.11 1016.8,1413.67C1006.52,1425.24 993.056,1433.33 966.667,1433.33C940.278,1433.33 926.817,1425.24 916.536,1413.67C906.256,1402.11 900,1384.72 900,1366.67C900,1348.61 906.256,1331.23 916.536,1319.66C926.817,1308.1 940.278,1300 966.667,1300ZM1366.67,1300C1393.06,1300 1406.52,1308.1 1416.8,1319.66C1427.08,1331.23 1433.33,1348.61 1433.33,1366.67C1433.33,1384.72 1427.08,1402.11 1416.8,1413.67C1406.52,1425.24 1393.06,1433.33 1366.67,1433.33C1340.28,1433.33 1326.82,1425.24 1316.54,1413.67C1306.26,1402.11 1300,1384.72 1300,1366.67C1300,1349.41 1305.81,1332.89 1315.3,1321.35C1322.81,1317.95 1329.37,1312.74 1334.38,1306.18C1342.54,1302.35 1352.73,1300 1366.67,1300Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/TrimLight.svg b/VDownload/Assets/Icons/TrimLight.svg deleted file mode 100644 index 0b7d608..0000000 --- a/VDownload/Assets/Icons/TrimLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.11651,0,0,1.11651,-114.651,-186.605)"> - <path d="M283.333,233.333C182.233,233.333 100,315.6 100,416.667L100,1183.33C100,1284.4 182.233,1366.67 283.333,1366.67L733.333,1366.67C733.333,1330.87 741.718,1297.03 756.185,1266.67L283.333,1266.67C237.4,1266.67 200,1229.3 200,1183.33L200,416.667C200,370.7 237.4,333.333 283.333,333.333L1316.67,333.333C1362.6,333.333 1400,370.7 1400,416.667L1400,701.693C1418.73,704.393 1437.12,711.141 1453.32,723.307C1479.05,742.607 1494.56,770.5 1498.83,800L1500,800L1500,416.667C1500,315.6 1417.77,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1386.2,765.625C1369.3,764.93 1353.15,772.857 1343.36,786.654L1158.98,1032.42L955.273,784.896C945.741,772.99 931.267,766.078 916.016,766.146C888.67,766.271 866.239,788.805 866.239,816.15C866.239,827.969 870.429,839.413 878.06,848.438L1097.2,1114.78L1025.72,1210.03C1007.54,1203.65 987.694,1200 966.667,1200C915.278,1200 870.405,1221.07 841.797,1253.26C813.189,1285.44 800,1326.39 800,1366.67C800,1406.94 813.189,1447.89 841.797,1480.08C870.405,1512.26 915.278,1533.33 966.667,1533.33C1018.06,1533.33 1062.93,1512.26 1091.54,1480.08C1120.14,1447.89 1133.33,1406.94 1133.33,1366.67C1133.33,1333.5 1123.82,1300.16 1104.69,1271.35L1162.57,1194.21L1227.41,1273.05C1209.06,1301.47 1200,1334.16 1200,1366.67C1200,1406.94 1213.19,1447.89 1241.8,1480.08C1270.4,1512.26 1315.28,1533.33 1366.67,1533.33C1418.06,1533.33 1462.93,1512.26 1491.54,1480.08C1520.14,1447.89 1533.33,1406.94 1533.33,1366.67C1533.33,1326.39 1520.14,1285.44 1491.54,1253.26C1462.93,1221.07 1418.06,1200 1366.67,1200C1344.9,1200 1324.42,1203.94 1305.73,1210.74L1224.35,1111.91L1423.31,846.68C1430.33,837.839 1434.15,826.876 1434.15,815.588C1434.15,788.931 1412.83,766.72 1386.2,765.625ZM283.333,1100C264.933,1100 250,1114.93 250,1133.33L250,1166.67C250,1185.07 264.933,1200 283.333,1200L316.667,1200C335.067,1200 350,1185.07 350,1166.67L350,1133.33C350,1114.93 335.067,1100 316.667,1100L283.333,1100ZM483.333,1100C464.933,1100 450,1114.93 450,1133.33L450,1166.67C450,1185.07 464.933,1200 483.333,1200L516.667,1200C535.067,1200 550,1185.07 550,1166.67L550,1133.33C550,1114.93 535.067,1100 516.667,1100L483.333,1100ZM683.333,1100C664.933,1100 650,1114.93 650,1133.33L650,1166.67C650,1185.07 664.933,1200 683.333,1200L716.667,1200C735.067,1200 750,1185.07 750,1166.67L750,1133.33C750,1114.93 735.067,1100 716.667,1100L683.333,1100ZM966.667,1300C979.861,1300 989.903,1302.06 997.852,1305.53C1003.13,1312.89 1010.31,1318.67 1018.62,1322.27C1027.7,1333.76 1033.33,1349.82 1033.33,1366.67C1033.33,1384.72 1027.08,1402.11 1016.8,1413.67C1006.52,1425.24 993.056,1433.33 966.667,1433.33C940.278,1433.33 926.817,1425.24 916.536,1413.67C906.256,1402.11 900,1384.72 900,1366.67C900,1348.61 906.256,1331.23 916.536,1319.66C926.817,1308.1 940.278,1300 966.667,1300ZM1366.67,1300C1393.06,1300 1406.52,1308.1 1416.8,1319.66C1427.08,1331.23 1433.33,1348.61 1433.33,1366.67C1433.33,1384.72 1427.08,1402.11 1416.8,1413.67C1406.52,1425.24 1393.06,1433.33 1366.67,1433.33C1340.28,1433.33 1326.82,1425.24 1316.54,1413.67C1306.26,1402.11 1300,1384.72 1300,1366.67C1300,1349.41 1305.81,1332.89 1315.3,1321.35C1322.81,1317.95 1329.37,1312.74 1334.38,1306.18C1342.54,1302.35 1352.73,1300 1366.67,1300Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/Twitch.svg b/VDownload/Assets/Icons/Twitch.svg deleted file mode 100644 index 93bd672..0000000 --- a/VDownload/Assets/Icons/Twitch.svg +++ /dev/null @@ -1,12 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.29514,0,0,1.29514,-236.108,-299.865)"> - <path d="M1400,922.533C1300,1037.23 1200,1151.93 1100,1266.67L866.667,1266.67C788.9,1333.33 711.1,1400 633.333,1466.67L533.333,1466.67L533.333,1266.67L200,1266.67L200,523.533C248.5,426.8 296.967,330.067 345.467,233.333L1400,233.333L1400,922.533Z" style="fill:rgb(126,87,194);fill-rule:nonzero;"/> - </g> - <g transform="matrix(1.29514,0,0,1.29514,-236.108,-299.865)"> - <path d="M1300,878.967C1244.43,941.533 1188.9,1004.1 1133.33,1066.67L900,1066.67C822.233,1133.33 744.433,1200 666.667,1266.67L666.667,1066.67C577.767,1066.4 488.9,1066.13 400,1065.87L400,333.333L1300,333.333L1300,878.967Z" style="fill:rgb(250,250,250);fill-rule:nonzero;"/> - </g> - <g transform="matrix(1.29514,0,0,1.29514,-236.108,-299.865)"> - <rect x="700" y="533.333" width="100" height="333.333" style="fill:rgb(126,87,194);"/> - </g> - <g transform="matrix(1.29514,0,0,1.29514,-236.108,-299.865)"> - <rect x="1000" y="533.333" width="100" height="333.333" style="fill:rgb(126,87,194);"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/UseHardwareAccelerationDark.svg b/VDownload/Assets/Icons/UseHardwareAccelerationDark.svg deleted file mode 100644 index 126e0b9..0000000 --- a/VDownload/Assets/Icons/UseHardwareAccelerationDark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1470 1470"><g transform="matrix(1,0,0,1,-30,-67)"> - <path d="M83.333,200C83.098,199.997 82.862,199.995 82.626,199.995C55.194,199.995 32.621,222.568 32.621,250C32.621,277.432 55.194,300.005 82.626,300.005C82.862,300.005 83.098,300.003 83.333,300L166.667,300L166.667,1383.33C166.663,1383.57 166.662,1383.81 166.662,1384.04C166.662,1411.47 189.235,1434.05 216.667,1434.05C244.099,1434.05 266.672,1411.47 266.672,1384.04C266.672,1383.81 266.67,1383.57 266.667,1383.33L266.667,1333.33L416.667,1333.33C444.095,1333.33 466.664,1310.76 466.667,1283.33L466.667,1200L700,1200L700,1283.33C700.003,1310.76 722.572,1333.33 750,1333.33L1316.67,1333.33C1344.1,1333.33 1366.66,1310.76 1366.67,1283.33L1366.67,1200L1383.33,1200C1447.27,1200 1500,1147.27 1500,1083.33L1500,550C1500,486.061 1447.27,433.333 1383.33,433.333L266.667,433.333L266.667,250C266.664,222.572 244.095,200.003 216.667,200L83.333,200ZM266.667,533.333L1383.33,533.333C1392.73,533.333 1400,540.606 1400,550L1400,1083.33C1400,1092.73 1392.73,1100 1383.33,1100L1316.67,1100C1289.24,1100 1266.67,1122.57 1266.67,1150L1266.67,1233.33L800,1233.33L800,1150C799.997,1122.57 777.428,1100 750,1100L416.667,1100C389.239,1100 366.669,1122.57 366.667,1150L366.667,1233.33L266.667,1233.33L266.667,1000L416.667,1000C444.095,999.997 466.664,977.428 466.667,950L466.667,683.333C466.664,655.905 444.095,633.336 416.667,633.333L266.667,633.333L266.667,533.333ZM616.667,633.333C616.431,633.33 616.195,633.328 615.96,633.328C588.528,633.328 565.955,655.901 565.955,683.333C565.955,710.765 588.528,733.338 615.96,733.338C616.195,733.338 616.431,733.337 616.667,733.333L750,733.333C750.236,733.337 750.471,733.338 750.707,733.338C778.139,733.338 800.712,710.765 800.712,683.333C800.712,655.901 778.139,633.328 750.707,633.328C750.471,633.328 750.236,633.33 750,633.333L616.667,633.333ZM983.333,633.333C955.905,633.336 933.336,655.905 933.333,683.333L933.333,950C933.336,977.428 955.905,999.997 983.333,1000L1250,1000C1277.43,999.997 1300,977.428 1300,950L1300,683.333C1300,655.905 1277.43,633.336 1250,633.333L983.333,633.333ZM266.667,733.333L366.667,733.333L366.667,900L266.667,900L266.667,733.333ZM1033.33,733.333L1200,733.333L1200,900L1033.33,900L1033.33,733.333ZM616.667,833.333C616.431,833.33 616.195,833.328 615.96,833.328C588.528,833.328 565.955,855.901 565.955,883.333C565.955,910.765 588.528,933.338 615.96,933.338C616.195,933.338 616.431,933.337 616.667,933.333L750,933.333C750.236,933.337 750.471,933.338 750.707,933.338C778.139,933.338 800.712,910.765 800.712,883.333C800.712,855.901 778.139,833.328 750.707,833.328C750.471,833.328 750.236,833.33 750,833.333L616.667,833.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/UseHardwareAccelerationLight.svg b/VDownload/Assets/Icons/UseHardwareAccelerationLight.svg deleted file mode 100644 index 4a53e9e..0000000 --- a/VDownload/Assets/Icons/UseHardwareAccelerationLight.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1470 1470"><g transform="matrix(1,0,0,1,-30,-67)"> - <path d="M83.333,200C83.098,199.997 82.862,199.995 82.626,199.995C55.194,199.995 32.621,222.568 32.621,250C32.621,277.432 55.194,300.005 82.626,300.005C82.862,300.005 83.098,300.003 83.333,300L166.667,300L166.667,1383.33C166.663,1383.57 166.662,1383.81 166.662,1384.04C166.662,1411.47 189.235,1434.05 216.667,1434.05C244.099,1434.05 266.672,1411.47 266.672,1384.04C266.672,1383.81 266.67,1383.57 266.667,1383.33L266.667,1333.33L416.667,1333.33C444.095,1333.33 466.664,1310.76 466.667,1283.33L466.667,1200L700,1200L700,1283.33C700.003,1310.76 722.572,1333.33 750,1333.33L1316.67,1333.33C1344.1,1333.33 1366.66,1310.76 1366.67,1283.33L1366.67,1200L1383.33,1200C1447.27,1200 1500,1147.27 1500,1083.33L1500,550C1500,486.061 1447.27,433.333 1383.33,433.333L266.667,433.333L266.667,250C266.664,222.572 244.095,200.003 216.667,200L83.333,200ZM266.667,533.333L1383.33,533.333C1392.73,533.333 1400,540.606 1400,550L1400,1083.33C1400,1092.73 1392.73,1100 1383.33,1100L1316.67,1100C1289.24,1100 1266.67,1122.57 1266.67,1150L1266.67,1233.33L800,1233.33L800,1150C799.997,1122.57 777.428,1100 750,1100L416.667,1100C389.239,1100 366.669,1122.57 366.667,1150L366.667,1233.33L266.667,1233.33L266.667,1000L416.667,1000C444.095,999.997 466.664,977.428 466.667,950L466.667,683.333C466.664,655.905 444.095,633.336 416.667,633.333L266.667,633.333L266.667,533.333ZM616.667,633.333C616.431,633.33 616.195,633.328 615.96,633.328C588.528,633.328 565.955,655.901 565.955,683.333C565.955,710.765 588.528,733.338 615.96,733.338C616.195,733.338 616.431,733.337 616.667,733.333L750,733.333C750.236,733.337 750.471,733.338 750.707,733.338C778.139,733.338 800.712,710.765 800.712,683.333C800.712,655.901 778.139,633.328 750.707,633.328C750.471,633.328 750.236,633.33 750,633.333L616.667,633.333ZM983.333,633.333C955.905,633.336 933.336,655.905 933.333,683.333L933.333,950C933.336,977.428 955.905,999.997 983.333,1000L1250,1000C1277.43,999.997 1300,977.428 1300,950L1300,683.333C1300,655.905 1277.43,633.336 1250,633.333L983.333,633.333ZM266.667,733.333L366.667,733.333L366.667,900L266.667,900L266.667,733.333ZM1033.33,733.333L1200,733.333L1200,900L1033.33,900L1033.33,733.333ZM616.667,833.333C616.431,833.33 616.195,833.328 615.96,833.328C588.528,833.328 565.955,855.901 565.955,883.333C565.955,910.765 588.528,933.338 615.96,933.338C616.195,933.338 616.431,933.337 616.667,933.333L750,933.333C750.236,933.337 750.471,933.338 750.707,933.338C778.139,933.338 800.712,910.765 800.712,883.333C800.712,855.901 778.139,833.328 750.707,833.328C750.471,833.328 750.236,833.33 750,833.333L616.667,833.333Z" style="fill-rule:nonzero;"/> - </g></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ViewsDark.svg b/VDownload/Assets/Icons/ViewsDark.svg deleted file mode 100644 index bbf57fd..0000000 --- a/VDownload/Assets/Icons/ViewsDark.svg +++ /dev/null @@ -1 +0,0 @@ -<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><path d="M799.544,300C422.224,300 89.762,561.531 1.563,904.232C0.52,908.294 -0.008,912.472 -0.008,916.667C-0.008,944.1 22.566,966.675 50,966.675C72.756,966.675 92.779,951.143 98.438,929.102C174.438,633.803 468.865,400 799.544,400C1130.22,400 1425.58,633.885 1501.56,929.102C1507.22,951.143 1527.24,966.675 1550,966.675C1577.43,966.675 1600.01,944.1 1600.01,916.667C1600.01,912.472 1599.48,908.294 1598.44,904.232C1510.22,561.448 1176.87,300 799.544,300ZM800.065,566.667C622.73,566.667 477.93,711.467 477.93,888.802C477.93,1066.14 622.73,1211 800.065,1211C977.401,1211 1122.27,1066.14 1122.27,888.802C1122.27,711.467 977.401,566.667 800.065,566.667ZM800.065,666.667C923.357,666.667 1022.27,765.511 1022.27,888.802C1022.27,1012.09 923.357,1111 800.065,1111C676.774,1111 577.93,1012.09 577.93,888.802C577.93,765.511 676.774,666.667 800.065,666.667Z"/></svg> \ No newline at end of file diff --git a/VDownload/Assets/Icons/ViewsLight.svg b/VDownload/Assets/Icons/ViewsLight.svg deleted file mode 100644 index 34718b7..0000000 --- a/VDownload/Assets/Icons/ViewsLight.svg +++ /dev/null @@ -1 +0,0 @@ -<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><path d="M799.544,300C422.224,300 89.762,561.531 1.563,904.232C0.52,908.294 -0.008,912.472 -0.008,916.667C-0.008,944.1 22.566,966.675 50,966.675C72.756,966.675 92.779,951.143 98.438,929.102C174.438,633.803 468.865,400 799.544,400C1130.22,400 1425.58,633.885 1501.56,929.102C1507.22,951.143 1527.24,966.675 1550,966.675C1577.43,966.675 1600.01,944.1 1600.01,916.667C1600.01,912.472 1599.48,908.294 1598.44,904.232C1510.22,561.448 1176.87,300 799.544,300ZM800.065,566.667C622.73,566.667 477.93,711.467 477.93,888.802C477.93,1066.14 622.73,1211 800.065,1211C977.401,1211 1122.27,1066.14 1122.27,888.802C1122.27,711.467 977.401,566.667 800.065,566.667ZM800.065,666.667C923.357,666.667 1022.27,765.511 1022.27,888.802C1022.27,1012.09 923.357,1111 800.065,1111C676.774,1111 577.93,1012.09 577.93,888.802C577.93,765.511 676.774,666.667 800.065,666.667Z"/></svg> \ No newline at end of file diff --git a/VDownload/Assets/Images/HomeTasksListPlaceholderImage.svg b/VDownload/Assets/Images/HomeTasksListPlaceholderImage.svg deleted file mode 100644 index f39f467..0000000 --- a/VDownload/Assets/Images/HomeTasksListPlaceholderImage.svg +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1600 1600"> -<g id="surface103053526" transform="matrix(1.14286,0,0,1.14286,-114.286,-114.286)"> - <path d="M283.332,233.332C182.676,233.332 100,316.008 100,416.668L100,1183.33C100,1283.99 182.676,1366.67 283.332,1366.67L1316.67,1366.67C1417.32,1366.67 1500,1283.99 1500,1183.33L1500,416.668C1500,316.008 1417.32,233.332 1316.67,233.332L283.332,233.332ZM283.332,333.332L1316.67,333.332C1363.27,333.332 1400,370.059 1400,416.668L1400,1183.33C1400,1229.94 1363.27,1266.67 1316.67,1266.67L283.332,1266.67C236.727,1266.67 200,1229.94 200,1183.33L200,416.668C200,370.059 236.727,333.332 283.332,333.332ZM699.152,566.668C687.734,566.813 676.379,569.863 666.211,575.848C645.844,587.848 633.332,609.734 633.332,633.332L633.332,966.668C633.332,990.266 645.844,1012.15 666.211,1024.15C676.609,1030.29 688.301,1033.33 700,1033.33C711.133,1033.33 722.289,1030.5 732.355,1024.93L1032.36,858.27C1053.52,846.5 1066.67,824.234 1066.67,800C1066.67,775.766 1053.52,753.5 1032.36,741.73L732.355,575.066C722.039,569.332 710.574,566.52 699.152,566.668Z" style="fill:rgb(170,170,170);fill-rule:nonzero;"/> - </g> -</svg> diff --git a/VDownload/Assets/Images/UnknownThumbnail.png b/VDownload/Assets/Images/UnknownThumbnail.png deleted file mode 100644 index 10e53ca..0000000 Binary files a/VDownload/Assets/Images/UnknownThumbnail.png and /dev/null differ diff --git a/VDownload/Assets/Logo/Logo.ico b/VDownload/Assets/Logo/Logo.ico new file mode 100644 index 0000000..9fc1211 Binary files /dev/null and b/VDownload/Assets/Logo/Logo.ico differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-16_altform-lightunplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-16_altform-lightunplated.png new file mode 100644 index 0000000..7357738 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-16_altform-lightunplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-16_altform-unplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-16_altform-unplated.png new file mode 100644 index 0000000..7357738 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-16_altform-unplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-24_altform-lightunplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-24_altform-lightunplated.png new file mode 100644 index 0000000..82f04f0 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-24_altform-lightunplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-24_altform-unplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000..82f04f0 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-256_altform-lightunplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-256_altform-lightunplated.png new file mode 100644 index 0000000..1fb0224 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-256_altform-lightunplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-256_altform-unplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-256_altform-unplated.png new file mode 100644 index 0000000..1fb0224 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-256_altform-unplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-32_altform-lightunplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-32_altform-lightunplated.png new file mode 100644 index 0000000..ae6d7cb Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-32_altform-lightunplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-32_altform-unplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-32_altform-unplated.png new file mode 100644 index 0000000..ae6d7cb Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-32_altform-unplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-48_altform-lightunplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-48_altform-lightunplated.png new file mode 100644 index 0000000..9e758c3 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-48_altform-lightunplated.png differ diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-48_altform-unplated.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-48_altform-unplated.png new file mode 100644 index 0000000..9e758c3 Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-48_altform-unplated.png differ diff --git a/VDownload/Assets/Other/Thumbnail.png b/VDownload/Assets/Other/Thumbnail.png new file mode 100644 index 0000000..1a593ab Binary files /dev/null and b/VDownload/Assets/Other/Thumbnail.png differ diff --git a/VDownload/Assets/SettingsView/NotificationOnSuccessfulDark.png b/VDownload/Assets/SettingsView/NotificationOnSuccessfulDark.png new file mode 100644 index 0000000..9e6d85f Binary files /dev/null and b/VDownload/Assets/SettingsView/NotificationOnSuccessfulDark.png differ diff --git a/VDownload/Assets/SettingsView/NotificationOnSuccessfulLight.png b/VDownload/Assets/SettingsView/NotificationOnSuccessfulLight.png new file mode 100644 index 0000000..439737f Binary files /dev/null and b/VDownload/Assets/SettingsView/NotificationOnSuccessfulLight.png differ diff --git a/VDownload/Assets/SettingsView/NotificationOnUnsuccessfulDark.png b/VDownload/Assets/SettingsView/NotificationOnUnsuccessfulDark.png new file mode 100644 index 0000000..fcae41b Binary files /dev/null and b/VDownload/Assets/SettingsView/NotificationOnUnsuccessfulDark.png differ diff --git a/VDownload/Assets/SettingsView/NotificationOnUnsuccessfulLight.png b/VDownload/Assets/SettingsView/NotificationOnUnsuccessfulLight.png new file mode 100644 index 0000000..57d3bff Binary files /dev/null and b/VDownload/Assets/SettingsView/NotificationOnUnsuccessfulLight.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingFFmpegLocationDark.png b/VDownload/Assets/SettingsView/ProcessingFFmpegLocationDark.png new file mode 100644 index 0000000..731e15e Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingFFmpegLocationDark.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingFFmpegLocationLight.png b/VDownload/Assets/SettingsView/ProcessingFFmpegLocationLight.png new file mode 100644 index 0000000..b82f380 Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingFFmpegLocationLight.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingSpeedDark.png b/VDownload/Assets/SettingsView/ProcessingSpeedDark.png new file mode 100644 index 0000000..28d3f9a Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingSpeedDark.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingSpeedLight.png b/VDownload/Assets/SettingsView/ProcessingSpeedLight.png new file mode 100644 index 0000000..5e7b7ee Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingSpeedLight.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingUseHardwareAccelerationDark.png b/VDownload/Assets/SettingsView/ProcessingUseHardwareAccelerationDark.png new file mode 100644 index 0000000..5a83f4e Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingUseHardwareAccelerationDark.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingUseHardwareAccelerationLight.png b/VDownload/Assets/SettingsView/ProcessingUseHardwareAccelerationLight.png new file mode 100644 index 0000000..2864771 Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingUseHardwareAccelerationLight.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingUseMultithreadingDark.png b/VDownload/Assets/SettingsView/ProcessingUseMultithreadingDark.png new file mode 100644 index 0000000..3f26960 Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingUseMultithreadingDark.png differ diff --git a/VDownload/Assets/SettingsView/ProcessingUseMultithreadingLight.png b/VDownload/Assets/SettingsView/ProcessingUseMultithreadingLight.png new file mode 100644 index 0000000..478965e Binary files /dev/null and b/VDownload/Assets/SettingsView/ProcessingUseMultithreadingLight.png differ diff --git a/VDownload/Assets/SettingsView/SearchingPlaylistCountDark.png b/VDownload/Assets/SettingsView/SearchingPlaylistCountDark.png new file mode 100644 index 0000000..01f741d Binary files /dev/null and b/VDownload/Assets/SettingsView/SearchingPlaylistCountDark.png differ diff --git a/VDownload/Assets/SettingsView/SearchingPlaylistCountLight.png b/VDownload/Assets/SettingsView/SearchingPlaylistCountLight.png new file mode 100644 index 0000000..4fb782f Binary files /dev/null and b/VDownload/Assets/SettingsView/SearchingPlaylistCountLight.png differ diff --git a/VDownload/Assets/SettingsView/TasksDefaultMediaOptionsDark.png b/VDownload/Assets/SettingsView/TasksDefaultMediaOptionsDark.png new file mode 100644 index 0000000..71fe363 Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksDefaultMediaOptionsDark.png differ diff --git a/VDownload/Assets/SettingsView/TasksDefaultMediaOptionsLight.png b/VDownload/Assets/SettingsView/TasksDefaultMediaOptionsLight.png new file mode 100644 index 0000000..685ac0d Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksDefaultMediaOptionsLight.png differ diff --git a/VDownload/Assets/SettingsView/TasksFilenameTemplateDark.png b/VDownload/Assets/SettingsView/TasksFilenameTemplateDark.png new file mode 100644 index 0000000..4a9b713 Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksFilenameTemplateDark.png differ diff --git a/VDownload/Assets/SettingsView/TasksFilenameTemplateLight.png b/VDownload/Assets/SettingsView/TasksFilenameTemplateLight.png new file mode 100644 index 0000000..bd9da97 Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksFilenameTemplateLight.png differ diff --git a/VDownload/Assets/SettingsView/TasksMeteredConnectionWarningDark.png b/VDownload/Assets/SettingsView/TasksMeteredConnectionWarningDark.png new file mode 100644 index 0000000..34cb119 Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksMeteredConnectionWarningDark.png differ diff --git a/VDownload/Assets/SettingsView/TasksMeteredConnectionWarningLight.png b/VDownload/Assets/SettingsView/TasksMeteredConnectionWarningLight.png new file mode 100644 index 0000000..14763bf Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksMeteredConnectionWarningLight.png differ diff --git a/VDownload/Assets/SettingsView/TasksOutputDirectoryDark.png b/VDownload/Assets/SettingsView/TasksOutputDirectoryDark.png new file mode 100644 index 0000000..731e15e Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksOutputDirectoryDark.png differ diff --git a/VDownload/Assets/SettingsView/TasksOutputDirectoryLight.png b/VDownload/Assets/SettingsView/TasksOutputDirectoryLight.png new file mode 100644 index 0000000..b82f380 Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksOutputDirectoryLight.png differ diff --git a/VDownload/Assets/SettingsView/TasksReplaceOutputFileDark.png b/VDownload/Assets/SettingsView/TasksReplaceOutputFileDark.png new file mode 100644 index 0000000..c9e398c Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksReplaceOutputFileDark.png differ diff --git a/VDownload/Assets/SettingsView/TasksReplaceOutputFileLight.png b/VDownload/Assets/SettingsView/TasksReplaceOutputFileLight.png new file mode 100644 index 0000000..451e87a Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksReplaceOutputFileLight.png differ diff --git a/VDownload/Assets/SettingsView/TasksRunningTasksDark.png b/VDownload/Assets/SettingsView/TasksRunningTasksDark.png new file mode 100644 index 0000000..da06dec Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksRunningTasksDark.png differ diff --git a/VDownload/Assets/SettingsView/TasksRunningTasksLight.png b/VDownload/Assets/SettingsView/TasksRunningTasksLight.png new file mode 100644 index 0000000..dc42751 Binary files /dev/null and b/VDownload/Assets/SettingsView/TasksRunningTasksLight.png differ diff --git a/VDownload/Assets/SettingsView/TempDeleteOnFailDark.png b/VDownload/Assets/SettingsView/TempDeleteOnFailDark.png new file mode 100644 index 0000000..8e33b76 Binary files /dev/null and b/VDownload/Assets/SettingsView/TempDeleteOnFailDark.png differ diff --git a/VDownload/Assets/SettingsView/TempDeleteOnFailLight.png b/VDownload/Assets/SettingsView/TempDeleteOnFailLight.png new file mode 100644 index 0000000..8db92f4 Binary files /dev/null and b/VDownload/Assets/SettingsView/TempDeleteOnFailLight.png differ diff --git a/VDownload/Assets/SettingsView/TempDirectoryDark.png b/VDownload/Assets/SettingsView/TempDirectoryDark.png new file mode 100644 index 0000000..c77de97 Binary files /dev/null and b/VDownload/Assets/SettingsView/TempDirectoryDark.png differ diff --git a/VDownload/Assets/SettingsView/TempDirectoryLight.png b/VDownload/Assets/SettingsView/TempDirectoryLight.png new file mode 100644 index 0000000..b8151ed Binary files /dev/null and b/VDownload/Assets/SettingsView/TempDirectoryLight.png differ diff --git a/VDownload/Assets/Sources/Twitch.png b/VDownload/Assets/Sources/Twitch.png index e5fa3b3..4a5c576 100644 Binary files a/VDownload/Assets/Sources/Twitch.png and b/VDownload/Assets/Sources/Twitch.png differ diff --git a/VDownload/Controls/PlaceholderableStackPanel.xaml b/VDownload/Controls/PlaceholderableStackPanel.xaml deleted file mode 100644 index 15c0593..0000000 --- a/VDownload/Controls/PlaceholderableStackPanel.xaml +++ /dev/null @@ -1,13 +0,0 @@ -<UserControl - x:Class="VDownload.Controls.PlaceholderableStackPanel" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Controls" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" - d:DesignHeight="300" - d:DesignWidth="400"> - - <StackPanel x:Name="StackPanel"/> -</UserControl> diff --git a/VDownload/Controls/PlaceholderableStackPanel.xaml.cs b/VDownload/Controls/PlaceholderableStackPanel.xaml.cs deleted file mode 100644 index b736e9c..0000000 --- a/VDownload/Controls/PlaceholderableStackPanel.xaml.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Controls -{ - public sealed partial class PlaceholderableStackPanel : UserControl - { - #region CONSTRUCTORS - - public PlaceholderableStackPanel() - { - InitializeComponent(); - - IsPlaceholderActive = true; - StackPanel.Children.Add(Placeholder); - StackPanel.VerticalAlignment = VerticalAlignment.Center; - } - - #endregion - - - - #region PROPERTIES - - private UIElement _Placeholder = new Grid(); - public UIElement Placeholder - { - get => _Placeholder; - set - { - _Placeholder = value; - if (IsPlaceholderActive) - { - StackPanel.Children.Clear(); - StackPanel.Children.Add(_Placeholder); - StackPanel.VerticalAlignment = VerticalAlignment.Center; - } - } - } - public double Spacing - { - get => StackPanel.Spacing; - set => StackPanel.Spacing = value; - } - - public bool IsPlaceholderActive { get; private set; } - - #endregion - - - - #region PUBLIC METHODS - - public void Add(UIElement item) - { - if (IsPlaceholderActive) - { - StackPanel.Children.Clear(); - IsPlaceholderActive = false; - StackPanel.VerticalAlignment = VerticalAlignment.Stretch; - } - StackPanel.Children.Add(item); - } - - public void Remove(UIElement item) - { - StackPanel.Children.Remove(item); - if (StackPanel.Children.Count == 0) - { - StackPanel.Children.Add(_Placeholder); - StackPanel.VerticalAlignment = VerticalAlignment.Center; - IsPlaceholderActive = true; - } - } - - public void Clear() - { - StackPanel.Children.Clear(); - StackPanel.Children.Add(_Placeholder); - StackPanel.VerticalAlignment = VerticalAlignment.Center; - IsPlaceholderActive = true; - } - - public UIElement[] GetAllItems() => IsPlaceholderActive ? StackPanel.Children.ToArray() : new UIElement[0]; - - #endregion - } -} diff --git a/VDownload/Controls/SettingControl.xaml b/VDownload/Controls/SettingControl.xaml deleted file mode 100644 index 654408b..0000000 --- a/VDownload/Controls/SettingControl.xaml +++ /dev/null @@ -1,53 +0,0 @@ -<UserControl - x:Class="VDownload.Controls.SettingControl" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Controls" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" - d:DesignHeight="300" - d:DesignWidth="400"> - - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.ThemeDictionaries> - <ResourceDictionary x:Key="Dark"> - <SolidColorBrush x:Key="SettingControlBackgroundColor" Color="#2B2B2B"/> - </ResourceDictionary> - <ResourceDictionary x:Key="Light"> - <SolidColorBrush x:Key="SettingControlBackgroundColor" Color="#FBFBFB"/> - </ResourceDictionary> - </ResourceDictionary.ThemeDictionaries> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </UserControl.Resources> - - - <Grid MinHeight="65" Padding="18" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource SettingControlBackgroundColor}" ColumnSpacing="18"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - - <!-- ICON --> - <Image Grid.Column="0" VerticalAlignment="Center" Width="20" Source="{x:Bind Icon, Mode=OneWay}"/> - - <!-- TITLE & DESCRIPTION --> - <Grid Grid.Column="1" VerticalAlignment="Center"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> - <TextBlock Grid.Row="0" Text="{x:Bind Title, Mode=OneWay}"/> - <TextBlock Grid.Row="1" Visibility="{x:Bind Description, Converter={StaticResource StringToVisibilityConverter}}" FontSize="12" Foreground="{ThemeResource SystemBaseMediumColor}" TextWrapping="Wrap" Text="{x:Bind Description, Mode=OneWay}"/> - </Grid> - - <!-- SETTING CONTROL --> - <ContentPresenter Grid.Column="2" VerticalAlignment="Center" Content="{x:Bind SettingContent, Mode=OneWay}"/> - </Grid> -</UserControl> diff --git a/VDownload/Controls/SettingControl.xaml.cs b/VDownload/Controls/SettingControl.xaml.cs deleted file mode 100644 index 313b1e5..0000000 --- a/VDownload/Controls/SettingControl.xaml.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Windows.UI; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; - -namespace VDownload.Controls -{ - public sealed partial class SettingControl : UserControl - { - #region CONSTRUCTORS - - public SettingControl() - { - this.InitializeComponent(); - } - - #endregion - - - - #region PROPERTIES - - // ICON - public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(ImageSource), typeof(SettingControl), new PropertyMetadata(null)); - public ImageSource Icon - { - get => (ImageSource)GetValue(IconProperty); - set => SetValue(IconProperty, value); - } - - // TITLE - public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(SettingControl), new PropertyMetadata(string.Empty)); - public string Title - { - get => (string)GetValue(TitleProperty); - set => SetValue(TitleProperty, value); - } - - // DESCRIPTION - public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(SettingControl), new PropertyMetadata(string.Empty)); - public string Description - { - get => (string)GetValue(DescriptionProperty); - set => SetValue(DescriptionProperty, value); - } - - // SETTING CONTENT - public FrameworkElement SettingContent { get; set; } - - #endregion - } -} diff --git a/VDownload/Converters/StringToVisibilityConverter.cs b/VDownload/Converters/StringToVisibilityConverter.cs deleted file mode 100644 index 3fd6697..0000000 --- a/VDownload/Converters/StringToVisibilityConverter.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Data; - -namespace VDownload.Converters -{ - public class StringToVisibilityConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, string language) - { - if (string.IsNullOrEmpty((string)value)) - { - return Visibility.Collapsed; - } - else - { - return Visibility.Visible; - } - } - - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotImplementedException(); - } - } -} diff --git a/VDownload/Converters/TimeSpanToTextBoxMaskConverter.cs b/VDownload/Converters/TimeSpanToTextBoxMaskConverter.cs deleted file mode 100644 index d7b6bee..0000000 --- a/VDownload/Converters/TimeSpanToTextBoxMaskConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Windows.UI.Xaml.Data; - -namespace VDownload.Converters -{ - public class TimeSpanToTextBoxMaskConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, string language) - { - string mask = string.Empty; - - if (Math.Floor(((TimeSpan)value).TotalHours) > 0) mask += $"{Math.Floor(((TimeSpan)value).TotalHours).ToString()[0]}{new string('9', Math.Floor(((TimeSpan)value).TotalHours).ToString().Length - 1)}:"; - if (Math.Floor(((TimeSpan)value).TotalMinutes) > 0) mask += Math.Floor(((TimeSpan)value).TotalHours) > 0 ? $"59:" : $"{((TimeSpan)value).Minutes.ToString()[0]}{new string('9', ((TimeSpan)value).Minutes.ToString().Length - 1)}:"; - mask += Math.Floor(((TimeSpan)value).TotalMinutes) > 0 ? $"59" : $"{((TimeSpan)value).Seconds.ToString()[0]}{new string('9', ((TimeSpan)value).Seconds.ToString().Length - 1)}"; - - return mask; - } - - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotImplementedException(); - } - } -} diff --git a/VDownload/Converters/TimeSpanToTextBoxMaskElementsConverter.cs b/VDownload/Converters/TimeSpanToTextBoxMaskElementsConverter.cs deleted file mode 100644 index 7f04cbf..0000000 --- a/VDownload/Converters/TimeSpanToTextBoxMaskElementsConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Windows.UI.Xaml.Data; - -namespace VDownload.Converters -{ - public class TimeSpanToTextBoxMaskElementsConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, string language) - { - HashSet<int> maskElements = new HashSet<int>(); - - if (Math.Floor(((TimeSpan)value).TotalHours) > 0) maskElements.Add(int.Parse(Math.Floor(((TimeSpan)value).TotalHours).ToString()[0].ToString())); - if (Math.Floor(((TimeSpan)value).TotalMinutes) > 0) - { - if (Math.Floor(((TimeSpan)value).TotalHours) > 0) maskElements.Add(5); - else maskElements.Add(int.Parse(((TimeSpan)value).Minutes.ToString()[0].ToString())); - } - if (Math.Floor(((TimeSpan)value).TotalMinutes) > 0) maskElements.Add(5); - else maskElements.Add(int.Parse(((TimeSpan)value).Seconds.ToString()[0].ToString())); - - List<string> maskElementsString = new List<string>(); - foreach (int i in maskElements) - { - if (i != 9) maskElementsString.Add($"{i}:[0-{i}]"); - } - - return string.Join(',', maskElementsString); - } - - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotImplementedException(); - } - } -} diff --git a/VDownload/Dictionaries/Colors.xaml b/VDownload/Dictionaries/Colors.xaml new file mode 100644 index 0000000..5d970a2 --- /dev/null +++ b/VDownload/Dictionaries/Colors.xaml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <SolidColorBrush x:Key="GreyText" Color="#626262"/> + <ResourceDictionary.ThemeDictionaries> + <ResourceDictionary x:Key="Light"> + <SolidColorBrush x:Key="ViewBackgroundColor" Color="#F5F5F5"/> + <SolidColorBrush x:Key="PanelBackgroundColor" Color="#FCFCFC"/> + <SolidColorBrush x:Key="OptionBarBackgroundColor" Color="#efefef"/> + </ResourceDictionary> + <ResourceDictionary x:Key="Dark"> + <SolidColorBrush x:Key="ViewBackgroundColor" Color="#242424"/> + <SolidColorBrush x:Key="PanelBackgroundColor" Color="#2F2F2F"/> + <SolidColorBrush x:Key="OptionBarBackgroundColor" Color="#1B1B1B"/> + </ResourceDictionary> + </ResourceDictionary.ThemeDictionaries> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Converters.xaml b/VDownload/Dictionaries/Converters.xaml new file mode 100644 index 0000000..350aef3 --- /dev/null +++ b/VDownload/Dictionaries/Converters.xaml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:v="using:VDownload.Core.Views" + xmlns:lt="using:SimpleToolkit.UI.WinUI.Converters" + xmlns:ct="using:CommunityToolkit.WinUI.Converters"> + <v:ViewModelToViewConverter x:Key="ViewModelToViewConverter"/> + + <lt:ObjectToStringConverter x:Key="ObjectToStringConverter"/> + <lt:ObjectToIntConverter x:Key="ObjectToIntConverter"/> + + <ct:StringFormatConverter x:Key="StringFormatConverter"/> + <ct:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/> + <ct:BoolNegationConverter x:Key="BoolNegationConverter"/> + <ct:EmptyObjectToObjectConverter x:Key="IsNotNullConverter" + EmptyValue="False" + NotEmptyValue="True"/> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesBaseView.xaml b/VDownload/Dictionaries/Images/ImagesBaseView.xaml new file mode 100644 index 0000000..e533eb6 --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesBaseView.xaml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <ResourceDictionary.ThemeDictionaries> + <ResourceDictionary x:Key="Dark"> + <x:String x:Key="ImageBaseViewAuthentication">/Assets/BaseView/AuthenticationDark.png</x:String> + <x:String x:Key="ImageBaseViewHome">/Assets/BaseView/HomeDark.png</x:String> + <x:String x:Key="ImageBaseViewAbout">/Assets/BaseView/AboutDark.png</x:String> + <x:String x:Key="ImageBaseViewSubscriptions">/Assets/BaseView/SubscriptionsDark.png</x:String> + </ResourceDictionary> + <ResourceDictionary x:Key="Light"> + <x:String x:Key="ImageBaseViewAuthentication">/Assets/BaseView/AuthenticationLight.png</x:String> + <x:String x:Key="ImageBaseViewHome">/Assets/BaseView/HomeLight.png</x:String> + <x:String x:Key="ImageBaseViewAbout">/Assets/BaseView/AboutLight.png</x:String> + <x:String x:Key="ImageBaseViewSubscriptions">/Assets/BaseView/SubscriptionsLight.png</x:String> + </ResourceDictionary> + </ResourceDictionary.ThemeDictionaries> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesHomeDownloadsView.xaml b/VDownload/Dictionaries/Images/ImagesHomeDownloadsView.xaml new file mode 100644 index 0000000..e63ab2d --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesHomeDownloadsView.xaml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <x:String x:Key="ImageHomeDownloadsViewNoTasks">/Assets/HomeDownloadsView/NoTasks.png</x:String> + <ResourceDictionary.ThemeDictionaries> + <ResourceDictionary x:Key="Light"> + <x:String x:Key="ImageHomeDownloadsViewQuality">/Assets/HomeDownloadsView/QualityLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewTime">/Assets/HomeDownloadsView/TimeLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewFile">/Assets/HomeDownloadsView/FileLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewIdle">/Assets/HomeDownloadsView/IdleLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewQueued">/Assets/HomeDownloadsView/QueuedLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewInitializing">/Assets/HomeDownloadsView/InitializingLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewCancelled">/Assets/HomeDownloadsView/CancelledLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewDownloading">/Assets/HomeDownloadsView/DownloadingLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewProcessing">/Assets/HomeDownloadsView/ProcessingLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewFinalizing">/Assets/HomeDownloadsView/FinalizingLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewDone">/Assets/HomeDownloadsView/DoneLight.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewError">/Assets/HomeDownloadsView/ErrorLight.png</x:String> + </ResourceDictionary> + <ResourceDictionary x:Key="Dark"> + <x:String x:Key="ImageHomeDownloadsViewQuality">/Assets/HomeDownloadsView/QualityDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewTime">/Assets/HomeDownloadsView/TimeDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewFile">/Assets/HomeDownloadsView/FileDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewIdle">/Assets/HomeDownloadsView/IdleDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewQueued">/Assets/HomeDownloadsView/QueuedDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewInitializing">/Assets/HomeDownloadsView/InitializingDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewCancelled">/Assets/HomeDownloadsView/CancelledDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewDownloading">/Assets/HomeDownloadsView/DownloadingDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewProcessing">/Assets/HomeDownloadsView/ProcessingDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewFinalizing">/Assets/HomeDownloadsView/FinalizingDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewDone">/Assets/HomeDownloadsView/DoneDark.png</x:String> + <x:String x:Key="ImageHomeDownloadsViewError">/Assets/HomeDownloadsView/ErrorDark.png</x:String> + </ResourceDictionary> + </ResourceDictionary.ThemeDictionaries> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesHomeVideoCollectionView.xaml b/VDownload/Dictionaries/Images/ImagesHomeVideoCollectionView.xaml new file mode 100644 index 0000000..a21a67d --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesHomeVideoCollectionView.xaml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <ResourceDictionary.ThemeDictionaries> + <ResourceDictionary x:Key="Light"> + <x:String x:Key="ImageHomeVideoCollectionViewAuthor">/Assets/HomeVideoCollectionView/AuthorLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewDate">/Assets/HomeVideoCollectionView/DateLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTime">/Assets/HomeVideoCollectionView/TimeLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewViews">/Assets/HomeVideoCollectionView/ViewLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewQuality">/Assets/HomeVideoCollectionView/QualityLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewMedia">/Assets/HomeVideoCollectionView/MediaLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTrimLeft">/Assets/HomeVideoCollectionView/TrimLeftLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTrim">/Assets/HomeVideoCollectionView/TrimLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTrimRight">/Assets/HomeVideoCollectionView/TrimRightLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewDirectory">/Assets/HomeVideoCollectionView/DirectoryLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewFilename">/Assets/HomeVideoCollectionView/FilenameLight.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewExtension">/Assets/HomeVideoCollectionView/ExtensionLight.png</x:String> + </ResourceDictionary> + <ResourceDictionary x:Key="Dark"> + <x:String x:Key="ImageHomeVideoCollectionViewAuthor">/Assets/HomeVideoCollectionView/AuthorDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewDate">/Assets/HomeVideoCollectionView/DateDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTime">/Assets/HomeVideoCollectionView/TimeDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewViews">/Assets/HomeVideoCollectionView/ViewDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewQuality">/Assets/HomeVideoCollectionView/QualityDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewMedia">/Assets/HomeVideoCollectionView/MediaDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTrimLeft">/Assets/HomeVideoCollectionView/TrimLeftDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTrim">/Assets/HomeVideoCollectionView/TrimDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewTrimRight">/Assets/HomeVideoCollectionView/TrimRightDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewDirectory">/Assets/HomeVideoCollectionView/DirectoryDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewFilename">/Assets/HomeVideoCollectionView/FilenameDark.png</x:String> + <x:String x:Key="ImageHomeVideoCollectionViewExtension">/Assets/HomeVideoCollectionView/ExtensionDark.png</x:String> + </ResourceDictionary> + </ResourceDictionary.ThemeDictionaries> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesHomeVideoView.xaml b/VDownload/Dictionaries/Images/ImagesHomeVideoView.xaml new file mode 100644 index 0000000..eb77eeb --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesHomeVideoView.xaml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <ResourceDictionary.ThemeDictionaries> + <ResourceDictionary x:Key="Light"> + <x:String x:Key="ImageHomeVideoViewAuthor">/Assets/HomeVideoView/AuthorLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewDate">/Assets/HomeVideoView/DateLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewTime">/Assets/HomeVideoView/TimeLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewViews">/Assets/HomeVideoView/ViewLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewQuality">/Assets/HomeVideoView/QualityLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewMedia">/Assets/HomeVideoView/MediaLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewTrimLeft">/Assets/HomeVideoView/TrimLeftLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewTrim">/Assets/HomeVideoView/TrimLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewTrimRight">/Assets/HomeVideoView/TrimRightLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewDirectory">/Assets/HomeVideoView/DirectoryLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewFilename">/Assets/HomeVideoView/FilenameLight.png</x:String> + <x:String x:Key="ImageHomeVideoViewExtension">/Assets/HomeVideoView/ExtensionLight.png</x:String> + </ResourceDictionary> + <ResourceDictionary x:Key="Dark"> + <x:String x:Key="ImageHomeVideoViewAuthor">/Assets/HomeVideoView/AuthorDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewDate">/Assets/HomeVideoView/DateDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewTime">/Assets/HomeVideoView/TimeDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewViews">/Assets/HomeVideoView/ViewDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewQuality">/Assets/HomeVideoView/QualityDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewMedia">/Assets/HomeVideoView/MediaDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewTrimLeft">/Assets/HomeVideoView/TrimLeftDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewTrim">/Assets/HomeVideoView/TrimDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewTrimRight">/Assets/HomeVideoView/TrimRightDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewDirectory">/Assets/HomeVideoView/DirectoryDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewFilename">/Assets/HomeVideoView/FilenameDark.png</x:String> + <x:String x:Key="ImageHomeVideoViewExtension">/Assets/HomeVideoView/ExtensionDark.png</x:String> + </ResourceDictionary> + </ResourceDictionary.ThemeDictionaries> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesHomeView.xaml b/VDownload/Dictionaries/Images/ImagesHomeView.xaml new file mode 100644 index 0000000..bfedf7a --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesHomeView.xaml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <x:String x:Key="ImageHomeViewError">/Assets/HomeView/Error.png</x:String> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesLogo.xaml b/VDownload/Dictionaries/Images/ImagesLogo.xaml new file mode 100644 index 0000000..1ebe7ea --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesLogo.xaml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <x:String x:Key="ImageLogo">/Assets/Logo/Logo.png</x:String> + <x:String x:Key="ImageLogoIcon">/Assets/Logo/Logo.ico</x:String> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesOther.xaml b/VDownload/Dictionaries/Images/ImagesOther.xaml new file mode 100644 index 0000000..6a91c88 --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesOther.xaml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <x:String x:Key="ImageOtherThumbnail">/Assets/Other/Thumbnail.png</x:String> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesSettingsView.xaml b/VDownload/Dictionaries/Images/ImagesSettingsView.xaml new file mode 100644 index 0000000..952de6b --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesSettingsView.xaml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <ResourceDictionary.ThemeDictionaries> + <ResourceDictionary x:Key="Light"> + <x:String x:Key="ImageSettingsViewSearchingPlaylistCount">/Assets/SettingsView/SearchingPlaylistCountLight.png</x:String> + <x:String x:Key="ImageSettingsViewTasksDefaultMediaOptions">/Assets/SettingsView/TasksDefaultMediaOptionsLight.png</x:String> + <x:String x:Key="ImageSettingsViewTasksRunningTasks">/Assets/SettingsView/TasksRunningTasksLight.png</x:String> + <x:String x:Key="ImageSettingsViewTasksFilenameTemplate">/Assets/SettingsView/TasksFilenameTemplateLight.png</x:String> + <x:String x:Key="ImageSettingsViewTasksMeteredConnectionWarning">/Assets/SettingsView/TasksMeteredConnectionWarningLight.png</x:String> + <x:String x:Key="ImageSettingsViewTasksOutputDirectory">/Assets/SettingsView/TasksOutputDirectoryLight.png</x:String> + <x:String x:Key="ImageSettingsViewTasksReplaceOutputFile">/Assets/SettingsView/TasksReplaceOutputFileLight.png</x:String> + <x:String x:Key="ImageSettingsViewTempDirectory">/Assets/SettingsView/TempDirectoryLight.png</x:String> + <x:String x:Key="ImageSettingsViewTempDeleteOnFail">/Assets/SettingsView/TempDeleteOnFailLight.png</x:String> + <x:String x:Key="ImageSettingsViewNotificationOnSuccessful">/Assets/SettingsView/NotificationOnSuccessfulLight.png</x:String> + <x:String x:Key="ImageSettingsViewNotificationOnUnsuccessful">/Assets/SettingsView/NotificationOnUnsuccessfulLight.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingFFmpegLocation">/Assets/SettingsView/ProcessingFFmpegLocationLight.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingUseHardwareAcceleration">/Assets/SettingsView/ProcessingUseHardwareAccelerationLight.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingUseMultithreading">/Assets/SettingsView/ProcessingUseMultithreadingLight.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingSpeed">/Assets/SettingsView/ProcessingSpeedLight.png</x:String> + </ResourceDictionary> + <ResourceDictionary x:Key="Dark"> + <x:String x:Key="ImageSettingsViewSearchingPlaylistCount">/Assets/SettingsView/SearchingPlaylistCountDark.png</x:String> + <x:String x:Key="ImageSettingsViewTasksDefaultMediaOptions">/Assets/SettingsView/TasksDefaultMediaOptionsDark.png</x:String> + <x:String x:Key="ImageSettingsViewTasksRunningTasks">/Assets/SettingsView/TasksRunningTasksDark.png</x:String> + <x:String x:Key="ImageSettingsViewTasksFilenameTemplate">/Assets/SettingsView/TasksFilenameTemplateDark.png</x:String> + <x:String x:Key="ImageSettingsViewTasksMeteredConnectionWarning">/Assets/SettingsView/TasksMeteredConnectionWarningDark.png</x:String> + <x:String x:Key="ImageSettingsViewTasksOutputDirectory">/Assets/SettingsView/TasksOutputDirectoryDark.png</x:String> + <x:String x:Key="ImageSettingsViewTasksReplaceOutputFile">/Assets/SettingsView/TasksReplaceOutputFileDark.png</x:String> + <x:String x:Key="ImageSettingsViewTempDirectory">/Assets/SettingsView/TempDirectoryDark.png</x:String> + <x:String x:Key="ImageSettingsViewTempDeleteOnFail">/Assets/SettingsView/TempDeleteOnFailDark.png</x:String> + <x:String x:Key="ImageSettingsViewNotificationOnSuccessful">/Assets/SettingsView/NotificationOnSuccessfulDark.png</x:String> + <x:String x:Key="ImageSettingsViewNotificationOnUnsuccessful">/Assets/SettingsView/NotificationOnUnsuccessfulDark.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingFFmpegLocation">/Assets/SettingsView/ProcessingFFmpegLocationDark.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingUseHardwareAcceleration">/Assets/SettingsView/ProcessingUseHardwareAccelerationDark.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingUseMultithreading">/Assets/SettingsView/ProcessingUseMultithreadingDark.png</x:String> + <x:String x:Key="ImageSettingsViewProcessingSpeed">/Assets/SettingsView/ProcessingSpeedDark.png</x:String> + </ResourceDictionary> + </ResourceDictionary.ThemeDictionaries> +</ResourceDictionary> diff --git a/VDownload/Dictionaries/Images/ImagesSources.xaml b/VDownload/Dictionaries/Images/ImagesSources.xaml new file mode 100644 index 0000000..233de50 --- /dev/null +++ b/VDownload/Dictionaries/Images/ImagesSources.xaml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <x:String x:Key="ImageSourcesTwitch">/Assets/Sources/Twitch.png</x:String> +</ResourceDictionary> diff --git a/VDownload/FFmpeg/ffmpeg.exe b/VDownload/FFmpeg/ffmpeg.exe new file mode 100644 index 0000000..6ad6869 --- /dev/null +++ b/VDownload/FFmpeg/ffmpeg.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c97605b04e235b26337aa0c2e052d1061f45e701867e3d85f469bef79cd2b73 +size 83374080 diff --git a/VDownload/FFmpeg/ffprobe.exe b/VDownload/FFmpeg/ffprobe.exe new file mode 100644 index 0000000..9534dd9 --- /dev/null +++ b/VDownload/FFmpeg/ffprobe.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f34eec6a9aa0b8a0b726e442e946b8d35f4ad63afe12673fd2bdb110807886d +size 83207680 diff --git a/VDownload/Package.appxmanifest b/VDownload/Package.appxmanifest index 4bdde34..0efd66c 100644 --- a/VDownload/Package.appxmanifest +++ b/VDownload/Package.appxmanifest @@ -4,14 +4,17 @@ xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" - IgnorableNamespaces="uap mp"> + xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" + xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" + xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" + IgnorableNamespaces="uap rescap com desktop"> <Identity Name="VDownload" Publisher="CN=Mateusz Skoczek" - Version="0.1.0.0" /> + Version="0.0.0.0" /> - <mp:PhoneIdentity PhoneProductId="b0f59998-a2e2-4edb-a764-b0cf8c657336" PhonePublisherId="00000000-0000-0000-0000-000000000000"/> + <mp:PhoneIdentity PhoneProductId="df3e7de0-430a-4a93-a68b-48c2f3ec497a" PhonePublisherId="00000000-0000-0000-0000-000000000000"/> <Properties> <DisplayName>VDownload</DisplayName> @@ -20,30 +23,44 @@ </Properties> <Dependencies> - <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" /> + <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" /> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" /> </Dependencies> <Resources> - <Resource Language="x-generate"/> + <Resource Language="en-US"/> </Resources> <Applications> <Application Id="App" Executable="$targetnametoken$.exe" - EntryPoint="VDownload.App"> + EntryPoint="$targetentrypoint$"> <uap:VisualElements DisplayName="VDownload" - Square150x150Logo="Assets\Logo\Square150x150Logo.png" - Square44x44Logo="Assets\Logo\Square44x44Logo.png" Description="VDownload" - BackgroundColor="transparent"> - <uap:DefaultTile Wide310x150Logo="Assets\Logo\Wide310x150Logo.png" Square71x71Logo="Assets\Logo\SmallTile.png" Square310x310Logo="Assets\Logo\LargeTile.png"/> + BackgroundColor="transparent" + Square150x150Logo="Assets\Logo\Square150x150Logo.png" + Square44x44Logo="Assets\Logo\Square44x44Logo.png"> + <uap:DefaultTile Wide310x150Logo="Assets\Logo\Wide310x150Logo.png" Square71x71Logo="Assets\Logo\SmallTile.png" Square310x310Logo="Assets\Logo\LargeTile.png"/> <uap:SplashScreen Image="Assets\Logo\SplashScreen.png" /> </uap:VisualElements> + <Extensions> + <desktop:Extension Category="windows.toastNotificationActivation"> + <desktop:ToastNotificationActivation ToastActivatorCLSID="1e95e58c-ce3f-421a-8cee-cd3332a87109" /> + </desktop:Extension> + + <com:Extension Category="windows.comServer"> + <com:ComServer> + <com:ExeServer Executable="App1.exe" Arguments="----AppNotificationActivated:" DisplayName="Toast activator"> + <com:Class Id="1e95e58c-ce3f-421a-8cee-cd3332a87109" DisplayName="Toast activator"/> + </com:ExeServer> + </com:ComServer> + </com:Extension> + </Extensions> </Application> </Applications> <Capabilities> - <Capability Name="internetClient" /> + <rescap:Capability Name="runFullTrust" /> </Capabilities> -</Package> \ No newline at end of file +</Package> diff --git a/VDownload/Resources/AppProperties.xaml b/VDownload/Resources/AppProperties.xaml deleted file mode 100644 index a74515d..0000000 --- a/VDownload/Resources/AppProperties.xaml +++ /dev/null @@ -1,9 +0,0 @@ -<ResourceDictionary - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <x:String x:Key="AppName">VDownload</x:String> - <x:String x:Key="AppVersion">1.0-prerelease1</x:String> - <x:String x:Key="AppAuthor">Mateusz Skoczek</x:String> - <x:String x:Key="AppRepositoryUrl">https://github.com/mateuszskoczek/VDownload</x:String> - <x:String x:Key="AppDonationUrl">https://paypal.me/mateuszskoczek</x:String> -</ResourceDictionary> diff --git a/VDownload/Resources/Colors.xaml b/VDownload/Resources/Colors.xaml deleted file mode 100644 index 8243534..0000000 --- a/VDownload/Resources/Colors.xaml +++ /dev/null @@ -1,23 +0,0 @@ -<ResourceDictionary - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <ResourceDictionary.ThemeDictionaries> - <ResourceDictionary x:Key="Light"> - <SolidColorBrush x:Key="HomeBackgroundColor" Color="#F5F5F5"/> - <SolidColorBrush x:Key="HomeOptionBarBackgroundColor" Color="#F5F5F5"/> - <SolidColorBrush x:Key="HomePlaylistAddingVideoPanelContentBackgroundColor" Color="#F5F5F5"/> - <SolidColorBrush x:Key="HomePlaylistAddingApplyToAllContentBackgroundColor" Color="#F5F5F5"/> - <SolidColorBrush x:Key="SubscriptionsSubscriptionPanelBackgroundColor" Color="#F5F5F5"/> - <SolidColorBrush x:Key="SubscriptionsAddingPanelBackgroundColor" Color="#F5F5F5"/> - </ResourceDictionary> - <ResourceDictionary x:Key="Dark"> - <SolidColorBrush x:Key="HomeBackgroundColor" Color="#242424"/> - <SolidColorBrush x:Key="HomeOptionBarBackgroundColor" Color="#1B1B1B"/> - <SolidColorBrush x:Key="HomePlaylistAddingVideoPanelContentBackgroundColor" Color="#272727"/> - <SolidColorBrush x:Key="HomePlaylistAddingApplyToAllContentBackgroundColor" Color="#272727"/> - <SolidColorBrush x:Key="SubscriptionsSubscriptionPanelBackgroundColor" Color="#272727"/> - <SolidColorBrush x:Key="SubscriptionsAddingPanelBackgroundColor" Color="#272727"/> - </ResourceDictionary> - </ResourceDictionary.ThemeDictionaries> - <SolidColorBrush x:Key="HomeTasksListPlaceholderTextColor" Color="#AAAAAA"/> -</ResourceDictionary> diff --git a/VDownload/Resources/Converters.xaml b/VDownload/Resources/Converters.xaml deleted file mode 100644 index 1642e3b..0000000 --- a/VDownload/Resources/Converters.xaml +++ /dev/null @@ -1,8 +0,0 @@ -<ResourceDictionary - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:converters="using:VDownload.Converters"> - <converters:StringToVisibilityConverter x:Key="StringToVisibilityConverter"/> - <converters:TimeSpanToTextBoxMaskConverter x:Key="TimeSpanToTextBoxMaskConverter"/> - <converters:TimeSpanToTextBoxMaskElementsConverter x:Key="TimeSpanToTextBoxMaskElementsConverter"/> -</ResourceDictionary> diff --git a/VDownload/Resources/Icons.xaml b/VDownload/Resources/Icons.xaml deleted file mode 100644 index be0b36c..0000000 --- a/VDownload/Resources/Icons.xaml +++ /dev/null @@ -1,92 +0,0 @@ -<ResourceDictionary - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <ResourceDictionary.ThemeDictionaries> - <ResourceDictionary x:Key="Light"> - <SvgImageSource x:Key="AuthorIcon" UriSource="ms-appx:///Assets/Icons/AuthorLight.svg"/> - <SvgImageSource x:Key="ViewsIcon" UriSource="ms-appx:///Assets/Icons/ViewsLight.svg"/> - <SvgImageSource x:Key="DateIcon" UriSource="ms-appx:///Assets/Icons/DateLight.svg"/> - <SvgImageSource x:Key="DurationIcon" UriSource="ms-appx:///Assets/Icons/DurationLight.svg"/> - <SvgImageSource x:Key="MediaTypeIcon" UriSource="ms-appx:///Assets/Icons/MediaTypeLight.svg"/> - <SvgImageSource x:Key="QualityIcon" UriSource="ms-appx:///Assets/Icons/QualityLight.svg"/> - <SvgImageSource x:Key="TrimIcon" UriSource="ms-appx:///Assets/Icons/TrimLight.svg"/> - <SvgImageSource x:Key="FileIcon" UriSource="ms-appx:///Assets/Icons/FileLight.svg"/> - <SvgImageSource x:Key="LocationIcon" UriSource="ms-appx:///Assets/Icons/LocationLight.svg"/> - <SvgImageSource x:Key="ScheduleIcon" UriSource="ms-appx:///Assets/Icons/ScheduleLight.svg"/> - <SvgImageSource x:Key="StateIdleIcon" UriSource="ms-appx:///Assets/Icons/StateIdleLight.svg"/> - <SvgImageSource x:Key="StateQueuedIcon" UriSource="ms-appx:///Assets/Icons/StateWaitingLight.svg"/> - <SvgImageSource x:Key="StateCancelledIcon" UriSource="ms-appx:///Assets/Icons/StateCancelledLight.svg"/> - <SvgImageSource x:Key="StateDownloadingIcon" UriSource="ms-appx:///Assets/Icons/StateDownloadingLight.svg"/> - <SvgImageSource x:Key="StateErrorIcon" UriSource="ms-appx:///Assets/Icons/StateErrorLight.svg"/> - <SvgImageSource x:Key="StateDoneIcon" UriSource="ms-appx:///Assets/Icons/StateDoneLight.svg"/> - <SvgImageSource x:Key="StateFinalizingIcon" UriSource="ms-appx:///Assets/Icons/StateFinalizingLight.svg"/> - <SvgImageSource x:Key="StateProcessingIcon" UriSource="ms-appx:///Assets/Icons/StateProcessingLight.svg"/> - <SvgImageSource x:Key="StateScheduledIcon" UriSource="ms-appx:///Assets/Icons/StateScheduledLight.svg"/> - <SvgImageSource x:Key="ApplyToAllOptionsIcon" UriSource="ms-appx:///Assets/Icons/ApplyToAllOptionsLight.svg"/> - <SvgImageSource x:Key="FilterIcon" UriSource="ms-appx:///Assets/Icons/FilterLight.svg"/> - <SvgImageSource x:Key="TemporaryFilesLocationIcon" UriSource="ms-appx:///Assets/Icons/TemporaryFilesLocationLight.svg"/> - <SvgImageSource x:Key="DeleteTemporaryFilesOnStartIcon" UriSource="ms-appx:///Assets/Icons/DeleteTemporaryFilesOnStartLight.svg"/> - <SvgImageSource x:Key="DeleteTasksTemporaryFilesIfEndedWithError" UriSource="ms-appx:///Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorLight.svg"/> - <SvgImageSource x:Key="EditingAlgorithm" UriSource="ms-appx:///Assets/Icons/EditingAlgorithmLight.svg"/> - <SvgImageSource x:Key="TranscodingAlgorithm" UriSource="ms-appx:///Assets/Icons/TranscodingAlgorithmLight.svg"/> - <SvgImageSource x:Key="UseHardwareAcceleration" UriSource="ms-appx:///Assets/Icons/UseHardwareAccelerationLight.svg"/> - <SvgImageSource x:Key="ShowNotifcationWhenSuccessful" UriSource="ms-appx:///Assets/Icons/ShowNotifcationWhenSuccessfulLight.svg"/> - <SvgImageSource x:Key="ShowNotifcationWhenUnsuccessful" UriSource="ms-appx:///Assets/Icons/ShowNotifcationWhenUnsuccessfulLight.svg"/> - <SvgImageSource x:Key="CustomMediaLocation" UriSource="ms-appx:///Assets/Icons/CustomMediaLocationLight.svg"/> - <SvgImageSource x:Key="LastMediaLocation" UriSource="ms-appx:///Assets/Icons/LastMediaLocationLight.svg"/> - <SvgImageSource x:Key="DefaultAudioExtension" UriSource="ms-appx:///Assets/Icons/DefaultAudioExtensionLight.svg"/> - <SvgImageSource x:Key="DefaultVideoExtension" UriSource="ms-appx:///Assets/Icons/DefaultVideoExtensionLight.svg"/> - <SvgImageSource x:Key="DefaultMediaType" UriSource="ms-appx:///Assets/Icons/DefaultMediaTypeLight.svg"/> - <SvgImageSource x:Key="FilenameTemplate" UriSource="ms-appx:///Assets/Icons/FilenameTemplateLight.svg"/> - <SvgImageSource x:Key="MeteredConnectionWarning" UriSource="ms-appx:///Assets/Icons/MeteredConnectionWarningLight.svg"/> - <SvgImageSource x:Key="MeteredConnectionDelay" UriSource="ms-appx:///Assets/Icons/MeteredConnectionDelayLight.svg"/> - <SvgImageSource x:Key="ReplaceFile" UriSource="ms-appx:///Assets/Icons/ReplaceFileLight.svg"/> - <SvgImageSource x:Key="RemoveSuccessfullyEndedTask" UriSource="ms-appx:///Assets/Icons/RemoveSuccessfullyEndedTaskLight.svg"/> - <SvgImageSource x:Key="ActiveTasksNumber" UriSource="ms-appx:///Assets/Icons/ActiveTasksNumberLight.svg"/> - </ResourceDictionary> - <ResourceDictionary x:Key="Dark"> - <SvgImageSource x:Key="AuthorIcon" UriSource="ms-appx:///Assets/Icons/AuthorDark.svg"/> - <SvgImageSource x:Key="ViewsIcon" UriSource="ms-appx:///Assets/Icons/ViewsDark.svg"/> - <SvgImageSource x:Key="DateIcon" UriSource="ms-appx:///Assets/Icons/DateDark.svg"/> - <SvgImageSource x:Key="DurationIcon" UriSource="ms-appx:///Assets/Icons/DurationDark.svg"/> - <SvgImageSource x:Key="MediaTypeIcon" UriSource="ms-appx:///Assets/Icons/MediaTypeDark.svg"/> - <SvgImageSource x:Key="QualityIcon" UriSource="ms-appx:///Assets/Icons/QualityDark.svg"/> - <SvgImageSource x:Key="TrimIcon" UriSource="ms-appx:///Assets/Icons/TrimDark.svg"/> - <SvgImageSource x:Key="FileIcon" UriSource="ms-appx:///Assets/Icons/FileDark.svg"/> - <SvgImageSource x:Key="LocationIcon" UriSource="ms-appx:///Assets/Icons/LocationDark.svg"/> - <SvgImageSource x:Key="ScheduleIcon" UriSource="ms-appx:///Assets/Icons/ScheduleDark.svg"/> - <SvgImageSource x:Key="StateIdleIcon" UriSource="ms-appx:///Assets/Icons/StateIdleDark.svg"/> - <SvgImageSource x:Key="StateQueuedIcon" UriSource="ms-appx:///Assets/Icons/StateWaitingDark.svg"/> - <SvgImageSource x:Key="StateCancelledIcon" UriSource="ms-appx:///Assets/Icons/StateCancelledDark.svg"/> - <SvgImageSource x:Key="StateDownloadingIcon" UriSource="ms-appx:///Assets/Icons/StateDownloadingDark.svg"/> - <SvgImageSource x:Key="StateErrorIcon" UriSource="ms-appx:///Assets/Icons/StateErrorDark.svg"/> - <SvgImageSource x:Key="StateDoneIcon" UriSource="ms-appx:///Assets/Icons/StateDoneDark.svg"/> - <SvgImageSource x:Key="StateFinalizingIcon" UriSource="ms-appx:///Assets/Icons/StateFinalizingDark.svg"/> - <SvgImageSource x:Key="StateProcessingIcon" UriSource="ms-appx:///Assets/Icons/StateProcessingDark.svg"/> - <SvgImageSource x:Key="StateScheduledIcon" UriSource="ms-appx:///Assets/Icons/StateScheduledDark.svg"/> - <SvgImageSource x:Key="ApplyToAllOptionsIcon" UriSource="ms-appx:///Assets/Icons/ApplyToAllOptionsDark.svg"/> - <SvgImageSource x:Key="FilterIcon" UriSource="ms-appx:///Assets/Icons/FilterDark.svg"/> - <SvgImageSource x:Key="TemporaryFilesLocationIcon" UriSource="ms-appx:///Assets/Icons/TemporaryFilesLocationDark.svg"/> - <SvgImageSource x:Key="DeleteTemporaryFilesOnStartIcon" UriSource="ms-appx:///Assets/Icons/DeleteTemporaryFilesOnStartDark.svg"/> - <SvgImageSource x:Key="DeleteTasksTemporaryFilesIfEndedWithError" UriSource="ms-appx:///Assets/Icons/DeleteTasksTemporaryFilesIfEndedWithErrorDark.svg"/> - <SvgImageSource x:Key="EditingAlgorithm" UriSource="ms-appx:///Assets/Icons/EditingAlgorithmDark.svg"/> - <SvgImageSource x:Key="TranscodingAlgorithm" UriSource="ms-appx:///Assets/Icons/TranscodingAlgorithmDark.svg"/> - <SvgImageSource x:Key="UseHardwareAcceleration" UriSource="ms-appx:///Assets/Icons/UseHardwareAccelerationDark.svg"/> - <SvgImageSource x:Key="ShowNotifcationWhenSuccessful" UriSource="ms-appx:///Assets/Icons/ShowNotifcationWhenSuccessfulDark.svg"/> - <SvgImageSource x:Key="ShowNotifcationWhenUnsuccessful" UriSource="ms-appx:///Assets/Icons/ShowNotifcationWhenUnsuccessfulDark.svg"/> - <SvgImageSource x:Key="CustomMediaLocation" UriSource="ms-appx:///Assets/Icons/CustomMediaLocationDark.svg"/> - <SvgImageSource x:Key="LastMediaLocation" UriSource="ms-appx:///Assets/Icons/LastMediaLocationDark.svg"/> - <SvgImageSource x:Key="DefaultAudioExtension" UriSource="ms-appx:///Assets/Icons/DefaultAudioExtensionDark.svg"/> - <SvgImageSource x:Key="DefaultVideoExtension" UriSource="ms-appx:///Assets/Icons/DefaultVideoExtensionDark.svg"/> - <SvgImageSource x:Key="DefaultMediaType" UriSource="ms-appx:///Assets/Icons/DefaultMediaTypeDark.svg"/> - <SvgImageSource x:Key="FilenameTemplate" UriSource="ms-appx:///Assets/Icons/FilenameTemplateDark.svg"/> - <SvgImageSource x:Key="MeteredConnectionWarning" UriSource="ms-appx:///Assets/Icons/MeteredConnectionWarningDark.svg"/> - <SvgImageSource x:Key="MeteredConnectionDelay" UriSource="ms-appx:///Assets/Icons/MeteredConnectionDelayDark.svg"/> - <SvgImageSource x:Key="ReplaceFile" UriSource="ms-appx:///Assets/Icons/ReplaceFileDark.svg"/> - <SvgImageSource x:Key="RemoveSuccessfullyEndedTask" UriSource="ms-appx:///Assets/Icons/RemoveSuccessfullyEndedTaskDark.svg"/> - <SvgImageSource x:Key="ActiveTasksNumber" UriSource="ms-appx:///Assets/Icons/ActiveTasksNumberDark.svg"/> - </ResourceDictionary> - </ResourceDictionary.ThemeDictionaries> - <SvgImageSource x:Key="TwitchIcon" UriSource="ms-appx:///Assets/Icons/Twitch.svg"/> - <SvgImageSource x:Key="ErrorIcon" UriSource="ms-appx:///Assets/Icons/Error.svg"/> -</ResourceDictionary> diff --git a/VDownload/Resources/Images.xaml b/VDownload/Resources/Images.xaml deleted file mode 100644 index 0bd1c60..0000000 --- a/VDownload/Resources/Images.xaml +++ /dev/null @@ -1,8 +0,0 @@ -<ResourceDictionary - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <BitmapImage x:Name="Logo" UriSource="ms-appx:///Assets/Logo/Logo.png"/> - <BitmapImage x:Name="UnknownThumbnailImage" UriSource="ms-appx:///Assets/Images/UnknownThumbnail.png"/> - <SvgImageSource x:Name="HomeTasksListPlaceholderImage" UriSource="ms-appx:///Assets/Images/HomeTasksListPlaceholderImage.svg"/> - -</ResourceDictionary> diff --git a/VDownload/Strings/en-US/Resources.resw b/VDownload/Strings/en-US/Resources.resw deleted file mode 100644 index 0cb5181..0000000 --- a/VDownload/Strings/en-US/Resources.resw +++ /dev/null @@ -1,526 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<root> - <!-- - Microsoft ResX Schema - - Version 2.0 - - The primary goals of this format is to allow a simple XML format - that is mostly human readable. The generation and parsing of the - various data types are done through the TypeConverter classes - associated with the data types. - - Example: - - ... ado.net/XML headers & schema ... - <resheader name="resmimetype">text/microsoft-resx</resheader> - <resheader name="version">2.0</resheader> - <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> - <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> - <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> - <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> - <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> - <value>[base64 mime encoded serialized .NET Framework object]</value> - </data> - <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> - <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> - <comment>This is a comment</comment> - </data> - - There are any number of "resheader" rows that contain simple - name/value pairs. - - Each data row contains a name, and value. The row also contains a - type or mimetype. Type corresponds to a .NET class that support - text/value conversion through the TypeConverter architecture. - Classes that don't support this are serialized and stored with the - mimetype set. - - The mimetype is used for serialized objects, and tells the - ResXResourceReader how to depersist the object. This is currently not - extensible. For a given mimetype the value must be set accordingly: - - Note - application/x-microsoft.net.object.binary.base64 is the format - that the ResXResourceWriter will generate, however the reader can - read any of the formats listed below. - - mimetype: application/x-microsoft.net.object.binary.base64 - value : The object must be serialized with - : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter - : and then encoded with base64 encoding. - - mimetype: application/x-microsoft.net.object.soap.base64 - value : The object must be serialized with - : System.Runtime.Serialization.Formatters.Soap.SoapFormatter - : and then encoded with base64 encoding. - - mimetype: application/x-microsoft.net.object.bytearray.base64 - value : The object must be serialized into a byte array - : using a System.ComponentModel.TypeConverter - : and then encoded with base64 encoding. - --> - <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> - <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> - <xsd:element name="root" msdata:IsDataSet="true"> - <xsd:complexType> - <xsd:choice maxOccurs="unbounded"> - <xsd:element name="metadata"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" /> - </xsd:sequence> - <xsd:attribute name="name" use="required" type="xsd:string" /> - <xsd:attribute name="type" type="xsd:string" /> - <xsd:attribute name="mimetype" type="xsd:string" /> - <xsd:attribute ref="xml:space" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="assembly"> - <xsd:complexType> - <xsd:attribute name="alias" type="xsd:string" /> - <xsd:attribute name="name" type="xsd:string" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="data"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> - <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> - <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> - <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> - <xsd:attribute ref="xml:space" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="resheader"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required" /> - </xsd:complexType> - </xsd:element> - </xsd:choice> - </xsd:complexType> - </xsd:element> - </xsd:schema> - <resheader name="resmimetype"> - <value>text/microsoft-resx</value> - </resheader> - <resheader name="version"> - <value>2.0</value> - </resheader> - <resheader name="reader"> - <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> - </resheader> - <resheader name="writer"> - <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> - </resheader> - <data name="Base_MediaType_AudioVideoText" xml:space="preserve"> - <value>Audio & Video</value> - </data> - <data name="Base_MediaType_OnlyAudioText" xml:space="preserve"> - <value>Only audio</value> - </data> - <data name="Base_MediaType_OnlyVideoText" xml:space="preserve"> - <value>Only video</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBlock.Text" xml:space="preserve"> - <value>Author</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBox.PlaceholderText" xml:space="preserve"> - <value>Enter regular expression</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_DateTextBlock.Text" xml:space="preserve"> - <value>Date</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_DurationTextBlock.Text" xml:space="preserve"> - <value>Duration</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Header_CountTextBlockPrefix" xml:space="preserve"> - <value>Hidden</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Header_TextBlock.Text" xml:space="preserve"> - <value>Filter</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_RestoreButton.Content" xml:space="preserve"> - <value>Restore</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_TextBlock.Text" xml:space="preserve"> - <value>Removed</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBlock.Text" xml:space="preserve"> - <value>Title</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBox.PlaceholderText" xml:space="preserve"> - <value>Enter regular expression</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_Filter_ViewsTextBlock.Text" xml:space="preserve"> - <value>Views</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_HeaderTextBlock.Text" xml:space="preserve"> - <value>Downloading options</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_MediaTypeSettingControl.Title" xml:space="preserve"> - <value>Media type</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_QualitySettingControl.Title" xml:space="preserve"> - <value>Quality</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_TrimSettingControl.Title" xml:space="preserve"> - <value>Trim</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl.Title" xml:space="preserve"> - <value>Location</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl_BrowseButton.Content" xml:space="preserve"> - <value>Browse</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileSettingControl.Title" xml:space="preserve"> - <value>File</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_HeaderTextBlock.Text" xml:space="preserve"> - <value>File options</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_HeaderTextBlock.Text" xml:space="preserve"> - <value>Task options</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_ScheduleSettingControl.Description" xml:space="preserve"> - <value>Number of minutes to start the task (after clicking downloading button).</value> - </data> - <data name="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_ScheduleSettingControl.Title" xml:space="preserve"> - <value>Schedule</value> - </data> - <data name="Home_DownloadTaskControlsStackPanel_Placeholder_TextBlock.Text" xml:space="preserve"> - <value>Click "Video/Playlist search" button to start</value> - </data> - <data name="Home_DownloadTaskControl_Error_InternetNotAvailable" xml:space="preserve"> - <value>Internet connection error</value> - </data> - <data name="Home_DownloadTaskControl_Notification_EndedSuccessfully_Description" xml:space="preserve"> - <value>downloading ended successfully</value> - </data> - <data name="Home_DownloadTaskControl_Notification_EndedSuccessfully_Header" xml:space="preserve"> - <value>Video downloading ended successfully</value> - </data> - <data name="Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Description" xml:space="preserve"> - <value>an error occured</value> - </data> - <data name="Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Header" xml:space="preserve"> - <value>Video downloading ended unsuccessfully</value> - </data> - <data name="Home_DownloadTaskControl_State_Cancelled" xml:space="preserve"> - <value>Cancelled</value> - </data> - <data name="Home_DownloadTaskControl_State_Done" xml:space="preserve"> - <value>Done</value> - </data> - <data name="Home_DownloadTaskControl_State_Downloading" xml:space="preserve"> - <value>Downloading</value> - </data> - <data name="Home_DownloadTaskControl_State_Error" xml:space="preserve"> - <value>An error occured</value> - </data> - <data name="Home_DownloadTaskControl_State_Finalizing" xml:space="preserve"> - <value>Finalizing</value> - </data> - <data name="Home_DownloadTaskControl_State_Idle" xml:space="preserve"> - <value>Idle</value> - </data> - <data name="Home_DownloadTaskControl_State_Processing" xml:space="preserve"> - <value>Processing</value> - </data> - <data name="Home_DownloadTaskControl_State_Queued" xml:space="preserve"> - <value>Queued</value> - </data> - <data name="Home_DownloadTaskControl_State_Scheduled" xml:space="preserve"> - <value>Scheduled</value> - </data> - <data name="Home_OptionsBar_DownloadAllButton.Label" xml:space="preserve"> - <value>Download all</value> - </data> - <data name="Home_OptionsBar_DownloadAllButton.Width" xml:space="preserve"> - <value>90</value> - </data> - <data name="Home_OptionsBar_LoadSubscripionsButton.Label" xml:space="preserve"> - <value>Load subscriptions</value> - </data> - <data name="Home_OptionsBar_LoadSubscripionsButton.Width" xml:space="preserve"> - <value>120</value> - </data> - <data name="Home_OptionsBar_PlaylistSearchButton.Label" xml:space="preserve"> - <value>Playlist search</value> - </data> - <data name="Home_OptionsBar_PlaylistSearchButton.Width" xml:space="preserve"> - <value>95</value> - </data> - <data name="Home_OptionsBar_PlaylistSearchControl_InfoBox.Subtitle" xml:space="preserve"> - <value>- Twitch (Channel) - -Number of videos got from playlist -The number in the numberbox indicades how many videos will be got from playlist. 0 = all. -</value> - </data> - <data name="Home_OptionsBar_PlaylistSearchControl_InfoBox.Title" xml:space="preserve"> - <value>Supported sources</value> - </data> - <data name="Home_OptionsBar_PlaylistSearchControl_SearchButton.Content" xml:space="preserve"> - <value>Search</value> - </data> - <data name="Home_OptionsBar_PlaylistSearchControl_UrlTextBox.PlaceholderText" xml:space="preserve"> - <value>Playlist URL</value> - </data> - <data name="Home_OptionsBar_VideoSearchButton.Label" xml:space="preserve"> - <value>Video search</value> - </data> - <data name="Home_OptionsBar_VideoSearchButton.Width" xml:space="preserve"> - <value>90</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_Base_ApplyButton.Content" xml:space="preserve"> - <value>Apply</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_HeaderTextBlock.Text" xml:space="preserve"> - <value>Apply to all options</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl.Title" xml:space="preserve"> - <value>Location</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl_BrowseButton.Content" xml:space="preserve"> - <value>Browse</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_ScheduleSettingControl.Description" xml:space="preserve"> - <value>Number of minutes to start the task (after clicking downloading button).</value> - </data> - <data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_ScheduleSettingControl.Title" xml:space="preserve"> - <value>Schedule</value> - </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Loading" xml:space="preserve"> - <value>Loading new videos</value> - </data> - <data name="Home_OptionsBar_VideoSearchControl_InfoBox.Subtitle" xml:space="preserve"> - <value>- Twitch (VODs, Clips)</value> - </data> - <data name="Home_OptionsBar_VideoSearchControl_InfoBox.Title" xml:space="preserve"> - <value>Supported sources</value> - </data> - <data name="Home_OptionsBar_VideoSearchControl_SearchButton.Content" xml:space="preserve"> - <value>Search</value> - </data> - <data name="Home_OptionsBar_VideoSearchControl_UrlTextBox.PlaceholderText" xml:space="preserve"> - <value>Video URL</value> - </data> - <data name="Main_NavigationPanel_About.Content" xml:space="preserve"> - <value>About</value> - </data> - <data name="Main_NavigationPanel_Home.Content" xml:space="preserve"> - <value>Home</value> - </data> - <data name="Main_NavigationPanel_Sources.Content" xml:space="preserve"> - <value>Sources</value> - </data> - <data name="Main_NavigationPanel_Subscriptions.Content" xml:space="preserve"> - <value>Subscriptions</value> - </data> - <data name="Sources_HeaderTextBlock.Text" xml:space="preserve"> - <value>Sources</value> - </data> - <data name="Sources_TwitchSettingControl.Description" xml:space="preserve"> - <value>Loading...</value> - </data> - <data name="Sources_TwitchSettingControl.Title" xml:space="preserve"> - <value>Twitch</value> - </data> - <data name="Sources_TwitchSettingControl_Description_InternetNotAvailable" xml:space="preserve"> - <value>Unable to connect to Twitch servers. Check your internet connection.</value> - </data> - <data name="Sources_TwitchSettingControl_Description_InvalidAccessToken" xml:space="preserve"> - <value>Your Twitch access token is invalid (maybe expired?). Please sign in to get new access token.</value> - </data> - <data name="Sources_TwitchSettingControl_Description_LoggedIn" xml:space="preserve"> - <value>Logged in as</value> - </data> - <data name="Sources_TwitchSettingControl_Description_NotLoggedIn" xml:space="preserve"> - <value>Not logged in. Twitch authentication is required to download videos.</value> - </data> - <data name="Sources_TwitchSettingControl_LoginButton.Content" xml:space="preserve"> - <value>Loading...</value> - </data> - <data name="Sources_TwitchSettingControl_LoginButton_Content_LoggedIn" xml:space="preserve"> - <value>Sign out</value> - </data> - <data name="Sources_TwitchSettingControl_LoginButton_Content_NotLoggedIn" xml:space="preserve"> - <value>Sign in</value> - </data> - <data name="Subscriptions_AddingButton.Content" xml:space="preserve"> - <value>Add</value> - </data> - <data name="Subscriptions_AddingTextBox.PlaceholderText" xml:space="preserve"> - <value>Playlist URL</value> - </data> - <data name="Subscriptions_HeaderTextBlock.Text" xml:space="preserve"> - <value>Subscriptions</value> - </data> - <data name="Subscriptions_SubscriptionPanel_CountTextBlock.Text" xml:space="preserve"> - <value>Loading...</value> - </data> - <data name="Subscriptions_SubscriptionPanel_CountTextBlock_SyncText" xml:space="preserve"> - <value>Syncing...</value> - </data> - <data name="Home_Adding_Subscriptions_HeaderTextBlock.Text" xml:space="preserve"> - <value>Subscriptions</value> - </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Found" xml:space="preserve"> - <value>Found videos</value> - </data> - <data name="Home_OptionsBar_SubscriptionsLoadControl_LoadVideosButton.Content" xml:space="preserve"> - <value>Load</value> - </data> - <data name="Settings_HeaderTextBlock.Text" xml:space="preserve"> - <value>Settings</value> - </data> - <data name="Settings_DownloadingTasks_HeaderTextBlock.Text" xml:space="preserve"> - <value>Downloading tasks</value> - </data> - <data name="Settings_DownloadingTasks_MaxNumberOfActiveTasksSettingControl.Title" xml:space="preserve"> - <value>Maximum number of active downloading tasks</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_AudioExtensionSettingControl.Title" xml:space="preserve"> - <value>Default audio files extension</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_CustomMediaLocationSettingControl.Title" xml:space="preserve"> - <value>Custom location</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_CustomMediaLocationSettingControlBrowseButton.Content" xml:space="preserve"> - <value>Browse</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_DefaultMediaTypeSettingControl.Title" xml:space="preserve"> - <value>Default media type</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_FilenameTemplateSettingControl.Title" xml:space="preserve"> - <value>Filename template</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_FilenameTemplateSettingControlInfoBox.Subtitle" xml:space="preserve"> - <value><title> - Title -<author> - Author -<views> - Views -<id> - Video ID -<date_pub:format> - Publication date -<date_now:format> - Actual date -<duration:format> - Video duration</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_FilenameTemplateSettingControlInfoBox.Title" xml:space="preserve"> - <value>Filename template</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_HeaderTextBlock.Text" xml:space="preserve"> - <value>Default downloading task settings</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_RememberLastMediaLocationSettingControl.Description" xml:space="preserve"> - <value>Otherwise, the location given below will be used</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_RememberLastMediaLocationSettingControl.Title" xml:space="preserve"> - <value>Remeber last used location</value> - </data> - <data name="Settings_DefaultDownloadingTaskSettings_VideoExtensionSettingControl.Title" xml:space="preserve"> - <value>Default video files extension</value> - </data> - <data name="Settings_DownloadingTasks_DelayWhenQueuedTaskStartsOnMeteredConnectionSettingControl.Title" xml:space="preserve"> - <value>Delay when queued task starts on metered connection</value> - </data> - <data name="Settings_DownloadingTasks_RemoveTaskWhenSuccessfullyEndedSettingControl.Title" xml:space="preserve"> - <value>Remove successfully ended task</value> - </data> - <data name="Settings_DownloadingTasks_ReplaceOutputFileIfExistsSettingControl.Title" xml:space="preserve"> - <value>Replace output file if exists</value> - </data> - <data name="Settings_DownloadingTasks_ShowWarningWhenTaskStartsOnMeteredConnectionSettingControl.Title" xml:space="preserve"> - <value>Show warning when task starts on metered connection</value> - </data> - <data name="Settings_Notifications_HeaderTextBlock.Text" xml:space="preserve"> - <value>Notifications</value> - </data> - <data name="Settings_Notifications_ShowNotificationWhenTaskEndedSuccessfullySettingControl.Title" xml:space="preserve"> - <value>Show notification when task ended successfully</value> - </data> - <data name="Settings_Notifications_ShowNotificationWhenTaskEndedUnsuccessfullySettingControl.Title" xml:space="preserve"> - <value>Show notification when task ended unsuccessfully</value> - </data> - <data name="Settings_Processing_EditingAlgorithmSettingControl.Title" xml:space="preserve"> - <value>Editing algorithm</value> - </data> - <data name="Settings_Processing_HeaderTextBlock.Text" xml:space="preserve"> - <value>Processing</value> - </data> - <data name="Settings_Processing_TranscodingAlgorithmSettingControl.Title" xml:space="preserve"> - <value>Transcoding algorithm</value> - </data> - <data name="Settings_Processing_UseHardwareAccelerationSettingControl.Title" xml:space="preserve"> - <value>Use hardware acceleration</value> - </data> - <data name="Settings_TemporaryFiles_DeleteTasksTemporaryFilesIfEndedWithErrorSettingControl.Title" xml:space="preserve"> - <value>Delete task's temporary files if ended with error</value> - </data> - <data name="Settings_TemporaryFiles_DeleteTemporaryFilesOnStartSettingControl.Title" xml:space="preserve"> - <value>Delete all temporary files on start</value> - </data> - <data name="Settings_TemporaryFiles_HeaderTextBlock.Text" xml:space="preserve"> - <value>Temporary files</value> - </data> - <data name="Settings_TemporaryFiles_TemporaryFilesLocationSettingControl.Title" xml:space="preserve"> - <value>Temporary files location</value> - </data> - <data name="Settings_TemporaryFiles_TemporaryFilesLocationSettingControlBrowseButton.Content" xml:space="preserve"> - <value>Browse</value> - </data> - <data name="Settings_Twitch_HeaderTextBlock.Text" xml:space="preserve"> - <value>Twitch</value> - </data> - <data name="Settings_Twitch_PassiveVodTrimmingSettingControl.Description" xml:space="preserve"> - <value>Skip downloading chunks that are out of trimming bounds</value> - </data> - <data name="Settings_Twitch_PassiveVodTrimmingSettingControl.Title" xml:space="preserve"> - <value>Passive VOD trimming</value> - </data> - <data name="Settings_Twitch_VodChunkDownloadingErrorMaxNumberOfRetriesSettingControl.Title" xml:space="preserve"> - <value>VOD chunk downloading error - maximum number of retries</value> - </data> - <data name="Settings_Twitch_VodChunkDownloadingErrorRetriesDelaySettingControl.Description" xml:space="preserve"> - <value>Delay between retries in miliseconds</value> - </data> - <data name="Settings_Twitch_VodChunkDownloadingErrorRetriesDelaySettingControl.Title" xml:space="preserve"> - <value>VOD chunk downloading error - delay between retries</value> - </data> - <data name="Settings_Twitch_VodChunkDownloadingErrorRetryAfterErrorSettingControl.Title" xml:space="preserve"> - <value>VOD chunk downloading error - retry after error</value> - </data> - <data name="About_Links_Header.Text" xml:space="preserve"> - <value>Links</value> - </data> - <data name="About_Developer_Header.Text" xml:space="preserve"> - <value>Developer</value> - </data> - <data name="About_Links_DonationButton.Content" xml:space="preserve"> - <value>Donation</value> - </data> - <data name="About_Links_RepositoryButton.Content" xml:space="preserve"> - <value>Repository</value> - </data> - <data name="About_TranslatedBy_Content.Text" xml:space="preserve"> - <value>Mateusz Skoczek</value> - </data> - <data name="About_TranslatedBy_Header.Text" xml:space="preserve"> - <value>Translated into English by</value> - </data> - <data name="Settings_RestoreDefaultSettingsButton.Content" xml:space="preserve"> - <value>Restore default settings</value> - </data> - <data name="Settings_DownloadingTasks_ReplaceOutputFileIfExistsSettingControl.Description" xml:space="preserve"> - <value>Otherwise, new filename will be generated (example: original_filename (1))</value> - </data> - <data name="Settings_Processing_EditingAlgorithmSettingControl.Description" xml:space="preserve"> - <value>Editing mode is used when download task requires merging audio & video separate channels</value> - </data> - <data name="Settings_Processing_TranscodingAlgorithmSettingControl.Description" xml:space="preserve"> - <value>Transcoding mode is used when download task only requires file conversion</value> - </data> -</root> \ No newline at end of file diff --git a/VDownload/VDownload.csproj b/VDownload/VDownload.csproj index 62044fb..eefb73b 100644 --- a/VDownload/VDownload.csproj +++ b/VDownload/VDownload.csproj @@ -1,485 +1,601 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">x86</Platform> - <ProjectGuid>{324AB81A-F68D-424D-B90E-5402F5E44240}</ProjectGuid> - <OutputType>AppContainerExe</OutputType> - <AppDesignerFolder>Properties</AppDesignerFolder> - <RootNamespace>VDownload</RootNamespace> - <AssemblyName>VDownload</AssemblyName> - <DefaultLanguage>en-US</DefaultLanguage> - <TargetPlatformIdentifier>UAP</TargetPlatformIdentifier> - <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22000.0</TargetPlatformVersion> - <TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion> - <MinimumVisualStudioVersion>14</MinimumVisualStudioVersion> - <FileAlignment>512</FileAlignment> - <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> - <WindowsXamlEnableOverview>true</WindowsXamlEnableOverview> - <AppxPackageSigningEnabled>True</AppxPackageSigningEnabled> - <GenerateAppInstallerFile>False</GenerateAppInstallerFile> - <AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision> - <GenerateTestArtifacts>True</GenerateTestArtifacts> - <AppxBundle>Always</AppxBundle> - <AppxBundlePlatforms>x64</AppxBundlePlatforms> - <HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks> - <PackageCertificateThumbprint>19004779324CA6C66C5D4549F0161C153221ABD8</PackageCertificateThumbprint> - <AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\x86\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <PlatformTarget>x86</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> - <OutputPath>bin\x86\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <PlatformTarget>x86</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'"> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\ARM\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <PlatformTarget>ARM</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'"> - <OutputPath>bin\ARM\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <PlatformTarget>ARM</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'"> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\ARM64\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <PlatformTarget>ARM64</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'"> - <OutputPath>bin\ARM64\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <PlatformTarget>ARM64</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> - <DebugSymbols>true</DebugSymbols> - <OutputPath>bin\x64\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <NoWarn>;2008</NoWarn> - <DebugType>full</DebugType> - <PlatformTarget>x64</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> - <OutputPath>bin\x64\Release\</OutputPath> - <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> - <Optimize>true</Optimize> - <NoWarn>;2008</NoWarn> - <DebugType>pdbonly</DebugType> - <PlatformTarget>x64</PlatformTarget> - <UseVSHostingProcess>false</UseVSHostingProcess> - <ErrorReport>prompt</ErrorReport> - <Prefer32Bit>true</Prefer32Bit> - <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> - </PropertyGroup> - <PropertyGroup> - <RestoreProjectStyle>PackageReference</RestoreProjectStyle> - </PropertyGroup> - <ItemGroup> - <Compile Include="App.xaml.cs"> - <DependentUpon>App.xaml</DependentUpon> - </Compile> - <Compile Include="Controls\PlaceholderableStackPanel.xaml.cs"> - <DependentUpon>PlaceholderableStackPanel.xaml</DependentUpon> - </Compile> - <Compile Include="Converters\StringToVisibilityConverter.cs" /> - <Compile Include="Converters\TimeSpanToTextBoxMaskConverter.cs" /> - <Compile Include="Converters\TimeSpanToTextBoxMaskElementsConverter.cs" /> - <Compile Include="Views\About\MainPage.xaml.cs"> - <DependentUpon>MainPage.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\Controls\SerialVideoAddingControl.xaml.cs"> - <DependentUpon>SerialVideoAddingControl.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\Controls\VideoAddingOptionsControl.xaml.cs"> - <DependentUpon>VideoAddingOptionsControl.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\Controls\SubscriptionsLoadControl.xaml.cs"> - <DependentUpon>SubscriptionsLoadControl.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\Controls\PlaylistSearchControl.xaml.cs"> - <DependentUpon>PlaylistSearchControl.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\Controls\VideoSearchControl.xaml.cs"> - <DependentUpon>VideoSearchControl.xaml</DependentUpon> - </Compile> - <Compile Include="Controls\SettingControl.xaml.cs"> - <DependentUpon>SettingControl.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\MainPage.xaml.cs"> - <DependentUpon>MainPage.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\SubscriptionsAddingPanel.xaml.cs"> - <DependentUpon>SubscriptionsAddingPanel.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\PlaylistAddingPanel.xaml.cs"> - <DependentUpon>PlaylistAddingPanel.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\Controls\SerialVideoAddingVideoControl.xaml.cs"> - <DependentUpon>SerialVideoAddingVideoControl.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\VideoAddingPanel.xaml.cs"> - <DependentUpon>VideoAddingPanel.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Home\Controls\DownloadTaskControl.xaml.cs"> - <DependentUpon>DownloadTaskControl.xaml</DependentUpon> - </Compile> - <Compile Include="Views\MainPage.xaml.cs"> - <DependentUpon>MainPage.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Sources\MainPage.xaml.cs"> - <DependentUpon>MainPage.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Settings\MainPage.xaml.cs"> - <DependentUpon>MainPage.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Subscriptions\Controls\SubscriptionPanel.xaml.cs"> - <DependentUpon>SubscriptionPanel.xaml</DependentUpon> - </Compile> - <Compile Include="Views\Subscriptions\MainPage.xaml.cs"> - <DependentUpon>MainPage.xaml</DependentUpon> - </Compile> +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + <RootNamespace>VDownload</RootNamespace> + <ApplicationManifest>app.manifest</ApplicationManifest> + <Platforms>x64;ARM64</Platforms> + <RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers> + <PublishProfile>win10-$(Platform).pubxml</PublishProfile> + <UseWinUI>true</UseWinUI> + <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> + <UseRidGraph>true</UseRidGraph> + <EnableCoreMrtTooling Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">false</EnableCoreMrtTooling> + <ApplicationIcon>Assets\Logo\Logo.ico</ApplicationIcon> + <PackageLicenseFile>LICENSE</PackageLicenseFile> + <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance> + <Version>0.0.0</Version> + <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> + <WindowsPackageType>None</WindowsPackageType> + <EnableMsixTooling>true</EnableMsixTooling> + </PropertyGroup> + <ItemGroup> + <Content Remove="Assets\BaseView\AuthenticationDark.png" /> + <Content Remove="Assets\BaseView\AuthenticationLight.png" /> + <Content Remove="Assets\BaseView\HomeDark.png" /> + <Content Remove="Assets\BaseView\HomeLight.png" /> + <Content Remove="Assets\HomeDownloadsView\CancelledDark.png" /> + <Content Remove="Assets\HomeDownloadsView\CancelledLight.png" /> + <Content Remove="Assets\HomeDownloadsView\DoneDark.png" /> + <Content Remove="Assets\HomeDownloadsView\DoneLight.png" /> + <Content Remove="Assets\HomeDownloadsView\DownloadingDark.png" /> + <Content Remove="Assets\HomeDownloadsView\DownloadingLight.png" /> + <Content Remove="Assets\HomeDownloadsView\ErrorDark.png" /> + <Content Remove="Assets\HomeDownloadsView\ErrorLight.png" /> + <Content Remove="Assets\HomeDownloadsView\FileDark.png" /> + <Content Remove="Assets\HomeDownloadsView\FileLight.png" /> + <Content Remove="Assets\HomeDownloadsView\FinalizingDark.png" /> + <Content Remove="Assets\HomeDownloadsView\FinalizingLight.png" /> + <Content Remove="Assets\HomeDownloadsView\IdleDark.png" /> + <Content Remove="Assets\HomeDownloadsView\IdleLight.png" /> + <Content Remove="Assets\HomeDownloadsView\InitializingDark.png" /> + <Content Remove="Assets\HomeDownloadsView\InitializingLight.png" /> + <Content Remove="Assets\HomeDownloadsView\NoTasks.png" /> + <Content Remove="Assets\HomeDownloadsView\ProcessingDark.png" /> + <Content Remove="Assets\HomeDownloadsView\ProcessingLight.png" /> + <Content Remove="Assets\HomeDownloadsView\QualityDark.png" /> + <Content Remove="Assets\HomeDownloadsView\QualityLight.png" /> + <Content Remove="Assets\HomeDownloadsView\QueuedDark.png" /> + <Content Remove="Assets\HomeDownloadsView\QueuedLight.png" /> + <Content Remove="Assets\HomeDownloadsView\TimeDark.png" /> + <Content Remove="Assets\HomeDownloadsView\TimeLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\AuthorDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\AuthorLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\DateDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\DateLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\DirectoryDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\DirectoryLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\ExtensionDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\ExtensionLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\FilenameDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\FilenameLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\MediaDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\MediaLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\QualityDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\QualityLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\TimeDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\TimeLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\TrimDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\TrimLight.png" /> + <Content Remove="Assets\HomeVideoCollectionView\ViewDark.png" /> + <Content Remove="Assets\HomeVideoCollectionView\ViewLight.png" /> + <Content Remove="Assets\HomeVideoView\AuthorDark.png" /> + <Content Remove="Assets\HomeVideoView\AuthorLight.png" /> + <Content Remove="Assets\HomeVideoView\DateDark.png" /> + <Content Remove="Assets\HomeVideoView\DateLight.png" /> + <Content Remove="Assets\HomeVideoView\DirectoryDark.png" /> + <Content Remove="Assets\HomeVideoView\DirectoryLight.png" /> + <Content Remove="Assets\HomeVideoView\ExtensionDark.png" /> + <Content Remove="Assets\HomeVideoView\ExtensionLight.png" /> + <Content Remove="Assets\HomeVideoView\FilenameDark.png" /> + <Content Remove="Assets\HomeVideoView\FilenameLight.png" /> + <Content Remove="Assets\HomeVideoView\MediaDark.png" /> + <Content Remove="Assets\HomeVideoView\MediaLight.png" /> + <Content Remove="Assets\HomeVideoView\TimeDark.png" /> + <Content Remove="Assets\HomeVideoView\TimeLight.png" /> + <Content Remove="Assets\HomeVideoView\TrimDark.png" /> + <Content Remove="Assets\HomeVideoView\TrimLight.png" /> + <Content Remove="Assets\HomeVideoView\ViewDark.png" /> + <Content Remove="Assets\HomeVideoView\ViewLight.png" /> + <Content Remove="Assets\HomeView\Error.png" /> + <Content Remove="Assets\Logo\Logo.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-16.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-24.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-256.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-32.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-48.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-16.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-24.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-256.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-32.png" /> + <Content Remove="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-48.png" /> + <Content Remove="Assets\SettingsView\ProcessingFFmpegLocationDark.png" /> + <Content Remove="Assets\SettingsView\ProcessingFFmpegLocationLight.png" /> + <Content Remove="Assets\SettingsView\TasksFilenameTemplateDark.png" /> + <Content Remove="Assets\SettingsView\TasksFilenameTemplateLight.png" /> + <Content Remove="Assets\SettingsView\TasksOutputDirectoryDark.png" /> + <Content Remove="Assets\SettingsView\TasksOutputDirectoryLight.png" /> + <Content Remove="Assets\Sources\Twitch.png" /> + </ItemGroup> + <ItemGroup> + <None Remove="Assets\Logo\LargeTile.scale-100.png" /> + <None Remove="Assets\Logo\LargeTile.scale-125.png" /> + <None Remove="Assets\Logo\LargeTile.scale-150.png" /> + <None Remove="Assets\Logo\LargeTile.scale-200.png" /> + <None Remove="Assets\Logo\LargeTile.scale-400.png" /> + <None Remove="Assets\Logo\SmallTile.scale-100.png" /> + <None Remove="Assets\Logo\SmallTile.scale-125.png" /> + <None Remove="Assets\Logo\SmallTile.scale-150.png" /> + <None Remove="Assets\Logo\SmallTile.scale-200.png" /> + <None Remove="Assets\Logo\SmallTile.scale-400.png" /> + <None Remove="Assets\Logo\SplashScreen.scale-100.png" /> + <None Remove="Assets\Logo\SplashScreen.scale-125.png" /> + <None Remove="Assets\Logo\SplashScreen.scale-150.png" /> + <None Remove="Assets\Logo\SplashScreen.scale-200.png" /> + <None Remove="Assets\Logo\SplashScreen.scale-400.png" /> + <None Remove="Assets\Logo\Square150x150Logo.scale-100.png" /> + <None Remove="Assets\Logo\Square150x150Logo.scale-125.png" /> + <None Remove="Assets\Logo\Square150x150Logo.scale-150.png" /> + <None Remove="Assets\Logo\Square150x150Logo.scale-200.png" /> + <None Remove="Assets\Logo\Square150x150Logo.scale-400.png" /> + <None Remove="Assets\Logo\Square44x44Logo.scale-100.png" /> + <None Remove="Assets\Logo\Square44x44Logo.scale-125.png" /> + <None Remove="Assets\Logo\Square44x44Logo.scale-150.png" /> + <None Remove="Assets\Logo\Square44x44Logo.scale-200.png" /> + <None Remove="Assets\Logo\Square44x44Logo.scale-400.png" /> + <None Remove="Assets\Logo\Square44x44Logo.targetsize-16.png" /> + <None Remove="Assets\Logo\Square44x44Logo.targetsize-24.png" /> + <None Remove="Assets\Logo\Square44x44Logo.targetsize-256.png" /> + <None Remove="Assets\Logo\Square44x44Logo.targetsize-32.png" /> + <None Remove="Assets\Logo\Square44x44Logo.targetsize-48.png" /> + <None Remove="Assets\Logo\StoreLogo.scale-100.png" /> + <None Remove="Assets\Logo\StoreLogo.scale-125.png" /> + <None Remove="Assets\Logo\StoreLogo.scale-150.png" /> + <None Remove="Assets\Logo\StoreLogo.scale-200.png" /> + <None Remove="Assets\Logo\StoreLogo.scale-400.png" /> + <None Remove="Assets\Logo\Wide310x150Logo.scale-100.png" /> + <None Remove="Assets\Logo\Wide310x150Logo.scale-125.png" /> + <None Remove="Assets\Logo\Wide310x150Logo.scale-150.png" /> + <None Remove="Assets\Logo\Wide310x150Logo.scale-200.png" /> + <None Remove="Assets\Logo\Wide310x150Logo.scale-400.png" /> + <None Remove="Assets\SettingsView\TasksDefaultMediaOptionsDark.png" /> + <None Remove="Assets\SettingsView\TasksDefaultMediaOptionsLight.png" /> + <None Remove="Dictionaries\Colors.xaml" /> + <None Remove="Dictionaries\Converters.xaml" /> + <None Remove="Dictionaries\Images\ImagesBaseView.xaml" /> + <None Remove="Dictionaries\Images\ImagesHomeDownloadsView.xaml" /> + <None Remove="Dictionaries\Images\ImagesHomeVideoCollectionView.xaml" /> + <None Remove="Dictionaries\Images\ImagesHomeVideoView.xaml" /> + <None Remove="Dictionaries\Images\ImagesHomeView.xaml" /> + <None Remove="Dictionaries\Images\ImagesLogo.xaml" /> + <None Remove="Dictionaries\Images\ImagesOther.xaml" /> + <None Remove="Dictionaries\Images\ImagesSettingsView.xaml" /> + <None Remove="Dictionaries\Images\ImagesSources.xaml" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.0.240109" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> + <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> + <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" /> + <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> + <PackageReference Include="SimpleToolkit.UI.WinUI.Converters" Version="1.7.4" /> + <Manifest Include="$(ApplicationManifest)" /> + </ItemGroup> + + <ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"> + <ProjectCapability Include="Msix" /> + </ItemGroup> + <ItemGroup> + <Folder Include="FFmpeg\" /> + </ItemGroup> + <ItemGroup> + <None Include="..\LICENSE"> + <Pack>True</Pack> + <PackagePath>\</PackagePath> + </None> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\VDownload.Core\VDownload.Core.Strings\VDownload.Core.Strings.csproj" /> + <ProjectReference Include="..\VDownload.Core\VDownload.Core.Tasks\VDownload.Core.Tasks.csproj" /> + <ProjectReference Include="..\VDownload.Core\VDownload.Core.ViewModels\VDownload.Core.ViewModels.csproj" /> + <ProjectReference Include="..\VDownload.Core\VDownload.Core.Views\VDownload.Core.Views.csproj" /> + <ProjectReference Include="..\VDownload.Models\VDownload.Models.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Common\VDownload.Services.Common.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Application\VDownload.Services.Data.Application.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Authentication\VDownload.Services.Data.Authentication.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Settings\VDownload.Services.Data.Settings.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Data\VDownload.Services.Data.Subscriptions\VDownload.Services.Data.Subscriptions.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.Dialogs\VDownload.Services.UI.Dialogs.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.DictionaryResources\VDownload.Services.UI.DictionaryResources.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.Notifications\VDownload.Services.UI.Notifications.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.StoragePicker\VDownload.Services.UI.StoragePicker.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.UI\VDownload.Services.UI.WebView\VDownload.Services.UI.WebView.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.Encryption\VDownload.Services.Utility.Encryption.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.FFmpeg\VDownload.Services.Utility.FFmpeg.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.Filename\VDownload.Services.Utility.Filename.csproj" /> + <ProjectReference Include="..\VDownload.Services\VDownload.Services.Utility\VDownload.Services.Utility.HttpClient\VDownload.Services.Utility.HttpClient.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources.Common\VDownload.Sources.Common.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Api\VDownload.Sources.Twitch.Api.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Authentication\VDownload.Sources.Twitch.Authentication.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Configuration\VDownload.Sources.Twitch.Configuration.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Models\VDownload.Sources.Twitch.Models.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Settings\VDownload.Sources.Twitch.Settings.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch\VDownload.Sources.Twitch.csproj" /> + <ProjectReference Include="..\VDownload.Sources\VDownload.Sources\VDownload.Sources.csproj" /> + </ItemGroup> + <ItemGroup> + <Content Update="Assets\BaseView\AboutDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\BaseView\AboutLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\BaseView\SubscriptionsDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\BaseView\SubscriptionsLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\HomeVideoView\QualityDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\HomeVideoView\QualityLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\Other\Thumbnail.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\NotificationOnSuccessfulDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\NotificationOnSuccessfulLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\NotificationOnUnsuccessfulDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\NotificationOnUnsuccessfulLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\ProcessingSpeedDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\ProcessingSpeedLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\ProcessingUseHardwareAccelerationDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\ProcessingUseHardwareAccelerationLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\ProcessingUseMultithreadingLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\ProcessingUseMultithreadingDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksDefaultMediaOptionsDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksDefaultMediaOptionsLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksMeteredConnectionWarningDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksMeteredConnectionWarningLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksReplaceOutputFileDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksReplaceOutputFileLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksRunningTasksDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TasksRunningTasksLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\SearchingPlaylistCountDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\SearchingPlaylistCountLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TempDeleteOnFailDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TempDeleteOnFailLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TempDirectoryDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Update="Assets\SettingsView\TempDirectoryLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> </ItemGroup> <ItemGroup> - <AppxManifest Include="Package.appxmanifest"> - <SubType>Designer</SubType> - </AppxManifest> + <None Update="Assets\BaseView\AuthenticationDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\BaseView\AuthenticationLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\BaseView\HomeDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\BaseView\HomeLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\CancelledDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\CancelledLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\DoneDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\DoneLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\DownloadingDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\DownloadingLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\ErrorDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\ErrorLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\FileDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\FileLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\FinalizingDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\FinalizingLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\IdleDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\IdleLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\InitializingDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\InitializingLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\NoTasks.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\ProcessingDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\ProcessingLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\QualityDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\QualityLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\QueuedDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\QueuedLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\TimeDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeDownloadsView\TimeLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\AuthorDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\AuthorLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\DateDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\DateLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\DirectoryDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\DirectoryLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\ExtensionDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\ExtensionLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\FilenameDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\FilenameLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\MediaDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\MediaLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\QualityDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\QualityLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\TimeDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\TimeLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\TrimDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\TrimLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\ViewDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoCollectionView\ViewLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\AuthorDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\AuthorLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\DateDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\DateLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\DirectoryDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\DirectoryLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\ExtensionDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\ExtensionLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\FilenameDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\FilenameLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\MediaDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\MediaLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\QualityDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\QualityLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\TimeDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\TimeLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\TrimDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\TrimLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\ViewDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeVideoView\ViewLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\HomeView\Error.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\Logo\Logo.ico"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\Logo\Logo.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\SettingsView\ProcessingFFmpegLocationDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\SettingsView\TasksOutputDirectoryDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\SettingsView\ProcessingFFmpegLocationLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\SettingsView\TasksOutputDirectoryLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\SettingsView\TasksFilenameTemplateDark.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\SettingsView\TasksFilenameTemplateLight.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="Assets\Sources\Twitch.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="configuration.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <Page Update="Dictionaries\Images\ImagesSettingsView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Images\ImagesHomeView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Images\ImagesOther.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Images\ImagesHomeVideoCollectionView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Images\ImagesHomeDownloadsView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Images\ImagesHomeVideoView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Images\ImagesSources.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Images\ImagesBaseView.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Colors.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Assets\AssetsLogo.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Update="Dictionaries\Converters.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + <None Update="FFmpeg\ffmpeg.exe"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="FFmpeg\ffprobe.exe"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> </ItemGroup> - <ItemGroup> - <Content Include="Assets\Icons\ActiveTasksNumberDark.svg" /> - <Content Include="Assets\Icons\ActiveTasksNumberLight.svg" /> - <Content Include="Assets\Icons\ApplyToAllOptionsDark.svg" /> - <Content Include="Assets\Icons\ApplyToAllOptionsLight.svg" /> - <Content Include="Assets\Icons\AuthorDark.svg" /> - <Content Include="Assets\Icons\AuthorLight.svg" /> - <Content Include="Assets\Icons\CustomMediaLocationDark.svg" /> - <Content Include="Assets\Icons\CustomMediaLocationLight.svg" /> - <Content Include="Assets\Icons\DateDark.svg" /> - <Content Include="Assets\Icons\DateLight.svg" /> - <Content Include="Assets\Icons\DefaultAudioExtensionDark.svg" /> - <Content Include="Assets\Icons\DefaultAudioExtensionLight.svg" /> - <Content Include="Assets\Icons\DefaultMediaTypeDark.svg" /> - <Content Include="Assets\Icons\DefaultMediaTypeLight.svg" /> - <Content Include="Assets\Icons\DefaultVideoExtensionDark.svg" /> - <Content Include="Assets\Icons\DefaultVideoExtensionLight.svg" /> - <Content Include="Assets\Icons\DeleteTasksTemporaryFilesIfEndedWithErrorDark.svg" /> - <Content Include="Assets\Icons\DeleteTasksTemporaryFilesIfEndedWithErrorLight.svg" /> - <Content Include="Assets\Icons\DeleteTemporaryFilesOnStartDark.svg" /> - <Content Include="Assets\Icons\DeleteTemporaryFilesOnStartLight.svg" /> - <Content Include="Assets\Icons\DurationDark.svg" /> - <Content Include="Assets\Icons\DurationLight.svg" /> - <Content Include="Assets\Icons\EditingAlgorithmDark.svg" /> - <Content Include="Assets\Icons\EditingAlgorithmLight.svg" /> - <Content Include="Assets\Icons\Error.svg" /> - <Content Include="Assets\Icons\FileDark.svg" /> - <Content Include="Assets\Icons\FileLight.svg" /> - <Content Include="Assets\Icons\FilenameTemplateDark.svg" /> - <Content Include="Assets\Icons\FilenameTemplateLight.svg" /> - <Content Include="Assets\Icons\FilterDark.svg" /> - <Content Include="Assets\Icons\FilterLight.svg" /> - <Content Include="Assets\Icons\LastMediaLocationDark.svg" /> - <Content Include="Assets\Icons\LastMediaLocationLight.svg" /> - <Content Include="Assets\Icons\LocationDark.svg" /> - <Content Include="Assets\Icons\LocationLight.svg" /> - <Content Include="Assets\Icons\MediaTypeDark.svg" /> - <Content Include="Assets\Icons\MediaTypeLight.svg" /> - <Content Include="Assets\Icons\MeteredConnectionDelayDark.svg" /> - <Content Include="Assets\Icons\MeteredConnectionDelayLight.svg" /> - <Content Include="Assets\Icons\MeteredConnectionWarningDark.svg" /> - <Content Include="Assets\Icons\MeteredConnectionWarningLight.svg" /> - <Content Include="Assets\Icons\QualityDark.svg" /> - <Content Include="Assets\Icons\QualityLight.svg" /> - <Content Include="Assets\Icons\RemoveSuccessfullyEndedTaskDark.svg" /> - <Content Include="Assets\Icons\RemoveSuccessfullyEndedTaskLight.svg" /> - <Content Include="Assets\Icons\ReplaceFileDark.svg" /> - <Content Include="Assets\Icons\ReplaceFileLight.svg" /> - <Content Include="Assets\Icons\ScheduleDark.svg" /> - <Content Include="Assets\Icons\ScheduleLight.svg" /> - <Content Include="Assets\Icons\ShowNotifcationWhenSuccessfulDark.svg" /> - <Content Include="Assets\Icons\ShowNotifcationWhenSuccessfulLight.svg" /> - <Content Include="Assets\Icons\ShowNotifcationWhenUnsuccessfulDark.svg" /> - <Content Include="Assets\Icons\ShowNotifcationWhenUnsuccessfulLight.svg" /> - <Content Include="Assets\Icons\StateCancelledDark.svg" /> - <Content Include="Assets\Icons\StateCancelledLight.svg" /> - <Content Include="Assets\Icons\StateDoneDark.svg" /> - <Content Include="Assets\Icons\StateDoneLight.svg" /> - <Content Include="Assets\Icons\StateDownloadingDark.svg" /> - <Content Include="Assets\Icons\StateDownloadingLight.svg" /> - <Content Include="Assets\Icons\StateErrorDark.svg" /> - <Content Include="Assets\Icons\StateErrorLight.svg" /> - <Content Include="Assets\Icons\StateFinalizingDark.svg" /> - <Content Include="Assets\Icons\StateFinalizingLight.svg" /> - <Content Include="Assets\Icons\StateIdleDark.svg" /> - <Content Include="Assets\Icons\StateIdleLight.svg" /> - <Content Include="Assets\Icons\StateProcessingDark.svg" /> - <Content Include="Assets\Icons\StateProcessingLight.svg" /> - <Content Include="Assets\Icons\StateScheduledDark.svg" /> - <Content Include="Assets\Icons\StateScheduledLight.svg" /> - <Content Include="Assets\Icons\StateQueuedDark.svg" /> - <Content Include="Assets\Icons\StateQueuedLight.svg" /> - <Content Include="Assets\Icons\TemporaryFilesLocationDark.svg" /> - <Content Include="Assets\Icons\TemporaryFilesLocationLight.svg" /> - <Content Include="Assets\Icons\TranscodingAlgorithmDark.svg" /> - <Content Include="Assets\Icons\TranscodingAlgorithmLight.svg" /> - <Content Include="Assets\Icons\TrimDark.svg" /> - <Content Include="Assets\Icons\TrimLight.svg" /> - <Content Include="Assets\Icons\Twitch.svg" /> - <Content Include="Assets\Icons\UseHardwareAccelerationDark.svg" /> - <Content Include="Assets\Icons\UseHardwareAccelerationLight.svg" /> - <Content Include="Assets\Icons\ViewsDark.svg" /> - <Content Include="Assets\Icons\ViewsLight.svg" /> - <Content Include="Assets\Images\HomeTasksListPlaceholderImage.svg" /> - <Content Include="Assets\Images\UnknownThumbnail.png" /> - <Content Include="Assets\Logo\Logo.png" /> - <Content Include="Assets\Logo\LargeTile.scale-100.png" /> - <Content Include="Assets\Logo\LargeTile.scale-125.png" /> - <Content Include="Assets\Logo\LargeTile.scale-150.png" /> - <Content Include="Assets\Logo\LargeTile.scale-200.png" /> - <Content Include="Assets\Logo\LargeTile.scale-400.png" /> - <Content Include="Assets\Logo\SmallTile.scale-100.png" /> - <Content Include="Assets\Logo\SmallTile.scale-125.png" /> - <Content Include="Assets\Logo\SmallTile.scale-150.png" /> - <Content Include="Assets\Logo\SmallTile.scale-200.png" /> - <Content Include="Assets\Logo\SmallTile.scale-400.png" /> - <Content Include="Assets\Logo\SplashScreen.scale-100.png" /> - <Content Include="Assets\Logo\SplashScreen.scale-125.png" /> - <Content Include="Assets\Logo\SplashScreen.scale-150.png" /> - <Content Include="Assets\Logo\SplashScreen.scale-200.png" /> - <Content Include="Assets\Logo\SplashScreen.scale-400.png" /> - <Content Include="Assets\Logo\Square150x150Logo.scale-100.png" /> - <Content Include="Assets\Logo\Square150x150Logo.scale-125.png" /> - <Content Include="Assets\Logo\Square150x150Logo.scale-150.png" /> - <Content Include="Assets\Logo\Square150x150Logo.scale-200.png" /> - <Content Include="Assets\Logo\Square150x150Logo.scale-400.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-16.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-24.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-256.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-32.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-lightunplated_targetsize-48.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-16.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-24.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-256.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-32.png" /> - <Content Include="Assets\Logo\Square44x44Logo.altform-unplated_targetsize-48.png" /> - <Content Include="Assets\Logo\Square44x44Logo.scale-100.png" /> - <Content Include="Assets\Logo\Square44x44Logo.scale-125.png" /> - <Content Include="Assets\Logo\Square44x44Logo.scale-150.png" /> - <Content Include="Assets\Logo\Square44x44Logo.scale-200.png" /> - <Content Include="Assets\Logo\Square44x44Logo.scale-400.png" /> - <Content Include="Assets\Logo\Square44x44Logo.targetsize-16.png" /> - <Content Include="Assets\Logo\Square44x44Logo.targetsize-24.png" /> - <Content Include="Assets\Logo\Square44x44Logo.targetsize-256.png" /> - <Content Include="Assets\Logo\Square44x44Logo.targetsize-32.png" /> - <Content Include="Assets\Logo\Square44x44Logo.targetsize-48.png" /> - <Content Include="Assets\Logo\StoreLogo.scale-100.png" /> - <Content Include="Assets\Logo\StoreLogo.scale-125.png" /> - <Content Include="Assets\Logo\StoreLogo.scale-150.png" /> - <Content Include="Assets\Logo\StoreLogo.scale-200.png" /> - <Content Include="Assets\Logo\StoreLogo.scale-400.png" /> - <Content Include="Assets\Logo\Wide310x150Logo.scale-100.png" /> - <Content Include="Assets\Logo\Wide310x150Logo.scale-125.png" /> - <Content Include="Assets\Logo\Wide310x150Logo.scale-150.png" /> - <Content Include="Assets\Logo\Wide310x150Logo.scale-200.png" /> - <Content Include="Assets\Logo\Wide310x150Logo.scale-400.png" /> - <Content Include="Assets\Sources\Twitch.png" /> - </ItemGroup> - <ItemGroup> - <ApplicationDefinition Include="App.xaml"> - <Generator>MSBuild:Compile</Generator> - <SubType>Designer</SubType> - </ApplicationDefinition> - <Page Include="Controls\PlaceholderableStackPanel.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Resources\AppProperties.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Resources\Colors.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Resources\Converters.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Resources\Icons.xaml"> - <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\MainPage.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\Controls\SerialVideoAddingControl.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\Controls\VideoAddingOptionsControl.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\Controls\SubscriptionsLoadControl.xaml"> - <Generator>MSBuild:Compile</Generator> - <SubType>Designer</SubType> - </Page> - <Page Include="Views\Home\Controls\PlaylistSearchControl.xaml"> - <Generator>MSBuild:Compile</Generator> - <SubType>Designer</SubType> - </Page> - <Page Include="Views\Home\Controls\VideoSearchControl.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Controls\SettingControl.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\MainPage.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\SubscriptionsAddingPanel.xaml"> - <Generator>MSBuild:Compile</Generator> - <SubType>Designer</SubType> - </Page> - <Page Include="Views\Home\PlaylistAddingPanel.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\Controls\SerialVideoAddingVideoControl.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\VideoAddingPanel.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Home\Controls\DownloadTaskControl.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\MainPage.xaml"> - <Generator>MSBuild:Compile</Generator> - <SubType>Designer</SubType> - </Page> - <Page Include="Views\Sources\MainPage.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Settings\MainPage.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Subscriptions\Controls\SubscriptionPanel.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - <Page Include="Views\Subscriptions\MainPage.xaml"> - <SubType>Designer</SubType> - <Generator>MSBuild:Compile</Generator> - </Page> - </ItemGroup> - <ItemGroup> - <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform"> - <Version>6.2.13</Version> - </PackageReference> - <PackageReference Include="Microsoft.Toolkit.Uwp.Connectivity"> - <Version>7.1.2</Version> - </PackageReference> - <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications"> - <Version>7.1.2</Version> - </PackageReference> - <PackageReference Include="Microsoft.Toolkit.Uwp.UI"> - <Version>7.1.2</Version> - </PackageReference> - <PackageReference Include="Microsoft.UI.Xaml"> - <Version>2.8.0-prerelease.220413001</Version> - </PackageReference> - </ItemGroup> - <ItemGroup> - <PRIResource Include="Strings\en-US\Resources.resw" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\VDownload.Core\VDownload.Core.csproj"> - <Project>{65c44d96-9c6c-47ad-a1f5-86bfaf2b15b8}</Project> - <Name>VDownload.Core</Name> - </ProjectReference> - </ItemGroup> - <ItemGroup> - <Folder Include="Properties\" /> - </ItemGroup> - <ItemGroup> - <PRIResource Include="Strings\en-US\DialogResources.resw" /> - </ItemGroup> - <ItemGroup> - <None Include="VDownload_TemporaryKey.pfx" /> - </ItemGroup> - <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' "> - <VisualStudioVersion>14.0</VisualStudioVersion> + + <PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"> + <HasPackageAndPublishMenu>true</HasPackageAndPublishMenu> </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"> - <OutputPath>..\..\..\..\..\bin\Debug\</OutputPath> - </PropertyGroup> - <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" /> - <!-- To modify your build process, add your task inside one of the targets below and uncomment it. - Other similar extension points exist, see Microsoft.Common.targets. - <Target Name="BeforeBuild"> - </Target> - <Target Name="AfterBuild"> - </Target> - --> -</Project> \ No newline at end of file +</Project> diff --git a/VDownload/Views/About/MainPage.xaml b/VDownload/Views/About/MainPage.xaml deleted file mode 100644 index 6c47b00..0000000 --- a/VDownload/Views/About/MainPage.xaml +++ /dev/null @@ -1,43 +0,0 @@ -<Page - x:Class="VDownload.Views.About.MainPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Views.About" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> - - <Page.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/AppProperties.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Images.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </Page.Resources> - - <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="30"> - <StackPanel Orientation="Vertical"> - <Image HorizontalAlignment="Center" Source="{StaticResource Logo}" Width="150"/> - <TextBlock HorizontalAlignment="Center" FontSize="32" FontWeight="SemiBold" Text="{StaticResource AppName}"/> - <TextBlock HorizontalAlignment="Center" FontSize="16" Text="{StaticResource AppVersion}"/> - </StackPanel> - <StackPanel Orientation="Vertical" Spacing="5"> - <TextBlock x:Uid="About_Developer_Header" HorizontalAlignment="Center" FontSize="16" FontWeight="SemiBold"/> - <TextBlock HorizontalAlignment="Center" FontSize="12" Text="{StaticResource AppAuthor}"/> - </StackPanel> - <StackPanel Orientation="Vertical" Spacing="5"> - <TextBlock x:Uid="About_TranslatedBy_Header" HorizontalAlignment="Center" FontSize="16" FontWeight="SemiBold"/> - <TextBlock x:Uid="About_TranslatedBy_Content" HorizontalAlignment="Center" FontSize="12"/> - </StackPanel> - <StackPanel Orientation="Vertical" Spacing="5"> - <TextBlock x:Uid="About_Links_Header" HorizontalAlignment="Center" FontSize="16" FontWeight="SemiBold"/> - <StackPanel Orientation="Horizontal"> - <HyperlinkButton x:Uid="About_Links_RepositoryButton" NavigateUri="{StaticResource AppRepositoryUrl}"/> - <HyperlinkButton x:Uid="About_Links_DonationButton" NavigateUri="{StaticResource AppDonationUrl}"/> - </StackPanel> - </StackPanel> - </StackPanel> -</Page> diff --git a/VDownload/Views/About/MainPage.xaml.cs b/VDownload/Views/About/MainPage.xaml.cs deleted file mode 100644 index 066b5a3..0000000 --- a/VDownload/Views/About/MainPage.xaml.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 - -namespace VDownload.Views.About -{ - /// <summary> - /// An empty page that can be used on its own or navigated to within a Frame. - /// </summary> - public sealed partial class MainPage : Page - { - public MainPage() - { - this.InitializeComponent(); - } - } -} diff --git a/VDownload/Views/Home/Controls/DownloadTaskControl.xaml b/VDownload/Views/Home/Controls/DownloadTaskControl.xaml deleted file mode 100644 index 6534f11..0000000 --- a/VDownload/Views/Home/Controls/DownloadTaskControl.xaml +++ /dev/null @@ -1,71 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.Controls.DownloadTaskControl" - 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" - mc:Ignorable="d" - CornerRadius="{ThemeResource ControlCornerRadius}" - d:DesignHeight="150" - d:DesignWidth="800"> - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - </ResourceDictionary.MergedDictionaries> - <x:String x:Key="MetadataIconSize">14</x:String> - <x:String x:Key="MetadataTextSize">11</x:String> - </ResourceDictionary> - </UserControl.Resources> - - <Grid Background="{ThemeResource HomeBackgroundColor}" CornerRadius="{ThemeResource ControlCornerRadius}" Padding="10" ColumnSpacing="10"> - <Grid.RowDefinitions> - <RowDefinition/> - <RowDefinition/> - <RowDefinition/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <Image Grid.Row="0" Grid.RowSpan="3" Grid.Column="0" Height="120" Source="{x:Bind ThumbnailImage}"/> - <Grid Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" ColumnSpacing="10" HorizontalAlignment="Left"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="*"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBlock Grid.Column="0" FontSize="18" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind DownloadTask.Video.Title}"/> - <TextBlock Grid.Column="1" FontSize="12" VerticalAlignment="Bottom" FontWeight="Light" Text="{x:Bind DownloadTask.Video.Author}" Margin="0,0,0,2"/> - </Grid> - <Grid Grid.Row="1" Grid.RowSpan="2" Grid.Column="1" ColumnSpacing="5"> - <Grid.RowDefinitions> - <RowDefinition/> - <RowDefinition/> - <RowDefinition/> - <RowDefinition/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <Image Grid.Row="0" Grid.Column="0" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource QualityIcon}"/> - <TextBlock Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind MediaTypeQuality}"/> - <Image Grid.Row="1" Grid.Column="0" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DurationIcon}"/> - <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="StateIcon" Grid.Row="3" Grid.Column="0" Width="{StaticResource MetadataIconSize}"/> - <TextBlock x:Name="StateText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}"/> - <muxc:ProgressBar x:Name="StateProgressBar" 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="SourceButton_Click"/> - <AppBarButton x:Name="StartStopButton" Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Download" Click="StartStopButton_Click"/> - <AppBarButton Grid.Row="2" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Cancel" Click="RemoveButton_Click"/> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/Controls/DownloadTaskControl.xaml.cs b/VDownload/Views/Home/Controls/DownloadTaskControl.xaml.cs deleted file mode 100644 index 06a743a..0000000 --- a/VDownload/Views/Home/Controls/DownloadTaskControl.xaml.cs +++ /dev/null @@ -1,249 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using Microsoft.Toolkit.Uwp.Notifications; -using System; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Extensions; -using VDownload.Core.Services; -using VDownload.Core.Structs; -using Windows.ApplicationModel.ExtendedExecution; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using Windows.Storage.AccessCache; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Media.Imaging; - -namespace VDownload.Views.Home.Controls -{ - public sealed partial class DownloadTaskControl : UserControl - { - #region CONSTANTS - - private static readonly ResourceDictionary IconsRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") }; - private static readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") }; - - #endregion - - - - #region CONSTRUCTORS - - public DownloadTaskControl(DownloadTask downloadTask) - { - this.InitializeComponent(); - - DownloadTask = downloadTask; - DownloadTask.StatusChanged += UpdateStatus; - - ThumbnailImage = DownloadTask.Video.Thumbnail != null ? new BitmapImage { UriSource = DownloadTask.Video.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"]; - - SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{DownloadTask.Video.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false }; - - TimeSpan newDuration = DownloadTask.Trim.End.Subtract(DownloadTask.Trim.Start); - StringBuilder durationBuilder = new StringBuilder(newDuration.ToStringOptTHBaseMMSS()); - if (DownloadTask.Video.Duration > newDuration) - { - durationBuilder.Append($" ({DownloadTask.Trim.Start.ToStringOptTHBaseMMSS(DownloadTask.Trim.End)} - {DownloadTask.Trim.End.ToStringOptTHBaseMMSS(DownloadTask.Trim.Start)})"); - } - Duration = durationBuilder.ToString(); - - StringBuilder mediaTypeQualityBuilder = new StringBuilder(ResourceLoader.GetForCurrentView().GetString($"Base_MediaType_{DownloadTask.MediaType}Text")); - if (DownloadTask.MediaType != MediaType.OnlyAudio) - { - mediaTypeQualityBuilder.Append($" ({DownloadTask.SelectedStream})"); - } - MediaTypeQuality = mediaTypeQualityBuilder.ToString(); - - File = DownloadTask.File.GetPath(); - - UpdateStatus(this, DownloadTask.LastStatusChangedEventArgs); - } - - #endregion - - - - #region PROPERTIES - - private DownloadTask DownloadTask { get; set; } - - private ImageSource ThumbnailImage { get; set; } - private IconElement SourceImage { get; set; } - private string Title { get; set; } - private string Author { get; set; } - private string Duration { get; set; } - private string MediaTypeQuality { get; set; } - private string File { get; set; } - - #endregion - - - - #region EVENT HANDLERS - - private async void StartStopButton_Click(object sender, RoutedEventArgs e) - { - if (DownloadTask.Status != DownloadTaskStatus.Idle) - { - DownloadTask.CancellationTokenSource.Cancel(); - } - else - { - bool delay = (bool)Config.GetValue("delay_task_when_queued_task_starts_on_metered_network"); - if (NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection) - { - ContentDialogResult dialogResult = await new ContentDialog - { - Title = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_Title"), - Content = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_Content"), - PrimaryButtonText = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_StartWithDelayButtonText"), - SecondaryButtonText = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_StartWithoutDelayButtonText1"), - CloseButtonText = ResourceLoader.GetForCurrentView().GetString("Base_CancelButtonText"), - }.ShowAsync(); - switch (dialogResult) - { - case ContentDialogResult.Primary: delay = true; break; - case ContentDialogResult.Secondary: delay = false; break; - case ContentDialogResult.None: return; - } - } - await DownloadTask.Run(delay); - } - } - - private void RemoveButton_Click(object sender, RoutedEventArgs e) - { - if (DownloadTask.Status != DownloadTaskStatus.Idle) - { - DownloadTask.CancellationTokenSource.Cancel(); - } - RemovingRequested.Invoke(this, EventArgs.Empty); - } - - private async void SourceButton_Click(object sender, RoutedEventArgs e) - { - await Windows.System.Launcher.LaunchUriAsync(DownloadTask.Video.Url); - } - - private void UpdateStatus(object sender, DownloadTaskStatusChangedEventArgs e) - { - if (e.Status == DownloadTaskStatus.Idle || e.Status == DownloadTaskStatus.EndedSuccessfully || e.Status == DownloadTaskStatus.EndedUnsuccessfully) - { - StartStopButton.Icon = new SymbolIcon(Symbol.Download); - } - else - { - StartStopButton.Icon = new SymbolIcon(Symbol.Stop); - } - - if (e.Status == DownloadTaskStatus.Scheduled) - { - StateIcon.Source = (SvgImageSource)IconsRes["StateScheduledIcon"]; - StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Scheduled")} ({e.ScheduledFor.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern)} {e.ScheduledFor.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortTimePattern)})"; - StateProgressBar.Visibility = Visibility.Collapsed; - } - else if (e.Status == DownloadTaskStatus.Queued) - { - StateIcon.Source = (SvgImageSource)IconsRes["StateQueuedIcon"]; - StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Queued"); - StateProgressBar.Visibility = Visibility.Visible; - StateProgressBar.IsIndeterminate = true; - } - else if (e.Status == DownloadTaskStatus.Downloading) - { - StateIcon.Source = (SvgImageSource)IconsRes["StateDownloadingIcon"]; - StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Downloading")} ({Math.Round(e.DownloadingProgress)}%)"; - StateProgressBar.Visibility = Visibility.Visible; - StateProgressBar.IsIndeterminate = false; - StateProgressBar.Value = e.DownloadingProgress; - } - else if (e.Status == DownloadTaskStatus.Processing) - { - StateIcon.Source = (SvgImageSource)IconsRes["StateProcessingIcon"]; - StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Processing")} ({Math.Round(e.ProcessingProgress)}%)"; - StateProgressBar.Visibility = Visibility.Visible; - StateProgressBar.IsIndeterminate = false; - StateProgressBar.Value = e.ProcessingProgress; - } - else if (e.Status == DownloadTaskStatus.Finalizing) - { - StateIcon.Source = (SvgImageSource)IconsRes["StateFinalizingIcon"]; - StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Finalizing"); - StateProgressBar.Visibility = Visibility.Visible; - StateProgressBar.IsIndeterminate = true; - } - else if (e.Status == DownloadTaskStatus.EndedSuccessfully) - { - StateIcon.Source = (SvgImageSource)IconsRes["StateDoneIcon"]; - StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Done")} ({e.ElapsedTime.ToStringOptTHBaseMMSS()})"; - StateProgressBar.Visibility = Visibility.Collapsed; - - if ((bool)Config.GetValue("show_notification_when_task_ended_successfully")) - { - new ToastContentBuilder() - .AddText(ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedSuccessfully_Header")) - .AddText($"\"{Title}\" - {ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedSuccessfully_Description")}") - .Show(); - } - } - else if (e.Status == DownloadTaskStatus.EndedUnsuccessfully) - { - if (e.Exception is OperationCanceledException) - { - StateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"]; - StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Cancelled"); - StateProgressBar.Visibility = Visibility.Collapsed; - } - else - { - string errorInfo; - if (e.Exception is WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) errorInfo = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Error_InternetNotAvailable"); - else throw e.Exception; - } - else - { - throw e.Exception; - } - StateIcon.Source = (SvgImageSource)IconsRes["StateErrorIcon"]; - StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Error")} ({errorInfo})"; - StateProgressBar.Visibility = Visibility.Collapsed; - - if ((bool)Config.GetValue("show_notification_when_task_ended_unsuccessfully")) - { - new ToastContentBuilder() - .AddText(ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Header")) - .AddText($"\"{Title}\" - {ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Description")} ({errorInfo})") - .Show(); - } - } - } - else - { - StateIcon.Source = (SvgImageSource)IconsRes["StateIdleIcon"]; - StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Idle"); - StateProgressBar.Visibility = Visibility.Collapsed; - } - } - - #endregion - - - - #region EVENT HANDLERS - - public event EventHandler RemovingRequested; - - #endregion - } -} diff --git a/VDownload/Views/Home/Controls/PlaylistSearchControl.xaml b/VDownload/Views/Home/Controls/PlaylistSearchControl.xaml deleted file mode 100644 index 365a154..0000000 --- a/VDownload/Views/Home/Controls/PlaylistSearchControl.xaml +++ /dev/null @@ -1,32 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.Controls.PlaylistSearchControl" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Controls" - 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" - mc:Ignorable="d" - d:DesignHeight="100" - d:DesignWidth="400"> - - - <Grid x:Name="Grid" Margin="10" ColumnSpacing="10"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBox x:Name="UrlTextBox" x:Uid="Home_OptionsBar_PlaylistSearchControl_UrlTextBox" Grid.Column="0" VerticalAlignment="Center"/> - <muxc:NumberBox x:Name="MaxVideosNumberBox" Grid.Column="1" VerticalAlignment="Center" SpinButtonPlacementMode="Compact" Minimum="0" LostFocus="MaxVideosNumberBox_LostFocus"/> - <Button x:Name="SearchButton" x:Uid="Home_OptionsBar_PlaylistSearchControl_SearchButton" Grid.Column="2" Click="SearchButton_Click"/> - <Button x:Name="HelpButton" Grid.Column="3" Content="?" Click="HelpButton_Click"> - <Button.Resources> - <muxc:TeachingTip x:Name="InfoBox" x:Uid="Home_OptionsBar_PlaylistSearchControl_InfoBox" Target="{x:Bind Grid}"/> - </Button.Resources> - </Button> - <ContentPresenter x:Name="StatusControlPresenter" Margin="-10,0,0,0" Grid.Column="4"/> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/Controls/PlaylistSearchControl.xaml.cs b/VDownload/Views/Home/Controls/PlaylistSearchControl.xaml.cs deleted file mode 100644 index a101149..0000000 --- a/VDownload/Views/Home/Controls/PlaylistSearchControl.xaml.cs +++ /dev/null @@ -1,147 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using System; -using System.Diagnostics; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Services.Sources; -using Windows.ApplicationModel.Resources; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media.Imaging; - -namespace VDownload.Views.Home.Controls -{ - public sealed partial class PlaylistSearchControl : UserControl - { - #region CONSTANTS - - private static readonly ResourceDictionary IconRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") }; - - private static readonly Microsoft.UI.Xaml.Controls.ProgressRing StatusControlProgressRing = new Microsoft.UI.Xaml.Controls.ProgressRing { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), IsActive = true }; - private static readonly Image StatusControlErrorImage = new Image { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), Source = (SvgImageSource)IconRes["ErrorIcon"] }; - - #endregion - - - - #region CONSTRUCTORS - - public PlaylistSearchControl() - { - this.InitializeComponent(); - - // Set default max videos - MaxVideosNumberBox.Value = (int)Config.GetValue("default_max_playlist_videos"); - } - - #endregion - - - - #region PROPERTIES - - public CancellationToken CancellationToken { get; set; } - - #endregion - - - - #region EVENT HANDLERS - - private void MaxVideosNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - if (double.IsNaN(MaxVideosNumberBox.Value)) MaxVideosNumberBox.Value = (int)Config.GetValue("default_max_playlist_videos"); - } - - private async void SearchButton_Click(object sender, RoutedEventArgs e) - { - async Task ShowDialog(string localErrorKey) - { - ContentDialog errorDialog = new ContentDialog - { - Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_OptionsBar_PlaylistSearchControl_Base_Title"), - Content = ResourceLoader.GetForCurrentView("DialogResources").GetString($"Home_OptionsBar_PlaylistSearchControl_{localErrorKey}_Content"), - CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CloseButtonText"), - }; - await errorDialog.ShowAsync(); - StatusControlPresenter.Content = StatusControlErrorImage; - } - - SearchButtonClicked?.Invoke(this, EventArgs.Empty); - - InfoBox.IsOpen = false; - - StatusControlPresenter.Content = StatusControlProgressRing; - - MaxVideosNumberBox_LostFocus(sender, e); - IPlaylist playlist = Source.GetPlaylist(UrlTextBox.Text); - if (playlist == null) - { - StatusControlPresenter.Content = StatusControlErrorImage; - } - else - { - try - { - await playlist.GetMetadataAsync(CancellationToken); - await playlist.GetVideosAsync((int)Math.Round(MaxVideosNumberBox.Value), CancellationToken); - } - catch (OperationCanceledException) - { - StatusControlPresenter.Content = null; - return; - } - catch (MediaNotFoundException) - { - StatusControlPresenter.Content = StatusControlErrorImage; - return; - } - catch (TwitchAccessTokenNotFoundException) - { - await ShowDialog("TwitchAccessTokenNotFound"); - return; - } - catch (TwitchAccessTokenNotValidException) - { - await ShowDialog("TwitchAccessTokenNotValid"); - return; - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - await ShowDialog("InternetNotAvailable"); - return; - } - else throw; - } - - StatusControlPresenter.Content = null; - - SearchingSuccessed?.Invoke(this, new PlaylistSearchSuccessedEventArgs(playlist)); - } - } - - private void HelpButton_Click(object sender, RoutedEventArgs e) - { - InfoBox.IsOpen = !InfoBox.IsOpen; - } - - #endregion - - - - #region EVENT HANDLERS - - public event EventHandler<PlaylistSearchSuccessedEventArgs> SearchingSuccessed; - public event EventHandler SearchButtonClicked; - - #endregion - } -} diff --git a/VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml b/VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml deleted file mode 100644 index 97e6e2f..0000000 --- a/VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml +++ /dev/null @@ -1,145 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.Controls.SerialVideoAddingControl" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Views.Home.Controls" - 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:cc="using:VDownload.Controls" - mc:Ignorable="d" - d:DesignHeight="300" - d:DesignWidth="400" - Loading="SerialVideoAddingControl_Loading"> - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </UserControl.Resources> - - <Grid> - <Grid.RowDefinitions> - <RowDefinition/> - </Grid.RowDefinitions> - - <ScrollViewer Grid.Row="0" Margin="0,0,0,60"> - <StackPanel x:Name="VideosStackPanel" x:FieldModifier="public" Spacing="10"/> - </ScrollViewer> - - <Grid Grid.Row="0" ColumnSpacing="10"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <muxc:Expander Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" HorizontalContentAlignment="Stretch" ExpandDirection="Up" Background="{ThemeResource HomePlaylistAddingApplyToAllContentBackgroundColor}"> - <muxc:Expander.Header> - <StackPanel Margin="-5,0,-10,0" Spacing="10" Orientation="Horizontal"> - <Image Height="18" Source="{ThemeResource ApplyToAllOptionsIcon}"/> - <TextBlock x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_HeaderTextBlock"/> - </StackPanel> - </muxc:Expander.Header> - <muxc:Expander.Content> - <StackPanel Spacing="10"> - <cc:SettingControl x:Name="ApplyToAllLocationSettingControl" x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl" Icon="{ThemeResource LocationIcon}"> - <cc:SettingControl.SettingContent> - <StackPanel Spacing="10" Orientation="Horizontal"> - <Button x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl_BrowseButton" Click="ApplyToAllLocationBrowseButton_Click"/> - <Button x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_Base_ApplyButton" Click="ApplyToAllApplyLocationButton_Click"/> - </StackPanel> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_ScheduleSettingControl" Icon="{ThemeResource ScheduleIcon}"> - <cc:SettingControl.SettingContent> - <StackPanel Spacing="10" Orientation="Horizontal"> - <muxc:NumberBox x:Name="ApplyToAllScheduleNumberBox" MaxWidth="100" Value="0" Minimum="0" SpinButtonPlacementMode="Compact" LostFocus="ApplyToAllScheduleNumberBox_LostFocus"/> - <Button x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_Base_ApplyButton" Click="ApplyToAllApplyScheduleButton_Click"/> - </StackPanel> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - </muxc:Expander.Content> - </muxc:Expander> - <muxc:Expander Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" HorizontalContentAlignment="Stretch" ExpandDirection="Up" Background="{ThemeResource HomePlaylistAddingApplyToAllContentBackgroundColor}"> - <muxc:Expander.Header> - <Grid Margin="-5,0,-10,0" ColumnSpacing="10"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <Image Grid.Column="0" Height="18" Source="{ThemeResource FilterIcon}"/> - <StackPanel Grid.Column="1" Margin="0,0,20,0" Spacing="10" Orientation="Horizontal" VerticalAlignment="Center"> - <TextBlock x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_Header_TextBlock"/> - <TextBlock x:Name="FilterHeaderCountTextBlock" FontWeight="Light"/> - </StackPanel> - </Grid> - </muxc:Expander.Header> - <muxc:Expander.Content> - <Grid RowSpacing="8" ColumnSpacing="10"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBlock" FontWeight="SemiBold"/> - <TextBox Grid.Row="0" Grid.Column="1" x:Name="FilterTitleTextBox" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBox" LostFocus="FilterTitleAndAuthorTextBox_LostFocus"/> - <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBlock" FontWeight="SemiBold"/> - <TextBox Grid.Row="1" Grid.Column="1" x:Name="FilterAuthorTextBox" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBox" LostFocus="FilterTitleAndAuthorTextBox_LostFocus"/> - <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_ViewsTextBlock" FontWeight="SemiBold"/> - <Grid Grid.Row="2" Grid.Column="1" ColumnSpacing="8"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="5"/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <muxc:NumberBox Grid.Column="0" x:Name="FilterMinViewsNumberBox" SpinButtonPlacementMode="Compact" LostFocus="FilterMinViewsNumberBox_LostFocus"/> - <TextBlock Grid.Column="1" VerticalAlignment="Center" Text="-"/> - <muxc:NumberBox Grid.Column="2" x:Name="FilterMaxViewsNumberBox" SpinButtonPlacementMode="Compact" LostFocus="FilterMaxViewsNumberBox_LostFocus"/> - </Grid> - <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_DateTextBlock" FontWeight="SemiBold"/> - <Grid Grid.Row="3" Grid.Column="1" ColumnSpacing="8"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="5"/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <CalendarDatePicker x:Name="FilterMinDateDatePicker" Grid.Column="0" DateChanged="FilterMinAndMaxDateDatePicker_DateChanged"/> - <TextBlock Grid.Column="1" VerticalAlignment="Center" Text="-"/> - <CalendarDatePicker x:Name="FilterMaxDateDatePicker" Grid.Column="2" DateChanged="FilterMinAndMaxDateDatePicker_DateChanged"/> - </Grid> - <TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_DurationTextBlock" FontWeight="SemiBold"/> - <Grid Grid.Row="4" Grid.Column="1" ColumnSpacing="8"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="5"/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <TextBox Grid.Column="0" x:Name="FilterMinDurationTextBox" LostFocus="FilterMinDurationTextBox_LostFocus"/> - <TextBlock Grid.Column="1" VerticalAlignment="Center" Text="-"/> - <TextBox Grid.Column="2" x:Name="FilterMaxDurationTextBox" LostFocus="FilterMaxDurationTextBox_LostFocus"/> - </Grid> - <TextBlock x:Name="FilterRemovedTextBlock" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_TextBlock" FontWeight="SemiBold" Visibility="Collapsed"/> - <Grid Grid.Row="5" Grid.Column="1" ColumnSpacing="8"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBlock Grid.Column="0" x:Name="FilterRemovedCountTextBlock" VerticalAlignment="Center" Visibility="Collapsed"/> - <Button Grid.Column="1" x:Name="FilterRemovedRestoreButton" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_RestoreButton" Visibility="Collapsed" Click="FilterRemovedRestoreButton_Click"/> - </Grid> - </Grid> - </muxc:Expander.Content> - </muxc:Expander> - </Grid> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml.cs b/VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml.cs deleted file mode 100644 index 21ee9a0..0000000 --- a/VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml.cs +++ /dev/null @@ -1,349 +0,0 @@ -using Microsoft.Toolkit.Uwp.UI; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Text.RegularExpressions; -using VDownload.Converters; -using VDownload.Core.Extensions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using Windows.ApplicationModel.Resources; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.Storage; -using Windows.Storage.AccessCache; -using Windows.Storage.Pickers; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Views.Home.Controls -{ - public sealed partial class SerialVideoAddingControl : UserControl - { - #region CONSTRUCTORS - - public SerialVideoAddingControl(IVideo[] videos) - { - this.InitializeComponent(); - - Videos = videos; - MinViews = Videos.Min(video => video.Views); - MaxViews = Videos.Max(video => video.Views); - MinDate = Videos.Min(video => video.Date); - MaxDate = Videos.Max(video => video.Date); - MinDuration = Videos.Min(video => video.Duration); - MaxDuration = Videos.Max(video => video.Duration); - - FilterMinViewsNumberBox.Minimum = FilterMaxViewsNumberBox.Minimum = FilterMinViewsNumberBox.Value = MinViews; - FilterMinViewsNumberBox.Maximum = FilterMaxViewsNumberBox.Maximum = FilterMaxViewsNumberBox.Value = MaxViews; - - FilterMinDateDatePicker.MinDate = FilterMaxDateDatePicker.MinDate = (DateTimeOffset)(FilterMinDateDatePicker.Date = MinDate); - FilterMinDateDatePicker.MaxDate = FilterMaxDateDatePicker.MaxDate = (DateTimeOffset)(FilterMaxDateDatePicker.Date = MaxDate); - - TextBoxExtensions.SetMask(FilterMinDurationTextBox, (string)new TimeSpanToTextBoxMaskConverter().Convert(MaxDuration, null, null, null)); - TextBoxExtensions.SetMask(FilterMaxDurationTextBox, (string)new TimeSpanToTextBoxMaskConverter().Convert(MaxDuration, null, null, null)); - HashSet<int> maskElements = new HashSet<int>(); - foreach (TimeSpan ts in new List<TimeSpan> { MinDuration, MaxDuration }) - { - if (Math.Floor(ts.TotalHours) > 0) maskElements.Add(int.Parse(Math.Floor(ts.TotalHours).ToString()[0].ToString())); - if (Math.Floor(ts.TotalMinutes) > 0) - { - if (Math.Floor(ts.TotalHours) > 0) maskElements.Add(5); - else maskElements.Add(int.Parse(ts.Minutes.ToString()[0].ToString())); - } - if (Math.Floor(ts.TotalMinutes) > 0) maskElements.Add(5); - else maskElements.Add(int.Parse(ts.Seconds.ToString()[0].ToString())); - } - List<string> maskElementsString = new List<string>(); - foreach (int i in maskElements) - { - if (i != 9) maskElementsString.Add($"{i}:[0-{i}]"); - } - TextBoxExtensions.SetCustomMask(FilterMinDurationTextBox, string.Join(',', maskElementsString)); - TextBoxExtensions.SetCustomMask(FilterMaxDurationTextBox, string.Join(',', maskElementsString)); - FilterMinDurationTextBox.Text = MinDuration.ToStringOptTHMMBaseSS(MaxDuration); - FilterMaxDurationTextBox.Text = MaxDuration.ToStringOptTHMMBaseSS(); - - ApplyToAllSchedule = 0; - - foreach (IVideo video in Videos) - { - SerialVideoAddingVideoControl videoControl = new SerialVideoAddingVideoControl(video); - videoControl.DeleteRequested += (s, a) => - { - DeletedVideos.Add(videoControl); - FilterRemovedTextBlock.Visibility = Visibility.Visible; - FilterRemovedCountTextBlock.Visibility = Visibility.Visible; - FilterRemovedRestoreButton.Visibility = Visibility.Visible; - FilterRemovedCountTextBlock.Text = $"{DeletedVideos.Count}"; - FilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("Home_Adding_Base_SerialVideoAddingControl_Filter_Header_CountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : ""; - VideosStackPanel.Children.Remove(videoControl); - }; - VideosStackPanel.Children.Add(videoControl); - } - } - - #endregion - - - - #region PROPERTIES - - private IVideo[] Videos { get; set; } - - public StorageFolder ApplyToAllLocation { get; set; } - public double ApplyToAllSchedule { get; set; } - - private List<SerialVideoAddingVideoControl> DeletedVideos { get; set; } = new List<SerialVideoAddingVideoControl>(); - private List<SerialVideoAddingVideoControl> HiddenVideos { get; set; } = new List<SerialVideoAddingVideoControl>(); - - private long MinViews { get; set; } - private long MaxViews { get; set; } - private DateTime MinDate { get; set; } - private DateTime MaxDate { get; set; } - private TimeSpan MinDuration { get; set; } - private TimeSpan MaxDuration { get; set; } - - #endregion - - - - #region EVENT HANDLERS - - private async void SerialVideoAddingControl_Loading(FrameworkElement sender, object args) - { - if (!(bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("last_media_location")) - { - ApplyToAllLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("last_media_location"); - ApplyToAllLocationSettingControl.Description = ApplyToAllLocation.Path; - } - else if ((bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_media_location")) - { - ApplyToAllLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_media_location"); - ApplyToAllLocationSettingControl.Description = ApplyToAllLocation.Path; - } - else - { - ApplyToAllLocation = null; - ApplyToAllLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload"; - } - } - - private async void ApplyToAllLocationBrowseButton_Click(object sender, RoutedEventArgs e) - { - FolderPicker picker = new FolderPicker - { - SuggestedStartLocation = PickerLocationId.Downloads - }; - picker.FileTypeFilter.Add("*"); - - StorageFolder selectedFolder = await picker.PickSingleFolderAsync(); - - if (selectedFolder != null) - { - try - { - await (await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync(); - StorageApplicationPermissions.FutureAccessList.AddOrReplace("last_media_location", selectedFolder); - ApplyToAllLocation = selectedFolder; - ApplyToAllLocationSettingControl.Description = ApplyToAllLocation.Path; - } - catch (UnauthorizedAccessException) { } - } - } - - private void ApplyToAllApplyLocationButton_Click(object sender, RoutedEventArgs e) - { - foreach (SerialVideoAddingVideoControl videoPanel in VideosStackPanel.Children) - { - videoPanel.OptionsControl.ChangeLocation(ApplyToAllLocation); - } - foreach (SerialVideoAddingVideoControl videoPanel in DeletedVideos) - { - videoPanel.OptionsControl.ChangeLocation(ApplyToAllLocation); - } - foreach (SerialVideoAddingVideoControl videoPanel in HiddenVideos) - { - videoPanel.OptionsControl.ChangeLocation(ApplyToAllLocation); - } - } - - private void ApplyToAllScheduleNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - if (double.IsNaN(ApplyToAllScheduleNumberBox.Value)) - { - ApplyToAllScheduleNumberBox.Value = ApplyToAllSchedule; - } - else - { - ApplyToAllSchedule = ApplyToAllScheduleNumberBox.Value; - } - } - - private void ApplyToAllApplyScheduleButton_Click(object sender, RoutedEventArgs e) - { - foreach (SerialVideoAddingVideoControl videoPanel in VideosStackPanel.Children) - { - videoPanel.OptionsControl.ChangeSchedule(ApplyToAllSchedule); - } - foreach (SerialVideoAddingVideoControl videoPanel in DeletedVideos) - { - videoPanel.OptionsControl.ChangeSchedule(ApplyToAllSchedule); - } - foreach (SerialVideoAddingVideoControl videoPanel in HiddenVideos) - { - videoPanel.OptionsControl.ChangeSchedule(ApplyToAllSchedule); - } - } - - private void FilterTitleAndAuthorTextBox_LostFocus(object sender, RoutedEventArgs e) - { - FilterChanged(); - } - - private void FilterMinViewsNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - if (double.IsNaN(FilterMinViewsNumberBox.Value)) - { - FilterMinViewsNumberBox.Value = MinViews; - } - FilterChanged(); - } - - private void FilterMaxViewsNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - if (double.IsNaN(FilterMaxViewsNumberBox.Value)) - { - FilterMaxViewsNumberBox.Value = MaxViews; - } - FilterChanged(); - } - - private void FilterMinAndMaxDateDatePicker_DateChanged(CalendarDatePicker sender, CalendarDatePickerDateChangedEventArgs args) - { - FilterChanged(); - } - - private void FilterMinDurationTextBox_LostFocus(object sender, RoutedEventArgs e) - { - if (!FilterMinDurationTextBox.Text.Contains('_')) - { - string[] segments = FilterMinDurationTextBox.Text.Split(':').Reverse().ToArray(); - int.TryParse(segments.ElementAtOrDefault(0), out int seconds); - int.TryParse(segments.ElementAtOrDefault(1), out int minutes); - int.TryParse(segments.ElementAtOrDefault(2), out int hours); - - TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds); - - if (parsedTimeSpan < MinDuration || parsedTimeSpan > MaxDuration) - { - FilterMinDurationTextBox.Text = MinDuration.ToStringOptTHMMBaseSS(MaxDuration); - } - } - else - { - FilterMinDurationTextBox.Text = MinDuration.ToStringOptTHMMBaseSS(MaxDuration); - } - FilterChanged(); - } - - private void FilterMaxDurationTextBox_LostFocus(object sender, RoutedEventArgs e) - { - if (!FilterMaxDurationTextBox.Text.Contains('_')) - { - string[] segments = FilterMaxDurationTextBox.Text.Split(':').Reverse().ToArray(); - int.TryParse(segments.ElementAtOrDefault(0), out int seconds); - int.TryParse(segments.ElementAtOrDefault(1), out int minutes); - int.TryParse(segments.ElementAtOrDefault(2), out int hours); - - TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds); - - if (parsedTimeSpan < MinDuration || parsedTimeSpan > MaxDuration) - { - FilterMaxDurationTextBox.Text = MaxDuration.ToStringOptTHMMBaseSS(); - } - } - else - { - FilterMaxDurationTextBox.Text = MaxDuration.ToStringOptTHMMBaseSS(); - } - FilterChanged(); - } - - private void FilterRemovedRestoreButton_Click(object sender, RoutedEventArgs e) - { - foreach (SerialVideoAddingVideoControl videoControl in DeletedVideos) - { - VideosStackPanel.Children.Add(videoControl); - } - FilterRemovedTextBlock.Visibility = Visibility.Collapsed; - FilterRemovedCountTextBlock.Visibility = Visibility.Collapsed; - FilterRemovedRestoreButton.Visibility = Visibility.Collapsed; - FilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterHeaderCountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : ""; - DeletedVideos.Clear(); - } - - #endregion - - - - #region PRIVATE METHODS - - private void FilterChanged() - { - string[] minSegments = FilterMinDurationTextBox.Text.Split(':').Reverse().ToArray(); - int.TryParse(minSegments.ElementAtOrDefault(0), out int minSeconds); - int.TryParse(minSegments.ElementAtOrDefault(1), out int minMinutes); - int.TryParse(minSegments.ElementAtOrDefault(2), out int minHours); - TimeSpan minDuration = new TimeSpan(minHours, minMinutes, minSeconds); - - string[] maxSegments = FilterMaxDurationTextBox.Text.Split(':').Reverse().ToArray(); - int.TryParse(maxSegments.ElementAtOrDefault(0), out int maxSeconds); - int.TryParse(maxSegments.ElementAtOrDefault(1), out int maxMinutes); - int.TryParse(maxSegments.ElementAtOrDefault(2), out int maxHours); - TimeSpan maxDuration = new TimeSpan(maxHours, maxMinutes, maxSeconds); - - Regex titleRegex = new Regex(""); - Regex authorRegex = new Regex(""); - try - { - titleRegex = new Regex(FilterTitleTextBox.Text); - authorRegex = new Regex(FilterAuthorTextBox.Text); - } - catch (ArgumentException) { } - - List<SerialVideoAddingVideoControl> allVideos = new List<SerialVideoAddingVideoControl>(); - foreach (SerialVideoAddingVideoControl video in VideosStackPanel.Children) - { - allVideos.Add(video); - } - allVideos.AddRange(HiddenVideos); - VideosStackPanel.Children.Clear(); - HiddenVideos.Clear(); - foreach (SerialVideoAddingVideoControl videoControl in allVideos) - { - if ( - !titleRegex.IsMatch(videoControl.Video.Title) || - !authorRegex.IsMatch(videoControl.Video.Author) || - FilterMinViewsNumberBox.Value > videoControl.Video.Views || - FilterMaxViewsNumberBox.Value < videoControl.Video.Views || - FilterMinDateDatePicker.Date > videoControl.Video.Date || - FilterMaxDateDatePicker.Date < videoControl.Video.Date || - minDuration > videoControl.Video.Duration || - maxDuration < videoControl.Video.Duration - ) HiddenVideos.Add(videoControl); - else VideosStackPanel.Children.Add(videoControl); - } - FilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("Home_Adding_Base_SerialVideoAddingControl_Filter_Header_CountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : string.Empty; - } - - #endregion - } -} diff --git a/VDownload/Views/Home/Controls/SerialVideoAddingVideoControl.xaml b/VDownload/Views/Home/Controls/SerialVideoAddingVideoControl.xaml deleted file mode 100644 index bbd4abc..0000000 --- a/VDownload/Views/Home/Controls/SerialVideoAddingVideoControl.xaml +++ /dev/null @@ -1,75 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.Controls.SerialVideoAddingVideoControl" - 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:cc="using:VDownload.Controls" - xmlns:ex="using:Microsoft.Toolkit.Uwp.UI" - mc:Ignorable="d" - d:DesignHeight="300" - d:DesignWidth="400" - Loading="SerialVideoAddingVideoControl_Loading"> - - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/> - </ResourceDictionary.MergedDictionaries> - <x:String x:Key="MetadataIconSize">14</x:String> - <x:String x:Key="MetadataTextSize">11</x:String> - </ResourceDictionary> - </UserControl.Resources> - - - <muxc:Expander x:Name="Expander" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Background="{ThemeResource HomePlaylistAddingVideoPanelContentBackgroundColor}"> - <muxc:Expander.Header> - <Grid Margin="-5,10,-15,10" ColumnSpacing="10"> - <Grid.RowDefinitions> - <RowDefinition/> - <RowDefinition/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Height="80" Source="{x:Bind ThumbnailImage}"/> - <Grid Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" ColumnSpacing="10"> - <Grid.RowDefinitions> - <RowDefinition/> - <RowDefinition/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <StackPanel Grid.Row="0" Grid.Column="0" Spacing="5" Orientation="Horizontal"> - <Image Width="{StaticResource MetadataIconSize}" Source="{ThemeResource AuthorIcon}"/> - <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Author}"/> - </StackPanel> - <StackPanel Grid.Row="1" Grid.Column="0" Spacing="5" Orientation="Horizontal"> - <Image Width="{StaticResource MetadataIconSize}" Source="{ThemeResource ViewsIcon}"/> - <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Views}"/> - </StackPanel> - <StackPanel Grid.Row="0" Grid.Column="1" Spacing="5" Orientation="Horizontal"> - <Image Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DateIcon}"/> - <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Date}"/> - </StackPanel> - <StackPanel Grid.Row="1" Grid.Column="1" Spacing="5" Orientation="Horizontal"> - <Image Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DurationIcon}"/> - <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Duration}"/> - </StackPanel> - </Grid> - <TextBlock Grid.Row="0" Grid.Column="1" FontSize="15" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind Title}"/> - <AppBarButton Grid.Row="0" Grid.Column="2" Margin="0,-4,0,-4" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="SourceButton_Click"/> - <AppBarButton Grid.Row="1" Grid.Column="2" Margin="0,-4,0,-4" Width="40" Height="48" Icon="Delete" Click="HideButton_Click"/> - </Grid> - </muxc:Expander.Header> - </muxc:Expander> -</UserControl> diff --git a/VDownload/Views/Home/Controls/SerialVideoAddingVideoControl.xaml.cs b/VDownload/Views/Home/Controls/SerialVideoAddingVideoControl.xaml.cs deleted file mode 100644 index 2cd5364..0000000 --- a/VDownload/Views/Home/Controls/SerialVideoAddingVideoControl.xaml.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using VDownload.Core.Extensions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Media.Imaging; - -namespace VDownload.Views.Home.Controls -{ - public sealed partial class SerialVideoAddingVideoControl : UserControl - { - #region CONSTANTS - - private readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") }; - - #endregion - - - - #region CONSTRUCTORS - - public SerialVideoAddingVideoControl(IVideo video) - { - this.InitializeComponent(); - - Video = video; - - ThumbnailImage = Video.Thumbnail != null ? new BitmapImage { UriSource = Video.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"]; - SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Video.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false }; - Title = Video.Title; - Author = Video.Author; - Views = Video.Views.ToString(); - Date = Video.Date.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern); - Duration = Video.Duration.ToStringOptTHBaseMMSS(); - - OptionsControl = new VideoAddingOptionsControl(Video); - Expander.Content = OptionsControl; - } - - #endregion - - - - #region PROPERTIES - - public IVideo Video { get; set; } - - private ImageSource ThumbnailImage { get; set; } - private IconElement SourceImage { get; set; } - private string Title { get; set; } - private string Author { get; set; } - private string Views { get; set; } - private string Date { get; set; } - private string Duration { get; set; } - - public VideoAddingOptionsControl OptionsControl { get; private set; } - - #endregion - - - - #region EVENT HANDLERS - - private async void SerialVideoAddingVideoControl_Loading(FrameworkElement sender, object args) - { - await OptionsControl.Init(); - } - - private async void SourceButton_Click(object sender, RoutedEventArgs e) - { - await Windows.System.Launcher.LaunchUriAsync(Video.Url); - } - - private void HideButton_Click(object sender, RoutedEventArgs e) - { - DeleteRequested?.Invoke(this, EventArgs.Empty); - } - - #endregion - - - - #region EVENTS - - public event EventHandler DeleteRequested; - - #endregion - } -} diff --git a/VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml b/VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml deleted file mode 100644 index 63a8e26..0000000 --- a/VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml +++ /dev/null @@ -1,23 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.Controls.SubscriptionsLoadControl" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Controls" - 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" - mc:Ignorable="d" - d:DesignHeight="100" - d:DesignWidth="400"> - - <Grid Margin="10" ColumnSpacing="10"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <muxc:ProgressRing x:Name="ProgressRing" Grid.Column="0" Width="15" Height="15" Margin="15,5,5,5" IsActive="True"/> - <TextBlock x:Name="InfoTextBlock" Grid.Column="1" VerticalAlignment="Center"/> - <Button x:Name="LoadVideosButton" x:Uid="Home_OptionsBar_SubscriptionsLoadControl_LoadVideosButton" Grid.Column="2" Visibility="Collapsed" Click="LoadVideosButton_Click"/> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml.cs b/VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml.cs deleted file mode 100644 index 8b15f96..0000000 --- a/VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml.cs +++ /dev/null @@ -1,185 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Services.Sources; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media.Imaging; - -namespace VDownload.Views.Home.Controls -{ - public sealed partial class SubscriptionsLoadControl : UserControl - { - #region CONSTRUCTORS - - public SubscriptionsLoadControl() - { - this.InitializeComponent(); - - InfoTextBlock.Text = ResourceLoader.GetForCurrentView().GetString("Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Loading"); - } - - #endregion - - - - #region PROPERTIES - - public CancellationToken CancellationToken { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - public async Task StartLoading() - { - List<IVideo> loadedVideos = new List<IVideo>(); - (Subscription Subscription, StorageFile SubscriptionFile)[] subscriptions = await SubscriptionsCollectionManagement.GetSubscriptionsAsync(); - foreach ((Subscription Subscription, StorageFile SubscriptionFile) subscription in subscriptions) - { - try - { - loadedVideos.AddRange(await subscription.Subscription.GetNewVideosAsync(CancellationToken)); - } - catch (OperationCanceledException) - { - return; - } - catch (MediaNotFoundException) - { - await ShowDialog("MediaNotFound", subscription.Subscription.Playlist.Name); - return; - } - catch (TwitchAccessTokenNotFoundException) - { - await ShowDialog("TwitchAccessTokenNotFound"); - return; - } - catch (TwitchAccessTokenNotValidException) - { - await ShowDialog("TwitchAccessTokenNotValid"); - return; - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - await ShowDialog("InternetNotAvailable"); - return; - } - else throw; - } - } - - ProgressRing.Visibility = Visibility.Collapsed; - InfoTextBlock.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Found")}: {loadedVideos.Count}"; - if (loadedVideos.Count > 0) - { - LoadVideosButton.Visibility = Visibility.Visible; - } - } - - #endregion - - - - #region EVENT HANDLERS - - private async void LoadVideosButton_Click(object sender, RoutedEventArgs e) - { - ProgressRing.Visibility = Visibility.Visible; - LoadVideosButton.IsEnabled = false; - - List<IVideo> loadedVideos = new List<IVideo>(); - (Subscription Subscription, StorageFile SubscriptionFile)[] subscriptions = await SubscriptionsCollectionManagement.GetSubscriptionsAsync(); - foreach ((Subscription Subscription, StorageFile SubscriptionFile) subscription in subscriptions) - { - try - { - loadedVideos.AddRange(await subscription.Subscription.GetNewVideosAndUpdateAsync(CancellationToken)); - await SubscriptionsCollectionManagement.UpdateSubscriptionFileAsync(subscription.Subscription, subscription.SubscriptionFile); - } - catch (OperationCanceledException) - { - return; - } - catch (MediaNotFoundException) - { - await ShowDialog("MediaNotFound", subscription.Subscription.Playlist.Name); - return; - } - catch (TwitchAccessTokenNotFoundException) - { - await ShowDialog("TwitchAccessTokenNotFound"); - return; - } - catch (TwitchAccessTokenNotValidException) - { - await ShowDialog("TwitchAccessTokenNotValid"); - return; - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - await ShowDialog("InternetNotAvailable"); - return; - } - else throw; - } - } - - LoadingSuccessed.Invoke(this, new SubscriptionLoadSuccessedEventArgs(loadedVideos.ToArray())); - - ProgressRing.Visibility = Visibility.Collapsed; - } - - #endregion - - - - #region PRIVATE METHODS - - private async Task ShowDialog(string localErrorKey, string playlistName = null) - { - StringBuilder content = new StringBuilder(ResourceLoader.GetForCurrentView("DialogResources").GetString($"Home_OptionsBar_SubscriptionsLoadControl_{localErrorKey}_Content")); - if (!string.IsNullOrEmpty(playlistName)) - { - content.Append($" {playlistName}"); - } - ContentDialog errorDialog = new ContentDialog - { - Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_OptionsBar_SubscriptionsLoadControl_Base_Title"), - Content = content.ToString(), - CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CloseButtonText"), - }; - await errorDialog.ShowAsync(); - ProgressRing.Visibility = Visibility.Collapsed; - InfoTextBlock.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Found")}: 0"; - } - - #endregion - - - - #region EVENTS - - public event EventHandler<SubscriptionLoadSuccessedEventArgs> LoadingSuccessed; - - #endregion - } -} diff --git a/VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml b/VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml deleted file mode 100644 index b69f9b5..0000000 --- a/VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml +++ /dev/null @@ -1,76 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.Controls.VideoAddingOptionsControl" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Views.Home.Controls" - 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:ex="using:Microsoft.Toolkit.Uwp.UI" - xmlns:cc="using:VDownload.Controls" - mc:Ignorable="d" - d:DesignHeight="300" - d:DesignWidth="400"> - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </UserControl.Resources> - - <StackPanel Spacing="30"> - <StackPanel> - <TextBlock x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_HeaderTextBlock" Margin="0,0,0,10" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_MediaTypeSettingControl" Margin="0,0,0,10" Icon="{ThemeResource MediaTypeIcon}"> - <cc:SettingControl.SettingContent> - <ComboBox x:Name="MediaTypeSettingControlComboBox" Width="150" SelectionChanged="MediaTypeSettingControlComboBox_SelectionChanged"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Name="QualitySettingControl" Margin="0,0,0,10" x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_QualitySettingControl" Icon="{ThemeResource QualityIcon}"> - <cc:SettingControl.SettingContent> - <ComboBox x:Name="QualitySettingControlComboBox" Width="150" SelectionChanged="QualitySettingControlComboBox_SelectionChanged"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_TrimSettingControl" Icon="{ThemeResource TrimIcon}"> - <cc:SettingControl.SettingContent> - <StackPanel Orientation="Horizontal" Spacing="5"> - <TextBox x:Name="TrimSettingControlStartTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" LostFocus="TrimSettingControlStartTextBox_LostFocus"/> - <TextBlock VerticalAlignment="Center" Text="-"/> - <TextBox x:Name="TrimSettingControlEndTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" LostFocus="TrimSettingControlEndTextBox_LostFocus"/> - </StackPanel> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileSettingControl" Icon="{ThemeResource FileIcon}"> - <cc:SettingControl.SettingContent> - <Grid ColumnSpacing="10"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="100"/> - </Grid.ColumnDefinitions> - <TextBox x:Name="FileSettingControlFilenameTextBox" Grid.Column="0" MaxWidth="300" HorizontalAlignment="Right" IsSpellCheckEnabled="False" LostFocus="FileSettingControlFilenameTextBox_LostFocus"/> - <ComboBox x:Name="FileSettingControlFileExtensionComboBox" Grid.Column="1" HorizontalAlignment="Stretch" SelectionChanged="FileSettingControlFileExtensionComboBox_SelectionChanged"/> - </Grid> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Name="FileLocationSettingControl" x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl" Icon="{ThemeResource LocationIcon}"> - <cc:SettingControl.SettingContent> - <Button x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl_BrowseButton" Click="FileSettingControlFileLocationBrowseButton_Click"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_ScheduleSettingControl" Icon="{ThemeResource ScheduleIcon}"> - <cc:SettingControl.SettingContent> - <muxc:NumberBox x:Name="ScheduleSettingControlNumberBox" MaxWidth="100" Value="0" Minimum="0" SpinButtonPlacementMode="Compact" LostFocus="ScheduleSettingControlNumberBox_LostFocus"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - </StackPanel> -</UserControl> diff --git a/VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml.cs b/VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml.cs deleted file mode 100644 index 70ba4f2..0000000 --- a/VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml.cs +++ /dev/null @@ -1,283 +0,0 @@ -using Microsoft.UI.Xaml.Controls; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using VDownload.Controls; -using VDownload.Core.Enums; -using VDownload.Core.Extensions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Structs; -using Windows.ApplicationModel.Resources; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.Storage; -using Windows.Storage.AccessCache; -using Windows.Storage.Pickers; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Views.Home.Controls -{ - public sealed partial class VideoAddingOptionsControl : UserControl - { - #region CONSTRUCTORS - - public VideoAddingOptionsControl(IVideo video) - { - this.InitializeComponent(); - - Video = video; - - foreach (string mediaType in Enum.GetNames(typeof(MediaType))) - { - MediaTypeSettingControlComboBox.Items.Add(ResourceLoader.GetForCurrentView().GetString($"Base_MediaType_{mediaType}Text")); - } - MediaTypeSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_media_type"); - - foreach (BaseStream stream in Video.BaseStreams) - { - QualitySettingControlComboBox.Items.Add(stream.ToString()); - } - QualitySettingControlComboBox.SelectedIndex = 0; - - TrimStart = TimeSpan.Zero; - TrimSettingControlStartTextBox.Text = TrimStart.ToStringOptTHMMBaseSS(Video.Duration); - - TrimEnd = Video.Duration; - TrimSettingControlEndTextBox.Text = TrimEnd.ToStringOptTHMMBaseSS(); - - string temporaryFilename = (string)Config.GetValue("default_filename"); - Dictionary<string, string> filenameStandardTemplates = new Dictionary<string, string>() - { - { "<title>", Video.Title }, - { "<author>", Video.Author }, - { "<views>", Video.Views.ToString() }, - { "<id>", Video.ID }, - }; - foreach (KeyValuePair<string, string> template in filenameStandardTemplates) - { - temporaryFilename = temporaryFilename.Replace(template.Key, template.Value); - } - Dictionary<Regex, IFormattable> filenameFormatTemplates = new Dictionary<Regex, IFormattable>() - { - { new Regex(@"<date_pub:(?<format>.*)>"), Video.Date }, - { new Regex(@"<date_now:(?<format>.*)>"), DateTime.Now }, - { new Regex(@"<duration:(?<format>.*)>"), Video.Duration }, - }; - foreach (KeyValuePair<Regex, IFormattable> template in filenameFormatTemplates) - { - foreach (Match templateMatch in template.Key.Matches(temporaryFilename)) - { - temporaryFilename = temporaryFilename.Replace(templateMatch.Value, template.Value.ToString(templateMatch.Groups["format"].Value, null)); - } - } - foreach (char c in Path.GetInvalidFileNameChars()) - { - temporaryFilename = temporaryFilename.Replace(c, ' '); - } - Filename = temporaryFilename; - FileSettingControlFilenameTextBox.Text = Filename; - - Schedule = 0; - } - - #endregion - - - - #region PROPERTIES - - private IVideo Video { get; set; } - - public MediaType MediaType { get; private set; } - public BaseStream Stream { get; private set; } - public TimeSpan TrimStart { get; private set; } - public TimeSpan TrimEnd { get; private set; } - public string Filename { get; private set; } - public MediaFileExtension FileExtension { get; private set; } - public StorageFolder FileLocation { get; private set; } - public double Schedule { get; private set; } - - #endregion - - - - #region PUBLIC METHODS - - public void ChangeLocation(StorageFolder location) - { - FileLocation = location; - FileLocationSettingControl.Description = FileLocation.Path ?? $@"{UserDataPaths.GetDefault().Downloads}\VDownload"; - } - - public void ChangeSchedule(double schedule) - { - Schedule = schedule; - ScheduleSettingControlNumberBox.Value = Schedule; - } - - #endregion - - - - #region PUBLIC METHODS - - public async Task Init() - { - if (!(bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("last_media_location")) - { - FileLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("last_media_location"); - FileLocationSettingControl.Description = FileLocation.Path; - } - else if ((bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_media_location")) - { - FileLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_media_location"); - FileLocationSettingControl.Description = FileLocation.Path; - } - else - { - FileLocation = null; - FileLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload"; - } - } - - #endregion - - - - #region EVENT HANDLERS - - private void MediaTypeSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - MediaType = (MediaType)((ComboBox)sender).SelectedIndex; - if (((ComboBox)sender).SelectedIndex == (int)MediaType.OnlyAudio) - { - QualitySettingControl.Visibility = Visibility.Collapsed; - QualitySettingControlComboBox.SelectedIndex = Video.BaseStreams.Count() - 1; - - FileSettingControlFileExtensionComboBox.Items.Clear(); - foreach (AudioFileExtension extension in Enum.GetValues(typeof(AudioFileExtension))) - { - FileSettingControlFileExtensionComboBox.Items.Add(extension); - } - FileSettingControlFileExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_audio_extension") - 3; - } - else - { - QualitySettingControl.Visibility = Visibility.Visible; - QualitySettingControlComboBox.SelectedIndex = 0; - - FileSettingControlFileExtensionComboBox.Items.Clear(); - foreach (VideoFileExtension extension in Enum.GetValues(typeof(VideoFileExtension))) - { - FileSettingControlFileExtensionComboBox.Items.Add(extension); - } - FileSettingControlFileExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_video_extension"); - } - } - - private void QualitySettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - Stream = Video.BaseStreams[((ComboBox)sender).SelectedIndex]; - } - - private void TrimSettingControlStartTextBox_LostFocus(object sender, RoutedEventArgs e) - { - if (!((TextBox)sender).Text.Contains('_')) - { - string[] segments = ((TextBox)sender).Text.Split(':').Reverse().ToArray(); - int.TryParse(segments.ElementAtOrDefault(0), out int seconds); - int.TryParse(segments.ElementAtOrDefault(1), out int minutes); - int.TryParse(segments.ElementAtOrDefault(2), out int hours); - - TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds); - - if (parsedTimeSpan < Video.Duration && parsedTimeSpan > TimeSpan.Zero) TrimStart = parsedTimeSpan; - else - { - TrimStart = TimeSpan.Zero; - ((TextBox)sender).Text = TrimStart.ToStringOptTHMMBaseSS(Video.Duration); - } - } - } - - private void TrimSettingControlEndTextBox_LostFocus(object sender, RoutedEventArgs e) - { - if (!((TextBox)sender).Text.Contains('_')) - { - string[] segments = ((TextBox)sender).Text.Split(':').Reverse().ToArray(); - int.TryParse(segments.ElementAtOrDefault(0), out int seconds); - int.TryParse(segments.ElementAtOrDefault(1), out int minutes); - int.TryParse(segments.ElementAtOrDefault(2), out int hours); - - TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds); - - if (parsedTimeSpan < Video.Duration && parsedTimeSpan > TimeSpan.Zero) TrimEnd = parsedTimeSpan; - else - { - TrimEnd = Video.Duration; - ((TextBox)sender).Text = TrimEnd.ToStringOptTHMMBaseSS(); - } - } - } - - private void FileSettingControlFilenameTextBox_LostFocus(object sender, RoutedEventArgs e) - { - foreach (char c in Path.GetInvalidFileNameChars()) - { - ((TextBox)sender).Text = ((TextBox)sender).Text.Replace(c, ' '); - } - Filename = ((TextBox)sender).Text; - } - - private void FileSettingControlFileExtensionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - FileExtension = (MediaFileExtension)((ComboBox)sender).SelectedIndex + (MediaType == MediaType.OnlyAudio ? 3 : 0); - } - - private async void FileSettingControlFileLocationBrowseButton_Click(object sender, RoutedEventArgs e) - { - FolderPicker picker = new FolderPicker - { - SuggestedStartLocation = PickerLocationId.Downloads - }; - picker.FileTypeFilter.Add("*"); - - StorageFolder selectedFolder = await picker.PickSingleFolderAsync(); - - if (selectedFolder != null) - { - try - { - await(await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync(); - StorageApplicationPermissions.FutureAccessList.AddOrReplace("last_media_location", selectedFolder); - FileLocation = selectedFolder; - FileLocationSettingControl.Description = FileLocation.Path; - } - catch (UnauthorizedAccessException) { } - } - } - - private void ScheduleSettingControlNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - if (((NumberBox)sender).Value == double.NaN) - { - ((NumberBox)sender).Value = 0; - } - Schedule = ((NumberBox)sender).Value; - } - - #endregion - } -} diff --git a/VDownload/Views/Home/Controls/VideoSearchControl.xaml b/VDownload/Views/Home/Controls/VideoSearchControl.xaml deleted file mode 100644 index 59028e1..0000000 --- a/VDownload/Views/Home/Controls/VideoSearchControl.xaml +++ /dev/null @@ -1,30 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.Controls.VideoSearchControl" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Controls" - 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" - mc:Ignorable="d" - d:DesignHeight="100" - d:DesignWidth="400"> - - - <Grid x:Name="Grid" Margin="10" ColumnSpacing="10"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBox x:Name="UrlTextBox" x:Uid="Home_OptionsBar_VideoSearchControl_UrlTextBox" Grid.Column="0" VerticalAlignment="Center"/> - <Button x:Name="SearchButton" x:Uid="Home_OptionsBar_VideoSearchControl_SearchButton" Grid.Column="1" Click="SearchButton_Click"/> - <Button x:Name="HelpButton" Grid.Column="2" Content="?" Click="HelpButton_Click"> - <Button.Resources> - <muxc:TeachingTip x:Name="InfoBox" x:Uid="Home_OptionsBar_VideoSearchControl_InfoBox" Target="{x:Bind Grid}"/> - </Button.Resources> - </Button> - <ContentPresenter x:Name="StatusControlPresenter" Margin="-10,0,0,0" Grid.Column="3"/> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/Controls/VideoSearchControl.xaml.cs b/VDownload/Views/Home/Controls/VideoSearchControl.xaml.cs deleted file mode 100644 index 3bd2db7..0000000 --- a/VDownload/Views/Home/Controls/VideoSearchControl.xaml.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using System; -using System.Diagnostics; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Services.Sources; -using Windows.ApplicationModel.Resources; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media.Imaging; - -namespace VDownload.Views.Home.Controls -{ - public sealed partial class VideoSearchControl : UserControl - { - #region CONSTANTS - - private static readonly ResourceDictionary IconRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") }; - - private static readonly Microsoft.UI.Xaml.Controls.ProgressRing StatusControlProgressRing = new Microsoft.UI.Xaml.Controls.ProgressRing { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), IsActive = true }; - private static readonly Image StatusControlErrorImage = new Image { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), Source = (SvgImageSource)IconRes["ErrorIcon"] }; - - #endregion - - - - #region CONSTRUCTORS - - public VideoSearchControl() - { - InitializeComponent(); - } - - #endregion - - - - #region PROPERTIES - - public CancellationToken CancellationToken { get; set; } - - #endregion - - - - #region EVENT HANDLERS - - private async void SearchButton_Click(object sender, RoutedEventArgs e) - { - async Task ShowDialog(string localErrorKey) - { - ContentDialog errorDialog = new ContentDialog - { - Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_OptionsBar_VideoSearchControl_Base_Title"), - Content = ResourceLoader.GetForCurrentView("DialogResources").GetString($"Home_OptionsBar_VideoSearchControl_{localErrorKey}_Content"), - CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CloseButtonText"), - }; - await errorDialog.ShowAsync(); - StatusControlPresenter.Content = StatusControlErrorImage; - } - - SearchButtonClicked.Invoke(this, EventArgs.Empty); - - InfoBox.IsOpen = false; - - StatusControlPresenter.Content = StatusControlProgressRing; - - IVideo video = Source.GetVideo(UrlTextBox.Text); - - if (video == null) - { - StatusControlPresenter.Content = StatusControlErrorImage; - } - else - { - try - { - await video.GetMetadataAsync(CancellationToken); - await video.GetStreamsAsync(CancellationToken); - } - catch (OperationCanceledException) - { - StatusControlPresenter.Content = null; - return; - } - catch (MediaNotFoundException) - { - StatusControlPresenter.Content = StatusControlErrorImage; - return; - } - catch (TwitchAccessTokenNotFoundException) - { - await ShowDialog("TwitchAccessTokenNotFound"); - return; - } - catch (TwitchAccessTokenNotValidException) - { - await ShowDialog("TwitchAccessTokenNotValid"); - return; - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - await ShowDialog("InternetNotAvailable"); - return; - } - else throw; - } - - StatusControlPresenter.Content = null; - - SearchingSuccessed?.Invoke(this, new VideoSearchSuccessedEventArgs(video)); - } - } - - private void HelpButton_Click(object sender, RoutedEventArgs e) - { - // Switch info box - InfoBox.IsOpen = !InfoBox.IsOpen; - } - - #endregion - - - - #region EVENT HANDLERS - - public event EventHandler<VideoSearchSuccessedEventArgs> SearchingSuccessed; - public event EventHandler SearchButtonClicked; - - #endregion - } -} diff --git a/VDownload/Views/Home/MainPage.xaml b/VDownload/Views/Home/MainPage.xaml deleted file mode 100644 index 71a7d29..0000000 --- a/VDownload/Views/Home/MainPage.xaml +++ /dev/null @@ -1,109 +0,0 @@ -<Page - x:Class="VDownload.Views.Home.MainPage" - 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:cc="using:VDownload.Controls" - mc:Ignorable="d" - Background="Transparent"> - - - <Page.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Images.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </Page.Resources> - - - <Grid> - <VisualStateManager.VisualStateGroups> - <VisualStateGroup> - <VisualState x:Name="Compact"> - <VisualState.StateTriggers> - <AdaptiveTrigger MinWindowWidth="0"/> - </VisualState.StateTriggers> - <VisualState.Setters> - <Setter Target="OptionsBarButtonsStackPanel.(Grid.ColumnSpan)" Value="4"/> - <Setter Target="OptionsBarButtonsStackPanel.(Grid.Column)" Value="0"/> - <Setter Target="SearchControlPresenter.(Grid.Row)" Value="0"/> - <Setter Target="SearchControlPresenter.(Grid.ColumnSpan)" Value="3"/> - <Setter Target="OptionsBarSearchingStatusControl.(Grid.Row)" Value="0"/> - <Setter Target="OptionsBarSearchingStatusControl.(Grid.Column)" Value="3"/> - </VisualState.Setters> - </VisualState> - <VisualState x:Name="Normal"> - <VisualState.StateTriggers> - <AdaptiveTrigger x:Name="NormalStateAdaptiveTrigger" MinWindowWidth="800"/> - </VisualState.StateTriggers> - <VisualState.Setters> - <Setter Target="OptionsBarButtonsStackPanel.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="OptionsBarButtonsStackPanel.(Grid.Column)" Value="3"/> - <Setter Target="SearchControlPresenter.(Grid.Row)" Value="1"/> - <Setter Target="SearchControlPresenter.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="SearchingStatusControlPresenter.(Grid.Row)" Value="1"/> - <Setter Target="SearchingStatusControlPresenter.(Grid.Column)" Value="1"/> - </VisualState.Setters> - </VisualState> - </VisualStateGroup> - </VisualStateManager.VisualStateGroups> - - <Grid.RowDefinitions> - <RowDefinition x:Name="DownloadTaskControlsStackPanelRow" Height="1*"/> - <RowDefinition x:Name="OptionsBarAndAddingPanelRow" Height="Auto"/> - </Grid.RowDefinitions> - - <ScrollViewer Margin="0,0,0,10" CornerRadius="{ThemeResource ControlCornerRadius}"> - <cc:PlaceholderableStackPanel x:Name="DownloadTaskControlsStackPanel" Spacing="10"> - <cc:PlaceholderableStackPanel.Placeholder> - <Viewbox Width="250"> - <Grid> - <Grid.RowDefinitions> - <RowDefinition/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> - <Image Grid.Row="0" Source="{StaticResource HomeTasksListPlaceholderImage}" Width="120"/> - <TextBlock Grid.Row="1" x:Uid="Home_DownloadTaskControlsStackPanel_Placeholder_TextBlock" Foreground="{StaticResource HomeTasksListPlaceholderTextColor}"/> - </Grid> - </Viewbox> - </cc:PlaceholderableStackPanel.Placeholder> - </cc:PlaceholderableStackPanel> - </ScrollViewer> - - <Grid Grid.Row="1" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource HomeBackgroundColor}"> - <Grid.RowDefinitions> - <RowDefinition/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> - <ContentPresenter x:Name="AddingPanelPresenter" Grid.Row="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/> - <Grid x:Name="OptionsBar" Grid.Row="1" Background="{ThemeResource HomeOptionBarBackgroundColor}"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="50"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <ContentPresenter x:Name="SearchControlPresenter" Grid.Row="1" Grid.Column="0"/> - <ContentPresenter x:Name="SearchingStatusControlPresenter" Grid.Row="1" Grid.Column="1"/> - <StackPanel x:Name="OptionsBarButtonsStackPanel" Grid.Row="1" Grid.Column="3" Margin="3,0,3,0" HorizontalAlignment="Center" Orientation="Horizontal"> - <AppBarToggleButton x:Name="LoadSubscripionsButton" x:Uid="Home_OptionsBar_LoadSubscripionsButton" Icon="Favorite" Width="120" Checked="LoadSubscripionsButton_Checked" Unchecked="AddingButtons_Unchecked"/> - <AppBarSeparator VerticalAlignment="Center" Height="50"/> - <AppBarToggleButton x:Name="PlaylistSearchButton" x:Uid="Home_OptionsBar_PlaylistSearchButton" Icon="List" Width="85" Checked="PlaylistSearchButton_Checked" Unchecked="AddingButtons_Unchecked"/> - <AppBarToggleButton x:Name="VideoSearchButton" x:Uid="Home_OptionsBar_VideoSearchButton" Icon="Video" Width="75" Checked="VideoSearchButton_Checked" Unchecked="AddingButtons_Unchecked"/> - <AppBarSeparator VerticalAlignment="Center" Height="50"/> - <AppBarButton x:Name="DownloadAllButton" x:Uid="Home_OptionsBar_DownloadAllButton" Icon="Download" Width="90" Click="DownloadAllButton_Click"/> - </StackPanel> - </Grid> - </Grid> - </Grid> -</Page> diff --git a/VDownload/Views/Home/MainPage.xaml.cs b/VDownload/Views/Home/MainPage.xaml.cs deleted file mode 100644 index 0605ebc..0000000 --- a/VDownload/Views/Home/MainPage.xaml.cs +++ /dev/null @@ -1,207 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Structs; -using VDownload.Views.Home.Controls; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media.Imaging; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Views.Home -{ - public sealed partial class MainPage : Page - { - #region CONSTRUCTORS - - public MainPage() - { - this.InitializeComponent(); - - SearchingCancellationToken = new CancellationTokenSource(); - } - - #endregion - - - - #region PROPERTIES - - private CancellationTokenSource SearchingCancellationToken { get; set; } - - #endregion - - - - #region EVENT HANDLERS - - protected override void OnNavigatedTo(NavigationEventArgs e) - { - foreach (DownloadTask downloadTask in DownloadTasksCollectionManagement.DownloadTasksCollection.Values) - { - DownloadTaskControl downloadTaskControl = new DownloadTaskControl(downloadTask); - downloadTaskControl.RemovingRequested += (s, a) => - { - DownloadTaskControlsStackPanel.Remove(downloadTaskControl); - DownloadTasksCollectionManagement.Remove(downloadTask.Id); - }; - DownloadTaskControlsStackPanel.Add(downloadTaskControl); - } - } - - private void VideoSearchButton_Checked(object sender, RoutedEventArgs e) - { - PlaylistSearchButton.IsChecked = false; - LoadSubscripionsButton.IsChecked = false; - - VideoSearchControl videoSearchControl = new VideoSearchControl(); - videoSearchControl.SearchingSuccessed += (s, a) => - { - OptionsBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star); - DownloadTaskControlsStackPanelRow.Height = new GridLength(0); - - VideoAddingPanel videoAddingPanel = new VideoAddingPanel(a.Video); - videoAddingPanel.TasksAddingRequested += DownloadTasksAddingRequest; - AddingPanelPresenter.Content = videoAddingPanel; - }; - videoSearchControl.SearchButtonClicked += (s, a) => - { - SearchingCancellationToken.Cancel(); - SearchingCancellationToken = new CancellationTokenSource(); - videoSearchControl.CancellationToken = SearchingCancellationToken.Token; - }; - SearchControlPresenter.Content = videoSearchControl; - } - - private void PlaylistSearchButton_Checked(object sender, RoutedEventArgs e) - { - VideoSearchButton.IsChecked = false; - LoadSubscripionsButton.IsChecked = false; - - PlaylistSearchControl playlistSearchControl = new PlaylistSearchControl(); - playlistSearchControl.SearchingSuccessed += (s, a) => - { - OptionsBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star); - DownloadTaskControlsStackPanelRow.Height = new GridLength(0); - - PlaylistAddingPanel playlistAddingPanel = new PlaylistAddingPanel(a.Playlist); - playlistAddingPanel.TasksAddingRequested += DownloadTasksAddingRequest; - AddingPanelPresenter.Content = playlistAddingPanel; - }; - playlistSearchControl.SearchButtonClicked += (s, a) => - { - SearchingCancellationToken.Cancel(); - SearchingCancellationToken = new CancellationTokenSource(); - playlistSearchControl.CancellationToken = SearchingCancellationToken.Token; - }; - SearchControlPresenter.Content = playlistSearchControl; - } - - private async void LoadSubscripionsButton_Checked(object sender, RoutedEventArgs e) - { - VideoSearchButton.IsChecked = false; - PlaylistSearchButton.IsChecked = false; - - SubscriptionsLoadControl subscriptionsLoadControl = new SubscriptionsLoadControl(); - SearchingCancellationToken.Cancel(); - SearchingCancellationToken = new CancellationTokenSource(); - subscriptionsLoadControl.CancellationToken = SearchingCancellationToken.Token; - subscriptionsLoadControl.LoadingSuccessed += (s, a) => - { - OptionsBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star); - DownloadTaskControlsStackPanelRow.Height = new GridLength(0); - - SubscriptionsAddingPanel subscriptionsAddingPanel = new SubscriptionsAddingPanel(a.Videos); - subscriptionsAddingPanel.TasksAddingRequested += DownloadTasksAddingRequest; - AddingPanelPresenter.Content = subscriptionsAddingPanel; - }; - SearchControlPresenter.Content = subscriptionsLoadControl; - await subscriptionsLoadControl.StartLoading(); - } - - private void AddingButtons_Unchecked(object sender, RoutedEventArgs e) - { - SearchingCancellationToken.Cancel(); - SearchingCancellationToken = new CancellationTokenSource(); - - OptionsBarAndAddingPanelRow.Height = GridLength.Auto; - DownloadTaskControlsStackPanelRow.Height = new GridLength(1, GridUnitType.Star); - - AddingPanelPresenter.Content = null; - SearchControlPresenter.Content = null; - SearchingStatusControlPresenter.Content = null; - } - - private void DownloadTasksAddingRequest(object sender, DownloadTasksAddingRequestedEventArgs e) - { - switch (e.RequestSource) - { - case DownloadTasksAddingRequestSource.Video: VideoSearchButton.IsChecked = false; break; - case DownloadTasksAddingRequestSource.Playlist: PlaylistSearchButton.IsChecked = false; break; - case DownloadTasksAddingRequestSource.Subscriptions: LoadSubscripionsButton.IsChecked = false; break; - } - - foreach (DownloadTask downloadTask in e.DownloadTasks) - { - DownloadTaskControl downloadTaskControl = new DownloadTaskControl(downloadTask); - DownloadTasksCollectionManagement.Add(downloadTask, downloadTask.Id); - downloadTaskControl.RemovingRequested += (s, a) => - { - DownloadTaskControlsStackPanel.Remove(downloadTaskControl); - DownloadTasksCollectionManagement.Remove(downloadTask.Id); - }; - - DownloadTaskControlsStackPanel.Add(downloadTaskControl); - } - } - - private async void DownloadAllButton_Click(object sender, RoutedEventArgs e) - { - DownloadTask[] idleDownloadTasks = DownloadTasksCollectionManagement.DownloadTasksCollection.Values.Where((task) => task.Status == DownloadTaskStatus.Idle).ToArray(); - if (idleDownloadTasks.Count() > 0) - { - bool delay = (bool)Config.GetValue("delay_task_when_queued_task_starts_on_metered_network"); - if (NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection) - { - ContentDialogResult dialogResult = await new ContentDialog - { - Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_Title"), - Content = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_Content"), - PrimaryButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_StartWithDelayButtonText"), - SecondaryButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_StartWithoutDelayButtonText"), - CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CancelButtonText"), - }.ShowAsync(); - switch (dialogResult) - { - case ContentDialogResult.Primary: delay = true; break; - case ContentDialogResult.Secondary: delay = false; break; - case ContentDialogResult.None: return; - } - } - - foreach (DownloadTask downloadTask in idleDownloadTasks) - { - await Task.Delay(1); - -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - downloadTask.Run(delay); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - } - - #endregion - } -} diff --git a/VDownload/Views/Home/PlaylistAddingPanel.xaml b/VDownload/Views/Home/PlaylistAddingPanel.xaml deleted file mode 100644 index 96ef08b..0000000 --- a/VDownload/Views/Home/PlaylistAddingPanel.xaml +++ /dev/null @@ -1,40 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.PlaylistAddingPanel" - 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:cc="using:VDownload.Controls" - mc:Ignorable="d"> - - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </UserControl.Resources> - - - <Grid x:Name="Base" Padding="10" RowSpacing="10"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition/> - </Grid.RowDefinitions> - - <Grid VerticalAlignment="Stretch"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBlock Grid.Column="0" Margin="12" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold" Text="{x:Bind Name}"/> - <AppBarButton Grid.Column="1" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="SourceButton_Click"/> - <AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Background="{ThemeResource SystemAccentColor}" Foreground="{ThemeResource SystemAltHighColor}" Click="AddButton_Click"/> - </Grid> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/PlaylistAddingPanel.xaml.cs b/VDownload/Views/Home/PlaylistAddingPanel.xaml.cs deleted file mode 100644 index 1875963..0000000 --- a/VDownload/Views/Home/PlaylistAddingPanel.xaml.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Microsoft.Toolkit.Uwp.UI; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using VDownload.Converters; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Structs; -using VDownload.Views.Home.Controls; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using Windows.Storage.AccessCache; -using Windows.Storage.Pickers; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace VDownload.Views.Home -{ - public sealed partial class PlaylistAddingPanel : UserControl - { - #region CONSTRUCTORS - - public PlaylistAddingPanel(IPlaylist playlist) - { - this.InitializeComponent(); - - Playlist = playlist; - - SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Playlist.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false }; - Name = Playlist.Name; - - SerialVideoAddingControl = new SerialVideoAddingControl(playlist.Videos); - Grid.SetRow(SerialVideoAddingControl, 1); - Base.Children.Add(SerialVideoAddingControl); - } - - #endregion - - - - #region PROPERTIES - - private IPlaylist Playlist { get; set; } - - private IconElement SourceImage { get; set; } - private string Name { get; set; } - - private SerialVideoAddingControl SerialVideoAddingControl { get; set;} - - #endregion - - - - #region EVENT HANDLERS - - private async void SourceButton_Click(object sender, RoutedEventArgs e) - { - await Windows.System.Launcher.LaunchUriAsync(Playlist.Url); - } - - private void AddButton_Click(object sender, RoutedEventArgs e) - { - List<DownloadTask> tasksList = new List<DownloadTask>(); - foreach (SerialVideoAddingVideoControl videoControl in SerialVideoAddingControl.VideosStackPanel.Children) - { - TrimData trim = new TrimData - { - Start = videoControl.OptionsControl.TrimStart, - End = videoControl.OptionsControl.TrimEnd, - }; - OutputFile file; - if (videoControl.OptionsControl.FileLocation is null) - { - file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension); - } - else - { - file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension, videoControl.OptionsControl.FileLocation); - } - string id = DownloadTasksCollectionManagement.GenerateID(); - tasksList.Add(new DownloadTask(id, videoControl.Video, videoControl.OptionsControl.MediaType, videoControl.OptionsControl.Stream, trim, file, videoControl.OptionsControl.Schedule)); - } - - TasksAddingRequested?.Invoke(this, new DownloadTasksAddingRequestedEventArgs(tasksList.ToArray(), DownloadTasksAddingRequestSource.Playlist)); - } - - #endregion - - - - #region EVENTS - - public event EventHandler<DownloadTasksAddingRequestedEventArgs> TasksAddingRequested; - - #endregion - } -} diff --git a/VDownload/Views/Home/SubscriptionsAddingPanel.xaml b/VDownload/Views/Home/SubscriptionsAddingPanel.xaml deleted file mode 100644 index c9ffcef..0000000 --- a/VDownload/Views/Home/SubscriptionsAddingPanel.xaml +++ /dev/null @@ -1,40 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.SubscriptionsAddingPanel" - 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:cc="using:VDownload.Controls" - mc:Ignorable="d"> - - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </UserControl.Resources> - - - <Grid x:Name="Base" Padding="10" RowSpacing="10"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition/> - </Grid.RowDefinitions> - - <Grid VerticalAlignment="Stretch"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBlock Grid.Column="0" x:Uid="Home_Adding_Subscriptions_HeaderTextBlock" Margin="12" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold"/> - <AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Background="{ThemeResource SystemAccentColor}" Foreground="{ThemeResource SystemAltHighColor}" Click="AddButton_Click"/> - </Grid> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/SubscriptionsAddingPanel.xaml.cs b/VDownload/Views/Home/SubscriptionsAddingPanel.xaml.cs deleted file mode 100644 index c2be98b..0000000 --- a/VDownload/Views/Home/SubscriptionsAddingPanel.xaml.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Microsoft.Toolkit.Uwp.UI; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using VDownload.Converters; -using VDownload.Core.Enums; -using VDownload.Core.EventArgs; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Structs; -using VDownload.Views.Home.Controls; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using Windows.Storage.AccessCache; -using Windows.Storage.Pickers; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace VDownload.Views.Home -{ - public sealed partial class SubscriptionsAddingPanel : UserControl - { - #region CONSTRUCTORS - - public SubscriptionsAddingPanel(IVideo[] videos) - { - this.InitializeComponent(); - - Videos = videos; - - SerialVideoAddingControl = new SerialVideoAddingControl(Videos); - Grid.SetRow(SerialVideoAddingControl, 1); - Base.Children.Add(SerialVideoAddingControl); - } - - #endregion - - - - #region PROPERTIES - - private IVideo[] Videos { get; set; } - - private SerialVideoAddingControl SerialVideoAddingControl { get; set; } - - #endregion - - - - #region EVENT HANDLERS VOIDS - - private void AddButton_Click(object sender, RoutedEventArgs e) - { - List<DownloadTask> tasksList = new List<DownloadTask>(); - foreach (SerialVideoAddingVideoControl videoControl in SerialVideoAddingControl.VideosStackPanel.Children) - { - TrimData trim = new TrimData - { - Start = videoControl.OptionsControl.TrimStart, - End = videoControl.OptionsControl.TrimEnd, - }; - OutputFile file; - if (videoControl.OptionsControl.FileLocation is null) - { - file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension); - } - else - { - file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension, videoControl.OptionsControl.FileLocation); - } - string id = DownloadTasksCollectionManagement.GenerateID(); - tasksList.Add(new DownloadTask(id, videoControl.Video, videoControl.OptionsControl.MediaType, videoControl.OptionsControl.Stream, trim, file, videoControl.OptionsControl.Schedule)); - } - - TasksAddingRequested?.Invoke(this, new DownloadTasksAddingRequestedEventArgs(tasksList.ToArray(), DownloadTasksAddingRequestSource.Subscriptions)); - } - - #endregion - - - - #region EVENT HANDLERS - - public event EventHandler<DownloadTasksAddingRequestedEventArgs> TasksAddingRequested; - - #endregion - } -} diff --git a/VDownload/Views/Home/VideoAddingPanel.xaml b/VDownload/Views/Home/VideoAddingPanel.xaml deleted file mode 100644 index 85d5a6e..0000000 --- a/VDownload/Views/Home/VideoAddingPanel.xaml +++ /dev/null @@ -1,204 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Home.VideoAddingPanel" - 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:ex="using:Microsoft.Toolkit.Uwp.UI" - xmlns:cc="using:VDownload.Controls" - xmlns:muxc="using:Microsoft.UI.Xaml.Controls" - Loading="VideoAddingPanel_Loading" - mc:Ignorable="d"> - - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - <ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/> - </ResourceDictionary.MergedDictionaries> - <x:String x:Key="MetadataIconSize">16</x:String> - <x:String x:Key="MetadataTextSize">12</x:String> - </ResourceDictionary> - </UserControl.Resources> - - - <Grid Padding="10" RowSpacing="40" VerticalAlignment="Stretch"> - <VisualStateManager.VisualStateGroups> - <VisualStateGroup> - <VisualState> - <VisualState.StateTriggers> - <AdaptiveTrigger MinWindowWidth="0"/> - </VisualState.StateTriggers> - <VisualState.Setters> - <Setter Target="Thumbnail.(Grid.Column)" Value="0"/> - <Setter Target="Thumbnail.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="Thumbnail.(Grid.Row)" Value="0"/> - <Setter Target="Thumbnail.(Grid.RowSpan)" Value="1"/> - <Setter Target="Thumbnail.Width" Value="NaN"/> - <Setter Target="TitleAndMetadataGrid.(Grid.Column)" Value="0"/> - <Setter Target="TitleAndMetadataGrid.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="TitleAndMetadataGrid.(Grid.Row)" Value="1"/> - <Setter Target="TitleAndMetadataGrid.(Grid.RowSpan)" Value="1"/> - <Setter Target="AuthorIcon.(Grid.Row)" Value="0"/> - <Setter Target="AuthorIcon.(Grid.RowSpan)" Value="2"/> - <Setter Target="AuthorIcon.(Grid.Column)" Value="0"/> - <Setter Target="AuthorIcon.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="AuthorText.(Grid.Row)" Value="2"/> - <Setter Target="AuthorText.(Grid.RowSpan)" Value="2"/> - <Setter Target="AuthorText.(Grid.Column)" Value="0"/> - <Setter Target="AuthorText.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="AuthorText.HorizontalAlignment" Value="Center"/> - <Setter Target="ViewsIcon.(Grid.Row)" Value="0"/> - <Setter Target="ViewsIcon.(Grid.RowSpan)" Value="2"/> - <Setter Target="ViewsIcon.(Grid.Column)" Value="1"/> - <Setter Target="ViewsIcon.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="ViewsText.(Grid.Row)" Value="2"/> - <Setter Target="ViewsText.(Grid.RowSpan)" Value="2"/> - <Setter Target="ViewsText.(Grid.Column)" Value="1"/> - <Setter Target="ViewsText.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="ViewsText.HorizontalAlignment" Value="Center"/> - <Setter Target="DateIcon.(Grid.Row)" Value="0"/> - <Setter Target="DateIcon.(Grid.RowSpan)" Value="2"/> - <Setter Target="DateIcon.(Grid.Column)" Value="2"/> - <Setter Target="DateIcon.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="DateText.(Grid.Row)" Value="2"/> - <Setter Target="DateText.(Grid.RowSpan)" Value="2"/> - <Setter Target="DateText.(Grid.Column)" Value="2"/> - <Setter Target="DateText.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="DateText.HorizontalAlignment" Value="Center"/> - <Setter Target="DurationIcon.(Grid.Row)" Value="0"/> - <Setter Target="DurationIcon.(Grid.RowSpan)" Value="2"/> - <Setter Target="DurationIcon.(Grid.Column)" Value="3"/> - <Setter Target="DurationIcon.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="DurationText.(Grid.Row)" Value="2"/> - <Setter Target="DurationText.(Grid.RowSpan)" Value="2"/> - <Setter Target="DurationText.(Grid.Column)" Value="3"/> - <Setter Target="DurationText.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="DurationText.HorizontalAlignment" Value="Center"/> - <Setter Target="MetadataR1.Height" Value="Auto"/> - <Setter Target="MetadataR2.Height" Value="Auto"/> - <Setter Target="MetadataC1.Width" Value="1*"/> - <Setter Target="MetadataC2.Width" Value="1*"/> - </VisualState.Setters> - </VisualState> - <VisualState> - <VisualState.StateTriggers> - <AdaptiveTrigger MinWindowWidth="600"/> - </VisualState.StateTriggers> - <VisualState.Setters> - <Setter Target="Thumbnail.(Grid.Column)" Value="0"/> - <Setter Target="Thumbnail.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="Thumbnail.(Grid.Row)" Value="0"/> - <Setter Target="Thumbnail.(Grid.RowSpan)" Value="2"/> - <Setter Target="Thumbnail.Width" Value="250"/> - <Setter Target="TitleAndMetadataGrid.(Grid.Column)" Value="1"/> - <Setter Target="TitleAndMetadataGrid.(Grid.ColumnSpan)" Value="1"/> - <Setter Target="TitleAndMetadataGrid.(Grid.Row)" Value="0"/> - <Setter Target="TitleAndMetadataGrid.(Grid.RowSpan)" Value="2"/> - <Setter Target="AuthorIcon.(Grid.Row)" Value="0"/> - <Setter Target="AuthorIcon.(Grid.RowSpan)" Value="1"/> - <Setter Target="AuthorIcon.(Grid.Column)" Value="0"/> - <Setter Target="AuthorIcon.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="AuthorText.(Grid.Row)" Value="0"/> - <Setter Target="AuthorText.(Grid.RowSpan)" Value="1"/> - <Setter Target="AuthorText.(Grid.Column)" Value="2"/> - <Setter Target="AuthorText.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="AuthorText.HorizontalAlignment" Value="Left"/> - <Setter Target="ViewsIcon.(Grid.Row)" Value="1"/> - <Setter Target="ViewsIcon.(Grid.RowSpan)" Value="1"/> - <Setter Target="ViewsIcon.(Grid.Column)" Value="0"/> - <Setter Target="ViewsIcon.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="ViewsText.(Grid.Row)" Value="1"/> - <Setter Target="ViewsText.(Grid.RowSpan)" Value="1"/> - <Setter Target="ViewsText.(Grid.Column)" Value="2"/> - <Setter Target="ViewsText.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="ViewsText.HorizontalAlignment" Value="Left"/> - <Setter Target="DateIcon.(Grid.Row)" Value="2"/> - <Setter Target="DateIcon.(Grid.RowSpan)" Value="1"/> - <Setter Target="DateIcon.(Grid.Column)" Value="0"/> - <Setter Target="DateIcon.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="DateText.(Grid.Row)" Value="2"/> - <Setter Target="DateText.(Grid.RowSpan)" Value="1"/> - <Setter Target="DateText.(Grid.Column)" Value="2"/> - <Setter Target="DateText.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="DateText.HorizontalAlignment" Value="Left"/> - <Setter Target="DurationIcon.(Grid.Row)" Value="3"/> - <Setter Target="DurationIcon.(Grid.RowSpan)" Value="1"/> - <Setter Target="DurationIcon.(Grid.Column)" Value="0"/> - <Setter Target="DurationIcon.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="DurationText.(Grid.Row)" Value="3"/> - <Setter Target="DurationText.(Grid.RowSpan)" Value="1"/> - <Setter Target="DurationText.(Grid.Column)" Value="2"/> - <Setter Target="DurationText.(Grid.ColumnSpan)" Value="2"/> - <Setter Target="DurationText.HorizontalAlignment" Value="Left"/> - <Setter Target="MetadataR1.Height" Value="1*"/> - <Setter Target="MetadataR2.Height" Value="1*"/> - <Setter Target="MetadataC1.Width" Value="Auto"/> - <Setter Target="MetadataC2.Width" Value="Auto"/> - </VisualState.Setters> - </VisualState> - </VisualStateGroup> - </VisualStateManager.VisualStateGroups> - - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition/> - </Grid.RowDefinitions> - - <Grid Grid.Row="0" ColumnSpacing="10"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <Image x:Name="Thumbnail" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="2" Source="{x:Bind ThumbnailImage}"/> - <Grid x:Name="TitleAndMetadataGrid" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="2"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition/> - </Grid.RowDefinitions> - <Grid Grid.Row="0" VerticalAlignment="Center"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBlock Grid.Column="0" FontSize="18" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind Title}"/> - <AppBarButton Grid.Column="1" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="SourceButton_Click"/> - <AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Background="{ThemeResource SystemAccentColor}" Foreground="{ThemeResource SystemAltHighColor}" Click="AddButton_Click"/> - </Grid> - <Grid Grid.Row="1" ColumnSpacing="10" RowSpacing="5"> - <Grid.RowDefinitions> - <RowDefinition x:Name="MetadataR1" Height="1*"/> - <RowDefinition x:Name="MetadataR2" Height="1*"/> - <RowDefinition/> - <RowDefinition/> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition x:Name="MetadataC1" Width="Auto"/> - <ColumnDefinition x:Name="MetadataC2" Width="Auto"/> - <ColumnDefinition/> - <ColumnDefinition/> - </Grid.ColumnDefinitions> - <Image x:Name="AuthorIcon" Grid.Row="0" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource AuthorIcon}"/> - <TextBlock x:Name="AuthorText" Grid.Row="0" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Author}"/> - <Image x:Name="ViewsIcon" Grid.Row="1" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource ViewsIcon}"/> - <TextBlock x:Name="ViewsText" Grid.Row="1" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Views}"/> - <Image x:Name="DateIcon" Grid.Row="2" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DateIcon}"/> - <TextBlock x:Name="DateText" Grid.Row="2" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Date}"/> - <Image x:Name="DurationIcon" Grid.Row="3" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DurationIcon}"/> - <TextBlock x:Name="DurationText" Grid.Row="3" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Duration}"/> - </Grid> - </Grid> - </Grid> - - <ScrollViewer Grid.Row="1"> - <ContentPresenter x:Name="OptionsControlPresenter" HorizontalContentAlignment="Stretch"/> - </ScrollViewer> - </Grid> -</UserControl> diff --git a/VDownload/Views/Home/VideoAddingPanel.xaml.cs b/VDownload/Views/Home/VideoAddingPanel.xaml.cs deleted file mode 100644 index c52a351..0000000 --- a/VDownload/Views/Home/VideoAddingPanel.xaml.cs +++ /dev/null @@ -1,122 +0,0 @@ -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.Extensions; -using VDownload.Core.EventArgs; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Structs; -using VDownload.Views.Home.Controls; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using Windows.Storage.AccessCache; -using Windows.Storage.Pickers; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Media.Imaging; - -namespace VDownload.Views.Home -{ - public sealed partial class VideoAddingPanel : UserControl - { - #region CONSTANTS - - private readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") }; - - #endregion - - - - #region CONSTRUCTORS - - public VideoAddingPanel(IVideo video) - { - this.InitializeComponent(); - - Video = video; - - ThumbnailImage = Video.Thumbnail != null ? new BitmapImage { UriSource = Video.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"]; - SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Video.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false }; - Title = Video.Title; - Author = Video.Author; - Views = Video.Views.ToString(); - Date = Video.Date.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern); - Duration = Video.Duration.ToStringOptTHBaseMMSS(); - - OptionsControl = new VideoAddingOptionsControl(Video); - OptionsControlPresenter.Content = OptionsControl; - } - - #endregion - - - - #region PROPERTIES - - private IVideo Video { get; set; } - - // VIDEO DATA - private ImageSource ThumbnailImage { get; set; } - private IconElement SourceImage { get; set; } - private string Title { get; set; } - private string Author { get; set; } - private string Views { get; set; } - private string Date { get; set; } - private string Duration { get; set; } - - private VideoAddingOptionsControl OptionsControl { get; set; } - - #endregion - - - - #region EVENT HANDLERS - - private async void VideoAddingPanel_Loading(FrameworkElement sender, object args) - { - await OptionsControl.Init(); - } - - private async void SourceButton_Click(object sender, RoutedEventArgs e) - { - await Windows.System.Launcher.LaunchUriAsync(Video.Url); - } - - private void AddButton_Click(object sender, RoutedEventArgs e) - { - TrimData trim = new TrimData - { - Start = OptionsControl.TrimStart, - End = OptionsControl.TrimEnd, - }; - OutputFile file; - if (OptionsControl.FileLocation is null) - { - file = new OutputFile(OptionsControl.Filename, OptionsControl.FileExtension); - } - else - { - file = new OutputFile(OptionsControl.Filename, OptionsControl.FileExtension, OptionsControl.FileLocation); - } - string id = DownloadTasksCollectionManagement.GenerateID(); - DownloadTask downloadTask = new DownloadTask(id, Video, OptionsControl.MediaType, OptionsControl.Stream, trim, file, OptionsControl.Schedule); - TasksAddingRequested?.Invoke(this, new DownloadTasksAddingRequestedEventArgs(new DownloadTask[] { downloadTask }, DownloadTasksAddingRequestSource.Video)); - } - - #endregion - - - - #region EVENTS - - public event EventHandler<DownloadTasksAddingRequestedEventArgs> TasksAddingRequested; - - #endregion - } -} diff --git a/VDownload/Views/MainPage.xaml b/VDownload/Views/MainPage.xaml deleted file mode 100644 index 70e0280..0000000 --- a/VDownload/Views/MainPage.xaml +++ /dev/null @@ -1,68 +0,0 @@ -<Page - x:Class="VDownload.Views.MainPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - 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" - mc:Ignorable="d" - muxc:BackdropMaterial.ApplyToRootOrPageBackground="True"> - - - <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"/> - </ResourceDictionary> - </Page.Resources> - - - <Grid> - <Border x:Name="AppTitleBar" - IsHitTestVisible="True" - VerticalAlignment="Top" - Background="Transparent" - Height="40" - Canvas.ZIndex="1" - Margin="55,4,0,0"> - <StackPanel Orientation="Horizontal"> - <Image x:Name="AppFontIcon" - HorizontalAlignment="Left" - VerticalAlignment="Center" - Source="ms-appx:///Assets/Logo/Logo.png" - Width="16" - Height="16"/> - <TextBlock x:Name="AppTitle" - Text="VDownload" - VerticalAlignment="Center" - Margin="12,0,0,0" - Style="{StaticResource CaptionTextBlockStyle}" /> - </StackPanel> - </Border> - - <muxc:NavigationView x:Name="NavigationPanel" Background="Transparent" IsTitleBarAutoPaddingEnabled="False" IsBackButtonVisible="Collapsed" PaneDisplayMode="LeftCompact" Canvas.ZIndex="0" ItemInvoked="NavigationPanel_ItemInvoked"> - <muxc:NavigationView.MenuItems> - <muxc:NavigationViewItem x:Uid="Main_NavigationPanel_Home" Tag="home" Icon="Video"/> - <muxc:NavigationViewItemSeparator/> - <muxc:NavigationViewItem x:Uid="Main_NavigationPanel_Subscriptions" Tag="subscriptions" Icon="Favorite"/> - </muxc:NavigationView.MenuItems> - <muxc:NavigationView.FooterMenuItems> - <muxc:NavigationViewItem x:Uid="Main_NavigationPanel_About" Tag="about"> - <muxc:NavigationViewItem.Icon> - <PathIcon Data="M3331.25,1.389c-1833.78,-0 -3325.69,1492.85 -3325.69,3327.78c-0,1834.92 1491.92,3327.78 3325.69,3327.78c1833.78,0 3325.69,-1492.85 3325.69,-3327.78c0,-1834.92 -1491.92,-3327.78 -3325.69,-3327.78Zm0,499.167c1564.18,-0 2826.84,1263.45 2826.84,2828.61c0,1565.15 -1262.66,2828.61 -2826.84,2828.61c-1564.18,-0 -2826.84,-1263.46 -2826.84,-2828.61c-0,-1565.16 1262.66,-2828.61 2826.84,-2828.61Zm0,1164.72c-182.442,-0 -332.569,150.221 -332.569,332.778c-0,182.556 150.127,332.777 332.569,332.777c182.442,0 332.569,-150.221 332.569,-332.777c0,-182.557 -150.127,-332.778 -332.569,-332.778Zm-3.897,1161.15c-136.709,2.137 -247.491,116.362 -245.53,253.158l-0,1830.28c-0.017,1.177 -0.025,2.353 -0.025,3.53c-0,136.931 112.607,249.608 249.452,249.608c136.845,0 249.452,-112.677 249.452,-249.608c0,-1.177 -0.008,-2.353 -0.025,-3.53l0,-1830.28c0.017,-1.193 0.026,-2.386 0.026,-3.58c-0,-136.931 -112.607,-249.608 -249.452,-249.608c-1.3,-0 -2.599,0.01 -3.898,0.03Z"/> - </muxc:NavigationViewItem.Icon> - </muxc:NavigationViewItem> - <muxc:NavigationViewItem x:Uid="Main_NavigationPanel_Sources" Tag="sources"> - <muxc:NavigationViewItem.Icon> - <PathIcon Data="M959.759,-4.167c-33.26,0 -65.978,1.941 -98.383,5.426c-64.583,6.944 -127.292,20.746 -187.509,40.509c-302.561,99.296 -541.778,351.698 -635.887,670.935c-18.731,63.536 -31.812,129.701 -38.394,197.844c-0.011,0.119 0.012,0.242 0,0.362c-3.279,34.075 -5.142,68.472 -5.142,103.443l0,4629.63c0,559.218 435.309,1018.52 965.315,1018.52l4738.82,0c530.005,0 965.314,-459.301 965.314,-1018.52l-0,-4629.63c-0,-35.074 -1.801,-69.606 -5.142,-103.805c-6.582,-68.143 -19.663,-134.308 -38.393,-197.844c-94.109,-319.237 -333.327,-571.639 -635.887,-670.935c-60.218,-19.763 -122.926,-33.565 -187.51,-40.509c-0.113,-0.012 -0.23,0.012 -0.343,-0c-32.302,-3.501 -64.914,-5.426 -98.039,-5.426l-4738.82,0Zm-0,555.556c145.323,-0 263.267,124.444 263.267,277.778c0,153.333 -117.944,277.777 -263.267,277.777c-145.324,0 -263.268,-124.444 -263.268,-277.777c0,-153.334 117.944,-277.778 263.268,-277.778Zm877.558,-0c145.324,-0 263.268,124.444 263.268,277.778c-0,153.333 -117.944,277.777 -263.268,277.777c-145.323,0 -263.267,-124.444 -263.267,-277.777c-0,-153.334 117.944,-277.778 263.267,-277.778Zm1053.07,-0l2808.19,-0c145.323,-0 263.267,124.444 263.267,277.778c0,153.333 -117.944,277.777 -263.267,277.777l-2808.19,0c-145.323,0 -263.267,-124.444 -263.267,-277.777c-0,-153.334 117.944,-277.778 263.267,-277.778Zm-2369.41,1111.11l5616.37,0l-0,3981.48c-0,258.931 -193.374,462.963 -438.779,462.963l-4738.82,0c-245.405,0 -438.779,-204.032 -438.779,-462.963l-0,-3981.48Zm2247.03,1111.11c-166.006,3.577 -316.401,144.126 -316.401,339.265l0,1543.69c0,260.371 267.48,423.128 482.657,294.054l1285.49,-771.484c216.757,-130.185 216.757,-458.831 0,-588.831l-1285.49,-771.485c-53.794,-32.268 -110.92,-46.403 -166.256,-45.211Z"/> - </muxc:NavigationViewItem.Icon> - </muxc:NavigationViewItem> - </muxc:NavigationView.FooterMenuItems> - <Frame x:Name="ContentFrame" CornerRadius="5" Margin="10"/> - </muxc:NavigationView> - </Grid> -</Page> diff --git a/VDownload/Views/MainPage.xaml.cs b/VDownload/Views/MainPage.xaml.cs deleted file mode 100644 index a7c4675..0000000 --- a/VDownload/Views/MainPage.xaml.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using Windows.ApplicationModel.Core; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace VDownload.Views -{ - public sealed partial class MainPage : Page - { - #region CONSTANTS - - private readonly Dictionary<string, Type> Pages = new Dictionary<string, Type>() - { - {"home", typeof(Home.MainPage)}, - {"subscriptions", typeof(Subscriptions.MainPage)}, - {"about", typeof(About.MainPage)}, - {"sources", typeof(Sources.MainPage)}, - {"settings", typeof(Settings.MainPage)}, - }; - - #endregion - - - - #region CONSTRUCTORS - - public MainPage() - { - this.InitializeComponent(); - - // Hide default title bar. - CoreApplicationViewTitleBar coreTitleBar = CoreApplication.GetCurrentView().TitleBar; - coreTitleBar.ExtendViewIntoTitleBar = true; - Window.Current.SetTitleBar(AppTitleBar); - - // Navigate to home page - ContentFrame.Navigate(Pages["home"]); - NavigationPanel.SelectedItem = NavigationPanel.MenuItems[0]; - } - - #endregion - - - - #region EVENT HANDLERS - - private void NavigationPanel_ItemInvoked(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs args) - { - if (args.IsSettingsInvoked) - { - ContentFrame.Navigate(Pages["settings"], args.RecommendedNavigationTransitionInfo); - } - else if (args.InvokedItemContainer != null) - { - ContentFrame.Navigate(Pages[args.InvokedItemContainer.Tag.ToString()], args.RecommendedNavigationTransitionInfo); - } - } - - #endregion - } -} diff --git a/VDownload/Views/Settings/MainPage.xaml b/VDownload/Views/Settings/MainPage.xaml deleted file mode 100644 index de627b7..0000000 --- a/VDownload/Views/Settings/MainPage.xaml +++ /dev/null @@ -1,176 +0,0 @@ -<Page - x:Class="VDownload.Views.Settings.MainPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Views.Settings" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:cc="using:VDownload.Controls" - xmlns:muxc="using:Microsoft.UI.Xaml.Controls" - mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> - - <Page.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </Page.Resources> - - <Grid Padding="20" RowSpacing="20"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition/> - </Grid.RowDefinitions> - - <TextBlock x:Uid="Settings_HeaderTextBlock" Grid.Row="0" FontSize="28" FontWeight="SemiBold"/> - - <ScrollViewer Grid.Row="1"> - <StackPanel VerticalAlignment="Stretch" Spacing="40"> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Settings_DownloadingTasks_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Settings_DownloadingTasks_MaxNumberOfActiveTasksSettingControl" Icon="{ThemeResource ActiveTasksNumber}"> - <cc:SettingControl.SettingContent> - <muxc:NumberBox x:Name="MaxNumberOfActiveTasksSettingControlNumberBox" SpinButtonPlacementMode="Compact" Minimum="1" LostFocus="MaxNumberOfActiveTasksSettingControlNumberBox_LostFocus"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DownloadingTasks_RemoveTaskWhenSuccessfullyEndedSettingControl" Icon="{ThemeResource RemoveSuccessfullyEndedTask}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="RemoveTaskWhenSuccessfullyEndedSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="RemoveTaskWhenSuccessfullyEndedSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DownloadingTasks_ReplaceOutputFileIfExistsSettingControl" Icon="{ThemeResource ReplaceFile}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="ReplaceOutputFileIfExistsSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="ReplaceOutputFileIfExistsSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DownloadingTasks_ShowWarningWhenTaskStartsOnMeteredConnectionSettingControl" Icon="{ThemeResource MeteredConnectionWarning}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="ShowWarningWhenTaskStartsOnMeteredConnectionSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="ShowWarningWhenTaskStartsOnMeteredConnectionSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DownloadingTasks_DelayWhenQueuedTaskStartsOnMeteredConnectionSettingControl" Icon="{ThemeResource MeteredConnectionDelay}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="DelayWhenQueuedTaskStartsOnMeteredConnectionSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="DelayWhenQueuedTaskStartsOnMeteredConnectionSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Settings_DefaultDownloadingTaskSettings_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Settings_DefaultDownloadingTaskSettings_FilenameTemplateSettingControl" Icon="{ThemeResource FilenameTemplate}"> - <cc:SettingControl.SettingContent> - <StackPanel Spacing="10" Orientation="Horizontal"> - <TextBox x:Name="FilenameTemplateSettingControlTextBox" LostFocus="FilenameTemplateSettingControlTextBox_LostFocus"/> - <Button x:Name="FilenameTemplateSettingControlHelpButton" Content="?" Click="FilenameTemplateSettingControlHelpButton_Click"> - <Button.Resources> - <muxc:TeachingTip x:Name="FilenameTemplateSettingControlInfoBox" x:Uid="Settings_DefaultDownloadingTaskSettings_FilenameTemplateSettingControlInfoBox" Target="{x:Bind FilenameTemplateSettingControlTextBox}"/> - </Button.Resources> - </Button> - </StackPanel> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DefaultDownloadingTaskSettings_DefaultMediaTypeSettingControl" Icon="{ThemeResource DefaultMediaType}"> - <cc:SettingControl.SettingContent> - <ComboBox x:Name="DefaultMediaTypeSettingControlComboBox" SelectionChanged="DefaultMediaTypeSettingControlComboBox_SelectionChanged"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DefaultDownloadingTaskSettings_VideoExtensionSettingControl" Icon="{ThemeResource DefaultVideoExtension}"> - <cc:SettingControl.SettingContent> - <ComboBox x:Name="VideoExtensionSettingControlComboBox" SelectionChanged="VideoExtensionSettingControlComboBox_SelectionChanged"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DefaultDownloadingTaskSettings_AudioExtensionSettingControl" Icon="{ThemeResource DefaultAudioExtension}"> - <cc:SettingControl.SettingContent> - <ComboBox x:Name="AudioExtensionSettingControlComboBox" SelectionChanged="AudioExtensionSettingControlComboBox_SelectionChanged"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_DefaultDownloadingTaskSettings_RememberLastMediaLocationSettingControl" Icon="{ThemeResource LastMediaLocation}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="RememberLastMediaLocationSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="RememberLastMediaLocationSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Name="CustomMediaLocationSettingControl" x:Uid="Settings_DefaultDownloadingTaskSettings_CustomMediaLocationSettingControl" Icon="{ThemeResource CustomMediaLocation}"> - <cc:SettingControl.SettingContent> - <Button x:Uid="Settings_DefaultDownloadingTaskSettings_CustomMediaLocationSettingControlBrowseButton" Click="CustomMediaLocationSettingControlBrowseButton_Click"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Settings_Notifications_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Settings_Notifications_ShowNotificationWhenTaskEndedSuccessfullySettingControl" Icon="{ThemeResource ShowNotifcationWhenSuccessful}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="ShowNotificationWhenTaskEndedSuccessfullySettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="ShowNotificationWhenTaskEndedSuccessfullySettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_Notifications_ShowNotificationWhenTaskEndedUnsuccessfullySettingControl" Icon="{ThemeResource ShowNotifcationWhenUnsuccessful}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="ShowNotificationWhenTaskEndedUnsuccessfullySettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="ShowNotificationWhenTaskEndedUnsuccessfullySettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Settings_Processing_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Settings_Processing_UseHardwareAccelerationSettingControl" Icon="{ThemeResource UseHardwareAcceleration}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="UseHardwareAccelerationSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="UseHardwareAccelerationSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_Processing_TranscodingAlgorithmSettingControl" Icon="{ThemeResource TranscodingAlgorithm}"> - <cc:SettingControl.SettingContent> - <ComboBox x:Name="TranscodingAlgorithmSettingControlComboBox" SelectionChanged="TranscodingAlgorithmSettingControlComboBox_SelectionChanged"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_Processing_EditingAlgorithmSettingControl" Icon="{ThemeResource EditingAlgorithm}"> - <cc:SettingControl.SettingContent> - <ComboBox x:Name="EditingAlgorithmSettingControlComboBox" SelectionChanged="EditingAlgorithmSettingControlComboBox_SelectionChanged"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Settings_TemporaryFiles_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Settings_TemporaryFiles_DeleteTemporaryFilesOnStartSettingControl" Icon="{ThemeResource DeleteTemporaryFilesOnStartIcon}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="DeleteTemporaryFilesOnStartSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="DeleteTemporaryFilesOnStartSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_TemporaryFiles_DeleteTasksTemporaryFilesIfEndedWithErrorSettingControl" Icon="{ThemeResource DeleteTasksTemporaryFilesIfEndedWithError}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="DeleteTasksTemporaryFilesIfEndedWithErrorSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="DeleteTasksTemporaryFilesIfEndedWithErrorSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Name="TemporaryFilesLocationSettingControl" x:Uid="Settings_TemporaryFiles_TemporaryFilesLocationSettingControl" Icon="{ThemeResource TemporaryFilesLocationIcon}"> - <cc:SettingControl.SettingContent> - <Button x:Uid="Settings_TemporaryFiles_TemporaryFilesLocationSettingControlBrowseButton" Click="TemporaryFilesLocationSettingControlBrowseButton_Click"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <StackPanel Spacing="10"> - <TextBlock x:Uid="Settings_Twitch_HeaderTextBlock" FontWeight="SemiBold"/> - <cc:SettingControl x:Uid="Settings_Twitch_PassiveVodTrimmingSettingControl" Icon="{StaticResource TwitchIcon}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="PassiveVodTrimmingSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="PassiveVodTrimmingSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_Twitch_VodChunkDownloadingErrorRetryAfterErrorSettingControl" Icon="{StaticResource TwitchIcon}"> - <cc:SettingControl.SettingContent> - <ToggleSwitch x:Name="VodChunkDownloadingErrorRetryAfterErrorSettingControlToggleSwitch" Margin="0,-4,0,-4" FlowDirection="RightToLeft" Toggled="VodChunkDownloadingErrorRetryAfterErrorSettingControlToggleSwitch_Toggled"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_Twitch_VodChunkDownloadingErrorMaxNumberOfRetriesSettingControl" Icon="{StaticResource TwitchIcon}"> - <cc:SettingControl.SettingContent> - <muxc:NumberBox x:Name="VodChunkDownloadingErrorMaxNumberOfRetriesSettingControlNumberBox" SpinButtonPlacementMode="Compact" Minimum="1" LostFocus="VodChunkDownloadingErrorMaxNumberOfRetriesSettingControlNumberBox_LostFocus"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - <cc:SettingControl x:Uid="Settings_Twitch_VodChunkDownloadingErrorRetriesDelaySettingControl" Icon="{StaticResource TwitchIcon}"> - <cc:SettingControl.SettingContent> - <muxc:NumberBox x:Name="VodChunkDownloadingErrorRetriesDelaySettingControlNumberBox" SpinButtonPlacementMode="Compact" Minimum="1" LostFocus="VodChunkDownloadingErrorRetriesDelaySettingControlNumberBox_LostFocus"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - <Button x:Uid="Settings_RestoreDefaultSettingsButton" Click="RestoreDefaultSettingsButton_Click"/> - </StackPanel> - </ScrollViewer> - </Grid> - -</Page> diff --git a/VDownload/Views/Settings/MainPage.xaml.cs b/VDownload/Views/Settings/MainPage.xaml.cs deleted file mode 100644 index 5bc13ff..0000000 --- a/VDownload/Views/Settings/MainPage.xaml.cs +++ /dev/null @@ -1,309 +0,0 @@ -using Microsoft.UI.Xaml.Controls; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using VDownload.Core.Enums; -using VDownload.Core.Services; -using Windows.ApplicationModel.Resources; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.Media.Editing; -using Windows.Media.Transcoding; -using Windows.Storage; -using Windows.Storage.AccessCache; -using Windows.Storage.Pickers; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Views.Settings -{ - public sealed partial class MainPage : Page - { - #region CONSTRUCTORS - - public MainPage() - { - this.InitializeComponent(); - } - - #endregion - - - - #region EVENT HANDLERS - - protected override async void OnNavigatedTo(NavigationEventArgs e) - { - MaxNumberOfActiveTasksSettingControlNumberBox.Value = (int)Config.GetValue("max_active_video_task"); - ReplaceOutputFileIfExistsSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("replace_output_file_if_exists"); - RemoveTaskWhenSuccessfullyEndedSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("remove_task_when_successfully_ended"); - ShowWarningWhenTaskStartsOnMeteredConnectionSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("show_warning_when_task_starts_on_metered_network"); - DelayWhenQueuedTaskStartsOnMeteredConnectionSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("delay_task_when_queued_task_starts_on_metered_network"); - FilenameTemplateSettingControlTextBox.Text = (string)Config.GetValue("default_filename"); - - foreach (string mediaType in Enum.GetNames(typeof(MediaType))) - { - DefaultMediaTypeSettingControlComboBox.Items.Add(ResourceLoader.GetForCurrentView().GetString($"Base_MediaType_{mediaType}Text")); - } - DefaultMediaTypeSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_media_type"); - - foreach (string videoExtension in Enum.GetNames(typeof(VideoFileExtension))) - { - VideoExtensionSettingControlComboBox.Items.Add(videoExtension); - } - VideoExtensionSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_video_extension"); - - foreach (string audioExtension in Enum.GetNames(typeof(AudioFileExtension))) - { - AudioExtensionSettingControlComboBox.Items.Add(audioExtension); - } - AudioExtensionSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_audio_extension") - 3; - - RememberLastMediaLocationSettingControlToggleSwitch.IsOn = !(bool)Config.GetValue("custom_media_location"); - - if (StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_media_location")) - { - CustomMediaLocationSettingControl.Description = (await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_media_location")).Path; - } - else - { - CustomMediaLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload"; - } - - ShowNotificationWhenTaskEndedSuccessfullySettingControlToggleSwitch.IsOn = (bool)Config.GetValue("show_notification_when_task_ended_successfully"); - - ShowNotificationWhenTaskEndedUnsuccessfullySettingControlToggleSwitch.IsOn = (bool)Config.GetValue("show_notification_when_task_ended_unsuccessfully"); - - UseHardwareAccelerationSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("media_transcoding_use_hardware_acceleration"); - - foreach (string transcodingAlgorithm in Enum.GetNames(typeof(MediaVideoProcessingAlgorithm))) - { - TranscodingAlgorithmSettingControlComboBox.Items.Add(transcodingAlgorithm); - } - TranscodingAlgorithmSettingControlComboBox.SelectedIndex = (int)Config.GetValue("media_transcoding_algorithm"); - - foreach (string editingAlgorithm in Enum.GetNames(typeof(MediaTrimmingPreference))) - { - EditingAlgorithmSettingControlComboBox.Items.Add(editingAlgorithm); - } - EditingAlgorithmSettingControlComboBox.SelectedIndex = (int)Config.GetValue("media_editing_algorithm"); - - DeleteTemporaryFilesOnStartSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("delete_temp_on_start"); - - if (StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_temp_location")) - { - TemporaryFilesLocationSettingControl.Description = (await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_temp_location")).Path; - } - else - { - TemporaryFilesLocationSettingControl.Description = ApplicationData.Current.TemporaryFolder.Path; - } - - DeleteTasksTemporaryFilesIfEndedWithErrorSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("delete_task_temp_when_ended_with_error"); - - PassiveVodTrimmingSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("twitch_vod_passive_trim"); - - VodChunkDownloadingErrorRetryAfterErrorSettingControlToggleSwitch.IsOn = (bool)Config.GetValue("twitch_vod_downloading_chunk_retry_after_error"); - - VodChunkDownloadingErrorMaxNumberOfRetriesSettingControlNumberBox.Value = (int)Config.GetValue("twitch_vod_downloading_chunk_max_retries"); - - VodChunkDownloadingErrorRetriesDelaySettingControlNumberBox.Value = (int)Config.GetValue("twitch_vod_downloading_chunk_retries_delay"); - } - - private void MaxNumberOfActiveTasksSettingControlNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - double value = ((NumberBox)sender).Value; - if (double.IsNaN(value)) - { - ((NumberBox)sender).Value = (int)Config.GetValue("max_active_video_task"); - } - else - { - Config.SetValue("max_active_video_task", (int)value); - } - } - - private void ReplaceOutputFileIfExistsSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("replace_output_file_if_exists", ((ToggleSwitch)sender).IsOn); - } - - private void RemoveTaskWhenSuccessfullyEndedSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("remove_task_when_successfully_ended", ((ToggleSwitch)sender).IsOn); - } - - private void ShowWarningWhenTaskStartsOnMeteredConnectionSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("show_warning_when_task_starts_on_metered_network", ((ToggleSwitch)sender).IsOn); - } - - private void DelayWhenQueuedTaskStartsOnMeteredConnectionSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("delay_task_when_queued_task_starts_on_metered_network", ((ToggleSwitch)sender).IsOn); - } - - private void FilenameTemplateSettingControlTextBox_LostFocus(object sender, RoutedEventArgs e) - { - Config.SetValue("default_filename", ((TextBox)sender).Text); - } - - private void FilenameTemplateSettingControlHelpButton_Click(object sender, RoutedEventArgs e) - { - FilenameTemplateSettingControlInfoBox.IsOpen = !FilenameTemplateSettingControlInfoBox.IsOpen; - } - - private void DefaultMediaTypeSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - Config.SetValue("default_media_type", ((ComboBox)sender).SelectedIndex); - } - - private void VideoExtensionSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - Config.SetValue("default_video_extension", ((ComboBox)sender).SelectedIndex); - } - - private void AudioExtensionSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - Config.SetValue("default_audio_extension", ((ComboBox)sender).SelectedIndex + 3); - } - - private void RememberLastMediaLocationSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("custom_media_location", !((ToggleSwitch)sender).IsOn); - } - - private async void CustomMediaLocationSettingControlBrowseButton_Click(object sender, RoutedEventArgs e) - { - FolderPicker picker = new FolderPicker - { - SuggestedStartLocation = PickerLocationId.Downloads - }; - picker.FileTypeFilter.Add("*"); - - StorageFolder selectedFolder = await picker.PickSingleFolderAsync(); - - if (selectedFolder != null) - { - try - { - await(await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync(); - StorageApplicationPermissions.FutureAccessList.AddOrReplace("custom_media_location", selectedFolder); - CustomMediaLocationSettingControl.Description = selectedFolder.Path; - } - catch (UnauthorizedAccessException) { } - } - } - - private void ShowNotificationWhenTaskEndedSuccessfullySettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("show_notification_when_task_ended_successfully", !((ToggleSwitch)sender).IsOn); - } - - private void ShowNotificationWhenTaskEndedUnsuccessfullySettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("show_notification_when_task_ended_unsuccessfully", !((ToggleSwitch)sender).IsOn); - } - - private void UseHardwareAccelerationSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("media_transcoding_use_hardware_acceleration", ((ToggleSwitch)sender).IsOn); - } - - private void TranscodingAlgorithmSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - Config.SetValue("media_transcoding_algorithm", ((ComboBox)sender).SelectedIndex); - } - - private void EditingAlgorithmSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - Config.SetValue("media_editing_algorithm", ((ComboBox)sender).SelectedIndex); - } - - private void DeleteTemporaryFilesOnStartSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("delete_temp_on_start", ((ToggleSwitch)sender).IsOn); - } - - private async void TemporaryFilesLocationSettingControlBrowseButton_Click(object sender, RoutedEventArgs e) - { - FolderPicker picker = new FolderPicker - { - SuggestedStartLocation = PickerLocationId.Downloads - }; - picker.FileTypeFilter.Add("*"); - - StorageFolder selectedFolder = await picker.PickSingleFolderAsync(); - - if (selectedFolder != null) - { - try - { - await(await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync(); - StorageApplicationPermissions.FutureAccessList.AddOrReplace("custom_temp_location", selectedFolder); - TemporaryFilesLocationSettingControl.Description = selectedFolder.Path; - } - catch (UnauthorizedAccessException) { } - } - } - - private void DeleteTasksTemporaryFilesIfEndedWithErrorSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("delete_task_temp_when_ended_with_error", ((ToggleSwitch)sender).IsOn); - } - - private void PassiveVodTrimmingSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("twitch_vod_passive_trim", ((ToggleSwitch)sender).IsOn); - } - - private void VodChunkDownloadingErrorRetryAfterErrorSettingControlToggleSwitch_Toggled(object sender, RoutedEventArgs e) - { - Config.SetValue("twitch_vod_downloading_chunk_retry_after_error", ((ToggleSwitch)sender).IsOn); - } - - private void VodChunkDownloadingErrorMaxNumberOfRetriesSettingControlNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - double value = ((NumberBox)sender).Value; - if (double.IsNaN(value)) - { - ((NumberBox)sender).Value = (int)Config.GetValue("twitch_vod_downloading_chunk_max_retries"); - } - else - { - Config.SetValue("twitch_vod_downloading_chunk_max_retries", (int)value); - } - } - - private void VodChunkDownloadingErrorRetriesDelaySettingControlNumberBox_LostFocus(object sender, RoutedEventArgs e) - { - double value = ((NumberBox)sender).Value; - if (double.IsNaN(value)) - { - ((NumberBox)sender).Value = (int)Config.GetValue("twitch_vod_downloading_chunk_retries_delay"); - } - else - { - Config.SetValue("twitch_vod_downloading_chunk_retries_delay", (int)value); - } - } - - private void RestoreDefaultSettingsButton_Click(object sender, RoutedEventArgs e) - { - Config.SetDefault(); - StorageApplicationPermissions.FutureAccessList.Remove("custom_media_location"); - StorageApplicationPermissions.FutureAccessList.Remove("custom_temp_location"); - OnNavigatedTo(null); - } - - #endregion - } -} diff --git a/VDownload/Views/Sources/MainPage.xaml b/VDownload/Views/Sources/MainPage.xaml deleted file mode 100644 index b72952c..0000000 --- a/VDownload/Views/Sources/MainPage.xaml +++ /dev/null @@ -1,34 +0,0 @@ -<Page - x:Class="VDownload.Views.Sources.MainPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Views.Sources" - xmlns:cc="using:VDownload.Controls" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> - - - <Page.Resources> - <ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/> - </Page.Resources> - - - <Grid Padding="20" RowSpacing="20"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition/> - </Grid.RowDefinitions> - - <TextBlock x:Uid="Sources_HeaderTextBlock" Grid.Row="0" FontSize="28" FontWeight="SemiBold"/> - - <StackPanel Grid.Row="1" Spacing="10"> - <cc:SettingControl x:Name="TwitchSettingControl" x:Uid="Sources_TwitchSettingControl" Grid.Row="0" Icon="{StaticResource TwitchIcon}" Title="Twitch"> - <cc:SettingControl.SettingContent> - <Button x:Name="TwitchSettingControlLoginButton" x:Uid="Sources_TwitchSettingControl_LoginButton" IsEnabled="False" Click="TwitchSettingControlLoginButton_Click"/> - </cc:SettingControl.SettingContent> - </cc:SettingControl> - </StackPanel> - </Grid> -</Page> diff --git a/VDownload/Views/Sources/MainPage.xaml.cs b/VDownload/Views/Sources/MainPage.xaml.cs deleted file mode 100644 index 40f34d9..0000000 --- a/VDownload/Views/Sources/MainPage.xaml.cs +++ /dev/null @@ -1,224 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using Microsoft.UI.Xaml.Controls; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using VDownload.Core.Structs; -using Windows.ApplicationModel.Resources; -using Windows.UI.WindowManagement; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Hosting; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Views.Sources -{ - public sealed partial class MainPage : Page - { - #region CONSTRUCTORS - - public MainPage() - { - InitializeComponent(); - } - - #endregion - - - - #region EVENT HANDLERS - - protected override async void OnNavigatedTo(NavigationEventArgs e) - { - Task[] checkingTasks = new Task[1]; - - checkingTasks[0] = CheckTwitch(); - - await Task.WhenAll(checkingTasks); - } - - private async void TwitchSettingControlLoginButton_Click(object sender, RoutedEventArgs e) - { - async Task ShowDialog(string localErrorKey, string unknownErrorInfo = null) - { - StringBuilder content = new StringBuilder(ResourceLoader.GetForCurrentView("DialogResources").GetString($"Sources_TwitchLogin_{localErrorKey}_Content")); - if (!string.IsNullOrEmpty(unknownErrorInfo)) - { - content.Append($" {unknownErrorInfo}"); - } - ContentDialog errorDialog = new ContentDialog - { - Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Sources_TwitchLogin_Base_Title"), - Content = content.ToString(), - CloseButtonText = ResourceLoader.GetForCurrentView().GetString("Base_CloseButtonText"), - }; - await errorDialog.ShowAsync(); - } - - TwitchAccessTokenValidationData accessTokenValidation = TwitchAccessTokenValidationData.Null; - try - { - string accessToken = await Core.Services.Sources.Twitch.Helpers.Authorization.ReadAccessTokenAsync(); - accessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Authorization.ValidateAccessTokenAsync(accessToken); - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - goto Check; - } - else throw; - } - - if (accessTokenValidation.IsValid) - { - await Core.Services.Sources.Twitch.Helpers.Authorization.RevokeAccessTokenAsync(accessTokenValidation.AccessToken); - await Core.Services.Sources.Twitch.Helpers.Authorization.DeleteAccessTokenAsync(); - } - else - { - string response = await LoginToTwitch(); - accessTokenValidation = TwitchAccessTokenValidationData.Null; - try - { - accessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Authorization.ValidateAccessTokenAsync(response); - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - await ShowDialog("InternetNotAvailable"); - goto Check; - } - else throw; - } - if (accessTokenValidation.IsValid) - { - await Core.Services.Sources.Twitch.Helpers.Authorization.SaveAccessTokenAsync(accessTokenValidation.AccessToken); - } - else - { - Dictionary<string, string> errorCodes = new Dictionary<string, string> - { - {"The user denied you access", string.Empty}, - }; - - if (!string.IsNullOrEmpty(response)) - { - if (errorCodes.ContainsKey(response)) - { - if (!string.IsNullOrEmpty(errorCodes[response])) - { - await ShowDialog(errorCodes[response]); - } - } - else - { - await ShowDialog("Unknown", response); - } - } - } - } - Check: await CheckTwitch(); - } - - #endregion - - - - #region PRIVATE METHODS - - private async Task CheckTwitch() - { - try - { - string twitchAccessToken = await Core.Services.Sources.Twitch.Helpers.Authorization.ReadAccessTokenAsync(); - TwitchAccessTokenValidationData twitchAccessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Authorization.ValidateAccessTokenAsync(twitchAccessToken); - if (twitchAccessTokenValidation.IsValid) - { - TwitchSettingControl.Description = $"{ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_LoggedIn")} {twitchAccessTokenValidation.Login}"; - TwitchSettingControlLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_LoginButton_Content_LoggedIn"); - } - else - { - if (twitchAccessToken != null) - { - TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_InvalidAccessToken"); - } - else - { - TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_NotLoggedIn"); - } - TwitchSettingControlLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_LoginButton_Content_NotLoggedIn"); - } - TwitchSettingControlLoginButton.IsEnabled = true; - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_InternetNotAvailable"); - TwitchSettingControlLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_LoginButton_Content_NotLoggedIn"); - TwitchSettingControlLoginButton.IsEnabled = false; - } - else throw; - } - } - - private async Task<string> LoginToTwitch() - { - string response = string.Empty; - AppWindow TwitchAuthWindow = await AppWindow.TryCreateAsync(); - TwitchAuthWindow.Title = "Twitch Authentication"; - -#pragma warning disable CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates. - - WebView2 TwitchAuthWebView = new WebView2(); - await TwitchAuthWebView.EnsureCoreWebView2Async(); - TwitchAuthWebView.Source = Core.Services.Sources.Twitch.Helpers.Authorization.AuthorizationUrl; - - ElementCompositionPreview.SetAppWindowContent(TwitchAuthWindow, TwitchAuthWebView); - - TwitchAuthWebView.NavigationStarting += async (s, a) => - { - if (new Uri(a.Uri).Host == Core.Services.Sources.Twitch.Helpers.Authorization.RedirectUrl.Host) - { - string login_response = a.Uri.Replace(Core.Services.Sources.Twitch.Helpers.Authorization.RedirectUrl.OriginalString, ""); - - if (login_response[1] == '#') - { - response = login_response.Split('&')[0].Replace("/#access_token=", ""); - } - else - { - response = (login_response.Split('&')[1].Replace("error_description=", "")).Replace('+', ' '); - } - - await TwitchAuthWindow.CloseAsync(); - } - }; - - await TwitchAuthWindow.TryShowAsync(); - - try - { - while (TwitchAuthWindow.IsVisible) - { - await Task.Delay(1); - } - } - catch (ObjectDisposedException) { } - - TwitchAuthWebView.CoreWebView2.CookieManager.DeleteAllCookies(); -#pragma warning restore CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates. - - return response; - } - - #endregion - } -} diff --git a/VDownload/Views/Subscriptions/Controls/SubscriptionPanel.xaml b/VDownload/Views/Subscriptions/Controls/SubscriptionPanel.xaml deleted file mode 100644 index c03f5f7..0000000 --- a/VDownload/Views/Subscriptions/Controls/SubscriptionPanel.xaml +++ /dev/null @@ -1,36 +0,0 @@ -<UserControl - x:Class="VDownload.Views.Subscriptions.Controls.SubscriptionPanel" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Views.Subscriptions.Controls" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" - d:DesignHeight="300" - d:DesignWidth="400" - CornerRadius="{ThemeResource ControlCornerRadius}"> - - <UserControl.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </UserControl.Resources> - - <Grid Background="{ThemeResource SubscriptionsSubscriptionPanelBackgroundColor}" Padding="5" ColumnSpacing="5"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBlock x:Name="TitleTextBlock" Grid.Column="1" FontSize="16" FontWeight="SemiBold" VerticalAlignment="Center"/> - <TextBlock x:Name="CountTextBlock" x:Uid="Subscriptions_SubscriptionPanel_CountTextBlock" Grid.Column="2" FontSize="12" VerticalAlignment="Center"/> - <AppBarButton x:Name="UpdateButton" Grid.Column="3" Width="40" Height="48" Margin="0,-4,0,-4" Icon="Sync" Click="UpdateButton_Click"/> - <AppBarButton x:Name="RemoveButton" Grid.Column="4" Width="40" Height="48" Margin="0,-4,0,-4" Icon="Delete" Click="RemoveButton_Click"/> - <AppBarButton x:Name="SourceButton" Grid.Column="5" Width="40" Height="48" Margin="0,-4,0,-4" Icon="Link" Click="SourceButton_Click"/> - </Grid> -</UserControl> diff --git a/VDownload/Views/Subscriptions/Controls/SubscriptionPanel.xaml.cs b/VDownload/Views/Subscriptions/Controls/SubscriptionPanel.xaml.cs deleted file mode 100644 index a47f26e..0000000 --- a/VDownload/Views/Subscriptions/Controls/SubscriptionPanel.xaml.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Threading.Tasks; -using VDownload.Core.Services; -using Windows.ApplicationModel.Resources; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.Storage; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Views.Subscriptions.Controls -{ - public sealed partial class SubscriptionPanel : UserControl - { - #region CONSTRUCTORS - - public SubscriptionPanel(Subscription subscription, StorageFile subscriptionFile) - { - InitializeComponent(); - Subscription = subscription; - SubscriptionFile = subscriptionFile; - - TitleTextBlock.Text = Subscription.Playlist.Name; - SourceButton.Icon = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Subscription.Playlist.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false }; - } - - #endregion - - - - #region PROPERTIES - - private Subscription Subscription { get; set; } - private StorageFile SubscriptionFile { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - public async Task UpdateNewVideosCounterAsync() - { - CountTextBlock.Text = (await Subscription.GetNewVideosAsync()).Length.ToString(); - } - - #endregion - - - - #region EVENT HANDLERS - - private async void RemoveButton_Click(object sender, RoutedEventArgs e) - { - await SubscriptionsCollectionManagement.DeleteSubscriptionFileAsync(SubscriptionFile); - ((StackPanel)Parent).Children.Remove(this); - } - - private async void UpdateButton_Click(object sender, RoutedEventArgs e) - { - CountTextBlock.Text = ResourceLoader.GetForCurrentView().GetString("Subscriptions_SubscriptionPanel_CountTextBlock_SyncText"); - await Subscription.GetNewVideosAndUpdateAsync(); - await SubscriptionsCollectionManagement.UpdateSubscriptionFileAsync(Subscription, SubscriptionFile); - CountTextBlock.Text = "0"; - } - - private async void SourceButton_Click(object sender, RoutedEventArgs e) - { - await Windows.System.Launcher.LaunchUriAsync(Subscription.Playlist.Url); - } - - #endregion - } -} diff --git a/VDownload/Views/Subscriptions/MainPage.xaml b/VDownload/Views/Subscriptions/MainPage.xaml deleted file mode 100644 index da99277..0000000 --- a/VDownload/Views/Subscriptions/MainPage.xaml +++ /dev/null @@ -1,44 +0,0 @@ -<Page - x:Class="VDownload.Views.Subscriptions.MainPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:VDownload.Views.Subscriptions" - 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" - mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> - - <Page.Resources> - <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/> - </ResourceDictionary.MergedDictionaries> - </ResourceDictionary> - </Page.Resources> - - <Grid Padding="20" RowSpacing="20"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> - - <TextBlock x:Uid="Subscriptions_HeaderTextBlock" Grid.Row="0" FontSize="28" FontWeight="SemiBold"/> - - <ScrollViewer Grid.Row="1"> - <StackPanel x:Name="SubscriptionsListStackPanel" VerticalAlignment="Stretch" Spacing="10"/> - </ScrollViewer> - - <Grid Grid.Row="2" VerticalAlignment="Center" Padding="8" CornerRadius="{ThemeResource ControlCornerRadius}" ColumnSpacing="10" Background="{ThemeResource SubscriptionsAddingPanelBackgroundColor}"> - <Grid.ColumnDefinitions> - <ColumnDefinition/> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBox x:Name="AddingTextBox" x:Uid="Subscriptions_AddingTextBox" Grid.Column="0" VerticalAlignment="Center"/> - <Button x:Name="AddingButton" x:Uid="Subscriptions_AddingButton" Grid.Column="1" Click="AddingButton_Click"/> - <muxc:ProgressRing x:Name="AddingProgressRing" Grid.Column="2" Visibility="Collapsed" Width="15" Height="15" VerticalAlignment="Center"/> - </Grid> - </Grid> -</Page> diff --git a/VDownload/Views/Subscriptions/MainPage.xaml.cs b/VDownload/Views/Subscriptions/MainPage.xaml.cs deleted file mode 100644 index e562f3a..0000000 --- a/VDownload/Views/Subscriptions/MainPage.xaml.cs +++ /dev/null @@ -1,148 +0,0 @@ -using Microsoft.Toolkit.Uwp.Connectivity; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; -using System.Threading.Tasks; -using VDownload.Core.Exceptions; -using VDownload.Core.Interfaces; -using VDownload.Core.Services; -using VDownload.Core.Services.Sources; -using VDownload.Views.Subscriptions.Controls; -using Windows.ApplicationModel.Resources; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.Storage; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -namespace VDownload.Views.Subscriptions -{ - public sealed partial class MainPage : Page - { - #region CONSTRUCTORS - - public MainPage() - { - InitializeComponent(); - } - - #endregion - - - - #region EVENT HANDLERS - - protected override async void OnNavigatedTo(NavigationEventArgs e) - { - (Subscription Subscription, StorageFile SubscriptionFile)[] subscriptions = await SubscriptionsCollectionManagement.GetSubscriptionsAsync(); - foreach((Subscription Subscription, StorageFile SubscriptionFile) subscription in subscriptions) - { - AddSubscriptionToList(subscription.Subscription, subscription.SubscriptionFile); - } - } - - private async void AddingButton_Click(object sender, RoutedEventArgs e) - { - async Task ShowDialog(string localErrorKey) - { - ContentDialog errorDialog = new ContentDialog - { - Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Subscription_Adding_Base_Title"), - Content = ResourceLoader.GetForCurrentView("DialogResources").GetString($"Subscription_Adding_{localErrorKey}_Content"), - CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CloseButtonText"), - }; - await errorDialog.ShowAsync(); - AddingProgressRing.Visibility = Visibility.Collapsed; - AddingButton.IsEnabled = true; - } - - AddingProgressRing.Visibility = Visibility.Visible; - AddingButton.IsEnabled = false; - - IPlaylist playlist = Source.GetPlaylist(AddingTextBox.Text); // TODO: Change name of class (method returns Playlist object, not source of playlist) - - if (playlist is null) - { - await ShowDialog("PlaylistNotFound"); - return; - } - else - { - try - { - await playlist.GetMetadataAsync(); - await playlist.GetVideosAsync(); - } - catch (MediaNotFoundException) - { - await ShowDialog("PlaylistNotFound"); - return; - } - catch (TwitchAccessTokenNotFoundException) - { - await ShowDialog("TwitchAccessTokenNotFound"); - return; - } - catch (TwitchAccessTokenNotValidException) - { - await ShowDialog("TwitchAccessTokenNotValid"); - return; - } - catch (WebException) - { - if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) - { - await ShowDialog("InternetNotAvailable"); - return; - } - else throw; - } - } - - Subscription subscription = new Subscription(playlist); - - StorageFile subscriptionFile = null; - try - { - subscriptionFile = await SubscriptionsCollectionManagement.CreateSubscriptionFileAsync(subscription); - } - catch (SubscriptionExistsException) - { - await ShowDialog("SubscriptionExists"); - return; - } - - AddSubscriptionToList(subscription, subscriptionFile); - - AddingProgressRing.Visibility = Visibility.Collapsed; - AddingButton.IsEnabled = true; - AddingTextBox.Text = string.Empty; - } - - #endregion - - - - #region PRIVATE METHODS - - private void AddSubscriptionToList(Subscription subscription, StorageFile subscriptionFile) - { - SubscriptionPanel subscriptionPanel = new SubscriptionPanel(subscription, subscriptionFile); - SubscriptionsListStackPanel.Children.Add(subscriptionPanel); - subscriptionPanel.UpdateNewVideosCounterAsync(); - } - - #endregion - } -} diff --git a/VDownload/app.manifest b/VDownload/app.manifest new file mode 100644 index 0000000..92e60ea --- /dev/null +++ b/VDownload/app.manifest @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity version="0.0.0.0" name="VDownload.app"/> + + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> + </application> + </compatibility> + + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> + </windowsSettings> + </application> +</assembly> diff --git a/VDownload/configuration.json b/VDownload/configuration.json new file mode 100644 index 0000000..0a6738c --- /dev/null +++ b/VDownload/configuration.json @@ -0,0 +1,246 @@ +{ + "common": { + "about": { + "repository_url": "https://github.com/mateuszskoczek/VDownload", + "donation_url": "https://paypal.me/mateuszskoczek", + "developers": [ + { + "name": "Mateusz Skoczek", + "url": "https://github.com/mateuszskoczek" + } + ], + "translation": [ + { + "code": "en-US", + "translators": [ + { + "name": "Mateusz Skoczek", + "url": "https://github.com/mateuszskoczek" + } + ] + } + ] + }, + "filename_templates": [ + { + "name": "id", + "wildcard": "{id}" + }, + { + "name": "title", + "wildcard": "{title}" + }, + { + "name": "author", + "wildcard": "{author}" + }, + { + "name": "views", + "wildcard": "{views}" + }, + { + "name": "source", + "wildcard": "{source}" + }, + { + "name": "date", + "wildcard": "{date:(.+)}" + }, + { + "name": "duration", + "wildcard": "{duration:(.+)}" + } + ], + "processing": { + "muxers": [ + { + "extension": "mp4", + "video_codecs": [ + "h264" + ], + "audio_codecs": [ + "aac" + ] + }, + { + "extension": "mkv", + "video_codecs": [ + "h264" + ], + "audio_codecs": [ + "vorbis", + "aac" + ] + }, + { + "extension": "avi", + "video_codecs": [ + "mpeg4", + "h264" + ], + "audio_codecs": [ + "mp3" + ] + }, + { + "extension": "mov", + "video_codecs": [ + "h264" + ], + "audio_codecs": [ + "aac" + ] + }, + { + "extension": "webm", + "video_codecs": [ + "vp9", + "vp8", + "av1" + ], + "audio_codecs": [ + "libopus", + "vorbis" + ] + }, + { + "extension": "wmv", + "video_codecs": [ + "msmpeg4v3", + "h264" + ], + "audio_codecs": [ + "wmav2" + ] + }, + { + "extension": "mp3", + "audio_codecs": [ + "mp3" + ] + }, + { + "extension": "flac", + "audio_codecs": [ + "flac" + ] + }, + { + "extension": "ogg", + "audio_codecs": [ + "aac", + "vorbis" + ] + }, + { + "extension": "wav", + "audio_codecs": [ + "aac", + "pcm_s16le" + ] + } + ], + "processed_filename": "processed" + }, + "path": { + "appdata": { + "directory_name": "VDownload", + "authentication_file": "authentication.json", + "settings_file": "settings.json", + "data_file": "data.json", + "subscriptions_file": "subscriptions.json" + }, + "temp": { + "tasks_directory": "tasks" + } + }, + "string_resources_assembly": "VDownload.Core.Strings" + }, + "twitch": { + "api": { + "auth": { + "token_schema": "OAuth", + "client_id": "yukkqkwp61wsv3u1pya17crpyaa98y", + "endpoints": { + "validate": "https://id.twitch.tv/oauth2/validate" + } + }, + "helix": { + "token_schema": "Bearer", + "client_id": "yukkqkwp61wsv3u1pya17crpyaa98y", + "endpoints": { + "get_videos": "https://api.twitch.tv/helix/videos", + "get_clips": "https://api.twitch.tv/helix/clips", + "get_users": "https://api.twitch.tv/helix/users" + } + }, + "gql": { + "client_id": "kd1unb4b3q4t58fwlpcbzcbnm76a8fp", + "endpoint": "https://gql.twitch.tv/gql", + "queries": { + "get_video_token": { + "operation_name": "PlaybackAccessToken_Template", + "query": "query PlaybackAccessToken_Template($login: String!, $isLive: Boolean!, $vodID: ID!, $isVod: Boolean!, $playerType: String!) { streamPlaybackAccessToken(channelName: $login, params: {platform: \"web\", playerBackend: \"mediaplayer\", playerType: $playerType}) @include(if: $isLive) { value signature __typename } videoPlaybackAccessToken(id: $vodID, params: {platform: \"web\", playerBackend: \"mediaplayer\", playerType: $playerType}) @include(if: $isVod) { value signature __typename }}" + }, + "get_clip_token": { + "operation_name": "VideoAccessToken_Clip", + "persisted_query_version": 1, + "persisted_query_hash": "36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11" + } + } + }, + "usher": { + "endpoints": { + "get_video_playlist": "https://usher.ttvnw.net/vod/{0}.m3u8" + } + } + }, + "search": { + "general_regexes": [ + "twitch\\.tv" + ], + "vod": { + "regexes": [ + "videos\\/(\\d+)" + ], + "thumbnail": { + "width": 1920, + "height": 1080 + }, + "live_thumbnail_url_regex": "https:\\/\\/vod-secure\\.twitch\\.tv\\/_404\\/404_processing_%{width}x%{height}\\.png", + "stream_playlist_regex": "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"\\w+\",NAME=\"(?<id>.+)\",AUTOSELECT=\\w+,DEFAULT=\\w+\\n#EXT-X-STREAM-INF:BANDWIDTH=\\d+,CODECS=\"(?<video_codec>.+),(?<audio_codec>.+)\",RESOLUTION=(?<width>\\d+)x(?<height>\\d+),VIDEO=\".+\"(?:,FRAME-RATE=(?<framerate>\\d+.\\d+))?\n(?<url>.+)" + }, + "clip": { + "regexes": [ + "clip\\/(\\w+-\\w+)", + "clips\\.twitch\\.tv\\/(\\w+-\\w+)" + ] + }, + "channel": { + "regexes": [ + "twitch\\.tv\\/(\\w+)(?:\\/)?(?:\\/videos)?$" + ], + "url": "https://www.twitch.tv/{0}" + } + }, + "download": { + "vod": { + "chunk_regex": "#EXTINF:(?<duration>\\d+.\\d+),\\n(?<file>.+)", + "file_name": "raw.ts" + }, + "clip": { + "file_name": "raw.mp4" + } + }, + "authentication": { + "url": "https://id.twitch.tv/oauth2/authorize?client_id={0}&redirect_uri={1}&response_type={2}&scope={3}", + "redirect_url": "https://www.vd.com", + "redirect_url_regex": "^https:\\/\\/www.vd.com\\/#access_token=(\\w+)", + "client_id": "yukkqkwp61wsv3u1pya17crpyaa98y", + "response_type": "token", + "scopes": [ + "user:read:subscriptions" + ] + } + } +} \ No newline at end of file