diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..d447dc9
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: ['https://paypal.me/mateuszskoczek']
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1e47a59
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,389 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+[Pp]roperties/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Nuget personal access tokens and Credentials
+nuget.config
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+.idea/
+*.sln.iml
\ No newline at end of file
diff --git a/VDownload.sln b/VDownload.sln
new file mode 100644
index 0000000..66d5080
--- /dev/null
+++ b/VDownload.sln
@@ -0,0 +1,56 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VDownload", "VDownload\VDownload.csproj", "{324AB81A-F68D-424D-B90E-5402F5E44240}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A087406C-5459-423E-BB0B-84BC93E5B38F}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ 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|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|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
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {878070E9-B074-4931-94AC-8BACD6906448}
+ EndGlobalSection
+EndGlobal
diff --git a/VDownload/App.xaml b/VDownload/App.xaml
new file mode 100644
index 0000000..e1d3280
--- /dev/null
+++ b/VDownload/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/VDownload/App.xaml.cs b/VDownload/App.xaml.cs
new file mode 100644
index 0000000..8e53bc2
--- /dev/null
+++ b/VDownload/App.xaml.cs
@@ -0,0 +1,78 @@
+// Internal
+using VDownload.Services;
+
+// System
+using System;
+using System.Collections.Generic;
+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;
+
+namespace VDownload
+{
+ sealed partial class App : Application
+ {
+ public App()
+ {
+ InitializeComponent();
+ Suspending += OnSuspending;
+ }
+
+ protected override async void OnLaunched(LaunchActivatedEventArgs e)
+ {
+ // Rebuild configuration file
+ Config.Rebuild();
+
+ // Delete temp on start
+ if (Config.GetValue("delete_temp_on_start") == "1")
+ {
+ IReadOnlyList tempItems = await ApplicationData.Current.TemporaryFolder.GetItemsAsync();
+ List tasks = new List();
+ 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(MainPage), e.Arguments);
+ }
+
+ // Ensure the current window is active
+ Window.Current.Activate();
+ }
+ }
+
+ private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
+ {
+ throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
+ }
+
+ private void OnSuspending(object sender, SuspendingEventArgs e)
+ {
+ var deferral = e.SuspendingOperation.GetDeferral();
+ //TODO: Save application state and stop any background activity
+ deferral.Complete();
+ }
+ }
+}
diff --git a/VDownload/Assets/Icons/AddVideo/NotFound.png b/VDownload/Assets/Icons/AddVideo/NotFound.png
new file mode 100644
index 0000000..38e14ac
Binary files /dev/null and b/VDownload/Assets/Icons/AddVideo/NotFound.png differ
diff --git a/VDownload/Assets/Icons/AddVideo/Start.png b/VDownload/Assets/Icons/AddVideo/Start.png
new file mode 100644
index 0000000..8c94413
Binary files /dev/null and b/VDownload/Assets/Icons/AddVideo/Start.png differ
diff --git a/VDownload/Assets/Icons/Sources/Twitch.png b/VDownload/Assets/Icons/Sources/Twitch.png
new file mode 100644
index 0000000..547fa37
Binary files /dev/null and b/VDownload/Assets/Icons/Sources/Twitch.png differ
diff --git a/VDownload/Assets/Icons/Sources/Unknown.png b/VDownload/Assets/Icons/Sources/Unknown.png
new file mode 100644
index 0000000..2d965e4
Binary files /dev/null and b/VDownload/Assets/Icons/Sources/Unknown.png differ
diff --git a/VDownload/Assets/Icons/Sources/Youtube.png b/VDownload/Assets/Icons/Sources/Youtube.png
new file mode 100644
index 0000000..2c8282b
Binary files /dev/null and b/VDownload/Assets/Icons/Sources/Youtube.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Author.png b/VDownload/Assets/Icons/Universal/Dark/Author.png
new file mode 100644
index 0000000..1520596
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Author.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Cancelled.png b/VDownload/Assets/Icons/Universal/Dark/Cancelled.png
new file mode 100644
index 0000000..94179cf
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Cancelled.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Date.png b/VDownload/Assets/Icons/Universal/Dark/Date.png
new file mode 100644
index 0000000..ad98de8
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Date.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Done.png b/VDownload/Assets/Icons/Universal/Dark/Done.png
new file mode 100644
index 0000000..7fde7fa
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Done.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Downloading.png b/VDownload/Assets/Icons/Universal/Dark/Downloading.png
new file mode 100644
index 0000000..28a207a
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Downloading.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Duration.png b/VDownload/Assets/Icons/Universal/Dark/Duration.png
new file mode 100644
index 0000000..1241f21
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Duration.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Error.png b/VDownload/Assets/Icons/Universal/Dark/Error.png
new file mode 100644
index 0000000..731fa5e
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Error.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Finalizing.png b/VDownload/Assets/Icons/Universal/Dark/Finalizing.png
new file mode 100644
index 0000000..f0f2e64
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Finalizing.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Idle.png b/VDownload/Assets/Icons/Universal/Dark/Idle.png
new file mode 100644
index 0000000..f17c6cd
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Idle.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/MediaType.png b/VDownload/Assets/Icons/Universal/Dark/MediaType.png
new file mode 100644
index 0000000..a6716de
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/MediaType.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Path.png b/VDownload/Assets/Icons/Universal/Dark/Path.png
new file mode 100644
index 0000000..47832bc
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Path.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Quality.png b/VDownload/Assets/Icons/Universal/Dark/Quality.png
new file mode 100644
index 0000000..272124f
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Quality.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Transcoding.png b/VDownload/Assets/Icons/Universal/Dark/Transcoding.png
new file mode 100644
index 0000000..c73c67b
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Transcoding.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Trim.png b/VDownload/Assets/Icons/Universal/Dark/Trim.png
new file mode 100644
index 0000000..9a8c828
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Trim.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Views.png b/VDownload/Assets/Icons/Universal/Dark/Views.png
new file mode 100644
index 0000000..1541196
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Views.png differ
diff --git a/VDownload/Assets/Icons/Universal/Dark/Waiting.png b/VDownload/Assets/Icons/Universal/Dark/Waiting.png
new file mode 100644
index 0000000..91e00a7
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Dark/Waiting.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Author.png b/VDownload/Assets/Icons/Universal/Light/Author.png
new file mode 100644
index 0000000..c0b94c4
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Author.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Cancelled.png b/VDownload/Assets/Icons/Universal/Light/Cancelled.png
new file mode 100644
index 0000000..d12aaa4
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Cancelled.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Date.png b/VDownload/Assets/Icons/Universal/Light/Date.png
new file mode 100644
index 0000000..4dd74af
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Date.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Done.png b/VDownload/Assets/Icons/Universal/Light/Done.png
new file mode 100644
index 0000000..c6420c2
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Done.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Downloading.png b/VDownload/Assets/Icons/Universal/Light/Downloading.png
new file mode 100644
index 0000000..a3cd4d4
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Downloading.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Duration.png b/VDownload/Assets/Icons/Universal/Light/Duration.png
new file mode 100644
index 0000000..ba92311
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Duration.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Error.png b/VDownload/Assets/Icons/Universal/Light/Error.png
new file mode 100644
index 0000000..4d7a52c
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Error.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Finalizing.png b/VDownload/Assets/Icons/Universal/Light/Finalizing.png
new file mode 100644
index 0000000..72c0fb4
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Finalizing.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Idle.png b/VDownload/Assets/Icons/Universal/Light/Idle.png
new file mode 100644
index 0000000..08a09c1
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Idle.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/MediaType.png b/VDownload/Assets/Icons/Universal/Light/MediaType.png
new file mode 100644
index 0000000..74f446c
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/MediaType.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Path.png b/VDownload/Assets/Icons/Universal/Light/Path.png
new file mode 100644
index 0000000..0b06bc3
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Path.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Quality.png b/VDownload/Assets/Icons/Universal/Light/Quality.png
new file mode 100644
index 0000000..2caf368
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Quality.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Transcoding.png b/VDownload/Assets/Icons/Universal/Light/Transcoding.png
new file mode 100644
index 0000000..c2ffd89
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Transcoding.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Trim.png b/VDownload/Assets/Icons/Universal/Light/Trim.png
new file mode 100644
index 0000000..6692db7
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Trim.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Views.png b/VDownload/Assets/Icons/Universal/Light/Views.png
new file mode 100644
index 0000000..625b748
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Views.png differ
diff --git a/VDownload/Assets/Icons/Universal/Light/Waiting.png b/VDownload/Assets/Icons/Universal/Light/Waiting.png
new file mode 100644
index 0000000..dce6a60
Binary files /dev/null and b/VDownload/Assets/Icons/Universal/Light/Waiting.png differ
diff --git a/VDownload/Assets/Logo/LargeTile.scale-100.png b/VDownload/Assets/Logo/LargeTile.scale-100.png
new file mode 100644
index 0000000..1caadc1
Binary files /dev/null and b/VDownload/Assets/Logo/LargeTile.scale-100.png differ
diff --git a/VDownload/Assets/Logo/LargeTile.scale-125.png b/VDownload/Assets/Logo/LargeTile.scale-125.png
new file mode 100644
index 0000000..33b0c9c
Binary files /dev/null and b/VDownload/Assets/Logo/LargeTile.scale-125.png differ
diff --git a/VDownload/Assets/Logo/LargeTile.scale-150.png b/VDownload/Assets/Logo/LargeTile.scale-150.png
new file mode 100644
index 0000000..c60f389
Binary files /dev/null and b/VDownload/Assets/Logo/LargeTile.scale-150.png differ
diff --git a/VDownload/Assets/Logo/LargeTile.scale-200.png b/VDownload/Assets/Logo/LargeTile.scale-200.png
new file mode 100644
index 0000000..f39860b
Binary files /dev/null and b/VDownload/Assets/Logo/LargeTile.scale-200.png differ
diff --git a/VDownload/Assets/Logo/LargeTile.scale-400.png b/VDownload/Assets/Logo/LargeTile.scale-400.png
new file mode 100644
index 0000000..8891077
Binary files /dev/null and b/VDownload/Assets/Logo/LargeTile.scale-400.png differ
diff --git a/VDownload/Assets/Logo/Logo.png b/VDownload/Assets/Logo/Logo.png
new file mode 100644
index 0000000..3083d5d
Binary files /dev/null and b/VDownload/Assets/Logo/Logo.png differ
diff --git a/VDownload/Assets/Logo/SmallTile.scale-100.png b/VDownload/Assets/Logo/SmallTile.scale-100.png
new file mode 100644
index 0000000..85a7b76
Binary files /dev/null and b/VDownload/Assets/Logo/SmallTile.scale-100.png differ
diff --git a/VDownload/Assets/Logo/SmallTile.scale-125.png b/VDownload/Assets/Logo/SmallTile.scale-125.png
new file mode 100644
index 0000000..1252c6a
Binary files /dev/null and b/VDownload/Assets/Logo/SmallTile.scale-125.png differ
diff --git a/VDownload/Assets/Logo/SmallTile.scale-150.png b/VDownload/Assets/Logo/SmallTile.scale-150.png
new file mode 100644
index 0000000..d0f17a4
Binary files /dev/null and b/VDownload/Assets/Logo/SmallTile.scale-150.png differ
diff --git a/VDownload/Assets/Logo/SmallTile.scale-200.png b/VDownload/Assets/Logo/SmallTile.scale-200.png
new file mode 100644
index 0000000..126c231
Binary files /dev/null and b/VDownload/Assets/Logo/SmallTile.scale-200.png differ
diff --git a/VDownload/Assets/Logo/SmallTile.scale-400.png b/VDownload/Assets/Logo/SmallTile.scale-400.png
new file mode 100644
index 0000000..a2a9a46
Binary files /dev/null and b/VDownload/Assets/Logo/SmallTile.scale-400.png differ
diff --git a/VDownload/Assets/Logo/SplashScreen.scale-100.png b/VDownload/Assets/Logo/SplashScreen.scale-100.png
new file mode 100644
index 0000000..085bfcc
Binary files /dev/null and b/VDownload/Assets/Logo/SplashScreen.scale-100.png differ
diff --git a/VDownload/Assets/Logo/SplashScreen.scale-125.png b/VDownload/Assets/Logo/SplashScreen.scale-125.png
new file mode 100644
index 0000000..07dccc7
Binary files /dev/null and b/VDownload/Assets/Logo/SplashScreen.scale-125.png differ
diff --git a/VDownload/Assets/Logo/SplashScreen.scale-150.png b/VDownload/Assets/Logo/SplashScreen.scale-150.png
new file mode 100644
index 0000000..e987bce
Binary files /dev/null and b/VDownload/Assets/Logo/SplashScreen.scale-150.png differ
diff --git a/VDownload/Assets/Logo/SplashScreen.scale-200.png b/VDownload/Assets/Logo/SplashScreen.scale-200.png
new file mode 100644
index 0000000..53dd18a
Binary files /dev/null and b/VDownload/Assets/Logo/SplashScreen.scale-200.png differ
diff --git a/VDownload/Assets/Logo/SplashScreen.scale-400.png b/VDownload/Assets/Logo/SplashScreen.scale-400.png
new file mode 100644
index 0000000..94a0981
Binary files /dev/null and b/VDownload/Assets/Logo/SplashScreen.scale-400.png differ
diff --git a/VDownload/Assets/Logo/Square150x150Logo.scale-100.png b/VDownload/Assets/Logo/Square150x150Logo.scale-100.png
new file mode 100644
index 0000000..854bac9
Binary files /dev/null and b/VDownload/Assets/Logo/Square150x150Logo.scale-100.png differ
diff --git a/VDownload/Assets/Logo/Square150x150Logo.scale-125.png b/VDownload/Assets/Logo/Square150x150Logo.scale-125.png
new file mode 100644
index 0000000..c51bff5
Binary files /dev/null and b/VDownload/Assets/Logo/Square150x150Logo.scale-125.png differ
diff --git a/VDownload/Assets/Logo/Square150x150Logo.scale-150.png b/VDownload/Assets/Logo/Square150x150Logo.scale-150.png
new file mode 100644
index 0000000..bf26b9a
Binary files /dev/null and b/VDownload/Assets/Logo/Square150x150Logo.scale-150.png differ
diff --git a/VDownload/Assets/Logo/Square150x150Logo.scale-200.png b/VDownload/Assets/Logo/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..0a3dfd4
Binary files /dev/null and b/VDownload/Assets/Logo/Square150x150Logo.scale-200.png differ
diff --git a/VDownload/Assets/Logo/Square150x150Logo.scale-400.png b/VDownload/Assets/Logo/Square150x150Logo.scale-400.png
new file mode 100644
index 0000000..8f56e66
Binary files /dev/null and b/VDownload/Assets/Logo/Square150x150Logo.scale-400.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-16.png b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-16.png
new file mode 100644
index 0000000..7357738
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-16.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-24.png b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-24.png
new file mode 100644
index 0000000..82f04f0
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-24.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-256.png b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-256.png
new file mode 100644
index 0000000..1fb0224
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-256.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-32.png b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-32.png
new file mode 100644
index 0000000..ae6d7cb
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-32.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-48.png b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-48.png
new file mode 100644
index 0000000..9e758c3
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-lightunplated_targetsize-48.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-16.png b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-16.png
new file mode 100644
index 0000000..7357738
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-16.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-24.png b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-24.png
new file mode 100644
index 0000000..82f04f0
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-24.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-256.png b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-256.png
new file mode 100644
index 0000000..1fb0224
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-256.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-32.png b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-32.png
new file mode 100644
index 0000000..ae6d7cb
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-32.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-48.png b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-48.png
new file mode 100644
index 0000000..9e758c3
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.altform-unplated_targetsize-48.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.scale-100.png b/VDownload/Assets/Logo/Square44x44Logo.scale-100.png
new file mode 100644
index 0000000..b5d08fe
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.scale-100.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.scale-125.png b/VDownload/Assets/Logo/Square44x44Logo.scale-125.png
new file mode 100644
index 0000000..fee188e
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.scale-125.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.scale-150.png b/VDownload/Assets/Logo/Square44x44Logo.scale-150.png
new file mode 100644
index 0000000..c14ee37
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.scale-150.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.scale-200.png b/VDownload/Assets/Logo/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..93d26a6
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.scale-200.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.scale-400.png b/VDownload/Assets/Logo/Square44x44Logo.scale-400.png
new file mode 100644
index 0000000..ed217a4
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.scale-400.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-16.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-16.png
new file mode 100644
index 0000000..541122a
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-16.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-24.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-24.png
new file mode 100644
index 0000000..ae1c6bb
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-24.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-256.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-256.png
new file mode 100644
index 0000000..ddc7b84
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-256.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-32.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-32.png
new file mode 100644
index 0000000..fbea8b7
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-32.png differ
diff --git a/VDownload/Assets/Logo/Square44x44Logo.targetsize-48.png b/VDownload/Assets/Logo/Square44x44Logo.targetsize-48.png
new file mode 100644
index 0000000..8357d90
Binary files /dev/null and b/VDownload/Assets/Logo/Square44x44Logo.targetsize-48.png differ
diff --git a/VDownload/Assets/Logo/StoreLogo.scale-100.png b/VDownload/Assets/Logo/StoreLogo.scale-100.png
new file mode 100644
index 0000000..db31a48
Binary files /dev/null and b/VDownload/Assets/Logo/StoreLogo.scale-100.png differ
diff --git a/VDownload/Assets/Logo/StoreLogo.scale-125.png b/VDownload/Assets/Logo/StoreLogo.scale-125.png
new file mode 100644
index 0000000..dcab8cd
Binary files /dev/null and b/VDownload/Assets/Logo/StoreLogo.scale-125.png differ
diff --git a/VDownload/Assets/Logo/StoreLogo.scale-150.png b/VDownload/Assets/Logo/StoreLogo.scale-150.png
new file mode 100644
index 0000000..acf854c
Binary files /dev/null and b/VDownload/Assets/Logo/StoreLogo.scale-150.png differ
diff --git a/VDownload/Assets/Logo/StoreLogo.scale-200.png b/VDownload/Assets/Logo/StoreLogo.scale-200.png
new file mode 100644
index 0000000..4c84405
Binary files /dev/null and b/VDownload/Assets/Logo/StoreLogo.scale-200.png differ
diff --git a/VDownload/Assets/Logo/StoreLogo.scale-400.png b/VDownload/Assets/Logo/StoreLogo.scale-400.png
new file mode 100644
index 0000000..75a7096
Binary files /dev/null and b/VDownload/Assets/Logo/StoreLogo.scale-400.png differ
diff --git a/VDownload/Assets/Logo/Wide310x150Logo.scale-100.png b/VDownload/Assets/Logo/Wide310x150Logo.scale-100.png
new file mode 100644
index 0000000..83a567b
Binary files /dev/null and b/VDownload/Assets/Logo/Wide310x150Logo.scale-100.png differ
diff --git a/VDownload/Assets/Logo/Wide310x150Logo.scale-125.png b/VDownload/Assets/Logo/Wide310x150Logo.scale-125.png
new file mode 100644
index 0000000..b490b49
Binary files /dev/null and b/VDownload/Assets/Logo/Wide310x150Logo.scale-125.png differ
diff --git a/VDownload/Assets/Logo/Wide310x150Logo.scale-150.png b/VDownload/Assets/Logo/Wide310x150Logo.scale-150.png
new file mode 100644
index 0000000..5da8ba7
Binary files /dev/null and b/VDownload/Assets/Logo/Wide310x150Logo.scale-150.png differ
diff --git a/VDownload/Assets/Logo/Wide310x150Logo.scale-200.png b/VDownload/Assets/Logo/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..085bfcc
Binary files /dev/null and b/VDownload/Assets/Logo/Wide310x150Logo.scale-200.png differ
diff --git a/VDownload/Assets/Logo/Wide310x150Logo.scale-400.png b/VDownload/Assets/Logo/Wide310x150Logo.scale-400.png
new file mode 100644
index 0000000..53dd18a
Binary files /dev/null and b/VDownload/Assets/Logo/Wide310x150Logo.scale-400.png differ
diff --git a/VDownload/Assets/Other/UnknownThumbnail.png b/VDownload/Assets/Other/UnknownThumbnail.png
new file mode 100644
index 0000000..38ed3d5
Binary files /dev/null and b/VDownload/Assets/Other/UnknownThumbnail.png differ
diff --git a/VDownload/Objects/Enums/VideoSource.cs b/VDownload/Objects/Enums/VideoSource.cs
new file mode 100644
index 0000000..6732770
--- /dev/null
+++ b/VDownload/Objects/Enums/VideoSource.cs
@@ -0,0 +1,10 @@
+namespace VDownload.Objects.Enums
+{
+ public enum VideoSource
+ {
+ TwitchVod,
+ TwitchClip,
+ YoutubeVideo,
+ Null
+ }
+}
diff --git a/VDownload/Objects/Enums/VideoStatus.cs b/VDownload/Objects/Enums/VideoStatus.cs
new file mode 100644
index 0000000..942cd83
--- /dev/null
+++ b/VDownload/Objects/Enums/VideoStatus.cs
@@ -0,0 +1,10 @@
+namespace VDownload.Objects.Enums
+{
+ public enum VideoStatus
+ {
+ Idle,
+ Waiting,
+ InProgress,
+ Removed
+ }
+}
diff --git a/VDownload/Package.appxmanifest b/VDownload/Package.appxmanifest
new file mode 100644
index 0000000..0e7c734
--- /dev/null
+++ b/VDownload/Package.appxmanifest
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+ VDownload
+ Mateusz Skoczek
+ Assets\Logo\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VDownload/Services/Config.cs b/VDownload/Services/Config.cs
new file mode 100644
index 0000000..52f96db
--- /dev/null
+++ b/VDownload/Services/Config.cs
@@ -0,0 +1,101 @@
+// System
+using System.Collections.Generic;
+using Windows.Storage;
+
+namespace VDownload.Services
+{
+ internal class Config
+ {
+ #region CONSTANTS
+
+ // DATA VALUES LISTS
+ public static readonly string[] DateFormatList = new string[]
+ {
+ "yyyy.MM.dd",
+ "yyyy.dd.MM",
+ "dd.MM.yyyy",
+ "MM.dd.yyyy",
+ };
+ public static readonly string[] DefaultMediaTypeList = new string[]
+ {
+ "AV",
+ "A",
+ "V"
+ };
+ public static readonly string[] DefaultVideoExtensionList = new string[]
+ {
+ "MP4",
+ "WMV",
+ "HEVC"
+ };
+ public static readonly string[] DefaultAudioExtensionList = new string[]
+ {
+ "MP3",
+ "FLAC",
+ "WAV",
+ "M4A",
+ "ALAC",
+ "WMA"
+ };
+
+ // DEFAULT SETTINGS
+ private static readonly Dictionary DefaultSettings = new Dictionary()
+ {
+ { "max_video_tasks" , "5" },
+ { "default_video_extension", DefaultVideoExtensionList[0] },
+ { "default_audio_extension", DefaultAudioExtensionList[0] },
+ { "date_format", DateFormatList[0] },
+ { "default_media_type", DefaultMediaTypeList[0] },
+ { "default_output_filename", "[%date_pub%] %title%" },
+ { "use_mrfcrf444", "1" },
+ { "use_hardware_acceleration", "1" },
+ { "delete_temp_on_start", "1" },
+ { "delete_video_temp_after_error", "1" }
+ };
+
+ // SETTINGS CONTAINER
+ private static readonly ApplicationDataContainer SettingsContainer = ApplicationData.Current.LocalSettings;
+
+ #endregion
+
+
+
+ #region MAIN
+
+ // GET VALUE
+ public static string GetValue(string key)
+ {
+ return SettingsContainer.Values[key].ToString();
+ }
+
+ // SET VALUE
+ public static void SetValue(string key, string value)
+ {
+ SettingsContainer.Values[key] = value;
+ }
+
+ // SET DEFAULT
+ public static void SetDefault()
+ {
+ foreach (KeyValuePair s in DefaultSettings)
+ {
+ SettingsContainer.Values[s.Key] = s.Value;
+ }
+ }
+
+
+ // REBUILD
+ public static void Rebuild()
+ {
+ foreach (KeyValuePair s in DefaultSettings)
+ {
+ if (!SettingsContainer.Values.ContainsKey(s.Key))
+ {
+ SettingsContainer.Values[s.Key] = s.Value;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/VDownload/Services/Media.cs b/VDownload/Services/Media.cs
new file mode 100644
index 0000000..c29e848
--- /dev/null
+++ b/VDownload/Services/Media.cs
@@ -0,0 +1,119 @@
+// System
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.ApplicationModel.Resources;
+using Windows.Foundation;
+using Windows.Media.MediaProperties;
+using Windows.Media.Transcoding;
+using Windows.Storage;
+using Windows.Storage.Streams;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace VDownload.Services
+{
+ internal class Media
+ {
+ #region VARIABLES
+
+ // PROGRESS UI ELEMENTS
+ private TextBlock ProgressLabelTextblock;
+ private ProgressBar ProgressBar;
+
+ #endregion
+
+
+
+ #region MAIN
+
+ // TRANSCODE MEDIA FILE
+ public async Task Transcode(StorageFile inputFile, StorageFile outputFile, string extension, string mediaType, TimeSpan duration, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken token, TextBlock progressLabelTextblock, ProgressBar progressBar)
+ {
+ // Set progress UI elements
+ ProgressLabelTextblock = progressLabelTextblock;
+ ProgressBar = progressBar;
+
+ // Init transcoder
+ MediaTranscoder transcoder = new MediaTranscoder
+ {
+ HardwareAccelerationEnabled = Config.GetValue("use_hardware_acceleration") == "1" ? true : false,
+ VideoProcessingAlgorithm = Config.GetValue("use_mrfcrf444") == "1" ? MediaVideoProcessingAlgorithm.MrfCrf444 : MediaVideoProcessingAlgorithm.Default
+ };
+ if (0 < trimStart.TotalMilliseconds && trimStart.TotalMilliseconds < duration.TotalMilliseconds) // Set trimming at start
+ {
+ transcoder.TrimStartTime = trimStart;
+ }
+ if (0 < trimEnd.TotalMilliseconds && trimEnd.TotalMilliseconds < duration.TotalMilliseconds) // Set trimming at end
+ {
+ transcoder.TrimStopTime = trimEnd;
+ }
+
+ // Set video encoding profile
+ MediaEncodingProfile profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD1080p);
+ switch (extension)
+ {
+ case "MP4": profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD1080p); break;
+ case "WMV": profile = MediaEncodingProfile.CreateWmv(VideoEncodingQuality.HD1080p); break;
+ case "HEVC": profile = MediaEncodingProfile.CreateHevc(VideoEncodingQuality.HD1080p); break;
+ case "MP3": profile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High); break;
+ case "FLAC": profile = MediaEncodingProfile.CreateFlac(AudioEncodingQuality.High); break;
+ case "WAV": profile = MediaEncodingProfile.CreateWav(AudioEncodingQuality.High); break;
+ case "M4A": profile = MediaEncodingProfile.CreateM4a(AudioEncodingQuality.High); break;
+ case "ALAC": profile = MediaEncodingProfile.CreateAlac(AudioEncodingQuality.High); break;
+ case "WMA": profile = MediaEncodingProfile.CreateWma(AudioEncodingQuality.High); break;
+ }
+ var videoData = await inputFile.Properties.GetVideoPropertiesAsync();
+ var audioData = await inputFile.Properties.GetMusicPropertiesAsync();
+ if (mediaType != "A")
+ {
+ profile.Video.Height = videoData.Height;
+ profile.Video.Width = videoData.Width;
+ profile.Video.Bitrate = videoData.Bitrate - audioData.Bitrate;
+ }
+ if (mediaType != "V")
+ {
+ profile.Audio.Bitrate = audioData.Bitrate;
+ }
+ if (mediaType == "V")
+ {
+ var audioTracks = profile.GetAudioTracks();
+ audioTracks.Clear();
+ profile.SetAudioTracks(audioTracks.AsEnumerable());
+ }
+
+ // Start transcoding operation
+ using (IRandomAccessStream outputFileOpened = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
+ {
+ PrepareTranscodeResult transcodingPreparated = await transcoder.PrepareStreamTranscodeAsync(await inputFile.OpenAsync(FileAccessMode.Read), outputFileOpened, profile);
+ IAsyncActionWithProgress transcodingTask = transcodingPreparated.TranscodeAsync();
+ try
+ {
+ await transcodingTask.AsTask(token, new Progress(OnProgress));
+ await outputFileOpened.FlushAsync();
+ }
+ catch (TaskCanceledException) { }
+ transcodingTask.Close();
+ }
+ }
+
+ #endregion
+
+
+
+ #region EVENTS
+
+ // ON PROGRESS
+ private void OnProgress(double percent)
+ {
+ // Set progress
+ ProgressLabelTextblock.Text = $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelTranscoding")} ({Math.Floor(percent)}%)";
+ ProgressBar.IsIndeterminate = false;
+ ProgressBar.Visibility = Visibility.Visible;
+ ProgressBar.Value = percent;
+ }
+
+ #endregion
+ }
+}
diff --git a/VDownload/Services/Source.cs b/VDownload/Services/Source.cs
new file mode 100644
index 0000000..0678f46
--- /dev/null
+++ b/VDownload/Services/Source.cs
@@ -0,0 +1,43 @@
+// Internal
+using VDownload.Objects.Enums;
+
+// System
+using System;
+using System.Linq;
+
+namespace VDownload.Services
+{
+ internal class Source
+ {
+ public static (VideoSource, string) GetVideoSourceData(Uri url)
+ {
+ // Twitch VOD
+ if (url.Host == "www.twitch.tv" && url.Segments.Contains("videos/"))
+ {
+ return (VideoSource.TwitchVod, url.Segments[url.Segments.Length - 1].Replace("/", ""));
+ }
+
+ // Twitch Clip
+ else if ((url.Host == "www.twitch.tv" && url.Segments.Contains("clip/")) || url.Host == "clips.twitch.tv")
+ {
+ return (VideoSource.TwitchClip, url.Segments[url.Segments.Length - 1].Replace("/", ""));
+ }
+
+ // Youtube Video
+ else if (url.Host == "www.youtube.com" && url.Segments.Contains("watch"))
+ {
+ return (VideoSource.YoutubeVideo, url.Query.Replace("?", "").Split('&')[0].Replace("v=", ""));
+ }
+ else if (url.Host == "youtu.be")
+ {
+ return (VideoSource.YoutubeVideo, url.Segments[url.Segments.Length - 1]);
+ }
+
+ // Unknown
+ else
+ {
+ return (VideoSource.Null, "");
+ }
+ }
+ }
+}
diff --git a/VDownload/Services/Videos.cs b/VDownload/Services/Videos.cs
new file mode 100644
index 0000000..992f29a
--- /dev/null
+++ b/VDownload/Services/Videos.cs
@@ -0,0 +1,64 @@
+// Internal
+using VDownload.Sources;
+
+// System
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace VDownload.Services
+{
+ internal class Videos
+ {
+ #region MAIN
+
+ // VIDEO OBJECTS LIST
+ public static List VideoObjectsList = new List();
+
+ // ACTIVE VIDEO TASKS LIST
+ public static List VideoTasksList = new List();
+
+ // WAIT FOR FREE SPACE IN ACTIVE VIDEO TASKS LIST
+ public static async Task WaitForFreeSpace(CancellationToken token)
+ {
+ await Task.Run(async () =>
+ {
+ while (!(VideoTasksList.Count < int.Parse((string)Config.GetValue("max_video_tasks"))) && !token.IsCancellationRequested)
+ {
+ await Task.Delay(50);
+ }
+ });
+ }
+
+ #endregion
+
+
+
+ #region GET UNIQUE ID
+
+ // VARIABLES
+ private static readonly Random Random = new Random();
+ private static readonly char[] CharsID = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
+ private static readonly int LengthID = 10;
+ private static readonly List UsedID = new List();
+
+ // METHOD
+ public static string GetUniqueID()
+ {
+ string id;
+ do
+ {
+ id = "";
+ while (id.Length < LengthID)
+ {
+ id += CharsID[Random.Next(0, CharsID.Length)];
+ }
+ } while (UsedID.Contains(id));
+ UsedID.Add(id);
+ return id;
+ }
+
+ #endregion
+ }
+}
diff --git a/VDownload/Sources/Twitch/Vod.cs b/VDownload/Sources/Twitch/Vod.cs
new file mode 100644
index 0000000..bedd5e2
--- /dev/null
+++ b/VDownload/Sources/Twitch/Vod.cs
@@ -0,0 +1,266 @@
+// Internal
+using VDownload.Services;
+
+// External
+using Newtonsoft.Json.Linq;
+
+// System
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.ApplicationModel.Resources;
+using Windows.Storage;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media.Imaging;
+
+namespace VDownload.Sources.Twitch
+{
+ internal class Vod
+ {
+ #region INIT
+
+ // ID
+ private string ID { get; set; }
+
+ // CONSTRUCTOR
+ public Vod(string id)
+ {
+ ID = id;
+ }
+
+ #endregion
+
+
+
+ #region MAIN
+
+ // GET METADATA
+ public async Task> GetMetadata()
+ {
+ // Client settings
+ WebClient Client = new WebClient();
+ Client.Headers.Add("Accept", "application/vnd.twitchtv.v5+json");
+ Client.Headers.Add("Client-ID", "v8kfhyc2980it9e7t5hhc7baukzuj2");
+
+ // Send request and get response
+ Uri requestUrl = new Uri($"https://api.twitch.tv/kraken/videos/{ID}");
+ JObject response = JObject.Parse(await Client.DownloadStringTaskAsync(requestUrl));
+
+ // Pack data into dictionary
+ Dictionary metadata = new Dictionary()
+ {
+ ["title"] = response["title"].ToString().Replace("\n", ""),
+ ["author"] = response["channel"]["display_name"].ToString(),
+ ["date"] = Convert.ToDateTime(response["created_at"].ToString()),
+ ["duration"] = TimeSpan.FromSeconds(int.Parse(response["length"].ToString())),
+ ["views"] = long.Parse(response["views"].ToString()),
+ ["url"] = new Uri(response["url"].ToString())
+ };
+ try
+ { metadata["thumbnail"] = new Uri(response["thumbnails"]["large"][0]["url"].ToString()); }
+ catch
+ { metadata["thumbnail"] = new Uri("Assets/Icons/Unknown/Thumbnail.png", UriKind.Relative); }
+
+ // Return metadata
+ return metadata;
+ }
+
+ // GET STREAMS
+ public async Task> GetStreams()
+ {
+ // Client settings
+ WebClient Client = new WebClient();
+ Client.Headers.Add("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko");
+
+ // Get access token
+ JObject accessToken = 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\"}}"));
+ string tokenVal = accessToken["data"]["videoPlaybackAccessToken"]["value"].ToString();
+ string tokenSig = accessToken["data"]["videoPlaybackAccessToken"]["signature"].ToString();
+
+ // Get streams
+ string[] response = Client.DownloadString($"http://usher.twitch.tv/vod/{ID}?nauth={tokenVal}&nauthsig={tokenSig}&allow_source=true&player=twitchweb").Split("\n");
+
+ // Pack data into dictionary
+ Dictionary streams = new Dictionary();
+ for (int i = 2; i < response.Length; i += 3)
+ {
+ string key = response[i].Replace("#EXT-X-MEDIA:", "").Split(',')[2].Replace("NAME=", "").Replace("\"", "");
+ Uri value = new Uri(response[i + 2]);
+ streams[key] = value;
+ }
+
+ // Return streams
+ return streams;
+ }
+
+ // DOWNLOAD VIDEO
+ private TextBlock ProgressLabelTextblock;
+ private ProgressBar ProgressBar;
+ private Image ProgressIcon;
+ public async Task Download(StorageFolder tempFolder, string quality, string extension, string mediaType, TimeSpan trimStart, TimeSpan trimEnd, TimeSpan duration, CancellationTokenSource token, TextBlock progressLabelTextblock, ProgressBar progressBar, Image progressIcon)
+ {
+ // Set variables
+ ProgressLabelTextblock = progressLabelTextblock;
+ ProgressBar = progressBar;
+ ProgressIcon = progressIcon;
+
+ // Set progress to downloading
+ if (!token.Token.IsCancellationRequested)
+ {
+ ProgressLabelTextblock.Text = $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelDownloading")} (0%)";
+ ProgressBar.IsIndeterminate = false;
+ ProgressBar.Visibility = Visibility.Visible;
+ ProgressBar.Value = 0;
+ ProgressIcon.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Downloading.png") };
+ }
+
+ // Get chunks
+ Uri[] chunks = new Uri[] { };
+ float chunksInTotal = 0;
+ float chunksDownloaded = 0;
+ if (!token.Token.IsCancellationRequested)
+ {
+ chunks = await ExtractChunksFromM3U8((await GetStreams())[quality]);
+ chunksInTotal = chunks.Length;
+ }
+
+ // Download
+ StorageFile rawFile = null;
+ if (!token.Token.IsCancellationRequested)
+ {
+ try
+ {
+ rawFile = await tempFolder.CreateFileAsync("raw.ts");
+ if (!token.Token.IsCancellationRequested)
+ {
+ Task writeTask = WriteChunkToFile(rawFile, await DownloadVodChunk(chunks[0]));
+ chunksDownloaded++;
+ ProgressLabelTextblock.Text = $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelDownloading")} ({Math.Floor(chunksDownloaded / chunksInTotal * 100)}%)";
+ ProgressBar.IsIndeterminate = false;
+ ProgressBar.Visibility = Visibility.Visible;
+ ProgressBar.Value = chunksDownloaded / chunksInTotal * 100;
+ Task downloadTask = DownloadVodChunk(chunks[1]);
+ for (int i = 2; i < chunks.Length; i++)
+ {
+ if (!token.Token.IsCancellationRequested)
+ {
+ await Task.WhenAll(downloadTask, writeTask);
+ chunksDownloaded++;
+ ProgressLabelTextblock.Text = $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelDownloading")} ({Math.Floor(chunksDownloaded / chunksInTotal * 100)}%)";
+ ProgressBar.IsIndeterminate = false;
+ ProgressBar.Visibility = Visibility.Visible;
+ ProgressBar.Value = chunksDownloaded / chunksInTotal * 100;
+ writeTask = WriteChunkToFile(rawFile, downloadTask.Result);
+ downloadTask = DownloadVodChunk(chunks[i]);
+ }
+ }
+ }
+ }
+ catch (WebException)
+ {
+ throw new Exception(ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelErrorInternetConnection"));
+ }
+ }
+
+ // Rendering
+ StorageFile outputFile = null;
+ if (!token.Token.IsCancellationRequested)
+ {
+ // Set progress to transcoding
+ ProgressLabelTextblock.Text = $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelTranscoding")} (0%)";
+ ProgressBar.IsIndeterminate = false;
+ ProgressBar.Visibility = Visibility.Visible;
+ ProgressBar.Value = 0;
+ ProgressIcon.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Transcoding.png") };
+
+ // Create transcoding output file
+ outputFile = await tempFolder.CreateFileAsync($"processed.{extension.ToLower()}");
+
+ // Run processing
+ await new Media().Transcode(rawFile, outputFile, extension, mediaType, duration, trimStart, trimEnd, token.Token, ProgressLabelTextblock, ProgressBar);
+ }
+
+ // Return output file
+ return outputFile;
+ }
+
+ #endregion
+
+
+
+ #region INTERNAL
+
+ // EXTRACT CHUNKS FROM M3U8 PLAYLIST
+ private async Task ExtractChunksFromM3U8(Uri streamUrl)
+ {
+ // Client settings
+ WebClient Client = new WebClient();
+ Client.Headers.Add("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko");
+
+ // Get playlist content
+ string request = await Client.DownloadStringTaskAsync(streamUrl);
+
+ // Pack into list
+ List videos = new List();
+ foreach (string l in request.Split("\n"))
+ {
+ if (l.Length > 0 && l[0] != '#')
+ {
+ string[] uriSegments = streamUrl.Segments;
+ string streamDir = "";
+ for (int i = 0; i < uriSegments.Length - 1; i++)
+ {
+ streamDir += uriSegments[i];
+ }
+ videos.Add(new Uri($@"{streamUrl.GetLeftPart(UriPartial.Scheme)}{streamUrl.Host}{streamDir}{l}"));
+ }
+ }
+
+ // Return videos
+ return videos.ToArray();
+ }
+
+ // DOWNLOAD VOD CHUNK
+ private async Task DownloadVodChunk(Uri url)
+ {
+ // Download
+ int errorCount = 0;
+ bool done = false;
+ while (!done && errorCount < 10)
+ {
+ try
+ {
+ using (WebClient Client = new WebClient())
+ {
+ return await Client.DownloadDataTaskAsync(url);
+ }
+ }
+ catch
+ {
+ errorCount++;
+ await Task.Delay(5000);
+ }
+ }
+ throw new WebException();
+ }
+
+ // WRITE CHUNK TO FILE
+ private Task WriteChunkToFile(StorageFile file, byte[] dataToWrite)
+ {
+ return Task.Factory.StartNew(() =>
+ {
+ using (var stream = new FileStream(file.Path, FileMode.Append))
+ {
+ stream.Write(dataToWrite, 0, dataToWrite.Length);
+ stream.Close();
+ }
+ });
+ }
+
+ #endregion
+ }
+}
diff --git a/VDownload/Sources/VObject.cs b/VDownload/Sources/VObject.cs
new file mode 100644
index 0000000..87874ee
--- /dev/null
+++ b/VDownload/Sources/VObject.cs
@@ -0,0 +1,673 @@
+// Internal
+using VDownload.Objects.Enums;
+using VDownload.Services;
+
+// System
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.ApplicationModel.ExtendedExecution;
+using Windows.ApplicationModel.Resources;
+using Windows.Storage;
+using Windows.UI.Text;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Imaging;
+using Windows.UI;
+using System.Diagnostics;
+
+namespace VDownload.Sources
+{
+ public class VObject
+ {
+ #region INIT
+
+ // VIDEO METADATA
+ private string UniqueID { get; set; }
+ public VideoSource SourceType { get; private set; }
+ public string ID { get; private set; }
+ public string Title { get; private set; }
+ public string Author { get; private set; }
+ public DateTime Date { get; private set; }
+ public long Views { get; private set; }
+ public TimeSpan Duration { get; private set; }
+ public Uri Url { get; private set; }
+ public Uri Thumbnail { get; private set; }
+ public Uri SourceIcon { get; private set; }
+ public Dictionary Streams { get; private set; }
+
+ // FILE PROCESS DATA
+ public string SelectedQuality { get; set; }
+ public TimeSpan TrimStart { get; set; }
+ public TimeSpan TrimEnd { get; set; }
+ public string MediaType { get; set; }
+ public string Filename { get; set; }
+ public string Extension { get; set; }
+ public string FilePath { get; set; }
+ public StorageFolder CustomSaveLocation { get; set; }
+
+ // VIDEO PANEL
+ private Grid VideoPanel { get; set; }
+ private StackPanel VideoPanelParent { get; set; }
+ private AppBarButton StartStopButton { get; set; }
+ private TextBlock ProgressLabelTextblock { get; set; }
+ private ProgressBar ProgressBar { get; set; }
+ private Image ProgressIcon { get; set; }
+
+ // VIDEO TASK
+ private object VideoSourceHandler { get; set; }
+ private CancellationTokenSource VideoTaskCancellationToken { get; set; }
+ private Task VideoTask { get; set; }
+ public VideoStatus VideoStatus { get; private set; }
+
+ // CONSTRUCTOR
+ public VObject(Uri url)
+ {
+ (SourceType, ID) = Source.GetVideoSourceData(url);
+ switch (SourceType) // (TODO)
+ {
+ case VideoSource.TwitchVod:
+ VideoSourceHandler = new Twitch.Vod(ID);
+ SourceIcon = new Uri("ms-appx:///Assets/Icons/Sources/Twitch.png");
+ break;
+ }
+ UniqueID = Videos.GetUniqueID();
+ }
+
+ #endregion
+
+
+
+ #region MAIN
+
+ // GET METADATA AND SET DATA VARIABLES
+ public async Task GetMetadata()
+ {
+ // Get metadata and streams
+ Task> metadataTask;
+ Task> streamsTask;
+ switch (SourceType) // (TODO)
+ {
+ case VideoSource.TwitchVod:
+ metadataTask = ((Twitch.Vod)VideoSourceHandler).GetMetadata();
+ streamsTask = ((Twitch.Vod)VideoSourceHandler).GetStreams();
+ break;
+ default:
+ throw new Exception(message: "Unknown video source");
+ }
+ await Task.WhenAll(metadataTask, streamsTask);
+ Dictionary metadata = metadataTask.Result;
+ Dictionary streams = streamsTask.Result;
+
+ // Set metadata
+ Title = (string)metadata["title"];
+ Author = (string)metadata["author"];
+ Date = (DateTime)metadata["date"];
+ Views = (long)metadata["views"];
+ Duration = (TimeSpan)metadata["duration"];
+ Thumbnail = (Uri)metadata["thumbnail"];
+ Url = (Uri)metadata["url"];
+ Streams = streams;
+
+ // Set default media type
+ MediaType = Config.GetValue("default_media_type");
+
+ // Set default quality
+ SelectedQuality = Streams.Keys.ToArray()[0];
+
+ // Set trim timestamps
+ TrimStart = new TimeSpan(0);
+ TrimEnd = Duration;
+
+ // Set defualt filename
+ Dictionary filenameTemplate = new Dictionary()
+ {
+ { "%title%", Title },
+ { "%author%", Author },
+ { "%date_pub%", Date.ToString(Config.GetValue("date_format")) },
+ { "%date_now%", DateTime.Now.ToString(Config.GetValue("date_format")) },
+ { "%views%", Views.ToString() },
+ { "%id%", ID }
+ };
+ string temporaryFilename = Config.GetValue("default_output_filename");
+ foreach (KeyValuePair t in filenameTemplate)
+ { temporaryFilename = temporaryFilename.Replace(t.Key, t.Value); }
+ foreach (char c in Path.GetInvalidFileNameChars())
+ { temporaryFilename = temporaryFilename.Replace(c, ' '); }
+ Filename = temporaryFilename;
+
+ // Set extension
+ Extension = MediaType == "A" ? Config.GetValue("default_audio_extension") : Config.GetValue("default_video_extension");
+
+ // Set visible path
+ if (CustomSaveLocation != null) FilePath = $@"{CustomSaveLocation.Path}\{Filename}.{Extension.ToLower()}";
+ else FilePath = $@"{UserDataPaths.GetDefault().Downloads}\VDownload\{Filename}.{Extension.ToLower()}";
+ }
+
+ // ADD VIDEO TO LIST
+ public void AddVideoToList(StackPanel parent)
+ {
+ // Set status to idle
+ VideoStatus = VideoStatus.Idle;
+
+ // Video panel management
+ VideoPanel = CreateVideoPanel();
+ VideoPanelParent = parent;
+ VideoPanelParent.Children.Add(VideoPanel);
+
+ // Add VObject to listed videos list
+ Videos.VideoObjectsList.Add(this);
+ }
+
+ // REMOVE VIDEO FROM LIST
+ public void RemoveVideoFromList()
+ {
+ // Remove video from video list
+ VideoPanelParent.Children.Remove(VideoPanel);
+
+ // Remove VObject from listed videos list
+ Videos.VideoObjectsList.Remove(this);
+
+ // Set status as removed
+ VideoStatus = VideoStatus.Removed;
+ }
+
+ // START VIDEO TASK
+ public async Task Start()
+ {
+ // Change video status to idle
+ VideoStatus = VideoStatus.Waiting;
+
+ // Set cancellation token
+ VideoTaskCancellationToken = new CancellationTokenSource();
+
+ // Set panel (start and waiting)
+ StartStopButton.Icon = new SymbolIcon(Symbol.Stop);
+ ProgressLabelTextblock.Text = ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelWaiting");
+ ProgressBar.IsIndeterminate = true;
+ ProgressBar.Visibility = Visibility.Visible;
+ ProgressIcon.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Waiting.png") };
+
+ // Wait for free space in active video tasks list
+ await Videos.WaitForFreeSpace(VideoTaskCancellationToken.Token);
+
+ // Set end message
+ string endMessage = ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelCancelled");
+ Uri endIconSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Cancelled.png");
+
+ // Start video downloading process
+ if (!VideoTaskCancellationToken.IsCancellationRequested)
+ {
+ // Init temp folder
+ StorageFolder tempFolder = await ApplicationData.Current.TemporaryFolder.CreateFolderAsync(UniqueID);
+
+ // Set video task handler (TODO)
+ switch (SourceType)
+ {
+ case VideoSource.TwitchVod:
+ VideoTask = ((Twitch.Vod)VideoSourceHandler).Download(tempFolder, SelectedQuality, Extension, MediaType, TrimStart, TrimEnd, Duration, VideoTaskCancellationToken, ProgressLabelTextblock, ProgressBar, ProgressIcon);
+ break;
+ default:
+ throw new Exception(message: "Unknown video source");
+ }
+
+ // Add video task to active video tasks list
+ Videos.VideoTasksList.Add(VideoTask);
+ VideoStatus = VideoStatus.InProgress;
+
+ // Start video task
+ StorageFile downloadedFile = null;
+ bool error = false;
+ string errorMessage = "";
+ Stopwatch executeStopwatch = new Stopwatch();
+ executeStopwatch.Start();
+ try
+ {
+ // Request no suspendable session
+ ExtendedExecutionSession session = new ExtendedExecutionSession { Reason = ExtendedExecutionReason.Unspecified };
+ await session.RequestExtensionAsync();
+
+ // Run task
+ downloadedFile = await VideoTask;
+
+ // Dispose session
+ session.Dispose();
+ }
+ catch (Exception ex)
+ {
+ // Set error info (error identifier and endMessage)
+ error = true;
+ errorMessage = ex.Message;
+ }
+ finally
+ {
+ // Set progress to finalizing
+ ProgressLabelTextblock.Text = ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelFinalizing");
+ ProgressBar.IsIndeterminate = true;
+ ProgressBar.Visibility = Visibility.Visible;
+ ProgressIcon.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Finalizing.png") };
+
+ // Move to output destination
+ if (!VideoTaskCancellationToken.IsCancellationRequested && !error)
+ {
+ // Set output file
+ StorageFile outputFile;
+ if (CustomSaveLocation != null) outputFile = await CustomSaveLocation.CreateFileAsync($"{Filename}.{Extension.ToLower()}", CreationCollisionOption.GenerateUniqueName);
+ else outputFile = await DownloadsFolder.CreateFileAsync($"{Filename}.{Extension.ToLower()}", CreationCollisionOption.GenerateUniqueName);
+
+ // Create and move file
+ await downloadedFile.MoveAndReplaceAsync(outputFile);
+ }
+
+ // Delete temp
+ if (!error || Config.GetValue("delete_video_temp_after_error") == "1")
+ await tempFolder.DeleteAsync();
+
+ // Remove video task from active video tasks list
+ Videos.VideoTasksList.Remove(VideoTask);
+ VideoStatus = VideoStatus.Idle;
+
+ // Stop stopwatch
+ executeStopwatch.Stop();
+
+ // Set end message
+ if (!VideoTaskCancellationToken.IsCancellationRequested)
+ {
+ if (error)
+ {
+ endMessage = $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelError")} ({errorMessage})";
+ endIconSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Error.png");
+ }
+ else
+ {
+ endMessage = $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelDone1")} {Math.Round(executeStopwatch.Elapsed.TotalSeconds, 0)} {ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelDone2")}";
+ endIconSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Done.png");
+ }
+ }
+ }
+ }
+
+ // End message
+ ProgressLabelTextblock.Text = endMessage;
+ StartStopButton.Icon = new SymbolIcon(Symbol.Download);
+ ProgressBar.Visibility = Visibility.Collapsed;
+ ProgressIcon.Source = new BitmapImage { UriSource = endIconSource };
+ }
+
+ #endregion
+
+
+
+ #region PANEL
+
+ // CREATE VIDEO PANEL
+ private Grid CreateVideoPanel()
+ {
+ // Base grid
+ Grid baseGrid = new Grid
+ {
+ Background = new SolidColorBrush((Color)Application.Current.Resources["SystemChromeAltHighColor"]),
+ Margin = new Thickness(0, 5, 0, 5),
+ BorderThickness = new Thickness(20),
+ BorderBrush = new SolidColorBrush((Color)Application.Current.Resources["SystemChromeAltHighColor"]),
+ CornerRadius = new CornerRadius(5),
+ };
+ baseGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ baseGrid.ColumnDefinitions.Add(new ColumnDefinition());
+ baseGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+
+
+
+ // Thumbnail image
+ Image thumbnailImage = new Image
+ {
+ Source = new BitmapImage { UriSource = Thumbnail },
+ Height = 144,
+ HorizontalAlignment = HorizontalAlignment.Left,
+ VerticalAlignment = VerticalAlignment.Top,
+ };
+ Grid.SetColumn(thumbnailImage, 0);
+ baseGrid.Children.Add(thumbnailImage);
+
+
+
+ // Data grid
+ Grid dataGrid = new Grid
+ {
+ Margin = new Thickness(20, 0, 20, 0),
+ };
+ dataGrid.RowDefinitions.Add(new RowDefinition());
+ dataGrid.RowDefinitions.Add(new RowDefinition());
+ dataGrid.RowDefinitions.Add(new RowDefinition());
+ dataGrid.RowDefinitions.Add(new RowDefinition());
+ Grid.SetColumn(dataGrid, 1);
+ baseGrid.Children.Add(dataGrid);
+
+ // Title textblock
+ TextBlock titleTextBlock = new TextBlock
+ {
+ Text = Title,
+ FontWeight = FontWeights.Bold,
+ FontSize = 16,
+ Margin = new Thickness(0, 0, 0, 8),
+ };
+ Grid.SetRow(titleTextBlock, 0);
+ dataGrid.Children.Add(titleTextBlock);
+
+
+ // Metadata grid
+ Grid metadataGrid = new Grid();
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(30) });
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(30) });
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ metadataGrid.ColumnDefinitions.Add(new ColumnDefinition());
+ metadataGrid.RowDefinitions.Add(new RowDefinition());
+ metadataGrid.RowDefinitions.Add(new RowDefinition());
+ metadataGrid.RowDefinitions.Add(new RowDefinition());
+ metadataGrid.RowDefinitions.Add(new RowDefinition());
+ Grid.SetRow(metadataGrid, 1);
+ dataGrid.Children.Add(metadataGrid);
+ double iconSize = 15;
+ double textSize = 11;
+ double iconMargin = 5;
+
+ // Author icon
+ Image authorIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Author.png") },
+ Margin = new Thickness(0, iconMargin, 0, iconMargin),
+ Width = iconSize,
+ };
+ Grid.SetColumn(authorIcon, 0);
+ Grid.SetRow(authorIcon, 0);
+ metadataGrid.Children.Add(authorIcon);
+
+ // Author data textblock
+ TextBlock authorDataTextBlock = new TextBlock
+ {
+ Text = Author,
+ Margin = new Thickness(10, 0, 0, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize,
+ };
+ Grid.SetColumn(authorDataTextBlock, 1);
+ Grid.SetRow(authorDataTextBlock, 0);
+ metadataGrid.Children.Add(authorDataTextBlock);
+
+ // Views icon
+ Image viewsIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Views.png") },
+ Margin = new Thickness(0, iconMargin, 0, iconMargin),
+ Width = iconSize,
+ };
+ Grid.SetColumn(viewsIcon, 3);
+ Grid.SetRow(viewsIcon, 0);
+ metadataGrid.Children.Add(viewsIcon);
+
+ // Views data textblock
+ TextBlock viewsDataTextBlock = new TextBlock
+ {
+ Text = Views.ToString(),
+ Margin = new Thickness(10, 0, 0, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize,
+ };
+ Grid.SetColumn(viewsDataTextBlock, 4);
+ Grid.SetRow(viewsDataTextBlock, 0);
+ metadataGrid.Children.Add(viewsDataTextBlock);
+
+ // Date icon
+ Image dateIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Date.png") },
+ Margin = new Thickness(0, iconMargin, 0, iconMargin),
+ Width = iconSize,
+ };
+ Grid.SetColumn(dateIcon, 6);
+ Grid.SetRow(dateIcon, 0);
+ metadataGrid.Children.Add(dateIcon);
+
+ // Date data textblock
+ TextBlock dateDataTextBlock = new TextBlock
+ {
+ Text = Date.ToString((string)Config.GetValue("date_format")),
+ Margin = new Thickness(10, 0, 0, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize,
+ };
+ Grid.SetColumn(dateDataTextBlock, 7);
+ Grid.SetRow(dateDataTextBlock, 0);
+ metadataGrid.Children.Add(dateDataTextBlock);
+
+ // Duration icon
+ Image durationIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Duration.png") },
+ Margin = new Thickness(0, iconMargin, 0, iconMargin),
+ Width = iconSize,
+ };
+ Grid.SetColumn(durationIcon, 0);
+ Grid.SetRow(durationIcon, 1);
+ metadataGrid.Children.Add(durationIcon);
+
+ // Duration data textblock
+ TextBlock durationDataTextBlock = new TextBlock
+ {
+ Text = Duration.ToString(),
+ Margin = new Thickness(10, 0, 0, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize,
+ };
+ Grid.SetColumn(durationDataTextBlock, 1);
+ Grid.SetRow(durationDataTextBlock, 1);
+ metadataGrid.Children.Add(durationDataTextBlock);
+
+ // Media type & quality icon
+ Image qualityIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Quality.png") },
+ Margin = new Thickness(0, iconMargin, 0, iconMargin),
+ Width = iconSize,
+ };
+ Grid.SetColumn(qualityIcon, 3);
+ Grid.SetRow(qualityIcon, 1);
+ metadataGrid.Children.Add(qualityIcon);
+
+ // Media type & quality data textblock
+ string mediaTypeQualityData = MediaType == "A" ? "" : SelectedQuality;
+ switch (MediaType)
+ {
+ case "AV": mediaTypeQualityData += $" ({ResourceLoader.GetForCurrentView().GetString("VideoPanelMediaTypeDataAV")})"; break;
+ case "A": mediaTypeQualityData += $"{ResourceLoader.GetForCurrentView().GetString("VideoPanelMediaTypeDataA")}"; break;
+ case "V": mediaTypeQualityData += $" ({ResourceLoader.GetForCurrentView().GetString("VideoPanelMediaTypeDataV")})"; break;
+ default: mediaTypeQualityData += $" ({ResourceLoader.GetForCurrentView().GetString("VideoPanelMediaTypeDataAV")})"; break;
+ }
+ TextBlock qualityDataTextBlock = new TextBlock
+ {
+ Text = mediaTypeQualityData,
+ Margin = new Thickness(10, 0, 0, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize,
+ };
+ Grid.SetColumn(qualityDataTextBlock, 4);
+ Grid.SetRow(qualityDataTextBlock, 1);
+ metadataGrid.Children.Add(qualityDataTextBlock);
+
+ // Trim icon
+ Image trimIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Trim.png") },
+ Margin = new Thickness(0, iconMargin, 0, iconMargin),
+ Width = iconSize,
+ };
+ Grid.SetColumn(trimIcon, 6);
+ Grid.SetRow(trimIcon, 1);
+ metadataGrid.Children.Add(trimIcon);
+
+ // Trim data textblock
+ TextBlock trimDataTextBlock = new TextBlock
+ {
+ Text = $"{TrimStart} - {TrimEnd}",
+ Margin = new Thickness(10, 0, 0, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize,
+ };
+ Grid.SetColumn(trimDataTextBlock, 7);
+ Grid.SetRow(trimDataTextBlock, 1);
+ metadataGrid.Children.Add(trimDataTextBlock);
+
+ // Path icon
+ Image pathIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Path.png") },
+ Margin = new Thickness(0, iconMargin, 0, iconMargin),
+ Width = iconSize,
+ };
+ Grid.SetColumn(pathIcon, 0);
+ Grid.SetRow(pathIcon, 2);
+ metadataGrid.Children.Add(pathIcon);
+
+ // File path data textblock
+ TextBlock filePathDataTextBlock = new TextBlock
+ {
+ Text = FilePath,
+ Margin = new Thickness(10, 0, 0, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize,
+ };
+ Grid.SetColumn(filePathDataTextBlock, 1);
+ Grid.SetColumnSpan(filePathDataTextBlock, 7);
+ Grid.SetRow(filePathDataTextBlock, 2);
+ metadataGrid.Children.Add(filePathDataTextBlock);
+
+
+ // Progress grid
+ Grid progressGrid = new Grid
+ {
+ Margin = new Thickness(0, 10, 0, 0),
+ };
+ progressGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ progressGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+ progressGrid.ColumnDefinitions.Add(new ColumnDefinition());
+ Grid.SetRow(progressGrid, 3);
+ Grid.SetColumnSpan(progressGrid, 8);
+ metadataGrid.Children.Add(progressGrid);
+
+ // Progress icon
+ ProgressIcon = new Image
+ {
+ Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Idle.png") },
+ Width = iconSize,
+ };
+ Grid.SetColumn(ProgressIcon, 0);
+ progressGrid.Children.Add(ProgressIcon);
+
+ // Progress textblock
+ ProgressLabelTextblock = new TextBlock
+ {
+ Text = ResourceLoader.GetForCurrentView().GetString("VideoPanelProgressLabelIdle"),
+ Margin = new Thickness(10, 0, 20, 0),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontSize = textSize
+ };
+ Grid.SetColumn(ProgressLabelTextblock, 1);
+ progressGrid.Children.Add(ProgressLabelTextblock);
+
+ // Progress bar
+ ProgressBar = new ProgressBar
+ {
+ Visibility = Visibility.Collapsed,
+ };
+ Grid.SetColumn(ProgressBar, 2);
+ progressGrid.Children.Add(ProgressBar);
+
+ // Buttons grid
+ Grid buttonsGrid = new Grid
+ {
+ VerticalAlignment = VerticalAlignment.Center,
+ };
+ buttonsGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
+ buttonsGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
+ buttonsGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
+ Grid.SetColumn(buttonsGrid, 2);
+ baseGrid.Children.Add(buttonsGrid);
+
+ // Source icon
+ AppBarButton sourceButton = new AppBarButton
+ {
+ Icon = new BitmapIcon { UriSource = SourceIcon, ShowAsMonochrome = false },
+ Width = 40,
+ Height = 48,
+ };
+ sourceButton.Click += SourceButtonClicked;
+ Grid.SetRow(sourceButton, 0);
+ buttonsGrid.Children.Add(sourceButton);
+
+ // Delete button
+ AppBarButton deleteButton = new AppBarButton
+ {
+ Icon = new SymbolIcon(Symbol.Clear),
+ Width = 40,
+ Height = 48,
+ };
+ deleteButton.Click += DeleteButtonClicked;
+ Grid.SetRow(deleteButton, 1);
+ buttonsGrid.Children.Add(deleteButton);
+
+ // Download/Stop button
+ StartStopButton = new AppBarButton
+ {
+ Icon = new SymbolIcon(Symbol.Download),
+ Width = 40,
+ Height = 48,
+ };
+ StartStopButton.Click += DownloadStopButtonClicked;
+ Grid.SetRow(StartStopButton, 2);
+ buttonsGrid.Children.Add(StartStopButton);
+
+ // Return panel
+ return baseGrid;
+ }
+
+ // SOURCE BUTTON
+ private async void SourceButtonClicked(object sender, RoutedEventArgs e)
+ {
+ // Launch the website
+ await Windows.System.Launcher.LaunchUriAsync(Url);
+ }
+
+ // DELETE BUTTON
+ private void DeleteButtonClicked(object sender, RoutedEventArgs e)
+ {
+ // Cancel if video downloading was started
+ if (VideoStatus == VideoStatus.InProgress || VideoStatus == VideoStatus.Waiting)
+ VideoTaskCancellationToken.Cancel();
+
+ // Remove video from the list
+ RemoveVideoFromList();
+ }
+
+ // DOWNLOAD STOP BUTTON
+ private async void DownloadStopButtonClicked(object sender, RoutedEventArgs e)
+ {
+ // Cancel if video downloading was started
+ if (VideoStatus == VideoStatus.InProgress || VideoStatus == VideoStatus.Waiting)
+ VideoTaskCancellationToken.Cancel();
+
+ // Start if video downloading wasn't started
+ else
+ await Start();
+ }
+
+ #endregion
+ }
+}
diff --git a/VDownload/Strings/en-US/Resources.resw b/VDownload/Strings/en-US/Resources.resw
new file mode 100644
index 0000000..1bc6390
--- /dev/null
+++ b/VDownload/Strings/en-US/Resources.resw
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Cancel
+
+
+ Add
+
+
+ ADD VIDEO
+
+
+ Only audio
+
+
+ Normal
+
+
+ Only video
+
+
+ Media type
+
+
+ Quality
+
+
+ Trim
+
+
+ File
+
+
+ Location
+
+
+ Video not found. Try again.
+
+
+ Search
+
+
+ Paste URL and click "Search" button
+
+
+ Add video
+
+
+ 75
+
+
+ Download All
+
+
+ 90
+
+
+ Settings
+
+
+ 65
+
+
+ Only audio
+
+
+ Audio & Video
+
+
+ Only video
+
+
+ Cancelled
+
+
+ Done in
+
+
+ seconds
+
+
+ Downloading
+
+
+ An error occured!
+
+
+ Internet connection error
+
+
+ Finalizing
+
+
+ Idle
+
+
+ Transcoding
+
+
+ Queued
+
+
+ Only audio
+
+
\ No newline at end of file
diff --git a/VDownload/VDownload.csproj b/VDownload/VDownload.csproj
new file mode 100644
index 0000000..90e9baf
--- /dev/null
+++ b/VDownload/VDownload.csproj
@@ -0,0 +1,305 @@
+
+
+
+
+ Debug
+ x86
+ {324AB81A-F68D-424D-B90E-5402F5E44240}
+ AppContainerExe
+ Properties
+ VDownload
+ VDownload
+ en-US
+ UAP
+ 10.0.19041.0
+ 10.0.19041.0
+ 14
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ true
+ false
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x86
+ false
+ prompt
+ true
+
+
+ bin\x86\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ bin\ARM\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ bin\ARM64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ bin\x64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+ true
+
+
+ PackageReference
+
+
+
+ App.xaml
+
+
+
+
+
+
+
+
+
+
+ AddVideoBase.xaml
+
+
+ AddVideoLoading.xaml
+
+
+ AddVideoMain.xaml
+
+
+ AddVideoNotFound.xaml
+
+
+ AddVideoStart.xaml
+
+
+ MainPage.xaml
+
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+ 6.2.13
+
+
+ 7.1.2
+
+
+ 2.7.0
+
+
+ 13.0.1
+
+
+
+
+
+
+ 14.0
+
+
+
+
\ No newline at end of file
diff --git a/VDownload/Views/AddVideo/AddVideoBase.xaml b/VDownload/Views/AddVideo/AddVideoBase.xaml
new file mode 100644
index 0000000..9447737
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoBase.xaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VDownload/Views/AddVideo/AddVideoBase.xaml.cs b/VDownload/Views/AddVideo/AddVideoBase.xaml.cs
new file mode 100644
index 0000000..843d843
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoBase.xaml.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using VDownload.Sources;
+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 Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+
+namespace VDownload.Views.AddVideo
+{
+ public sealed partial class AddVideoBase : ContentDialog
+ {
+ public AddVideoBase()
+ {
+ this.InitializeComponent();
+ AddVideoContent.Navigate(typeof(AddVideoStart));
+ }
+
+ private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
+ {
+ // Attach video info as content
+ Content = AddVideoMain.Video;
+ }
+
+ private async void AddVideoSearchButton_Click(object sender, RoutedEventArgs e)
+ {
+ // Navigate to loading page
+ AddVideoContent.Navigate(typeof(AddVideoLoading));
+ IsPrimaryButtonEnabled = false;
+
+ // Check url and get data
+ Uri url = null;
+ try
+ {
+ // Get url from textbox
+ url = new Uri(AddVideoUrlTextBox.Text);
+ }
+ catch
+ {
+ // Navigate to not found page if url is invalid
+ AddVideoContent.Navigate(typeof(AddVideoNotFound));
+ }
+ finally
+ {
+ if (url != null)
+ {
+ try
+ {
+ // Get video data
+ VObject video = new VObject(url);
+ await video.GetMetadata();
+
+ // Navigate to video found page
+ AddVideoContent.Navigate(typeof(AddVideoMain), video);
+ IsPrimaryButtonEnabled = true;
+ }
+ catch
+ {
+ // Navigate to not found page if url is invalid
+ AddVideoContent.Navigate(typeof(AddVideoNotFound));
+ }
+ }
+ else
+ {
+ // Navigate to not found page if url is invalid
+ AddVideoContent.Navigate(typeof(AddVideoNotFound));
+ }
+ }
+ }
+ }
+}
diff --git a/VDownload/Views/AddVideo/AddVideoLoading.xaml b/VDownload/Views/AddVideo/AddVideoLoading.xaml
new file mode 100644
index 0000000..3864305
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoLoading.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/VDownload/Views/AddVideo/AddVideoLoading.xaml.cs b/VDownload/Views/AddVideo/AddVideoLoading.xaml.cs
new file mode 100644
index 0000000..2e9b05d
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoLoading.xaml.cs
@@ -0,0 +1,30 @@
+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.AddVideo
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AddVideoLoading : Page
+ {
+ public AddVideoLoading()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/VDownload/Views/AddVideo/AddVideoMain.xaml b/VDownload/Views/AddVideo/AddVideoMain.xaml
new file mode 100644
index 0000000..dad4314
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoMain.xaml
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VDownload/Views/AddVideo/AddVideoMain.xaml.cs b/VDownload/Views/AddVideo/AddVideoMain.xaml.cs
new file mode 100644
index 0000000..bcf3185
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoMain.xaml.cs
@@ -0,0 +1,272 @@
+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.Services;
+using VDownload.Sources;
+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.Media.Imaging;
+using Windows.UI.Xaml.Navigation;
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+
+namespace VDownload.Views.AddVideo
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AddVideoMain : Page
+ {
+ #region INIT
+
+ // VIDEO OBJECT
+ public static VObject Video;
+
+ // CONSTRUCTOR
+ public AddVideoMain()
+ {
+ InitializeComponent();
+ }
+
+ #endregion
+
+
+
+ #region MAIN
+
+ // NAVIGATED TO THIS PAGE
+ protected override async void OnNavigatedTo(NavigationEventArgs e)
+ {
+ // Get video object from parent
+ base.OnNavigatedTo(e);
+ Video = (VObject)e.Parameter;
+
+ // Set icons theme
+ AddVideoVideoDataAuthorIconImage.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Author.png") };
+ AddVideoVideoDataViewsIconImage.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Views.png") };
+ AddVideoVideoDataDateIconImage.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Date.png") };
+ AddVideoVideoDataDurationIconImage.Source = new BitmapImage { UriSource = new Uri($"ms-appx:///Assets/Icons/Universal/{(Application.Current.RequestedTheme == ApplicationTheme.Dark ? "Dark" : "Light")}/Duration.png") };
+
+ // Set source image
+ AddVideoVideoDataSourceIconImage.Source = new BitmapImage(Video.SourceIcon);
+
+ // Set title
+ AddVideoVideoDataTitleTextBlock.Text = Video.Title;
+
+ // Set thumbnail
+ try
+ {
+ AddVideoVideoDataThumbnailImage.Source = new BitmapImage(Video.Thumbnail);
+ }
+ catch { }
+
+ // Set metadata
+ AddVideoVideoDataAuthorTextBlock.Text = Video.Author;
+ AddVideoVideoDataViewsTextBlock.Text = Video.Views.ToString();
+ AddVideoVideoDataDateTextBlock.Text = Video.Date.ToString((string)Config.GetValue("date_format"));
+ AddVideoVideoDataDurationTextBlock.Text = Video.Duration.ToString();
+
+ // Set items in quality combobox
+ foreach (string q in Video.Streams.Keys)
+ {
+ AddVideoDownloadOptionsQualityComboBox.Items.Add(q);
+ }
+
+ // Set items in extension combobox
+ foreach (string x in Video.MediaType == "A" ? Config.DefaultAudioExtensionList : Config.DefaultVideoExtensionList)
+ {
+ AddVideoFileDataExtensionComboBox.Items.Add(x);
+ }
+
+ // Set quality option
+ if (Video.MediaType == "A")
+ {
+ AddVideoDownloadOptionsQualityComboBox.SelectedValue = ResourceLoader.GetForCurrentView().GetString("AddVideoQualityNoVideoStream");
+ AddVideoDownloadOptionsQualityComboBox.IsEnabled = false;
+ }
+ else
+ {
+ AddVideoDownloadOptionsQualityComboBox.SelectedValue = Video.SelectedQuality;
+ AddVideoDownloadOptionsQualityComboBox.IsEnabled = true;
+ }
+
+ // Set trim options
+ AddVideoDownloadOptionsTrimStartTextBox.Text = Video.TrimStart.ToString();
+ AddVideoDownloadOptionsTrimEndTextBox.Text = Video.TrimEnd.ToString();
+
+ // Set media type option
+ switch (Video.MediaType)
+ {
+ case "AV": AddVideoDownloadOptionsMediaTypeRadiobuttonAV.IsChecked = true; break;
+ case "V": AddVideoDownloadOptionsMediaTypeRadiobuttonV.IsChecked = true; break;
+ case "A": AddVideoDownloadOptionsMediaTypeRadiobuttonA.IsChecked = true; break;
+ default: AddVideoDownloadOptionsMediaTypeRadiobuttonAV.IsChecked = true; break;
+ }
+
+ // Set filename option
+ AddVideoFileDataFilenameTextBox.Text = Video.Filename;
+
+ // Set extension option
+ AddVideoFileDataExtensionComboBox.SelectedItem = Video.Extension;
+
+ // Set location option
+ if (StorageApplicationPermissions.FutureAccessList.ContainsItem("save"))
+ {
+ Video.CustomSaveLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("save");
+ AddVideoLocationDataLocationTextBlock.Text = Video.CustomSaveLocation.Path;
+ Video.FilePath = $@"{(Video.CustomSaveLocation.Path[Video.CustomSaveLocation.Path.Length - 1] == '\\' ? Video.CustomSaveLocation.Path : Video.CustomSaveLocation.Path + '\\')}{Video.Filename}.{Video.Extension.ToLower()}";
+ }
+ else
+ {
+ AddVideoLocationDataLocationTextBlock.Text = $@"{UserDataPaths.GetDefault().Downloads}\VDownload\";
+ Video.FilePath = $@"{UserDataPaths.GetDefault().Downloads}\VDownload\{Video.Filename}.{Video.Extension.ToLower()}";
+ }
+ }
+
+ #endregion
+
+
+
+ #region VALUE CHANGED HANDLERS
+
+ // MEDIA TYPE RADIOBUTTON (AV)
+ private void AddVideoDownloadOptionsMediaTypeRadiobuttonAV_Checked(object sender, RoutedEventArgs e)
+ {
+ Video.MediaType = "AV";
+ AddVideoDownloadOptionsQualityComboBox.IsEnabled = true;
+ AddVideoDownloadOptionsQualityComboBox.SelectedValue = Video.Streams.Keys.ToArray()[0];
+ AddVideoFileDataExtensionComboBox.Items.Clear();
+ foreach (string x in Config.DefaultVideoExtensionList)
+ {
+ AddVideoFileDataExtensionComboBox.Items.Add(x);
+ }
+ AddVideoFileDataExtensionComboBox.SelectedItem = Config.GetValue("default_video_extension");
+ }
+
+ // MEDIA TYPE RADIOBUTTON (A)
+ private void AddVideoDownloadOptionsMediaTypeRadiobuttonA_Checked(object sender, RoutedEventArgs e)
+ {
+ Video.MediaType = "A";
+ AddVideoDownloadOptionsQualityComboBox.IsEnabled = false;
+ AddVideoDownloadOptionsQualityComboBox.SelectedValue = ResourceLoader.GetForCurrentView().GetString("AddVideoQualityNoVideoStream");
+ AddVideoFileDataExtensionComboBox.Items.Clear();
+ foreach (string x in Config.DefaultAudioExtensionList)
+ {
+ AddVideoFileDataExtensionComboBox.Items.Add(x);
+ }
+ AddVideoFileDataExtensionComboBox.SelectedItem = Config.GetValue("default_audio_extension");
+ }
+
+ // MEDIA TYPE RADIOBUTTON (V)
+ private void AddVideoDownloadOptionsMediaTypeRadiobuttonV_Checked(object sender, RoutedEventArgs e)
+ {
+ Video.MediaType = "V";
+ AddVideoDownloadOptionsQualityComboBox.IsEnabled = true;
+ AddVideoDownloadOptionsQualityComboBox.SelectedValue = Video.Streams.Keys.ToArray()[0];
+ AddVideoFileDataExtensionComboBox.Items.Clear();
+ foreach (string x in Config.DefaultVideoExtensionList)
+ {
+ AddVideoFileDataExtensionComboBox.Items.Add(x);
+ }
+ AddVideoFileDataExtensionComboBox.SelectedItem = Config.GetValue("default_video_extension");
+ }
+
+ // QUALITY COMBOBOX
+ private void AddVideoDownloadOptionsQualityComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (Video.MediaType == "A")
+ Video.SelectedQuality = Video.Streams.Keys.ToArray()[0];
+ else
+ Video.SelectedQuality = (string)AddVideoDownloadOptionsQualityComboBox.SelectedItem;
+ }
+
+ // TRIM START TEXTBOX
+ private void AddVideoDownloadOptionsTrimStartTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ try
+ {
+ Video.TrimStart = TimeSpan.Parse(AddVideoDownloadOptionsTrimStartTextBox.Text);
+ }
+ catch { }
+ }
+
+ // TRIM END TEXTBOX
+ private void AddVideoDownloadOptionsTrimEndTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ try
+ {
+ Video.TrimEnd = TimeSpan.Parse(AddVideoDownloadOptionsTrimEndTextBox.Text);
+ }
+ catch { }
+ }
+
+ // FILENAME TEXTBOX
+ private void AddVideoFileDataFilenameTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ foreach (char c in Path.GetInvalidFileNameChars())
+ {
+ AddVideoFileDataFilenameTextBox.Text = AddVideoFileDataFilenameTextBox.Text.Replace(c, ' ');
+ }
+ Video.Filename = AddVideoFileDataFilenameTextBox.Text;
+ if (Video.CustomSaveLocation != null)
+ Video.FilePath = $@"{(Video.CustomSaveLocation.Path[Video.CustomSaveLocation.Path.Length - 1] == '\\' ? Video.CustomSaveLocation.Path : Video.CustomSaveLocation.Path + '\\')}{Video.Filename}.{Video.Extension.ToLower()}";
+ else
+ Video.FilePath = $@"{UserDataPaths.GetDefault().Downloads}\VDownload\{Video.Filename}.{Video.Extension.ToLower()}";
+ }
+
+ // EXTENSION COMBOBOX
+ private void AddVideoFileDataExtensionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (!(AddVideoFileDataExtensionComboBox.SelectedItem == null))
+ {
+ Video.Extension = (string)AddVideoFileDataExtensionComboBox.SelectedItem;
+ if (Video.CustomSaveLocation != null)
+ Video.FilePath = $@"{(Video.CustomSaveLocation.Path[Video.CustomSaveLocation.Path.Length - 1] == '\\' ? Video.CustomSaveLocation.Path : Video.CustomSaveLocation.Path + '\\')}{Video.Filename}.{Video.Extension.ToLower()}";
+ else
+ Video.FilePath = $@"{UserDataPaths.GetDefault().Downloads}\VDownload\{Video.Filename}.{Video.Extension.ToLower()}";
+ }
+ }
+
+ #endregion
+
+
+
+ #region BUTTON CLICK HANDLERS
+
+ // CHOOSE LOCATION BUTTON
+ private async void AddVideoLocationDataChooseLocationButton_Click(object sender, RoutedEventArgs e)
+ {
+ FolderPicker picker = new FolderPicker();
+ picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
+ picker.FileTypeFilter.Add("*");
+ StorageFolder folder = await picker.PickSingleFolderAsync();
+ if (folder != null)
+ {
+ try
+ {
+ await (await folder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync();
+ Video.CustomSaveLocation = folder;
+ AddVideoLocationDataLocationTextBlock.Text = Video.CustomSaveLocation.Path[Video.CustomSaveLocation.Path.Length - 1] == '\\' ? Video.CustomSaveLocation.Path : Video.CustomSaveLocation.Path + '\\';
+ Video.FilePath = $@"{(Video.CustomSaveLocation.Path[Video.CustomSaveLocation.Path.Length - 1] == '\\' ? Video.CustomSaveLocation.Path : Video.CustomSaveLocation.Path + '\\')}{Video.Filename}.{Video.Extension.ToLower()}";
+ }
+ catch { }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/VDownload/Views/AddVideo/AddVideoNotFound.xaml b/VDownload/Views/AddVideo/AddVideoNotFound.xaml
new file mode 100644
index 0000000..0f1ae7e
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoNotFound.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VDownload/Views/AddVideo/AddVideoNotFound.xaml.cs b/VDownload/Views/AddVideo/AddVideoNotFound.xaml.cs
new file mode 100644
index 0000000..407a142
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoNotFound.xaml.cs
@@ -0,0 +1,30 @@
+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.AddVideo
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AddVideoNotFound : Page
+ {
+ public AddVideoNotFound()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/VDownload/Views/AddVideo/AddVideoStart.xaml b/VDownload/Views/AddVideo/AddVideoStart.xaml
new file mode 100644
index 0000000..56482db
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoStart.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VDownload/Views/AddVideo/AddVideoStart.xaml.cs b/VDownload/Views/AddVideo/AddVideoStart.xaml.cs
new file mode 100644
index 0000000..b9c0630
--- /dev/null
+++ b/VDownload/Views/AddVideo/AddVideoStart.xaml.cs
@@ -0,0 +1,30 @@
+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.AddVideo
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AddVideoStart : Page
+ {
+ public AddVideoStart()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/VDownload/Views/MainPage.xaml b/VDownload/Views/MainPage.xaml
new file mode 100644
index 0000000..e36eae6
--- /dev/null
+++ b/VDownload/Views/MainPage.xaml
@@ -0,0 +1,52 @@
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VDownload/Views/MainPage.xaml.cs b/VDownload/Views/MainPage.xaml.cs
new file mode 100644
index 0000000..e49e592
--- /dev/null
+++ b/VDownload/Views/MainPage.xaml.cs
@@ -0,0 +1,77 @@
+// Internal
+using VDownload.Views.AddVideo;
+using VDownload.Sources;
+using VDownload.Services;
+using VDownload.Objects.Enums;
+
+// System
+using System;
+using Windows.ApplicationModel.Core;
+using Windows.UI;
+using Windows.UI.ViewManagement;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace VDownload
+{
+ public sealed partial class MainPage : Page
+ {
+ #region INIT
+
+ // CONSTRUCTOR
+ public MainPage()
+ {
+ InitializeComponent();
+
+ // Hide default title bar.
+ ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
+ titleBar.ButtonBackgroundColor = Colors.Transparent;
+ titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
+ CoreApplicationViewTitleBar coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
+ coreTitleBar.ExtendViewIntoTitleBar = true;
+ }
+
+ #endregion
+
+
+
+ #region CLICK BUTTON HANDLERS
+
+ // DOWNLOAD ALL BUTTON
+ private void AppBarDownloadAllButton_Click(object sender, RoutedEventArgs e)
+ {
+ // Start all video tasks from listed videos list if they wasn't started
+ foreach (VObject v in Videos.VideoObjectsList)
+ {
+ if (v.VideoStatus != VideoStatus.InProgress && v.VideoStatus != VideoStatus.Waiting)
+ v.Start();
+ }
+ }
+
+ // ADD VIDEO BUTTON
+ private async void AppBarAddVideoButton_Click(object sender, RoutedEventArgs e)
+ {
+ // Create window
+ AddVideoBase addVideoPage = new AddVideoBase();
+ ContentDialogResult result = await addVideoPage.ShowAsync();
+
+ // Add video to list
+ if (result == ContentDialogResult.Primary)
+ {
+ // Get video info
+ VObject video = (VObject)addVideoPage.Content;
+
+ // Create and attach video panel
+ video.AddVideoToList(VideoPanel);
+ }
+ }
+
+ // SETTINGS BUTTON
+ private void AppBarSettingsButton_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ #endregion
+ }
+}