Video adding and subscriptions finished
This commit is contained in:
14
VDownload.Core/Enums/DownloadTaskStatus.cs
Normal file
14
VDownload.Core/Enums/DownloadTaskStatus.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace VDownload.Core.Enums
|
||||
{
|
||||
public enum DownloadTaskStatus
|
||||
{
|
||||
Idle,
|
||||
Scheduled,
|
||||
Queued,
|
||||
Downloading,
|
||||
Processing,
|
||||
Finalizing,
|
||||
EndedSuccessfully,
|
||||
EndedUnsuccessfully,
|
||||
}
|
||||
}
|
||||
9
VDownload.Core/Enums/DownloadTasksAddingRequestSource.cs
Normal file
9
VDownload.Core/Enums/DownloadTasksAddingRequestSource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace VDownload.Core.Enums
|
||||
{
|
||||
public enum DownloadTasksAddingRequestSource
|
||||
{
|
||||
Video,
|
||||
Playlist,
|
||||
Subscriptions,
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VDownload.Core.Enums
|
||||
{
|
||||
public enum SubscriptionStatus
|
||||
{
|
||||
Added,
|
||||
Loaded,
|
||||
Ready
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VDownload.Core.Enums
|
||||
{
|
||||
public enum TaskAddingRequestSource
|
||||
{
|
||||
Video,
|
||||
Playlist
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace VDownload.Core.Enums
|
||||
{
|
||||
public enum TaskStatus
|
||||
{
|
||||
Idle,
|
||||
Scheduled,
|
||||
Waiting,
|
||||
InProgress
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class DownloadTaskEndedSuccessfullyEventArgs : System.EventArgs
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public DownloadTaskEndedSuccessfullyEventArgs(TimeSpan elapsedTime)
|
||||
{
|
||||
ElapsedTime = elapsedTime;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public TimeSpan ElapsedTime { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class DownloadTaskEndedUnsuccessfullyEventArgs : System.EventArgs
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public DownloadTaskEndedUnsuccessfullyEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public Exception Exception { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
24
VDownload.Core/EventArgs/DownloadTaskScheduledEventArgs.cs
Normal file
24
VDownload.Core/EventArgs/DownloadTaskScheduledEventArgs.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class DownloadTaskScheduledEventArgs : System.EventArgs
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public DownloadTaskScheduledEventArgs(DateTime scheduledFor)
|
||||
{
|
||||
ScheduledFor = scheduledFor;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public DateTime ScheduledFor { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Structs;
|
||||
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class DownloadTasksAddingRequestedEventArgs : System.EventArgs
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public DownloadTasksAddingRequestedEventArgs(DownloadTask[] downloadTasks, DownloadTasksAddingRequestSource requestSource)
|
||||
{
|
||||
DownloadTasks = downloadTasks;
|
||||
RequestSource = requestSource;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public DownloadTask[] DownloadTasks { get; private set; }
|
||||
public DownloadTasksAddingRequestSource RequestSource { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,21 @@ namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class PlaylistSearchSuccessedEventArgs : System.EventArgs
|
||||
{
|
||||
public IPlaylist PlaylistService { get; set; }
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public PlaylistSearchSuccessedEventArgs(IPlaylist playlist)
|
||||
{
|
||||
Playlist = playlist;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public IPlaylist Playlist { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public double Progress { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public double Progress { get; private set; }
|
||||
public bool IsCompleted { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using VDownload.Core.Interfaces;
|
||||
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class SubscriptionLoadSuccessedEventArgs : System.EventArgs
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public SubscriptionLoadSuccessedEventArgs(IVideo[] videos)
|
||||
{
|
||||
Videos = videos;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public IVideo[] Videos { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Structs;
|
||||
|
||||
namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class TasksAddingRequestedEventArgs : System.EventArgs
|
||||
{
|
||||
public TaskData[] TaskData { get; set; }
|
||||
public TaskAddingRequestSource RequestSource { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,21 @@ namespace VDownload.Core.EventArgs
|
||||
{
|
||||
public class VideoSearchSuccessedEventArgs : System.EventArgs
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public VideoSearchSuccessedEventArgs(IVideo video)
|
||||
{
|
||||
Video = video;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public IVideo Video { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
66
VDownload.Core/Extensions/TimeSpanExtension.cs
Normal file
66
VDownload.Core/Extensions/TimeSpanExtension.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace VDownload.Core.Extensions
|
||||
{
|
||||
public static class TimeSpanExtension
|
||||
{
|
||||
// To string (TH:)MM:SS
|
||||
public static string ToStringOptTHBaseMMSS(this TimeSpan timeSpan, params TimeSpan[] formatBase)
|
||||
{
|
||||
StringBuilder formattedTimeSpan = new StringBuilder();
|
||||
|
||||
int maxTHLength = 0;
|
||||
foreach (TimeSpan format in formatBase.Concat(new TimeSpan[] { timeSpan }))
|
||||
{
|
||||
int THLength = Math.Floor(format.TotalHours) > 0 ? Math.Floor(timeSpan.TotalHours).ToString().Length : 0;
|
||||
if (THLength > maxTHLength)
|
||||
{
|
||||
maxTHLength = THLength;
|
||||
}
|
||||
}
|
||||
if (maxTHLength > 0)
|
||||
{
|
||||
formattedTimeSpan.Append($"{((int)Math.Floor(timeSpan.TotalHours)).ToString($"D{maxTHLength}")}:");
|
||||
}
|
||||
|
||||
formattedTimeSpan.Append(maxTHLength == 0 ? $"{timeSpan.Minutes}:" : $"{timeSpan.Minutes:00}:");
|
||||
|
||||
formattedTimeSpan.Append($"{timeSpan.Seconds:00}");
|
||||
|
||||
return formattedTimeSpan.ToString();
|
||||
}
|
||||
|
||||
// To string ((TH:)MM:)SS
|
||||
public static string ToStringOptTHMMBaseSS(this TimeSpan timeSpan, params TimeSpan[] formatBase)
|
||||
{
|
||||
StringBuilder formattedTimeSpan = new StringBuilder();
|
||||
|
||||
int maxTHLength = 0;
|
||||
foreach (TimeSpan format in formatBase.Concat(new TimeSpan[] { timeSpan }))
|
||||
{
|
||||
int THLength = Math.Floor(format.TotalHours) > 0 ? Math.Floor(timeSpan.TotalHours).ToString().Length : 0;
|
||||
if (THLength > maxTHLength)
|
||||
{
|
||||
maxTHLength = THLength;
|
||||
}
|
||||
}
|
||||
if (maxTHLength > 0)
|
||||
{
|
||||
formattedTimeSpan.Append($"{((int)Math.Floor(timeSpan.TotalHours)).ToString($"D{maxTHLength}")}:");
|
||||
}
|
||||
|
||||
bool MM = false;
|
||||
if (Math.Floor(timeSpan.TotalMinutes) > 0 || maxTHLength > 0)
|
||||
{
|
||||
formattedTimeSpan.Append(maxTHLength > 0 ? $"{timeSpan.Minutes:00}:" : $"{timeSpan.Minutes}:");
|
||||
MM = true;
|
||||
}
|
||||
|
||||
formattedTimeSpan.Append(MM ? $"{timeSpan.Seconds:00}" : $"{timeSpan.Seconds}");
|
||||
|
||||
return formattedTimeSpan.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,8 @@ namespace VDownload.Core.Interfaces
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
// PLAYLIST PROPERTIES
|
||||
string ID { get; }
|
||||
PlaylistSource Source { get; }
|
||||
string ID { get; }
|
||||
Uri Url { get; }
|
||||
string Name { get; }
|
||||
IVideo[] Videos { get; }
|
||||
@@ -22,10 +21,8 @@ namespace VDownload.Core.Interfaces
|
||||
|
||||
#region METHODS
|
||||
|
||||
// GET PLAYLIST METADATA
|
||||
Task GetMetadataAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
// GET VIDEOS FROM PLAYLIST
|
||||
Task GetVideosAsync(CancellationToken cancellationToken = default);
|
||||
Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default);
|
||||
|
||||
|
||||
@@ -12,11 +12,15 @@ namespace VDownload.Core.Interfaces
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
// VIDEO PROPERTIES
|
||||
VideoSource Source { get; }
|
||||
string ID { get; }
|
||||
Uri Url { get; }
|
||||
Metadata Metadata { get; }
|
||||
string Title { get; }
|
||||
string Author { get; }
|
||||
DateTime Date { get; }
|
||||
TimeSpan Duration { get; }
|
||||
long Views { get; }
|
||||
Uri Thumbnail { get; }
|
||||
BaseStream[] BaseStreams { get; }
|
||||
|
||||
#endregion
|
||||
@@ -25,20 +29,17 @@ namespace VDownload.Core.Interfaces
|
||||
|
||||
#region METHODS
|
||||
|
||||
// GET VIDEO METADATA
|
||||
Task GetMetadataAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
// GET VIDEO STREAMS
|
||||
Task GetStreamsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
// DOWNLOAD VIDEO
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default);
|
||||
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TrimData trim, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
#region EVENTS
|
||||
|
||||
event EventHandler<EventArgs.ProgressChangedEventArgs> DownloadingProgressChanged;
|
||||
event EventHandler<EventArgs.ProgressChangedEventArgs> ProcessingProgressChanged;
|
||||
|
||||
@@ -10,10 +10,6 @@ namespace VDownload.Core.Services
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
// SETTINGS CONTAINER
|
||||
private static readonly ApplicationDataContainer SettingsContainer = ApplicationData.Current.LocalSettings;
|
||||
|
||||
// DEFAULT SETTINGS
|
||||
private static readonly Dictionary<string, object> DefaultSettings = new Dictionary<string, object>()
|
||||
{
|
||||
{ "delete_temp_on_start", true },
|
||||
@@ -45,37 +41,33 @@ namespace VDownload.Core.Services
|
||||
|
||||
|
||||
|
||||
#region METHODS
|
||||
#region PUBLIC METHODS
|
||||
|
||||
// GET VALUE
|
||||
public static object GetValue(string key)
|
||||
{
|
||||
return SettingsContainer.Values[key];
|
||||
return ApplicationData.Current.LocalSettings.Values[key];
|
||||
}
|
||||
|
||||
// SET VALUE
|
||||
public static void SetValue(string key, object value)
|
||||
{
|
||||
SettingsContainer.Values[key] = value;
|
||||
ApplicationData.Current.LocalSettings.Values[key] = value;
|
||||
}
|
||||
|
||||
// SET DEFAULT
|
||||
public static void SetDefault()
|
||||
{
|
||||
foreach (KeyValuePair<string, object> s in DefaultSettings)
|
||||
{
|
||||
SettingsContainer.Values[s.Key] = s.Value;
|
||||
ApplicationData.Current.LocalSettings.Values[s.Key] = s.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// REBUILD
|
||||
public static void Rebuild()
|
||||
{
|
||||
foreach (KeyValuePair<string, object> s in DefaultSettings)
|
||||
{
|
||||
if (!SettingsContainer.Values.ContainsKey(s.Key))
|
||||
if (!ApplicationData.Current.LocalSettings.Values.ContainsKey(s.Key))
|
||||
{
|
||||
SettingsContainer.Values[s.Key] = s.Value;
|
||||
ApplicationData.Current.LocalSettings.Values[s.Key] = s.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
176
VDownload.Core/Services/DownloadTask.cs
Normal file
176
VDownload.Core/Services/DownloadTask.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using Windows.ApplicationModel.ExtendedExecution;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
|
||||
namespace VDownload.Core.Structs
|
||||
{
|
||||
public class DownloadTask
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public DownloadTask(string id, IVideo video, MediaType mediaType, BaseStream selectedStream, TrimData trim, OutputFile file, double schedule)
|
||||
{
|
||||
Id = id;
|
||||
Video = video;
|
||||
MediaType = mediaType;
|
||||
SelectedStream = selectedStream;
|
||||
Trim = trim;
|
||||
File = file;
|
||||
Schedule = schedule;
|
||||
|
||||
Status = DownloadTaskStatus.Idle;
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public string Id { get; set; }
|
||||
public IVideo Video { get; set; }
|
||||
public MediaType MediaType { get; set; }
|
||||
public BaseStream SelectedStream { get; set; }
|
||||
public TrimData Trim { get; set; }
|
||||
public OutputFile File { get; set; }
|
||||
public double Schedule { get; set; }
|
||||
public DownloadTaskStatus Status { get; private set; }
|
||||
public CancellationTokenSource CancellationTokenSource { get; private set; }
|
||||
|
||||
public DateTime ScheduledFor { get; private set; }
|
||||
public double DownloadingProgress { get; private set; }
|
||||
public double ProcessingProgress { get; private set; }
|
||||
public TimeSpan ElapsedTime { get; private set; }
|
||||
public Exception Exception { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region METHODS
|
||||
|
||||
public async Task Run(bool delayWhenOnMeteredConnection)
|
||||
{
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (Schedule > 0)
|
||||
{
|
||||
ScheduledFor = DateTime.Now.AddMinutes(Schedule);
|
||||
Status = DownloadTaskStatus.Scheduled;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
while (DateTime.Now < ScheduledFor && !CancellationTokenSource.Token.IsCancellationRequested) await Task.Delay(100);
|
||||
}
|
||||
|
||||
Status = DownloadTaskStatus.Queued;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
await DownloadTasksCollectionManagement.WaitInQueue(delayWhenOnMeteredConnection, CancellationTokenSource.Token);
|
||||
|
||||
if (!CancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
DownloadingProgress = 0;
|
||||
Status = DownloadTaskStatus.Downloading;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
StorageFolder tempFolder;
|
||||
if ((bool)Config.GetValue("custom_temp_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_temp_location"))
|
||||
{
|
||||
tempFolder = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_temp_location");
|
||||
}
|
||||
else
|
||||
{
|
||||
tempFolder = ApplicationData.Current.TemporaryFolder;
|
||||
}
|
||||
tempFolder = await tempFolder.CreateFolderAsync(Id);
|
||||
|
||||
bool endedWithError = false;
|
||||
|
||||
try
|
||||
{
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
Stopwatch taskStopwatch = Stopwatch.StartNew();
|
||||
|
||||
ExtendedExecutionSession session = new ExtendedExecutionSession { Reason = ExtendedExecutionReason.Unspecified };
|
||||
await session.RequestExtensionAsync();
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
Video.DownloadingProgressChanged += DownloadingProgressChanged;
|
||||
Video.ProcessingProgressChanged += ProcessingProgressChanged;
|
||||
StorageFile tempOutputFile = await Video.DownloadAndTranscodeAsync(tempFolder, SelectedStream, File.Extension, MediaType, Trim, CancellationTokenSource.Token);
|
||||
|
||||
session.Dispose();
|
||||
|
||||
Status = DownloadTaskStatus.Finalizing;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
StorageFile outputFile = await File.Create();
|
||||
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
await tempOutputFile.MoveAndReplaceAsync(outputFile);
|
||||
|
||||
taskStopwatch.Stop();
|
||||
|
||||
ElapsedTime = taskStopwatch.Elapsed;
|
||||
Status = DownloadTaskStatus.EndedSuccessfully;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
endedWithError = true;
|
||||
Exception = ex;
|
||||
Status = DownloadTaskStatus.EndedUnsuccessfully;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!endedWithError || (bool)Config.GetValue("delete_task_temp_when_ended_with_error"))
|
||||
{
|
||||
// Delete temporary files
|
||||
await tempFolder.DeleteAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Exception = new OperationCanceledException(CancellationTokenSource.Token);
|
||||
Status = DownloadTaskStatus.EndedUnsuccessfully;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void DownloadingProgressChanged(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
DownloadingProgress = e.Progress;
|
||||
Status = DownloadTaskStatus.Downloading;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void ProcessingProgressChanged(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
ProcessingProgress = e.Progress;
|
||||
Status = DownloadTaskStatus.Processing;
|
||||
StatusChanged.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT
|
||||
|
||||
public event EventHandler StatusChanged;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
64
VDownload.Core/Services/DownloadTasksCollectionManagement.cs
Normal file
64
VDownload.Core/Services/DownloadTasksCollectionManagement.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Structs;
|
||||
|
||||
namespace VDownload.Core.Services
|
||||
{
|
||||
public static class DownloadTasksCollectionManagement
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
private static readonly Dictionary<string, DownloadTask> ChangeableDownloadTasksCollection = new Dictionary<string, DownloadTask>();
|
||||
public static readonly ReadOnlyDictionary<string, DownloadTask> DownloadTasksCollection = new ReadOnlyDictionary<string, DownloadTask>(ChangeableDownloadTasksCollection);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public static string GenerateID()
|
||||
{
|
||||
char[] idChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
|
||||
int idLength = 10;
|
||||
|
||||
string id;
|
||||
do
|
||||
{
|
||||
id = "";
|
||||
while (id.Length < idLength)
|
||||
{
|
||||
id += idChars[new Random().Next(0, idChars.Length)];
|
||||
}
|
||||
} while (DownloadTasksCollection.Keys.Contains(id));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public static void Add(DownloadTask downloadTask, string id)
|
||||
{
|
||||
ChangeableDownloadTasksCollection[id] = downloadTask;
|
||||
}
|
||||
|
||||
public static void Remove(string id)
|
||||
{
|
||||
ChangeableDownloadTasksCollection.Remove(id);
|
||||
}
|
||||
|
||||
public static async Task WaitInQueue(bool delayWhenOnMeteredConnection, CancellationToken cancellationToken = default)
|
||||
{
|
||||
while ((ChangeableDownloadTasksCollection.Values.Where((DownloadTask task) => task.Status == Enums.DownloadTaskStatus.Downloading || task.Status == Enums.DownloadTaskStatus.Processing || task.Status == Enums.DownloadTaskStatus.Finalizing).Count() >= (int)Config.GetValue("max_active_video_task") || (delayWhenOnMeteredConnection && NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)) && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Structs;
|
||||
using Windows.Foundation;
|
||||
using Windows.Media.Editing;
|
||||
using Windows.Media.MediaProperties;
|
||||
@@ -14,105 +15,83 @@ namespace VDownload.Core.Services
|
||||
{
|
||||
public class MediaProcessor
|
||||
{
|
||||
#region STANDARD METHODS
|
||||
#region PUBLIC METHODS
|
||||
|
||||
// SINGLE AUDIO & VIDEO FILE PROCESSING
|
||||
public async Task Run(StorageFile mediaFile, MediaFileExtension extension, MediaType mediaType, StorageFile outputFile, TimeSpan? trimStart = null, TimeSpan? trimEnd = null, CancellationToken cancellationToken = default)
|
||||
public async Task Run(StorageFile mediaFile, MediaFileExtension extension, MediaType mediaType, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Invoke event at start
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0));
|
||||
|
||||
// Init transcoder
|
||||
MediaTranscoder mediaTranscoder = new MediaTranscoder
|
||||
{
|
||||
HardwareAccelerationEnabled = (bool)Config.GetValue("media_transcoding_use_hardware_acceleration"),
|
||||
VideoProcessingAlgorithm = (MediaVideoProcessingAlgorithm)Config.GetValue("media_transcoding_algorithm"),
|
||||
};
|
||||
if (trimStart != null) mediaTranscoder.TrimStartTime = (TimeSpan)trimStart;
|
||||
if (trimEnd != null) mediaTranscoder.TrimStopTime = (TimeSpan)trimEnd;
|
||||
if (trim.Start != null) mediaTranscoder.TrimStartTime = trim.Start;
|
||||
if (trim.End != null) mediaTranscoder.TrimStopTime = trim.End;
|
||||
|
||||
// Start transcoding operation
|
||||
using (IRandomAccessStream openedOutputFile = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
|
||||
{
|
||||
// Prepare transcode task
|
||||
PrepareTranscodeResult transcodingPreparated = await mediaTranscoder.PrepareStreamTranscodeAsync(await mediaFile.OpenAsync(FileAccessMode.Read), openedOutputFile, await GetMediaEncodingProfile(mediaFile, extension, mediaType));
|
||||
|
||||
// Start transcoding
|
||||
IAsyncActionWithProgress<double> transcodingTask = transcodingPreparated.TranscodeAsync();
|
||||
await transcodingTask.AsTask(cancellationToken, new Progress<double>((percent) => { ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(percent)); }));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Finalizing
|
||||
await openedOutputFile.FlushAsync();
|
||||
transcodingTask.Close();
|
||||
}
|
||||
|
||||
// Invoke event at end
|
||||
ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(100, true));
|
||||
}
|
||||
|
||||
// SEPARATE AUDIO & VIDEO FILES PROCESSING
|
||||
public async Task Run(StorageFile audioFile, StorageFile videoFile, VideoFileExtension extension, StorageFile outputFile, TimeSpan? trimStart = null, TimeSpan? trimEnd = null, CancellationToken cancellationToken = default)
|
||||
public async Task Run(StorageFile audioFile, StorageFile videoFile, VideoFileExtension extension, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Invoke event at start
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0));
|
||||
|
||||
// Init editor
|
||||
MediaComposition mediaEditor = new MediaComposition();
|
||||
|
||||
// Add media files
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Task<MediaClip> getVideoFileTask = MediaClip.CreateFromFileAsync(videoFile).AsTask();
|
||||
Task<BackgroundAudioTrack> getAudioFileTask = BackgroundAudioTrack.CreateFromFileAsync(audioFile).AsTask();
|
||||
await Task.WhenAll(getVideoFileTask, getAudioFileTask);
|
||||
|
||||
MediaClip videoElement = getVideoFileTask.Result;
|
||||
if (trimStart != null) videoElement.TrimTimeFromStart = (TimeSpan)trimStart;
|
||||
if (trimEnd != null) videoElement.TrimTimeFromEnd = (TimeSpan)trimEnd;
|
||||
if (trim.Start != null) videoElement.TrimTimeFromStart = trim.Start;
|
||||
if (trim.End != null) videoElement.TrimTimeFromEnd = trim.End;
|
||||
BackgroundAudioTrack audioElement = getAudioFileTask.Result;
|
||||
if (trimStart != null) audioElement.TrimTimeFromStart = (TimeSpan)trimStart;
|
||||
if (trimEnd != null) audioElement.TrimTimeFromEnd = (TimeSpan)trimEnd;
|
||||
if (trim.Start != null) audioElement.TrimTimeFromStart = trim.Start;
|
||||
if (trim.End != null) audioElement.TrimTimeFromEnd = trim.End;
|
||||
|
||||
mediaEditor.Clips.Add(videoElement);
|
||||
mediaEditor.BackgroundAudioTracks.Add(audioElement);
|
||||
|
||||
// Start rendering operation
|
||||
var renderOperation = mediaEditor.RenderToFileAsync(outputFile, (MediaTrimmingPreference)Config.GetValue("media_editing_algorithm"), await GetMediaEncodingProfile(videoFile, audioFile, (MediaFileExtension)extension, MediaType.AudioVideo));
|
||||
renderOperation.Progress += (info, progress) => { ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(progress)); };
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await renderOperation.AsTask(cancellationToken);
|
||||
|
||||
// Invoke event at end
|
||||
ProgressChanged(this, new EventArgs.ProgressChangedEventArgs(100, true));
|
||||
}
|
||||
|
||||
// AUDIO FILE PROCESSING
|
||||
public async Task Run(StorageFile audioFile, AudioFileExtension extension, StorageFile outputFile, TimeSpan? trimStart = null, TimeSpan? trimEnd = null, CancellationToken cancellationToken = default)
|
||||
public async Task Run(StorageFile audioFile, AudioFileExtension extension, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Run(audioFile, (MediaFileExtension)extension, MediaType.OnlyAudio, outputFile, trimStart, trimEnd, cancellationToken);
|
||||
await Run(audioFile, (MediaFileExtension)extension, MediaType.OnlyAudio, outputFile, trim, cancellationToken);
|
||||
}
|
||||
|
||||
// VIDEO FILE PROCESSING
|
||||
public async Task Run(StorageFile videoFile, VideoFileExtension extension, StorageFile outputFile, TimeSpan? trimStart = null, TimeSpan? trimEnd = null, CancellationToken cancellationToken = default)
|
||||
public async Task Run(StorageFile videoFile, VideoFileExtension extension, StorageFile outputFile, TrimData trim, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Run(videoFile, (MediaFileExtension)extension, MediaType.OnlyVideo, outputFile, trimStart, trimEnd, cancellationToken);
|
||||
await Run(videoFile, (MediaFileExtension)extension, MediaType.OnlyVideo, outputFile, trim, cancellationToken);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region LOCAL METHODS
|
||||
#region PRIVATE METHODS
|
||||
|
||||
// GET ENCODING PROFILE
|
||||
private static async Task<MediaEncodingProfile> GetMediaEncodingProfile(StorageFile videoFile, StorageFile audioFile, MediaFileExtension extension, MediaType mediaType)
|
||||
{
|
||||
// Create profile object
|
||||
MediaEncodingProfile profile;
|
||||
|
||||
// Set extension
|
||||
switch (extension)
|
||||
{
|
||||
default:
|
||||
@@ -127,7 +106,6 @@ namespace VDownload.Core.Services
|
||||
case MediaFileExtension.WMA: profile = MediaEncodingProfile.CreateWma(AudioEncodingQuality.High); break;
|
||||
}
|
||||
|
||||
// Set video parameters
|
||||
if (mediaType != MediaType.OnlyAudio)
|
||||
{
|
||||
var videoData = await videoFile.Properties.GetVideoPropertiesAsync();
|
||||
@@ -135,16 +113,12 @@ namespace VDownload.Core.Services
|
||||
profile.Video.Width = videoData.Width;
|
||||
profile.Video.Bitrate = videoData.Bitrate;
|
||||
}
|
||||
|
||||
// Set audio parameters
|
||||
if (mediaType != MediaType.OnlyVideo)
|
||||
{
|
||||
var audioData = await audioFile.Properties.GetMusicPropertiesAsync();
|
||||
profile.Audio.Bitrate = audioData.Bitrate;
|
||||
if (mediaType == MediaType.AudioVideo) profile.Video.Bitrate -= audioData.Bitrate;
|
||||
}
|
||||
|
||||
// Delete audio tracks
|
||||
if (mediaType == MediaType.OnlyVideo)
|
||||
{
|
||||
var audioTracks = profile.GetAudioTracks();
|
||||
@@ -152,7 +126,6 @@ namespace VDownload.Core.Services
|
||||
profile.SetAudioTracks(audioTracks.AsEnumerable());
|
||||
}
|
||||
|
||||
// Return profile
|
||||
return profile;
|
||||
}
|
||||
private static async Task<MediaEncodingProfile> GetMediaEncodingProfile(StorageFile audioVideoFile, MediaFileExtension extension, MediaType mediaType)
|
||||
@@ -164,7 +137,7 @@ namespace VDownload.Core.Services
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
#region EVENTS
|
||||
|
||||
public event EventHandler<EventArgs.ProgressChangedEventArgs> ProgressChanged;
|
||||
|
||||
|
||||
56
VDownload.Core/Services/OutputFile.cs
Normal file
56
VDownload.Core/Services/OutputFile.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace VDownload.Core.Services
|
||||
{
|
||||
public class OutputFile
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public OutputFile(string name, MediaFileExtension extension, StorageFolder location)
|
||||
{
|
||||
Name = name;
|
||||
Extension = extension;
|
||||
Location = location;
|
||||
}
|
||||
|
||||
public OutputFile(string name, MediaFileExtension extension)
|
||||
{
|
||||
Name = name;
|
||||
Extension = extension;
|
||||
Location = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public string Name { get; private set; }
|
||||
public MediaFileExtension Extension { get; private set; }
|
||||
public StorageFolder Location { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public async Task<StorageFile> Create()
|
||||
{
|
||||
string filename = $"{Name}.{Extension.ToString().ToLower()}";
|
||||
CreationCollisionOption collisionOption = (bool)Config.GetValue("replace_output_file_if_exists") ? CreationCollisionOption.ReplaceExisting : CreationCollisionOption.GenerateUniqueName;
|
||||
return await(!(Location is null) ? Location.CreateFileAsync(filename, collisionOption) : DownloadsFolder.CreateFileAsync(filename, collisionOption));
|
||||
}
|
||||
|
||||
public string GetPath() => $@"{(Location != null ? Location.Path : $@"{UserDataPaths.GetDefault().Downloads}\VDownload")}\{Name}.{Extension.ToString().ToLower()}";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Interfaces;
|
||||
|
||||
@@ -32,15 +33,15 @@ namespace VDownload.Core.Services
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public async Task<IVideo[]> GetNewVideosAsync()
|
||||
public async Task<IVideo[]> GetNewVideosAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Playlist.GetVideosAsync();
|
||||
await Playlist.GetVideosAsync(cancellationToken);
|
||||
return GetUnsavedVideos();
|
||||
}
|
||||
|
||||
public async Task<IVideo[]> GetNewVideosAndUpdateAsync()
|
||||
public async Task<IVideo[]> GetNewVideosAndUpdateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Playlist.GetVideosAsync();
|
||||
await Playlist.GetVideosAsync(cancellationToken);
|
||||
IVideo[] newVideos = GetUnsavedVideos();
|
||||
SavedVideos = Playlist.Videos;
|
||||
return newVideos;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace VDownload.Core.Services
|
||||
public static async Task<(Subscription Subscription, StorageFile SubscriptionFile)[]> GetSubscriptionsAsync()
|
||||
{
|
||||
List<(Subscription Subscription, StorageFile SubscriptionFile)> subscriptions = new List<(Subscription Subscription,StorageFile SubscriptionFile)> ();
|
||||
StorageFolder subscriptionsFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(SubscriptionsFolderName, CreationCollisionOption.OpenIfExists);
|
||||
StorageFolder subscriptionsFolder = await SubscriptionFolderLocation.CreateFolderAsync(SubscriptionsFolderName, CreationCollisionOption.OpenIfExists);
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
foreach (StorageFile file in await subscriptionsFolder.GetFilesAsync())
|
||||
{
|
||||
@@ -43,7 +43,7 @@ namespace VDownload.Core.Services
|
||||
|
||||
public static async Task<StorageFile> CreateSubscriptionFileAsync(Subscription subscription)
|
||||
{
|
||||
StorageFolder subscriptionsFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(SubscriptionsFolderName, CreationCollisionOption.OpenIfExists);
|
||||
StorageFolder subscriptionsFolder = await SubscriptionFolderLocation.CreateFolderAsync(SubscriptionsFolderName, CreationCollisionOption.OpenIfExists);
|
||||
try
|
||||
{
|
||||
StorageFile subscriptionFile = await subscriptionsFolder.CreateFileAsync($"{(int)subscription.Playlist.Source}-{subscription.Playlist.ID}.{SubscriptionFileExtension}", CreationCollisionOption.FailIfExists);
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VDownload.Core.Services
|
||||
{
|
||||
public static class TaskId
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
// ID SETTINGS
|
||||
private static readonly char[] IDChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
|
||||
private static readonly int IDLength = 10;
|
||||
|
||||
// IDS LIST
|
||||
private static readonly List<string> IDList = new List<string>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region METHODS
|
||||
|
||||
// GET TASK ID
|
||||
public static string Get()
|
||||
{
|
||||
string id;
|
||||
do
|
||||
{
|
||||
id = "";
|
||||
while (id.Length < IDLength)
|
||||
{
|
||||
id += IDChars[new Random().Next(0, IDChars.Length)];
|
||||
}
|
||||
} while (IDList.Contains(id));
|
||||
IDList.Add(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
// DISPOSE TASK ID
|
||||
public static void Dispose(string id)
|
||||
{
|
||||
IDList.Remove(id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace VDownload.Core.Services
|
||||
{
|
||||
public static class TimeSpanCustomFormat
|
||||
{
|
||||
// (TH:)MM:SS
|
||||
public static string ToOptTHBaseMMSS(TimeSpan timeSpan, params TimeSpan[] formatBase)
|
||||
{
|
||||
string formattedTimeSpan = string.Empty;
|
||||
|
||||
int maxTHLength = 0;
|
||||
foreach (TimeSpan format in formatBase.Concat(new TimeSpan[] { timeSpan }))
|
||||
{
|
||||
int THLength = Math.Floor(format.TotalHours) > 0 ? Math.Floor(timeSpan.TotalHours).ToString().Length : 0;
|
||||
if (THLength > maxTHLength) maxTHLength = THLength;
|
||||
}
|
||||
formattedTimeSpan += $"{((int)Math.Floor(timeSpan.TotalHours)).ToString($"D{maxTHLength}")}:";
|
||||
|
||||
formattedTimeSpan += maxTHLength == 0 ? $"{timeSpan.Minutes}:" : $"{timeSpan.Minutes:00}:";
|
||||
|
||||
formattedTimeSpan += $"{timeSpan.Seconds:00}";
|
||||
|
||||
return formattedTimeSpan;
|
||||
}
|
||||
|
||||
// ((TH:)MM:)SS
|
||||
public static string ToOptTHMMBaseSS(TimeSpan timeSpan, params TimeSpan[] formatBase)
|
||||
{
|
||||
string formattedTimeSpan = string.Empty;
|
||||
|
||||
int maxTHLength = 0;
|
||||
foreach (TimeSpan format in formatBase.Concat(new TimeSpan[] { timeSpan }))
|
||||
{
|
||||
int THLength = Math.Floor(format.TotalHours) > 0 ? Math.Floor(timeSpan.TotalHours).ToString().Length : 0;
|
||||
if (THLength > maxTHLength) maxTHLength = THLength;
|
||||
}
|
||||
formattedTimeSpan += $"{((int)Math.Floor(timeSpan.TotalHours)).ToString($"D{maxTHLength}")}:";
|
||||
|
||||
bool MM = false;
|
||||
if (Math.Floor(timeSpan.TotalMinutes) > 0 || maxTHLength > 0)
|
||||
{
|
||||
formattedTimeSpan += maxTHLength > 0 ? $"{timeSpan.Minutes:00}:" : $"{timeSpan.Minutes}:";
|
||||
MM = true;
|
||||
}
|
||||
|
||||
formattedTimeSpan += MM ? $"{timeSpan.Seconds:00}:" : $"{timeSpan.Seconds}:";
|
||||
|
||||
return formattedTimeSpan;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
VDownload.Core/Sources/AuthorizationData.cs
Normal file
11
VDownload.Core/Sources/AuthorizationData.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Windows.Storage;
|
||||
|
||||
namespace VDownload.Core.Sources
|
||||
{
|
||||
internal static class AuthorizationData
|
||||
{
|
||||
internal static StorageFolder FolderLocation = ApplicationData.Current.LocalCacheFolder;
|
||||
internal static string FolderName = "AuthData";
|
||||
internal static string FilesExtension = "auth";
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,9 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
public Channel(string id)
|
||||
{
|
||||
ID = id;
|
||||
Source = PlaylistSource.TwitchChannel;
|
||||
ID = id;
|
||||
Url = new Uri($"https://twitch.tv/{ID}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -31,25 +32,23 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public string ID { get; private set; }
|
||||
public PlaylistSource Source { get; private set; }
|
||||
public string ID { get; private set; }
|
||||
private string UniqueID { get; set; }
|
||||
public Uri Url { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public IVideo[] Videos { get; private set; }
|
||||
private string UniqueUserID { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region STANDARD METHODS
|
||||
#region PUBLIC METHODS
|
||||
|
||||
// GET CHANNEL METADATA
|
||||
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get response
|
||||
JToken response = null;
|
||||
using (WebClient client = await Client.Helix())
|
||||
{
|
||||
@@ -59,52 +58,38 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
else throw new MediaNotFoundException($"Twitch Channel (ID: {ID}) was not found");
|
||||
}
|
||||
|
||||
// Create unified playlist url
|
||||
Url = new Uri($"https://twitch.tv/{ID}");
|
||||
|
||||
// Set parameters
|
||||
UniqueUserID = (string)response["id"];
|
||||
UniqueID = (string)response["id"];
|
||||
Name = (string)response["display_name"];
|
||||
}
|
||||
|
||||
// GET CHANNEL VIDEOS
|
||||
public async Task GetVideosAsync(CancellationToken cancellationToken = default) => await GetVideosAsync(0, cancellationToken);
|
||||
public async Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Set page id
|
||||
string pagination = "";
|
||||
|
||||
// Set array of videos
|
||||
List<Vod> videos = new List<Vod>();
|
||||
|
||||
// Get all
|
||||
bool getAll = numberOfVideos == 0;
|
||||
|
||||
// Get videos
|
||||
int count;
|
||||
JToken[] videosData;
|
||||
List<Task> getStreamsTasks = new List<Task>();
|
||||
do
|
||||
{
|
||||
// Set number of videos to get in this iteration
|
||||
count = numberOfVideos < 100 && !getAll ? numberOfVideos : 100;
|
||||
|
||||
// Get response
|
||||
JToken response = null;
|
||||
using (WebClient client = await Client.Helix())
|
||||
{
|
||||
client.QueryString.Add("user_id", UniqueUserID);
|
||||
client.QueryString.Add("user_id", UniqueID);
|
||||
client.QueryString.Add("first", count.ToString());
|
||||
client.QueryString.Add("after", pagination);
|
||||
response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/videos"));
|
||||
}
|
||||
|
||||
// Set page id
|
||||
pagination = (string)response["pagination"]["cursor"];
|
||||
|
||||
// Set videos data
|
||||
videosData = response["data"].ToArray();
|
||||
foreach (JToken videoData in videosData)
|
||||
{
|
||||
@@ -118,10 +103,8 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
}
|
||||
while ((getAll || numberOfVideos > 0) && count == videosData.Length);
|
||||
|
||||
// Wait for all getStreams tasks
|
||||
await Task.WhenAll(getStreamsTasks);
|
||||
|
||||
// Set videos
|
||||
Videos = videos.ToArray();
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
public Clip(string id)
|
||||
{
|
||||
ID = id;
|
||||
Source = VideoSource.TwitchClip;
|
||||
ID = id;
|
||||
Url = new Uri($"https://clips.twitch.tv/{ID}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -38,21 +39,24 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
public VideoSource Source { get; private set; }
|
||||
public string ID { get; private set; }
|
||||
public Uri Url { get; private set; }
|
||||
public Metadata Metadata { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
public string Author { get; private set; }
|
||||
public DateTime Date { get; private set; }
|
||||
public TimeSpan Duration { get; private set; }
|
||||
public long Views { get; private set; }
|
||||
public Uri Thumbnail { get; private set; }
|
||||
public BaseStream[] BaseStreams { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region STANDARD METHODS
|
||||
#region PUBLIC METHODS
|
||||
|
||||
// GET CLIP METADATA
|
||||
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get response
|
||||
JToken response = null;
|
||||
using (WebClient client = await Client.Helix())
|
||||
{
|
||||
@@ -62,39 +66,28 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
else throw new MediaNotFoundException($"Twitch Clip (ID: {ID}) was not found");
|
||||
}
|
||||
|
||||
// Create unified video url
|
||||
Url = new Uri($"https://clips.twitch.tv/{ID}");
|
||||
|
||||
// Set metadata
|
||||
Metadata = new Metadata()
|
||||
{
|
||||
Title = (string)response["title"],
|
||||
Author = (string)response["broadcaster_name"],
|
||||
Date = Convert.ToDateTime(response["created_at"]),
|
||||
Duration = TimeSpan.FromSeconds((double)response["duration"]),
|
||||
Views = (long)response["view_count"],
|
||||
Thumbnail = new Uri((string)response["thumbnail_url"]),
|
||||
};
|
||||
Title = (string)response["title"];
|
||||
Author = (string)response["broadcaster_name"];
|
||||
Date = Convert.ToDateTime(response["created_at"]);
|
||||
Duration = TimeSpan.FromSeconds((double)response["duration"]);
|
||||
Views = (long)response["view_count"];
|
||||
Thumbnail = new Uri((string)response["thumbnail_url"]);
|
||||
}
|
||||
|
||||
public async Task GetStreamsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get response
|
||||
JToken[] response;
|
||||
using (WebClient client = Client.GQL())
|
||||
{
|
||||
response = JArray.Parse(await client.UploadStringTaskAsync("https://gql.twitch.tv/gql", "[{\"operationName\":\"VideoAccessToken_Clip\",\"variables\":{\"slug\":\"" + ID + "\"},\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11\"}}}]"))[0]["data"]["clip"]["videoQualities"].ToArray();
|
||||
}
|
||||
|
||||
// Init streams list
|
||||
List<BaseStream> streams = new List<BaseStream>();
|
||||
|
||||
// Parse response
|
||||
foreach (JToken streamData in response)
|
||||
{
|
||||
// Create stream
|
||||
BaseStream stream = new BaseStream()
|
||||
{
|
||||
Url = new Uri((string)streamData["sourceURL"]),
|
||||
@@ -102,21 +95,17 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
FrameRate = (int)streamData["frameRate"],
|
||||
};
|
||||
|
||||
// Add stream
|
||||
streams.Add(stream);
|
||||
}
|
||||
|
||||
// Set streams
|
||||
BaseStreams = streams.ToArray();
|
||||
}
|
||||
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default)
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TrimData trim, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Invoke DownloadingStarted event
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0));
|
||||
|
||||
// Get video GQL access token
|
||||
JToken videoAccessToken = null;
|
||||
using (WebClient client = Client.GQL())
|
||||
{
|
||||
@@ -141,7 +130,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
// Processing
|
||||
StorageFile outputFile = rawFile;
|
||||
if (extension != MediaFileExtension.MP4 || mediaType != MediaType.AudioVideo || trimStart != null || trimEnd != null)
|
||||
if (extension != MediaFileExtension.MP4 || mediaType != MediaType.AudioVideo || trim.Start != null || trim.End != null)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
outputFile = await downloadingFolder.CreateFileAsync($"transcoded.{extension.ToString().ToLower()}");
|
||||
@@ -149,15 +138,9 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
MediaProcessor mediaProcessor = new MediaProcessor();
|
||||
mediaProcessor.ProgressChanged += ProcessingProgressChanged;
|
||||
|
||||
Task mediaProcessorTask;
|
||||
if (trimStart == TimeSpan.Zero && trimEnd == Metadata.Duration) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, cancellationToken: cancellationToken);
|
||||
else if (trimStart == TimeSpan.Zero) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimStart: trimStart, cancellationToken: cancellationToken);
|
||||
else if (trimEnd == Metadata.Duration) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimEnd: trimEnd, cancellationToken: cancellationToken);
|
||||
else mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimStart, trimEnd, cancellationToken);
|
||||
await mediaProcessorTask;
|
||||
await mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trim, cancellationToken);
|
||||
}
|
||||
|
||||
// Return output file
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
@@ -165,7 +148,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
#region EVENTS
|
||||
|
||||
public event EventHandler<EventArgs.ProgressChangedEventArgs> DownloadingProgressChanged;
|
||||
public event EventHandler<EventArgs.ProgressChangedEventArgs> ProcessingProgressChanged;
|
||||
@@ -4,11 +4,13 @@ using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Sources;
|
||||
using VDownload.Core.Structs;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
{
|
||||
public static class Auth
|
||||
public static class Authorization
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
@@ -16,7 +18,6 @@ namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
public readonly static string GQLApiClientID = "kimne78kx3ncx6brgo4mv6wki5h1ko";
|
||||
public readonly static Uri RedirectUrl = new Uri("https://www.vd.com");
|
||||
|
||||
// AUTHORIZATION URL
|
||||
private readonly static string ResponseType = "token";
|
||||
private readonly static string[] Scopes = new[]
|
||||
{
|
||||
@@ -34,8 +35,8 @@ namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
{
|
||||
try
|
||||
{
|
||||
StorageFolder authDataFolder = await ApplicationData.Current.LocalCacheFolder.GetFolderAsync("AuthData");
|
||||
StorageFile authDataFile = await authDataFolder.GetFileAsync("Twitch.auth");
|
||||
StorageFolder authDataFolder = await AuthorizationData.FolderLocation.GetFolderAsync(AuthorizationData.FolderName);
|
||||
StorageFile authDataFile = await authDataFolder.GetFileAsync($"Twitch.{AuthorizationData.FilesExtension}");
|
||||
|
||||
return await FileIO.ReadTextAsync(authDataFile);
|
||||
}
|
||||
@@ -47,8 +48,8 @@ namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
|
||||
public static async Task SaveAccessTokenAsync(string accessToken)
|
||||
{
|
||||
StorageFolder authDataFolder = await ApplicationData.Current.LocalCacheFolder.CreateFolderAsync("AuthData", CreationCollisionOption.OpenIfExists);
|
||||
StorageFile authDataFile = await authDataFolder.CreateFileAsync("Twitch.auth", CreationCollisionOption.ReplaceExisting);
|
||||
StorageFolder authDataFolder = await AuthorizationData.FolderLocation.CreateFolderAsync(AuthorizationData.FolderName, CreationCollisionOption.OpenIfExists);
|
||||
StorageFile authDataFile = await authDataFolder.CreateFileAsync($"Twitch.{AuthorizationData.FilesExtension}", CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
await FileIO.WriteTextAsync(authDataFile, accessToken);
|
||||
}
|
||||
@@ -57,15 +58,15 @@ namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
{
|
||||
try
|
||||
{
|
||||
StorageFolder authDataFolder = await ApplicationData.Current.LocalCacheFolder.GetFolderAsync("AuthData");
|
||||
StorageFile authDataFile = await authDataFolder.GetFileAsync("Twitch.auth");
|
||||
StorageFolder authDataFolder = await AuthorizationData.FolderLocation.GetFolderAsync(AuthorizationData.FolderName);
|
||||
StorageFile authDataFile = await authDataFolder.GetFileAsync($"Twitch.{AuthorizationData.FilesExtension}");
|
||||
|
||||
await authDataFile.DeleteAsync();
|
||||
}
|
||||
catch (FileNotFoundException) { }
|
||||
}
|
||||
|
||||
public static async Task<(bool IsValid, string Login, DateTime? ExpirationDate)> ValidateAccessTokenAsync(string accessToken)
|
||||
public static async Task<TwitchAccessTokenValidationData> ValidateAccessTokenAsync(string accessToken)
|
||||
{
|
||||
WebClient client = new WebClient { Encoding = Encoding.UTF8 };
|
||||
client.Headers.Add("Authorization", $"Bearer {accessToken}");
|
||||
@@ -77,17 +78,20 @@ namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
string login = response["login"].ToString();
|
||||
DateTime? expirationDate = DateTime.Now.AddSeconds(long.Parse(response["expires_in"].ToString()));
|
||||
|
||||
return (true, login, expirationDate);
|
||||
return new TwitchAccessTokenValidationData(accessToken, true, login, expirationDate);
|
||||
}
|
||||
catch (WebException wex)
|
||||
catch (WebException ex)
|
||||
{
|
||||
if (wex.Response != null)
|
||||
if (ex.Response is null)
|
||||
{
|
||||
JObject wexInfo = JObject.Parse(new StreamReader(wex.Response.GetResponseStream()).ReadToEnd());
|
||||
if ((int)wexInfo["status"] == 401) return (false, null, null);
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
JObject exInfo = JObject.Parse(new StreamReader(ex.Response.GetResponseStream()).ReadToEnd());
|
||||
if ((int)exInfo["status"] == 401) return new TwitchAccessTokenValidationData(accessToken, false, null, null);
|
||||
else throw;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@ namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
{
|
||||
internal static async Task<WebClient> Helix()
|
||||
{
|
||||
string accessToken = await Auth.ReadAccessTokenAsync();
|
||||
string accessToken = await Authorization.ReadAccessTokenAsync();
|
||||
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
|
||||
|
||||
var twitchAccessTokenValidation = await Auth.ValidateAccessTokenAsync(accessToken);
|
||||
var twitchAccessTokenValidation = await Authorization.ValidateAccessTokenAsync(accessToken);
|
||||
if (!twitchAccessTokenValidation.IsValid) throw new TwitchAccessTokenNotValidException();
|
||||
|
||||
WebClient client = new WebClient();
|
||||
client.Headers.Add("Authorization", $"Bearer {accessToken}");
|
||||
client.Headers.Add("Client-Id", Auth.ClientID);
|
||||
client.Headers.Add("Client-Id", Authorization.ClientID);
|
||||
|
||||
return client;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace VDownload.Core.Services.Sources.Twitch.Helpers
|
||||
internal static WebClient GQL()
|
||||
{
|
||||
WebClient client = new WebClient();
|
||||
client.Headers.Add("Client-Id", Auth.GQLApiClientID);
|
||||
client.Headers.Add("Client-Id", Authorization.GQLApiClientID);
|
||||
|
||||
return client;
|
||||
}
|
||||
@@ -24,8 +24,9 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
public Vod(string id)
|
||||
{
|
||||
ID = id;
|
||||
Source = VideoSource.TwitchVod;
|
||||
ID = id;
|
||||
Url = new Uri($"https://www.twitch.tv/videos/{ID}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -37,21 +38,24 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
public VideoSource Source { get; private set; }
|
||||
public string ID { get; private set; }
|
||||
public Uri Url { get; private set; }
|
||||
public Metadata Metadata { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
public string Author { get; private set; }
|
||||
public DateTime Date { get; private set; }
|
||||
public TimeSpan Duration { get; private set; }
|
||||
public long Views { get; private set; }
|
||||
public Uri Thumbnail { get; private set; }
|
||||
public BaseStream[] BaseStreams { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region STANDARD METHODS
|
||||
#region PUBLIC METHODS
|
||||
|
||||
// GET VOD METADATA
|
||||
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get response
|
||||
JToken response = null;
|
||||
using (WebClient client = await Client.Helix())
|
||||
{
|
||||
@@ -69,57 +73,40 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
}
|
||||
}
|
||||
|
||||
// Set parameters
|
||||
GetMetadataAsync(response);
|
||||
}
|
||||
internal void GetMetadataAsync(JToken response)
|
||||
{
|
||||
// Create unified video url
|
||||
Url = new Uri($"https://www.twitch.tv/videos/{ID}");
|
||||
|
||||
// Set metadata
|
||||
Metadata = new Metadata()
|
||||
{
|
||||
Title = ((string)response["title"]).Replace("\n", ""),
|
||||
Author = (string)response["user_name"],
|
||||
Date = Convert.ToDateTime(response["created_at"]),
|
||||
Duration = ParseDuration((string)response["duration"]),
|
||||
Views = (long)response["view_count"],
|
||||
Thumbnail = (string)response["thumbnail_url"] == string.Empty ? null : new Uri(((string)response["thumbnail_url"]).Replace("%{width}", "1920").Replace("%{height}", "1080")),
|
||||
};
|
||||
Title = ((string)response["title"]).Replace("\n", "");
|
||||
Author = (string)response["user_name"];
|
||||
Date = Convert.ToDateTime(response["created_at"]);
|
||||
Duration = ParseDuration((string)response["duration"]);
|
||||
Views = (long)response["view_count"];
|
||||
Thumbnail = (string)response["thumbnail_url"] == string.Empty ? null : new Uri(((string)response["thumbnail_url"]).Replace("%{width}", "1920").Replace("%{height}", "1080"));
|
||||
}
|
||||
|
||||
// GET VOD STREAMS
|
||||
public async Task GetStreamsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get response
|
||||
string[] response = null;
|
||||
using (WebClient client = Client.GQL())
|
||||
{
|
||||
// Get video GQL access token
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
JToken videoAccessToken = JObject.Parse(await client.UploadStringTaskAsync("https://gql.twitch.tv/gql", "{\"operationName\":\"PlaybackAccessToken_Template\",\"query\":\"query PlaybackAccessToken_Template($login: String!, $isLive: Boolean!, $vodID: ID!, $isVod: Boolean!, $playerType: String!) { streamPlaybackAccessToken(channelName: $login, params: {platform: \\\"web\\\", playerBackend: \\\"mediaplayer\\\", playerType: $playerType}) @include(if: $isLive) { value signature __typename } videoPlaybackAccessToken(id: $vodID, params: {platform: \\\"web\\\", playerBackend: \\\"mediaplayer\\\", playerType: $playerType}) @include(if: $isVod) { value signature __typename }}\",\"variables\":{\"isLive\":false,\"login\":\"\",\"isVod\":true,\"vodID\":\"" + ID + "\",\"playerType\":\"embed\"}}"))["data"]["videoPlaybackAccessToken"];
|
||||
|
||||
// Get video streams
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
response = (await client.DownloadStringTaskAsync($"http://usher.twitch.tv/vod/{ID}?nauth={videoAccessToken["value"]}&nauthsig={videoAccessToken["signature"]}&allow_source=true&player=twitchweb")).Split("\n");
|
||||
}
|
||||
|
||||
// Init streams list
|
||||
List<BaseStream> streams = new List<BaseStream>();
|
||||
|
||||
// Stream data line2 regular expression
|
||||
Regex streamDataL2Regex = new Regex(@"^#EXT-X-STREAM-INF:BANDWIDTH=\d+,CODECS=""\S+,\S+"",RESOLUTION=\d+x(?<height>\d+),VIDEO=""\w+""(,FRAME-RATE=(?<frame_rate>\d+.\d+))?");
|
||||
|
||||
// Parse response
|
||||
for (int i = 2; i < response.Length; i += 3)
|
||||
{
|
||||
// Parse line 2
|
||||
Match line2 = streamDataL2Regex.Match(response[i + 1]);
|
||||
|
||||
// Create stream
|
||||
BaseStream stream = new BaseStream()
|
||||
{
|
||||
Url = new Uri(response[i + 2]),
|
||||
@@ -127,30 +114,22 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
FrameRate = line2.Groups["frame_rate"].Value != string.Empty ? (int)Math.Round(double.Parse(line2.Groups["frame_rate"].Value)) : 0,
|
||||
};
|
||||
|
||||
// Add stream
|
||||
streams.Add(stream);
|
||||
}
|
||||
|
||||
// Set streams
|
||||
BaseStreams = streams.ToArray();
|
||||
}
|
||||
|
||||
// DOWNLOAD AND TRANSCODE VOD
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default)
|
||||
public async Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, BaseStream baseStream, MediaFileExtension extension, MediaType mediaType, TrimData trim, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Invoke DownloadingStarted event
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
DownloadingProgressChanged(this, new EventArgs.ProgressChangedEventArgs(0));
|
||||
|
||||
// Get video chunks
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
DownloadingProgressChanged.Invoke(this, new EventArgs.ProgressChangedEventArgs(0));
|
||||
List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList = await ExtractChunksFromM3U8Async(baseStream.Url, cancellationToken);
|
||||
|
||||
// Changeable duration
|
||||
TimeSpan duration = Metadata.Duration;
|
||||
TimeSpan duration = Duration;
|
||||
|
||||
// Passive trim
|
||||
if ((bool)Config.GetValue("twitch_vod_passive_trim") && trimStart != TimeSpan.Zero && trimEnd != duration) (trimStart, trimEnd, duration) = PassiveVideoTrim(chunksList, trimStart, trimEnd, Metadata.Duration);
|
||||
if ((bool)Config.GetValue("twitch_vod_passive_trim") && trim.Start != TimeSpan.Zero && trim.End != duration) (trim, duration) = PassiveVideoTrim(chunksList, trim, Duration);
|
||||
|
||||
// Download
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -183,12 +162,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
MediaProcessor mediaProcessor = new MediaProcessor();
|
||||
mediaProcessor.ProgressChanged += ProcessingProgressChanged;
|
||||
|
||||
Task mediaProcessorTask;
|
||||
if (trimStart == TimeSpan.Zero && trimEnd == duration) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, cancellationToken: cancellationToken);
|
||||
else if (trimStart == TimeSpan.Zero) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimStart: trimStart, cancellationToken: cancellationToken);
|
||||
else if (trimEnd == duration) mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimEnd: trimEnd, cancellationToken: cancellationToken);
|
||||
else mediaProcessorTask = mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trimStart, trimEnd, cancellationToken);
|
||||
await mediaProcessorTask;
|
||||
await mediaProcessor.Run(rawFile, extension, mediaType, outputFile, trim, cancellationToken);
|
||||
|
||||
// Return output file
|
||||
return outputFile;
|
||||
@@ -198,30 +172,24 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
|
||||
|
||||
#region LOCAL METHODS
|
||||
#region PRIVATE METHODS
|
||||
|
||||
// GET CHUNKS DATA FROM M3U8 PLAYLIST
|
||||
private static async Task<List<(Uri ChunkUrl, TimeSpan ChunkDuration)>> ExtractChunksFromM3U8Async(Uri streamUrl, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Get response
|
||||
string response = null;
|
||||
using (WebClient client = Client.GQL())
|
||||
{
|
||||
response = await client.DownloadStringTaskAsync(streamUrl);
|
||||
}
|
||||
|
||||
// Create dictionary
|
||||
List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunks = new List<(Uri ChunkUrl, TimeSpan ChunkDuration)>();
|
||||
|
||||
// Chunk data regular expression
|
||||
Regex chunkDataRegex = new Regex(@"#EXTINF:(?<duration>\d+.\d+),\n(?<filename>\S+.ts)");
|
||||
|
||||
// Chunks location
|
||||
string chunkLocationPath = streamUrl.AbsoluteUri.Replace(System.IO.Path.GetFileName(streamUrl.AbsoluteUri), "");
|
||||
string chunkLocationPath = streamUrl.AbsoluteUri.Replace(Path.GetFileName(streamUrl.AbsoluteUri), "");
|
||||
|
||||
// Pack data into dictionary
|
||||
foreach (Match chunk in chunkDataRegex.Matches(response))
|
||||
{
|
||||
Uri chunkUrl = new Uri($"{chunkLocationPath}{chunk.Groups["filename"].Value}");
|
||||
@@ -229,37 +197,30 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
chunks.Add((chunkUrl, chunkDuration));
|
||||
}
|
||||
|
||||
// Return chunks data
|
||||
return chunks;
|
||||
}
|
||||
|
||||
// PASSIVE TRIM
|
||||
private static (TimeSpan NewTrimStart, TimeSpan NewTrimEnd, TimeSpan NewDuration) PassiveVideoTrim(List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList, TimeSpan trimStart, TimeSpan trimEnd, TimeSpan duration)
|
||||
private static (TrimData Trim, TimeSpan NewDuration) PassiveVideoTrim(List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList, TrimData trim, TimeSpan duration)
|
||||
{
|
||||
// Copy duration
|
||||
TimeSpan newDuration = duration;
|
||||
|
||||
// Trim at start
|
||||
while (chunksList[0].ChunkDuration <= trimStart)
|
||||
while (chunksList[0].ChunkDuration <= trim.Start)
|
||||
{
|
||||
trimStart = trimStart.Subtract(chunksList[0].ChunkDuration);
|
||||
trimEnd = trimEnd.Subtract(chunksList[0].ChunkDuration);
|
||||
trim.Start = trim.Start.Subtract(chunksList[0].ChunkDuration);
|
||||
trim.End = trim.End.Subtract(chunksList[0].ChunkDuration);
|
||||
newDuration = newDuration.Subtract(chunksList[0].ChunkDuration);
|
||||
chunksList.RemoveAt(0);
|
||||
}
|
||||
|
||||
// Trim at end
|
||||
while (chunksList.Last().ChunkDuration <= newDuration.Subtract(trimEnd))
|
||||
while (chunksList.Last().ChunkDuration <= newDuration.Subtract(trim.End))
|
||||
{
|
||||
newDuration = newDuration.Subtract(chunksList.Last().ChunkDuration);
|
||||
chunksList.RemoveAt(chunksList.Count - 1);
|
||||
}
|
||||
|
||||
// Return data
|
||||
return (trimStart, trimEnd, newDuration);
|
||||
return (trim, newDuration);
|
||||
}
|
||||
|
||||
// DOWNLOAD CHUNK
|
||||
private static async Task<byte[]> DownloadChunkAsync(Uri chunkUrl, CancellationToken cancellationToken = default)
|
||||
{
|
||||
int retriesCount = 0;
|
||||
@@ -285,12 +246,11 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
}
|
||||
}
|
||||
|
||||
// WRITE CHUNK TO FILE
|
||||
private static Task WriteChunkToFileAsync(StorageFile file, byte[] chunk)
|
||||
{
|
||||
return Task.Factory.StartNew(() =>
|
||||
{
|
||||
using (var stream = new System.IO.FileStream(file.Path, System.IO.FileMode.Append))
|
||||
using (var stream = new FileStream(file.Path, FileMode.Append))
|
||||
{
|
||||
stream.Write(chunk, 0, chunk.Length);
|
||||
stream.Close();
|
||||
@@ -298,7 +258,6 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
});
|
||||
}
|
||||
|
||||
// PARSE DURATION
|
||||
private static TimeSpan ParseDuration(string duration)
|
||||
{
|
||||
char[] separators = { 'h', 'm', 's' };
|
||||
@@ -316,7 +275,7 @@ namespace VDownload.Core.Services.Sources.Twitch
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
#region EVENTS
|
||||
|
||||
public event EventHandler<EventArgs.ProgressChangedEventArgs> DownloadingProgressChanged;
|
||||
public event EventHandler<EventArgs.ProgressChangedEventArgs> ProcessingProgressChanged;
|
||||
@@ -9,8 +9,20 @@ namespace VDownload.Core.Structs
|
||||
[Serializable]
|
||||
public struct BaseStream
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
public Uri Url { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int FrameRate { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region METHODS
|
||||
|
||||
public override string ToString() => $"{Height}p{(FrameRate > 0 ? FrameRate.ToString() : "N/A")}";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VDownload.Core.Structs
|
||||
{
|
||||
[Serializable]
|
||||
public struct Metadata
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Author { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public TimeSpan Duration { get; set; }
|
||||
public long Views { get; set; }
|
||||
public Uri Thumbnail { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Interfaces;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace VDownload.Core.Structs
|
||||
{
|
||||
public struct TaskData
|
||||
{
|
||||
public IVideo VideoService { get; set; }
|
||||
public TaskOptions TaskOptions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using VDownload.Core.Enums;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace VDownload.Core.Structs
|
||||
{
|
||||
public struct TaskOptions
|
||||
{
|
||||
public MediaType MediaType { get; set; }
|
||||
public BaseStream Stream { get; set; }
|
||||
public TimeSpan TrimStart { get; set; }
|
||||
public TimeSpan TrimEnd { get; set; }
|
||||
public string Filename { get; set; }
|
||||
public MediaFileExtension Extension { get; set; }
|
||||
public StorageFolder Location { get; set; }
|
||||
public double Schedule { get; set; }
|
||||
}
|
||||
}
|
||||
18
VDownload.Core/Structs/TrimData.cs
Normal file
18
VDownload.Core/Structs/TrimData.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VDownload.Core.Structs
|
||||
{
|
||||
public struct TrimData
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
public TimeSpan Start { get; set; }
|
||||
public TimeSpan End { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
32
VDownload.Core/Structs/TwitchAccessTokenValidationData.cs
Normal file
32
VDownload.Core/Structs/TwitchAccessTokenValidationData.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace VDownload.Core.Structs
|
||||
{
|
||||
public struct TwitchAccessTokenValidationData
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public TwitchAccessTokenValidationData(string accessToken, bool isValid, string login, DateTime? expirationDate)
|
||||
{
|
||||
AccessToken = accessToken;
|
||||
IsValid = isValid;
|
||||
Login = login;
|
||||
ExpirationDate = expirationDate;
|
||||
}
|
||||
|
||||
public static TwitchAccessTokenValidationData Null = new TwitchAccessTokenValidationData(string.Empty, false, string.Empty, null);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public string AccessToken { get; private set; }
|
||||
public bool IsValid { get; private set; }
|
||||
public string Login { get; private set; }
|
||||
public DateTime? ExpirationDate { get; private set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -124,42 +124,50 @@
|
||||
<Compile Include="Enums\MediaFileExtension.cs" />
|
||||
<Compile Include="Enums\MediaType.cs" />
|
||||
<Compile Include="Enums\PlaylistSource.cs" />
|
||||
<Compile Include="Enums\SubscriptionStatus.cs" />
|
||||
<Compile Include="Enums\TaskAddingRequestSource.cs" />
|
||||
<Compile Include="Enums\DownloadTasksAddingRequestSource.cs" />
|
||||
<Compile Include="Enums\VideoFileExtension.cs" />
|
||||
<Compile Include="Enums\VideoSource.cs" />
|
||||
<Compile Include="Enums\TaskStatus.cs" />
|
||||
<Compile Include="Enums\DownloadTaskStatus.cs" />
|
||||
<Compile Include="EventArgs\DownloadTaskEndedUnsuccessfullyEventArgs.cs" />
|
||||
<Compile Include="EventArgs\DownloadTaskEndedSuccessfullyEventArgs.cs" />
|
||||
<Compile Include="EventArgs\DownloadTaskScheduledEventArgs.cs" />
|
||||
<Compile Include="EventArgs\ProgressChangedEventArgs.cs" />
|
||||
<Compile Include="EventArgs\TasksAddingRequestedEventArgs.cs" />
|
||||
<Compile Include="EventArgs\DownloadTasksAddingRequestedEventArgs.cs" />
|
||||
<Compile Include="EventArgs\PlaylistSearchSuccessedEventArgs.cs" />
|
||||
<Compile Include="EventArgs\SubscriptionLoadSuccessedEventArgs.cs" />
|
||||
<Compile Include="EventArgs\VideoSearchSuccessedEventArgs.cs" />
|
||||
<Compile Include="Exceptions\MediaNotFoundException.cs" />
|
||||
<Compile Include="Exceptions\SubscriptionExistsException.cs" />
|
||||
<Compile Include="Exceptions\TwitchAccessTokenNotFoundException.cs" />
|
||||
<Compile Include="Exceptions\TwitchAccessTokenNotValidException.cs" />
|
||||
<Compile Include="Extensions\TimeSpanExtension.cs" />
|
||||
<Compile Include="Interfaces\IPlaylist.cs" />
|
||||
<Compile Include="Interfaces\IVideo.cs" />
|
||||
<Compile Include="Services\Sources\Twitch\Helpers\Client.cs" />
|
||||
<Compile Include="Services\DownloadTasksCollectionManagement.cs" />
|
||||
<Compile Include="Services\OutputFile.cs" />
|
||||
<Compile Include="Sources\AuthorizationData.cs" />
|
||||
<Compile Include="Sources\Twitch\Helpers\Client.cs" />
|
||||
<Compile Include="Services\Subscription.cs" />
|
||||
<Compile Include="Services\SubscriptionsCollectionManagement.cs" />
|
||||
<Compile Include="Services\TimeSpanCustomFormat.cs" />
|
||||
<Compile Include="Structs\BaseStream.cs" />
|
||||
<Compile Include="Structs\Metadata.cs" />
|
||||
<Compile Include="Structs\TaskData.cs" />
|
||||
<Compile Include="Services\DownloadTask.cs" />
|
||||
<Compile Include="Services\Config.cs" />
|
||||
<Compile Include="Services\MediaProcessor.cs" />
|
||||
<Compile Include="Services\Sources\Source.cs" />
|
||||
<Compile Include="Services\Sources\Twitch\Helpers\Auth.cs" />
|
||||
<Compile Include="Services\Sources\Twitch\Channel.cs" />
|
||||
<Compile Include="Services\Sources\Twitch\Clip.cs" />
|
||||
<Compile Include="Services\Sources\Twitch\Vod.cs" />
|
||||
<Compile Include="Services\TaskId.cs" />
|
||||
<Compile Include="Structs\TaskOptions.cs" />
|
||||
<Compile Include="Sources\Source.cs" />
|
||||
<Compile Include="Sources\Twitch\Helpers\Authorization.cs" />
|
||||
<Compile Include="Sources\Twitch\Channel.cs" />
|
||||
<Compile Include="Sources\Twitch\Clip.cs" />
|
||||
<Compile Include="Sources\Twitch\Vod.cs" />
|
||||
<Compile Include="Structs\TrimData.cs" />
|
||||
<Compile Include="Structs\TwitchAccessTokenValidationData.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.2.13</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Connectivity">
|
||||
<Version>7.1.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>13.0.1</Version>
|
||||
</PackageReference>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,14 +1,13 @@
|
||||
<Page
|
||||
x:Class="VDownload.Views.Settings.SettingsMain"
|
||||
<UserControl
|
||||
x:Class="VDownload.Controls.PlaceholderableStackPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Settings"
|
||||
xmlns:local="using:VDownload.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
||||
<StackPanel x:Name="StackPanel"/>
|
||||
</UserControl>
|
||||
100
VDownload/Controls/PlaceholderableStackPanel.xaml.cs
Normal file
100
VDownload/Controls/PlaceholderableStackPanel.xaml.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace VDownload.Controls
|
||||
{
|
||||
public sealed partial class PlaceholderableStackPanel : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public PlaceholderableStackPanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
IsPlaceholderActive = true;
|
||||
StackPanel.Children.Add(Placeholder);
|
||||
StackPanel.VerticalAlignment = VerticalAlignment.Center;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private UIElement _Placeholder = new Grid();
|
||||
public UIElement Placeholder
|
||||
{
|
||||
get => _Placeholder;
|
||||
set
|
||||
{
|
||||
_Placeholder = value;
|
||||
if (IsPlaceholderActive)
|
||||
{
|
||||
StackPanel.Children.Clear();
|
||||
StackPanel.Children.Add(_Placeholder);
|
||||
StackPanel.VerticalAlignment = VerticalAlignment.Center;
|
||||
}
|
||||
}
|
||||
}
|
||||
public double Spacing
|
||||
{
|
||||
get => StackPanel.Spacing;
|
||||
set => StackPanel.Spacing = value;
|
||||
}
|
||||
|
||||
public bool IsPlaceholderActive { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public void Add(UIElement item)
|
||||
{
|
||||
if (IsPlaceholderActive)
|
||||
{
|
||||
StackPanel.Children.Clear();
|
||||
IsPlaceholderActive = false;
|
||||
StackPanel.VerticalAlignment = VerticalAlignment.Stretch;
|
||||
}
|
||||
StackPanel.Children.Add(item);
|
||||
}
|
||||
|
||||
public void Remove(UIElement item)
|
||||
{
|
||||
StackPanel.Children.Remove(item);
|
||||
if (StackPanel.Children.Count == 0)
|
||||
{
|
||||
StackPanel.Children.Add(_Placeholder);
|
||||
StackPanel.VerticalAlignment = VerticalAlignment.Center;
|
||||
IsPlaceholderActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
StackPanel.Children.Clear();
|
||||
StackPanel.Children.Add(_Placeholder);
|
||||
StackPanel.VerticalAlignment = VerticalAlignment.Center;
|
||||
IsPlaceholderActive = true;
|
||||
}
|
||||
|
||||
public UIElement[] GetAllItems() => IsPlaceholderActive ? StackPanel.Children.ToArray() : new UIElement[0];
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
<SvgImageSource x:Key="LocationIcon" UriSource="ms-appx:///Assets/Icons/LocationLight.svg"/>
|
||||
<SvgImageSource x:Key="ScheduleIcon" UriSource="ms-appx:///Assets/Icons/ScheduleLight.svg"/>
|
||||
<SvgImageSource x:Key="StateIdleIcon" UriSource="ms-appx:///Assets/Icons/StateIdleLight.svg"/>
|
||||
<SvgImageSource x:Key="StateWaitingIcon" UriSource="ms-appx:///Assets/Icons/StateWaitingLight.svg"/>
|
||||
<SvgImageSource x:Key="StateQueuedIcon" UriSource="ms-appx:///Assets/Icons/StateWaitingLight.svg"/>
|
||||
<SvgImageSource x:Key="StateCancelledIcon" UriSource="ms-appx:///Assets/Icons/StateCancelledLight.svg"/>
|
||||
<SvgImageSource x:Key="StateDownloadingIcon" UriSource="ms-appx:///Assets/Icons/StateDownloadingLight.svg"/>
|
||||
<SvgImageSource x:Key="StateErrorIcon" UriSource="ms-appx:///Assets/Icons/StateErrorLight.svg"/>
|
||||
@@ -37,7 +37,7 @@
|
||||
<SvgImageSource x:Key="LocationIcon" UriSource="ms-appx:///Assets/Icons/LocationDark.svg"/>
|
||||
<SvgImageSource x:Key="ScheduleIcon" UriSource="ms-appx:///Assets/Icons/ScheduleDark.svg"/>
|
||||
<SvgImageSource x:Key="StateIdleIcon" UriSource="ms-appx:///Assets/Icons/StateIdleDark.svg"/>
|
||||
<SvgImageSource x:Key="StateWaitingIcon" UriSource="ms-appx:///Assets/Icons/StateWaitingDark.svg"/>
|
||||
<SvgImageSource x:Key="StateQueuedIcon" UriSource="ms-appx:///Assets/Icons/StateWaitingDark.svg"/>
|
||||
<SvgImageSource x:Key="StateCancelledIcon" UriSource="ms-appx:///Assets/Icons/StateCancelledDark.svg"/>
|
||||
<SvgImageSource x:Key="StateDownloadingIcon" UriSource="ms-appx:///Assets/Icons/StateDownloadingDark.svg"/>
|
||||
<SvgImageSource x:Key="StateErrorIcon" UriSource="ms-appx:///Assets/Icons/StateErrorDark.svg"/>
|
||||
|
||||
@@ -117,9 +117,84 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Base_CancelButtonText" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="Base_CloseButtonText" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
</data>
|
||||
<data name="Home_DownloadAll_MeteredConnection_Content" xml:space="preserve">
|
||||
<value>You are on the metered connection now. You can delay tasks until the network changes. Do you want to start the tasks?</value>
|
||||
</data>
|
||||
<data name="Home_DownloadAll_MeteredConnection_StartWithDelayButtonText" xml:space="preserve">
|
||||
<value>Yes (With delay)</value>
|
||||
</data>
|
||||
<data name="Home_DownloadAll_MeteredConnection_StartWithoutDelayButtonText" xml:space="preserve">
|
||||
<value>Yes (Without delay)</value>
|
||||
</data>
|
||||
<data name="Home_DownloadAll_MeteredConnection_Title" xml:space="preserve">
|
||||
<value>Metered connection detected</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Start_MeteredConnection_Content" xml:space="preserve">
|
||||
<value>You are on the metered connection now. You can delay task until the network changes. Do you want to start the task?</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Start_MeteredConnection_StartWithDelayButtonText" xml:space="preserve">
|
||||
<value>Yes (With delay)</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Start_MeteredConnection_StartWithoutDelayButtonText1" xml:space="preserve">
|
||||
<value>Yes (Without delay)</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Start_MeteredConnection_Title" xml:space="preserve">
|
||||
<value>Metered connection detected</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_Base_Title" xml:space="preserve">
|
||||
<value>Playlist search error</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_InternetNotAvailable_Content" xml:space="preserve">
|
||||
<value>Unable to connect to servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_TwitchAccessTokenNotFound_Content" xml:space="preserve">
|
||||
<value>To get information about Twitch playlists (Channels), you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_TwitchAccessTokenNotValid_Content" xml:space="preserve">
|
||||
<value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_Base_Title" xml:space="preserve">
|
||||
<value>Subscriptions loading error</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_InternetNotAvailable_Content" xml:space="preserve">
|
||||
<value>Unable to connect to servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_MediaNotFound_Content" xml:space="preserve">
|
||||
<value>Subscribed playlist not found. Name:</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_TwitchAccessTokenNotFound_Content" xml:space="preserve">
|
||||
<value>To get information about Twitch videos and playlists, you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_TwitchAccessTokenNotValid_Content" xml:space="preserve">
|
||||
<value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_Base_Title" xml:space="preserve">
|
||||
<value>Video search error</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_InternetNotAvailable_Content" xml:space="preserve">
|
||||
<value>Unable to connect to servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_TwitchAccessTokenNotFound_Content" xml:space="preserve">
|
||||
<value>To get information about Twitch videos (VODs and Clips), you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_TwitchAccessTokenNotValid_Content" xml:space="preserve">
|
||||
<value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchLogin_Base_Title" xml:space="preserve">
|
||||
<value>Login to Twitch failed</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchLogin_InternetNotAvailable_Content" xml:space="preserve">
|
||||
<value>Unable to connect to servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchLogin_Unknown_Content" xml:space="preserve">
|
||||
<value>Unknown error. </value>
|
||||
</data>
|
||||
<data name="Subscription_Adding_Base_Title" xml:space="preserve">
|
||||
<value>Playlist adding error</value>
|
||||
</data>
|
||||
|
||||
@@ -117,304 +117,211 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CloseErrorDialogButtonText" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
<data name="Base_MediaType_AudioVideoText" xml:space="preserve">
|
||||
<value>Audio & Video</value>
|
||||
</data>
|
||||
<data name="HomeAddingDownloadingOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<data name="Base_MediaType_OnlyAudioText" xml:space="preserve">
|
||||
<value>Only audio</value>
|
||||
</data>
|
||||
<data name="Base_MediaType_OnlyVideoText" xml:space="preserve">
|
||||
<value>Only video</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBlock.Text" xml:space="preserve">
|
||||
<value>Author</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Enter regular expression</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_DateTextBlock.Text" xml:space="preserve">
|
||||
<value>Date</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_DurationTextBlock.Text" xml:space="preserve">
|
||||
<value>Duration</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Header_CountTextBlockPrefix" xml:space="preserve">
|
||||
<value>Hidden</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Header_TextBlock.Text" xml:space="preserve">
|
||||
<value>Filter</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_RestoreButton.Content" xml:space="preserve">
|
||||
<value>Restore</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_TextBlock.Text" xml:space="preserve">
|
||||
<value>Removed</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBlock.Text" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Enter regular expression</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_Filter_ViewsTextBlock.Text" xml:space="preserve">
|
||||
<value>Views</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_HeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Downloading options</value>
|
||||
</data>
|
||||
<data name="HomeAddingFileOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>File options</value>
|
||||
</data>
|
||||
<data name="HomeAddingFileSettingControl.Title" xml:space="preserve">
|
||||
<value>File</value>
|
||||
</data>
|
||||
<data name="HomeAddingLocationBrowseButton.Content" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="HomeAddingLocationSettingControl.Title" xml:space="preserve">
|
||||
<value>Location</value>
|
||||
</data>
|
||||
<data name="HomeAddingMediaTypeSettingControl.Title" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_MediaTypeSettingControl.Title" xml:space="preserve">
|
||||
<value>Media type</value>
|
||||
</data>
|
||||
<data name="HomeAddingQualitySettingControl.Title" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_QualitySettingControl.Title" xml:space="preserve">
|
||||
<value>Quality</value>
|
||||
</data>
|
||||
<data name="HomeAddingScheduleSettingControl.Description" xml:space="preserve">
|
||||
<value>Number of minutes to start the task (after clicking downloading button).</value>
|
||||
</data>
|
||||
<data name="HomeAddingScheduleSettingControl.Title" xml:space="preserve">
|
||||
<value>Schedule</value>
|
||||
</data>
|
||||
<data name="HomeAddingTaskOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Task options</value>
|
||||
</data>
|
||||
<data name="HomeAddingTrimSettingControl.Title" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_TrimSettingControl.Title" xml:space="preserve">
|
||||
<value>Trim</value>
|
||||
</data>
|
||||
<data name="HomeDownloadAllButtonMeteredConnectionDialogCancel" xml:space="preserve">
|
||||
<value>No</value>
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl.Title" xml:space="preserve">
|
||||
<value>Location</value>
|
||||
</data>
|
||||
<data name="HomeDownloadAllButtonMeteredConnectionDialogDescription" xml:space="preserve">
|
||||
<value>You are on the metered connection now. You can delay tasks until the network changes. Do you want to start the tasks?</value>
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl_BrowseButton.Content" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="HomeDownloadAllButtonMeteredConnectionDialogStartAndDelayText" xml:space="preserve">
|
||||
<value>Yes (With delay)</value>
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileSettingControl.Title" xml:space="preserve">
|
||||
<value>File</value>
|
||||
</data>
|
||||
<data name="HomeDownloadAllButtonMeteredConnectionDialogStartWithoutDelayText" xml:space="preserve">
|
||||
<value>Yes (Without delay)</value>
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_HeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>File options</value>
|
||||
</data>
|
||||
<data name="HomeDownloadAllButtonMeteredConnectionDialogTitle" xml:space="preserve">
|
||||
<value>Metered connection detected</value>
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_HeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Task options</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarDownloadAllButton.Label" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_ScheduleSettingControl.Description" xml:space="preserve">
|
||||
<value>Number of minutes to start the task (after clicking downloading button).</value>
|
||||
</data>
|
||||
<data name="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_ScheduleSettingControl.Title" xml:space="preserve">
|
||||
<value>Schedule</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControlsStackPanel_Placeholder_TextBlock.Text" xml:space="preserve">
|
||||
<value>Click "Video/Playlist search" button to start</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Error_InternetNotAvailable" xml:space="preserve">
|
||||
<value>Internet connection error</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Notification_EndedSuccessfully_Description" xml:space="preserve">
|
||||
<value>downloading ended successfully</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Notification_EndedSuccessfully_Header" xml:space="preserve">
|
||||
<value>Video downloading ended successfully</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Description" xml:space="preserve">
|
||||
<value>an error occured</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Header" xml:space="preserve">
|
||||
<value>Video downloading ended unsuccessfully</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Cancelled" xml:space="preserve">
|
||||
<value>Cancelled</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Done" xml:space="preserve">
|
||||
<value>Done</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Downloading" xml:space="preserve">
|
||||
<value>Downloading</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Error" xml:space="preserve">
|
||||
<value>An error occured</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Finalizing" xml:space="preserve">
|
||||
<value>Finalizing</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Idle" xml:space="preserve">
|
||||
<value>Idle</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Processing" xml:space="preserve">
|
||||
<value>Processing</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Queued" xml:space="preserve">
|
||||
<value>Queued</value>
|
||||
</data>
|
||||
<data name="Home_DownloadTaskControl_State_Scheduled" xml:space="preserve">
|
||||
<value>Scheduled</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_DownloadAllButton.Label" xml:space="preserve">
|
||||
<value>Download all</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarDownloadAllButton.Width" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_DownloadAllButton.Width" xml:space="preserve">
|
||||
<value>90</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarLoadSubscripionsButton.Label" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_LoadSubscripionsButton.Label" xml:space="preserve">
|
||||
<value>Load subscriptions</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarLoadSubscripionsButton.Width" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_LoadSubscripionsButton.Width" xml:space="preserve">
|
||||
<value>120</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchButton.Label" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_PlaylistSearchButton.Label" xml:space="preserve">
|
||||
<value>Playlist search</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchButton.Width" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_PlaylistSearchButton.Width" xml:space="preserve">
|
||||
<value>95</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchControlInfoBox.Subtitle" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_InfoBox.Subtitle" xml:space="preserve">
|
||||
<value>- Twitch (Channel)
|
||||
|
||||
Number of videos got from playlist
|
||||
The number in the numberbox indicades how many videos will be got from playlist. 0 = all.
|
||||
</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchControlInfoBox.Title" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_InfoBox.Title" xml:space="preserve">
|
||||
<value>Supported sources</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchControlSearchButton.Content" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_SearchButton.Content" xml:space="preserve">
|
||||
<value>Search</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchControlUrlTextBox.PlaceholderText" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_PlaylistSearchControl_UrlTextBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Playlist URL</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchingErrorDialogTitle" xml:space="preserve">
|
||||
<value>Playlist search error</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchingInternetConnectionErrorDialogDescription" xml:space="preserve">
|
||||
<value>Unable to connect to servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchingTwitchAccessTokenNotFoundErrorDialogDescription" xml:space="preserve">
|
||||
<value>To get information about Twitch playlists (Channels), you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarPlaylistSearchingTwitchAccessTokenNotValidErrorDialogDescription" xml:space="preserve">
|
||||
<value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchButton.Label" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_VideoSearchButton.Label" xml:space="preserve">
|
||||
<value>Video search</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchButton.Width" xml:space="preserve">
|
||||
<data name="Home_OptionsBar_VideoSearchButton.Width" xml:space="preserve">
|
||||
<value>90</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchControlInfoBox.Subtitle" xml:space="preserve">
|
||||
<value>- Twitch (VODs, Clips)</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchControlInfoBox.Title" xml:space="preserve">
|
||||
<value>Supported sources</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchControlSearchButton.Content" xml:space="preserve">
|
||||
<value>Search</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchControlUrlTextBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Video URL</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchingErrorDialogTitle" xml:space="preserve">
|
||||
<value>Video search error</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchingInternetConnectionErrorDialogDescription" xml:space="preserve">
|
||||
<value>Unable to connect to servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchingTwitchAccessTokenNotFoundErrorDialogDescription" xml:space="preserve">
|
||||
<value>To get information about Twitch videos (VODs and Clips), you have to link your Twitch account with VDownload. Go to Sources page to sign in.</value>
|
||||
</data>
|
||||
<data name="HomeOptionsBarVideoSearchingTwitchAccessTokenNotValidErrorDialogDescription" xml:space="preserve">
|
||||
<value>There is a problem with linked Twitch account. Check Twitch login status in Sources page.</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingApplyToAllApplyButton.Content" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_Base_ApplyButton.Content" xml:space="preserve">
|
||||
<value>Apply</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingApplyToAllLocationBrowseButton.Content" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingApplyToAllLocationSettingControl.Title" xml:space="preserve">
|
||||
<value>Location</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingApplyToAllScheduleSettingControl.Description" xml:space="preserve">
|
||||
<value>Number of minutes to start the task (after clicking downloading button).</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingApplyToAllScheduleSettingControl.Title" xml:space="preserve">
|
||||
<value>Schedule</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelApplyToAllOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_HeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Apply to all options</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterAuthorTextBlock.Text" xml:space="preserve">
|
||||
<value>Author</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterAuthorTextBoxPlaceholderText" xml:space="preserve">
|
||||
<value>Enter regular expression</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterDateTextBlock.Text" xml:space="preserve">
|
||||
<value>Date</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterDurationTextBlock.Text" xml:space="preserve">
|
||||
<value>Duration</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterHeaderApplyButton.Content" xml:space="preserve">
|
||||
<value>Apply</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterHeaderCountTextBlockPrefix" xml:space="preserve">
|
||||
<value>Hidden</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterHeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Filter</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterRemovedRestoreButton.Content" xml:space="preserve">
|
||||
<value>Restore</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterRemovedTextBlock.Text" xml:space="preserve">
|
||||
<value>Removed</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterTitleTextBlock.Text" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterTitleTextBoxPlaceholderText" xml:space="preserve">
|
||||
<value>Enter regular expression</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingPanelFilterViewsTextBlock.Text" xml:space="preserve">
|
||||
<value>Views</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelDownloadingOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Downloading options</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelFileOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>File options</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelFileSettingControl.Title" xml:space="preserve">
|
||||
<value>File</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelLocationBrowseButton.Content" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelLocationSettingControl.Title" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl.Title" xml:space="preserve">
|
||||
<value>Location</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelMediaTypeSettingControl.Title" xml:space="preserve">
|
||||
<value>Media type</value>
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl_BrowseButton.Content" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelQualitySettingControl.Title" xml:space="preserve">
|
||||
<value>Quality</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelScheduleSettingControl.Description" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_ScheduleSettingControl.Description" xml:space="preserve">
|
||||
<value>Number of minutes to start the task (after clicking downloading button).</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelScheduleSettingControl.Title" xml:space="preserve">
|
||||
<data name="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_ScheduleSettingControl.Title" xml:space="preserve">
|
||||
<value>Schedule</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelTaskOptionsHeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Task options</value>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Loading" xml:space="preserve">
|
||||
<value>Loading new videos</value>
|
||||
</data>
|
||||
<data name="HomePlaylistAddingVideoPanelTrimSettingControl.Title" xml:space="preserve">
|
||||
<value>Trim</value>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_InfoBox.Subtitle" xml:space="preserve">
|
||||
<value>- Twitch (VODs, Clips)</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextCancelled" xml:space="preserve">
|
||||
<value>Cancelled</value>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_InfoBox.Title" xml:space="preserve">
|
||||
<value>Supported sources</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextDone" xml:space="preserve">
|
||||
<value>Done</value>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_SearchButton.Content" xml:space="preserve">
|
||||
<value>Search</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextDownloading" xml:space="preserve">
|
||||
<value>Downloading</value>
|
||||
<data name="Home_OptionsBar_VideoSearchControl_UrlTextBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Video URL</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextError" xml:space="preserve">
|
||||
<value>An error occured</value>
|
||||
<data name="Main_NavigationPanel_About.Content" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextFinalizing" xml:space="preserve">
|
||||
<value>Finalizing</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextIdle" xml:space="preserve">
|
||||
<value>Idle</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextProcessing" xml:space="preserve">
|
||||
<value>Processing</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextScheduled" xml:space="preserve">
|
||||
<value>Scheduled</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelStateTextWaiting" xml:space="preserve">
|
||||
<value>Queued</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelTaskStartMeteredConnectionDialogCancel" xml:space="preserve">
|
||||
<value>No</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelTaskStartMeteredConnectionDialogDescription" xml:space="preserve">
|
||||
<value>You are on the metered connection now. You can delay task until the network changes. Do you want to start the task?</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelTaskStartMeteredConnectionDialogStartAndDelayText" xml:space="preserve">
|
||||
<value>Yes (With delay)</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelTaskStartMeteredConnectionDialogStartWithoutDelayText" xml:space="preserve">
|
||||
<value>Yes (Without delay)</value>
|
||||
</data>
|
||||
<data name="HomeTaskPanelTaskStartMeteredConnectionDialogTitle" xml:space="preserve">
|
||||
<value>Metered connection detected</value>
|
||||
</data>
|
||||
<data name="HomeTasksListPlaceholderTextBlock.Text" xml:space="preserve">
|
||||
<value>Click "Video/Playlist search" button to start</value>
|
||||
</data>
|
||||
<data name="MainPageNavigationPanelHomeItem.Content" xml:space="preserve">
|
||||
<data name="Main_NavigationPanel_Home.Content" xml:space="preserve">
|
||||
<value>Home</value>
|
||||
</data>
|
||||
<data name="MainPageNavigationPanelSourcesItem.Content" xml:space="preserve">
|
||||
<data name="Main_NavigationPanel_Sources.Content" xml:space="preserve">
|
||||
<value>Sources</value>
|
||||
</data>
|
||||
<data name="MainPageNavigationPanelSubscriptionsItem.Content" xml:space="preserve">
|
||||
<data name="Main_NavigationPanel_Subscriptions.Content" xml:space="preserve">
|
||||
<value>Subscriptions</value>
|
||||
</data>
|
||||
<data name="MediaTypeAudioVideoText" xml:space="preserve">
|
||||
<value>Audio & Video</value>
|
||||
</data>
|
||||
<data name="MediaTypeOnlyAudioText" xml:space="preserve">
|
||||
<value>Only audio</value>
|
||||
</data>
|
||||
<data name="MediaTypeOnlyVideoText" xml:space="preserve">
|
||||
<value>Only video</value>
|
||||
</data>
|
||||
<data name="NotificationTaskEndedSuccessfullyDescription" xml:space="preserve">
|
||||
<value>downloading ended successfully</value>
|
||||
</data>
|
||||
<data name="NotificationTaskEndedSuccessfullyHeader" xml:space="preserve">
|
||||
<value>Video downloading ended successfully</value>
|
||||
</data>
|
||||
<data name="NotificationTaskEndedUnsuccessfullyDescription" xml:space="preserve">
|
||||
<value>an error occured</value>
|
||||
</data>
|
||||
<data name="NotificationTaskEndedUnsuccessfullyHeader" xml:space="preserve">
|
||||
<value>Video downloading ended unsuccessfully</value>
|
||||
</data>
|
||||
<data name="SourcesTwitchLoginErrorDialogDescriptionInternetConnectionError" xml:space="preserve">
|
||||
<value>Unable to connect to Twitch servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="SourcesTwitchLoginErrorDialogDescriptionUnknown" xml:space="preserve">
|
||||
<value>Unknown error</value>
|
||||
</data>
|
||||
<data name="SourcesTwitchLoginErrorDialogTitle" xml:space="preserve">
|
||||
<value>Login to Twitch failed</value>
|
||||
</data>
|
||||
<data name="Sources_HeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Sources</value>
|
||||
</data>
|
||||
@@ -424,12 +331,12 @@ The number in the numberbox indicades how many videos will be got from playlist.
|
||||
<data name="Sources_TwitchSettingControl.Title" xml:space="preserve">
|
||||
<value>Twitch</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchSettingControl_Description_AccessTokenExpired" xml:space="preserve">
|
||||
<value>Your Twitch access token has expired. Please sign in.</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchSettingControl_Description_InternetNotAvailable" xml:space="preserve">
|
||||
<value>Unable to connect to Twitch servers. Check your internet connection.</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchSettingControl_Description_InvalidAccessToken" xml:space="preserve">
|
||||
<value>Your Twitch access token is invalid (maybe expired?). Please sign in to get new access token.</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchSettingControl_Description_LoggedIn" xml:space="preserve">
|
||||
<value>Logged in as</value>
|
||||
</data>
|
||||
@@ -440,7 +347,7 @@ The number in the numberbox indicades how many videos will be got from playlist.
|
||||
<value>Loading...</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchSettingControl_LoginButton_Content_LoggedIn" xml:space="preserve">
|
||||
<value>Log out</value>
|
||||
<value>Sign out</value>
|
||||
</data>
|
||||
<data name="Sources_TwitchSettingControl_LoginButton_Content_NotLoggedIn" xml:space="preserve">
|
||||
<value>Sign in</value>
|
||||
@@ -460,7 +367,16 @@ The number in the numberbox indicades how many videos will be got from playlist.
|
||||
<data name="Subscriptions_SubscriptionPanel_CountTextBlock_SyncText" xml:space="preserve">
|
||||
<value>Syncing...</value>
|
||||
</data>
|
||||
<data name="TaskErrorInternetConnection" xml:space="preserve">
|
||||
<value>Internet connection error</value>
|
||||
<data name="Home_Adding_Subscriptions_HeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Subscriptions</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Found" xml:space="preserve">
|
||||
<value>Found videos</value>
|
||||
</data>
|
||||
<data name="Home_OptionsBar_SubscriptionsLoadControl_LoadVideosButton.Content" xml:space="preserve">
|
||||
<value>Load</value>
|
||||
</data>
|
||||
<data name="Settings_HeaderTextBlock.Text" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -119,41 +119,50 @@
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\PlaceholderableStackPanel.xaml.cs">
|
||||
<DependentUpon>PlaceholderableStackPanel.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Converters\StringToVisibilityConverter.cs" />
|
||||
<Compile Include="Converters\TimeSpanToTextBoxMaskConverter.cs" />
|
||||
<Compile Include="Converters\TimeSpanToTextBoxMaskElementsConverter.cs" />
|
||||
<Compile Include="Views\About\AboutMain.xaml.cs">
|
||||
<DependentUpon>AboutMain.xaml</DependentUpon>
|
||||
<Compile Include="Views\About\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\HomeAddingVideoOptions.xaml.cs">
|
||||
<DependentUpon>HomeAddingVideoOptions.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\Controls\SerialVideoAddingControl.xaml.cs">
|
||||
<DependentUpon>SerialVideoAddingControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\HomeOptionsBarPlaylistSearch.xaml.cs">
|
||||
<DependentUpon>HomeOptionsBarPlaylistSearch.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\Controls\VideoAddingOptionsControl.xaml.cs">
|
||||
<DependentUpon>VideoAddingOptionsControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\HomeOptionsBarVideoSearch.xaml.cs">
|
||||
<DependentUpon>HomeOptionsBarVideoSearch.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\Controls\SubscriptionsLoadControl.xaml.cs">
|
||||
<DependentUpon>SubscriptionsLoadControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\PlaylistSearchControl.xaml.cs">
|
||||
<DependentUpon>PlaylistSearchControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\VideoSearchControl.xaml.cs">
|
||||
<DependentUpon>VideoSearchControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\SettingControl.xaml.cs">
|
||||
<DependentUpon>SettingControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\HomeMain.xaml.cs">
|
||||
<DependentUpon>HomeMain.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\HomePlaylistAddingPanel.xaml.cs">
|
||||
<DependentUpon>HomePlaylistAddingPanel.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\SubscriptionsAddingPanel.xaml.cs">
|
||||
<DependentUpon>SubscriptionsAddingPanel.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\HomeSerialAddingVideoPanel.xaml.cs">
|
||||
<DependentUpon>HomeSerialAddingVideoPanel.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\PlaylistAddingPanel.xaml.cs">
|
||||
<DependentUpon>PlaylistAddingPanel.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\HomeTasksListPlaceholder.xaml.cs">
|
||||
<DependentUpon>HomeTasksListPlaceholder.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\Controls\SerialVideoAddingVideoControl.xaml.cs">
|
||||
<DependentUpon>SerialVideoAddingVideoControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\HomeVideoAddingPanel.xaml.cs">
|
||||
<DependentUpon>HomeVideoAddingPanel.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\VideoAddingPanel.xaml.cs">
|
||||
<DependentUpon>VideoAddingPanel.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Home\Controls\HomeTaskPanel.xaml.cs">
|
||||
<DependentUpon>HomeTaskPanel.xaml</DependentUpon>
|
||||
<Compile Include="Views\Home\Controls\DownloadTaskControl.xaml.cs">
|
||||
<DependentUpon>DownloadTaskControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
@@ -161,8 +170,8 @@
|
||||
<Compile Include="Views\Sources\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Settings\SettingsMain.xaml.cs">
|
||||
<DependentUpon>SettingsMain.xaml</DependentUpon>
|
||||
<Compile Include="Views\Settings\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Subscriptions\Controls\SubscriptionPanel.xaml.cs">
|
||||
<DependentUpon>SubscriptionPanel.xaml</DependentUpon>
|
||||
@@ -214,8 +223,8 @@
|
||||
<Content Include="Assets\Icons\StateProcessingLight.svg" />
|
||||
<Content Include="Assets\Icons\StateScheduledDark.svg" />
|
||||
<Content Include="Assets\Icons\StateScheduledLight.svg" />
|
||||
<Content Include="Assets\Icons\StateWaitingDark.svg" />
|
||||
<Content Include="Assets\Icons\StateWaitingLight.svg" />
|
||||
<Content Include="Assets\Icons\StateQueuedDark.svg" />
|
||||
<Content Include="Assets\Icons\StateQueuedLight.svg" />
|
||||
<Content Include="Assets\Icons\TrimDark.svg" />
|
||||
<Content Include="Assets\Icons\TrimLight.svg" />
|
||||
<Content Include="Assets\Icons\Twitch.svg" />
|
||||
@@ -281,6 +290,10 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="Controls\PlaceholderableStackPanel.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Resources\Colors.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -297,19 +310,27 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\About\AboutMain.xaml">
|
||||
<Page Include="Views\About\MainPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\HomeAddingVideoOptions.xaml">
|
||||
<Page Include="Views\Home\Controls\SerialVideoAddingControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\HomeOptionsBarPlaylistSearch.xaml">
|
||||
<Page Include="Views\Home\Controls\VideoAddingOptionsControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\SubscriptionsLoadControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\HomeOptionsBarVideoSearch.xaml">
|
||||
<Page Include="Views\Home\Controls\PlaylistSearchControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\VideoSearchControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
@@ -317,27 +338,27 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\HomeMain.xaml">
|
||||
<Page Include="Views\Home\MainPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\HomePlaylistAddingPanel.xaml">
|
||||
<Page Include="Views\Home\SubscriptionsAddingPanel.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Home\PlaylistAddingPanel.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\HomeSerialAddingVideoPanel.xaml">
|
||||
<Page Include="Views\Home\Controls\SerialVideoAddingVideoControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\HomeTasksListPlaceholder.xaml">
|
||||
<Page Include="Views\Home\VideoAddingPanel.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\HomeVideoAddingPanel.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Home\Controls\HomeTaskPanel.xaml">
|
||||
<Page Include="Views\Home\Controls\DownloadTaskControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
@@ -349,7 +370,7 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Settings\SettingsMain.xaml">
|
||||
<Page Include="Views\Settings\MainPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<Page
|
||||
x:Class="VDownload.Views.About.AboutMain"
|
||||
x:Class="VDownload.Views.About.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.About"
|
||||
@@ -20,9 +20,9 @@ namespace VDownload.Views.About
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class AboutMain : Page
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
public AboutMain()
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.HomeTaskPanel"
|
||||
x:Class="VDownload.Views.Home.Controls.DownloadTaskControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
@@ -39,8 +39,8 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" FontSize="18" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind Data.VideoService.Metadata.Title}"/>
|
||||
<TextBlock Grid.Column="1" FontSize="12" VerticalAlignment="Bottom" FontWeight="Light" Text="{x:Bind Data.VideoService.Metadata.Author}" Margin="0,0,0,2"/>
|
||||
<TextBlock Grid.Column="0" FontSize="18" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind DownloadTask.Video.Title}"/>
|
||||
<TextBlock Grid.Column="1" FontSize="12" VerticalAlignment="Bottom" FontWeight="Light" Text="{x:Bind DownloadTask.Video.Author}" Margin="0,0,0,2"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" Grid.RowSpan="2" Grid.Column="1" ColumnSpacing="5">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -60,12 +60,12 @@
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Duration}"/>
|
||||
<Image Grid.Row="2" Grid.Column="0" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource FileIcon}"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind File}"/>
|
||||
<Image x:Name="HomeTaskPanelStateIcon" Grid.Row="3" Grid.Column="0" Width="{StaticResource MetadataIconSize}"/>
|
||||
<TextBlock x:Name="HomeTaskPanelStateText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}"/>
|
||||
<muxc:ProgressBar x:Name="HomeTaskPanelStateProgressBar" Grid.Row="3" Grid.Column="2" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
||||
<Image x:Name="StateIcon" Grid.Row="3" Grid.Column="0" Width="{StaticResource MetadataIconSize}"/>
|
||||
<TextBlock x:Name="StateText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" FontSize="{StaticResource MetadataTextSize}"/>
|
||||
<muxc:ProgressBar x:Name="StateProgressBar" Grid.Row="3" Grid.Column="2" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
<AppBarButton Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="{x:Bind SourceImage}" Click="HomeTaskPanelSourceButton_Click"/>
|
||||
<AppBarButton x:Name="HomeTaskPanelStartStopButton" Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Download" Click="HomeTaskPanelStartStopButton_Click"/>
|
||||
<AppBarButton Grid.Row="2" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Cancel" Click="HomeTaskPanelRemoveButton_Click"/>
|
||||
<AppBarButton Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="{x:Bind SourceImage}" Click="SourceButton_Click"/>
|
||||
<AppBarButton x:Name="StartStopButton" Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Download" Click="StartStopButton_Click"/>
|
||||
<AppBarButton Grid.Row="2" Grid.Column="2" VerticalAlignment="Center" Width="40" Height="48" Margin="-3" Icon="Cancel" Click="RemoveButton_Click"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
248
VDownload/Views/Home/Controls/DownloadTaskControl.xaml.cs
Normal file
248
VDownload/Views/Home/Controls/DownloadTaskControl.xaml.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Extensions;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using Windows.ApplicationModel.ExtendedExecution;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class DownloadTaskControl : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private static readonly ResourceDictionary IconsRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") };
|
||||
private static readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public DownloadTaskControl(DownloadTask downloadTask)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
DownloadTask = downloadTask;
|
||||
DownloadTask.StatusChanged += UpdateStatus;
|
||||
|
||||
ThumbnailImage = DownloadTask.Video.Thumbnail != null ? new BitmapImage { UriSource = DownloadTask.Video.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{DownloadTask.Video.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
|
||||
TimeSpan newDuration = DownloadTask.Trim.End.Subtract(DownloadTask.Trim.Start);
|
||||
StringBuilder durationBuilder = new StringBuilder(newDuration.ToStringOptTHBaseMMSS());
|
||||
if (DownloadTask.Video.Duration > newDuration)
|
||||
{
|
||||
durationBuilder.Append($" ({DownloadTask.Trim.Start.ToStringOptTHBaseMMSS(DownloadTask.Trim.End)} - {DownloadTask.Trim.End.ToStringOptTHBaseMMSS(DownloadTask.Trim.Start)})");
|
||||
}
|
||||
Duration = durationBuilder.ToString();
|
||||
|
||||
StringBuilder mediaTypeQualityBuilder = new StringBuilder(ResourceLoader.GetForCurrentView().GetString($"Base_MediaType_{DownloadTask.MediaType}Text"));
|
||||
if (DownloadTask.MediaType != MediaType.OnlyAudio)
|
||||
{
|
||||
mediaTypeQualityBuilder.Append($" ({DownloadTask.SelectedStream})");
|
||||
}
|
||||
MediaTypeQuality = mediaTypeQualityBuilder.ToString();
|
||||
|
||||
File = DownloadTask.File.GetPath();
|
||||
|
||||
UpdateStatus(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private DownloadTask DownloadTask { get; set; }
|
||||
|
||||
private ImageSource ThumbnailImage { get; set; }
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Title { get; set; }
|
||||
private string Author { get; set; }
|
||||
private string Duration { get; set; }
|
||||
private string MediaTypeQuality { get; set; }
|
||||
private string File { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private async void StartStopButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DownloadTask.Status != DownloadTaskStatus.Idle)
|
||||
{
|
||||
DownloadTask.CancellationTokenSource.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool delay = (bool)Config.GetValue("delay_task_when_queued_task_starts_on_metered_network");
|
||||
if (NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)
|
||||
{
|
||||
ContentDialogResult dialogResult = await new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_Title"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_Content"),
|
||||
PrimaryButtonText = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_StartWithDelayButtonText"),
|
||||
SecondaryButtonText = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Start_MeteredConnection_StartWithoutDelayButtonText1"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("Base_CancelButtonText"),
|
||||
}.ShowAsync();
|
||||
switch (dialogResult)
|
||||
{
|
||||
case ContentDialogResult.Primary: delay = true; break;
|
||||
case ContentDialogResult.Secondary: delay = false; break;
|
||||
case ContentDialogResult.None: return;
|
||||
}
|
||||
}
|
||||
await DownloadTask.Run(delay);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DownloadTask.Status != DownloadTaskStatus.Idle)
|
||||
{
|
||||
DownloadTask.CancellationTokenSource.Cancel();
|
||||
}
|
||||
RemovingRequested.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async void SourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Windows.System.Launcher.LaunchUriAsync(DownloadTask.Video.Url);
|
||||
}
|
||||
|
||||
private void UpdateStatus(object sender, EventArgs e)
|
||||
{
|
||||
if (DownloadTask.Status == DownloadTaskStatus.Idle || DownloadTask.Status == DownloadTaskStatus.EndedSuccessfully || DownloadTask.Status == DownloadTaskStatus.EndedUnsuccessfully)
|
||||
{
|
||||
StartStopButton.Icon = new SymbolIcon(Symbol.Download);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartStopButton.Icon = new SymbolIcon(Symbol.Stop);
|
||||
}
|
||||
|
||||
if (DownloadTask.Status == DownloadTaskStatus.Scheduled)
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateScheduledIcon"];
|
||||
StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Scheduled")} ({DownloadTask.ScheduledFor.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern)} {DownloadTask.ScheduledFor.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortTimePattern)})";
|
||||
StateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else if (DownloadTask.Status == DownloadTaskStatus.Queued)
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateQueuedIcon"];
|
||||
StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Queued");
|
||||
StateProgressBar.Visibility = Visibility.Visible;
|
||||
StateProgressBar.IsIndeterminate = true;
|
||||
}
|
||||
else if (DownloadTask.Status == DownloadTaskStatus.Downloading)
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateDownloadingIcon"];
|
||||
StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Downloading")} ({Math.Round(DownloadTask.DownloadingProgress)}%)";
|
||||
StateProgressBar.Visibility = Visibility.Visible;
|
||||
StateProgressBar.IsIndeterminate = false;
|
||||
StateProgressBar.Value = DownloadTask.DownloadingProgress;
|
||||
}
|
||||
else if (DownloadTask.Status == DownloadTaskStatus.Processing)
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateProcessingIcon"];
|
||||
StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Processing")} ({Math.Round(DownloadTask.ProcessingProgress)}%)";
|
||||
StateProgressBar.Visibility = Visibility.Visible;
|
||||
StateProgressBar.IsIndeterminate = false;
|
||||
StateProgressBar.Value = DownloadTask.ProcessingProgress;
|
||||
}
|
||||
else if (DownloadTask.Status == DownloadTaskStatus.Finalizing)
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateFinalizingIcon"];
|
||||
StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Finalizing");
|
||||
StateProgressBar.Visibility = Visibility.Visible;
|
||||
StateProgressBar.IsIndeterminate = true;
|
||||
}
|
||||
else if (DownloadTask.Status == DownloadTaskStatus.EndedSuccessfully)
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateDoneIcon"];
|
||||
StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Done")} ({DownloadTask.ElapsedTime.ToStringOptTHBaseMMSS()})";
|
||||
StateProgressBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
if ((bool)Config.GetValue("show_notification_when_task_ended_successfully"))
|
||||
{
|
||||
new ToastContentBuilder()
|
||||
.AddText(ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedSuccessfully_Header"))
|
||||
.AddText($"\"{Title}\" - {ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedSuccessfully_Description")}")
|
||||
.Show();
|
||||
}
|
||||
}
|
||||
else if (DownloadTask.Status == DownloadTaskStatus.EndedUnsuccessfully)
|
||||
{
|
||||
if (DownloadTask.Exception is OperationCanceledException)
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"];
|
||||
StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Cancelled");
|
||||
StateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorInfo;
|
||||
if (DownloadTask.Exception is WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable) errorInfo = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Error_InternetNotAvailable");
|
||||
else throw DownloadTask.Exception;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw DownloadTask.Exception;
|
||||
}
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateErrorIcon"];
|
||||
StateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Error")} ({errorInfo})";
|
||||
StateProgressBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
if ((bool)Config.GetValue("show_notification_when_task_ended_unsuccessfully"))
|
||||
{
|
||||
new ToastContentBuilder()
|
||||
.AddText(ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Header"))
|
||||
.AddText($"\"{Title}\" - {ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_Notification_EndedUnsuccessfully_Description")} ({errorInfo})")
|
||||
.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StateIcon.Source = (SvgImageSource)IconsRes["StateIdleIcon"];
|
||||
StateText.Text = ResourceLoader.GetForCurrentView().GetString("Home_DownloadTaskControl_State_Idle");
|
||||
StateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler RemovingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.HomeAddingVideoOptions"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:ex="using:Microsoft.Toolkit.Uwp.UI"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<StackPanel Spacing="30">
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="HomeAddingDownloadingOptionsHeaderTextBlock" Margin="0,0,0,10" FontWeight="SemiBold"/>
|
||||
<cc:SettingControl x:Uid="HomeAddingMediaTypeSettingControl" Margin="0,0,0,10" Icon="{ThemeResource MediaTypeIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<ComboBox x:Name="HomeAddingMediaTypeSettingControlComboBox" Width="150" SelectionChanged="HomeAddingMediaTypeSettingControlComboBox_SelectionChanged"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
<cc:SettingControl x:Name="HomeAddingQualitySettingControl" Margin="0,0,0,10" x:Uid="HomeAddingQualitySettingControl" Icon="{ThemeResource QualityIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<ComboBox x:Name="HomeAddingQualitySettingControlComboBox" Width="150" SelectionChanged="HomeAddingQualitySettingControlComboBox_SelectionChanged"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
<cc:SettingControl x:Uid="HomeAddingTrimSettingControl" Icon="{ThemeResource TrimIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBox x:Name="HomeAddingTrimStartTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind VideoService.Metadata.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind VideoService.Metadata.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" LostFocus="HomeAddingTrimStartTextBox_LostFocus"/>
|
||||
<TextBlock VerticalAlignment="Center" Text="-"/>
|
||||
<TextBox x:Name="HomeAddingTrimEndTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind VideoService.Metadata.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind VideoService.Metadata.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" LostFocus="HomeAddingTrimEndTextBox_LostFocus"/>
|
||||
</StackPanel>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock x:Uid="HomeAddingFileOptionsHeaderTextBlock" FontWeight="SemiBold"/>
|
||||
<cc:SettingControl x:Uid="HomeAddingFileSettingControl" Icon="{ThemeResource FileIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<Grid ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="HomeAddingFilenameTextBox" Grid.Column="0" MaxWidth="300" HorizontalAlignment="Right" IsSpellCheckEnabled="False" LostFocus="HomeAddingFilenameTextBox_LostFocus"/>
|
||||
<ComboBox x:Name="HomeAddingExtensionComboBox" Grid.Column="1" HorizontalAlignment="Stretch" SelectionChanged="HomeAddingExtensionComboBox_SelectionChanged"/>
|
||||
</Grid>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
<cc:SettingControl x:Name="HomeAddingLocationSettingControl" x:Uid="HomeAddingLocationSettingControl" Icon="{ThemeResource LocationIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<Button x:Uid="HomeAddingLocationBrowseButton" Click="HomeAddingLocationBrowseButton_Click"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock x:Uid="HomeAddingTaskOptionsHeaderTextBlock" FontWeight="SemiBold"/>
|
||||
<cc:SettingControl x:Uid="HomeAddingScheduleSettingControl" Icon="{ThemeResource ScheduleIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<muxc:NumberBox x:Name="HomeAddingScheduleNumberBox" MaxWidth="100" Value="0" Minimum="0" SpinButtonPlacementMode="Compact" LostFocus="HomeAddingScheduleNumberBox_LostFocus"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,279 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class HomeAddingVideoOptions : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeAddingVideoOptions(IVideo videoService)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set video service
|
||||
VideoService = videoService;
|
||||
}
|
||||
|
||||
// INIT CONTROL
|
||||
public async Task Init()
|
||||
{
|
||||
// Set media type
|
||||
foreach (string mediaType in Enum.GetNames(typeof(MediaType)))
|
||||
{
|
||||
HomeAddingMediaTypeSettingControlComboBox.Items.Add(ResourceLoader.GetForCurrentView().GetString($"MediaType{mediaType}Text"));
|
||||
}
|
||||
HomeAddingMediaTypeSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_media_type");
|
||||
|
||||
// Set quality
|
||||
foreach (BaseStream stream in VideoService.BaseStreams)
|
||||
{
|
||||
HomeAddingQualitySettingControlComboBox.Items.Add($"{stream.Height}p{(stream.FrameRate > 0 ? stream.FrameRate.ToString() : "N/A")}");
|
||||
}
|
||||
HomeAddingQualitySettingControlComboBox.SelectedIndex = 0;
|
||||
|
||||
// Set trim start
|
||||
TrimStart = TimeSpan.Zero;
|
||||
HomeAddingTrimStartTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(TrimStart, VideoService.Metadata.Duration);
|
||||
|
||||
// Set trim end
|
||||
TrimEnd = VideoService.Metadata.Duration;
|
||||
HomeAddingTrimEndTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(TrimEnd);
|
||||
|
||||
// Set filename
|
||||
string temporaryFilename = (string)Config.GetValue("default_filename");
|
||||
Dictionary<string, string> filenameStandardTemplates = new Dictionary<string, string>()
|
||||
{
|
||||
{ "<title>", VideoService.Metadata.Title },
|
||||
{ "<author>", VideoService.Metadata.Author },
|
||||
{ "<views>", VideoService.Metadata.Views.ToString() },
|
||||
{ "<id>", VideoService.ID },
|
||||
};
|
||||
foreach (KeyValuePair<string, string> template in filenameStandardTemplates) temporaryFilename = temporaryFilename.Replace(template.Key, template.Value);
|
||||
Dictionary<Regex, IFormattable> filenameFormatTemplates = new Dictionary<Regex, IFormattable>()
|
||||
{
|
||||
{ new Regex(@"<date_pub:(?<format>.*)>"), VideoService.Metadata.Date },
|
||||
{ new Regex(@"<date_now:(?<format>.*)>"), DateTime.Now },
|
||||
{ new Regex(@"<duration:(?<format>.*)>"), VideoService.Metadata.Duration },
|
||||
};
|
||||
foreach (KeyValuePair<Regex, IFormattable> template in filenameFormatTemplates) foreach (Match templateMatch in template.Key.Matches(temporaryFilename)) temporaryFilename = temporaryFilename.Replace(templateMatch.Value, template.Value.ToString(templateMatch.Groups["format"].Value, null));
|
||||
foreach (char c in Path.GetInvalidFileNameChars()) temporaryFilename = temporaryFilename.Replace(c, ' ');
|
||||
Filename = temporaryFilename;
|
||||
HomeAddingFilenameTextBox.Text = Filename;
|
||||
|
||||
// Set location
|
||||
if (!(bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("last_media_location"))
|
||||
{
|
||||
Location = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("last_media_location");
|
||||
HomeAddingLocationSettingControl.Description = Location.Path;
|
||||
}
|
||||
else if ((bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_media_location"))
|
||||
{
|
||||
Location = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("selected_media_location");
|
||||
HomeAddingLocationSettingControl.Description = Location.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
Location = null;
|
||||
HomeAddingLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload";
|
||||
}
|
||||
|
||||
// Set schedule
|
||||
Schedule = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// VIDEO SERVICE
|
||||
private IVideo VideoService { get; set; }
|
||||
|
||||
// TASK OPTIONS
|
||||
public MediaType MediaType { get; private set; }
|
||||
public BaseStream Stream { get; private set; }
|
||||
public TimeSpan TrimStart { get; private set; }
|
||||
public TimeSpan TrimEnd { get; private set; }
|
||||
public string Filename { get; private set; }
|
||||
public MediaFileExtension Extension { get; private set; }
|
||||
public StorageFolder Location { get; private set; }
|
||||
public double Schedule { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CHANGE PROPERTIES FROM PARENT VOIDS
|
||||
|
||||
// LOCATION
|
||||
public void ChangeLocation(StorageFolder location)
|
||||
{
|
||||
Location = location;
|
||||
HomeAddingLocationSettingControl.Description = Location.Path ?? $@"{UserDataPaths.GetDefault().Downloads}\VDownload";
|
||||
}
|
||||
|
||||
// SCHEDULE
|
||||
public void ChangeSchedule(double schedule)
|
||||
{
|
||||
Schedule = schedule;
|
||||
HomeAddingScheduleNumberBox.Value = Schedule;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// MEDIA TYPE COMBOBOX SELECTION CHANGED
|
||||
private void HomeAddingMediaTypeSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
MediaType = (MediaType)HomeAddingMediaTypeSettingControlComboBox.SelectedIndex;
|
||||
if (HomeAddingMediaTypeSettingControlComboBox.SelectedIndex == (int)MediaType.OnlyAudio)
|
||||
{
|
||||
HomeAddingQualitySettingControl.Visibility = Visibility.Collapsed;
|
||||
HomeAddingQualitySettingControlComboBox.SelectedIndex = VideoService.BaseStreams.Count() - 1;
|
||||
|
||||
HomeAddingExtensionComboBox.Items.Clear();
|
||||
foreach (AudioFileExtension extension in Enum.GetValues(typeof(AudioFileExtension)))
|
||||
{
|
||||
HomeAddingExtensionComboBox.Items.Add(extension);
|
||||
}
|
||||
HomeAddingExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_audio_extension") - 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
HomeAddingQualitySettingControl.Visibility = Visibility.Visible;
|
||||
HomeAddingQualitySettingControlComboBox.SelectedIndex = 0;
|
||||
|
||||
HomeAddingExtensionComboBox.Items.Clear();
|
||||
foreach (VideoFileExtension extension in Enum.GetValues(typeof(VideoFileExtension)))
|
||||
{
|
||||
HomeAddingExtensionComboBox.Items.Add(extension);
|
||||
}
|
||||
HomeAddingExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_video_extension");
|
||||
}
|
||||
}
|
||||
|
||||
// QUALITY COMBOBOX SELECTION CHANGED
|
||||
private void HomeAddingQualitySettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
Stream = VideoService.BaseStreams[HomeAddingQualitySettingControlComboBox.SelectedIndex];
|
||||
}
|
||||
|
||||
// TRIM START TEXTBOX LOST FOCUS
|
||||
private void HomeAddingTrimStartTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!HomeAddingTrimStartTextBox.Text.Contains('_'))
|
||||
{
|
||||
string[] segments = HomeAddingTrimStartTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < VideoService.Metadata.Duration && parsedTimeSpan > TimeSpan.Zero) TrimStart = parsedTimeSpan;
|
||||
else
|
||||
{
|
||||
TrimStart = TimeSpan.Zero;
|
||||
HomeAddingTrimStartTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(TrimStart, VideoService.Metadata.Duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TRIM END TEXTBOX LOST FOCUS
|
||||
private void HomeAddingTrimEndTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!HomeAddingTrimEndTextBox.Text.Contains('_'))
|
||||
{
|
||||
string[] segments = HomeAddingTrimEndTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < VideoService.Metadata.Duration && parsedTimeSpan > TimeSpan.Zero) TrimEnd = parsedTimeSpan;
|
||||
else
|
||||
{
|
||||
TrimEnd = VideoService.Metadata.Duration;
|
||||
HomeAddingTrimEndTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(TrimEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FILENAME TEXTBOX LOST FOCUS
|
||||
private void HomeAddingFilenameTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (char c in Path.GetInvalidFileNameChars()) HomeAddingFilenameTextBox.Text = HomeAddingFilenameTextBox.Text.Replace(c, ' ');
|
||||
Filename = HomeAddingFilenameTextBox.Text;
|
||||
}
|
||||
|
||||
// EXTENSION COMBOBOX SELECTION CHANGED
|
||||
private void HomeAddingExtensionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
Extension = (MediaFileExtension)HomeAddingExtensionComboBox.SelectedIndex + (MediaType == MediaType.OnlyAudio ? 3 : 0);
|
||||
}
|
||||
|
||||
// SCHEDULE NUMBERBOX LOST FOCUS
|
||||
private void HomeAddingScheduleNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (HomeAddingScheduleNumberBox.Value == double.NaN) HomeAddingScheduleNumberBox.Value = 0;
|
||||
Schedule = HomeAddingScheduleNumberBox.Value;
|
||||
}
|
||||
|
||||
// LOCATION BROWSE BUTTON CLICKED
|
||||
private async void HomeAddingLocationBrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create location picker
|
||||
FolderPicker picker = new FolderPicker
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.Downloads
|
||||
};
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
// Select location
|
||||
StorageFolder selectedFolder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (selectedFolder != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await(await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync();
|
||||
StorageApplicationPermissions.FutureAccessList.AddOrReplace("last_media_location", selectedFolder);
|
||||
Location = selectedFolder;
|
||||
HomeAddingLocationSettingControl.Description = Location.Path;
|
||||
}
|
||||
catch (UnauthorizedAccessException) { }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.HomeOptionsBarPlaylistSearch"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="100"
|
||||
d:DesignWidth="400">
|
||||
|
||||
|
||||
<Grid x:Name="HomeOptionsBarPlaylistSearchControlGrid" Margin="10" ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- URL TEXTBOX -->
|
||||
<TextBox x:Name="HomeOptionsBarPlaylistSearchControlUrlTextBox" x:Uid="HomeOptionsBarPlaylistSearchControlUrlTextBox" Grid.Column="0" VerticalAlignment="Center"/>
|
||||
|
||||
<!-- MAX VIDEOS NUMBERBOX -->
|
||||
<muxc:NumberBox x:Name="HomeOptionsBarPlaylistSearchControlMaxVideosNumberBox" Grid.Column="1" VerticalAlignment="Center" SpinButtonPlacementMode="Compact" Minimum="0" LostFocus="HomeOptionsBarPlaylistSearchControlMaxVideosNumberBox_LostFocus"/>
|
||||
|
||||
<!-- SEARCH BUTTON-->
|
||||
<Button x:Name="HomeOptionsBarPlaylistSearchControlSearchButton" x:Uid="HomeOptionsBarPlaylistSearchControlSearchButton" Grid.Column="2" Click="HomeOptionsBarPlaylistSearchControlSearchButton_Click"/>
|
||||
|
||||
<!-- HELP BUTTON -->
|
||||
<Button x:Name="HomeOptionsBarPlaylistSearchControlHelpButton" Grid.Column="3" Content="?" Click="HomeOptionsBarPlaylistSearchControlHelpButton_Click">
|
||||
<Button.Resources>
|
||||
<muxc:TeachingTip x:Name="HomeOptionsBarPlaylistSearchControlInfoBox" x:Uid="HomeOptionsBarPlaylistSearchControlInfoBox" Target="{x:Bind HomeOptionsBarPlaylistSearchControlGrid}"/>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
|
||||
<!-- STATUS CONTROL -->
|
||||
<ContentPresenter x:Name="HomeOptionsBarPlaylistSearchControlStatusControl" Margin="-10,0,0,0" Grid.Column="4"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,167 +0,0 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Services.Sources;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class HomeOptionsBarPlaylistSearch : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
// RESOURCES
|
||||
private static readonly ResourceDictionary IconRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") };
|
||||
|
||||
// SEARCHING STATUS CONTROLS
|
||||
private static readonly Microsoft.UI.Xaml.Controls.ProgressRing HomeOptionsBarPlaylistSearchStatusProgressRing = new Microsoft.UI.Xaml.Controls.ProgressRing { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), IsActive = true };
|
||||
private static readonly Image HomeOptionsBarPlaylistSearchStatusErrorImage = new Image { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), Source = (SvgImageSource)IconRes["ErrorIcon"] };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeOptionsBarPlaylistSearch()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set default max videos
|
||||
HomeOptionsBarPlaylistSearchControlMaxVideosNumberBox.Value = (int)Config.GetValue("default_max_playlist_videos");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// NUMBERBOX FOCUS LOST
|
||||
private void HomeOptionsBarPlaylistSearchControlMaxVideosNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(HomeOptionsBarPlaylistSearchControlMaxVideosNumberBox.Value)) HomeOptionsBarPlaylistSearchControlMaxVideosNumberBox.Value = (int)Config.GetValue("default_max_playlist_videos");
|
||||
}
|
||||
|
||||
// SEARCH BUTTON CLICKED
|
||||
private async void HomeOptionsBarPlaylistSearchControlSearchButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Invoke search button click event
|
||||
SearchButtonClick?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
// Close info box
|
||||
HomeOptionsBarPlaylistSearchControlInfoBox.IsOpen = false;
|
||||
|
||||
// Set SearchingStatusControl
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = HomeOptionsBarPlaylistSearchStatusProgressRing;
|
||||
|
||||
// Parse url
|
||||
IPlaylist playlistService = Source.GetPlaylist(HomeOptionsBarPlaylistSearchControlUrlTextBox.Text);
|
||||
if (playlistService == null)
|
||||
{
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = HomeOptionsBarPlaylistSearchStatusErrorImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get metadata and streams
|
||||
try
|
||||
{
|
||||
await playlistService.GetMetadataAsync(CancellationToken);
|
||||
await playlistService.GetVideosAsync((int)Math.Round(HomeOptionsBarPlaylistSearchControlMaxVideosNumberBox.Value), CancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = null;
|
||||
return;
|
||||
}
|
||||
catch (MediaNotFoundException)
|
||||
{
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = HomeOptionsBarPlaylistSearchStatusErrorImage;
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotFoundException)
|
||||
{
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = HomeOptionsBarPlaylistSearchStatusErrorImage;
|
||||
ContentDialog twitchAccessTokenNotFoundErrorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarPlaylistSearchingErrorDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarPlaylistSearchingTwitchAccessTokenNotFoundErrorDialogDescription"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await twitchAccessTokenNotFoundErrorDialog.ShowAsync();
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotValidException)
|
||||
{
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = HomeOptionsBarPlaylistSearchStatusErrorImage;
|
||||
ContentDialog twitchAccessTokenNotValidErrorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarPlaylistSearchingErrorDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarPlaylistSearchingTwitchAccessTokenNotValidErrorDialogDescription"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await twitchAccessTokenNotValidErrorDialog.ShowAsync();
|
||||
return;
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = HomeOptionsBarPlaylistSearchStatusErrorImage;
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
ContentDialog internetAccessErrorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarPlaylistSearchingErrorDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarPlaylistSearchingInternetConnectionErrorDialogDescription"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await internetAccessErrorDialog.ShowAsync();
|
||||
return;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
|
||||
// Set searching status control to done (null)
|
||||
HomeOptionsBarPlaylistSearchControlStatusControl.Content = null;
|
||||
|
||||
// Invoke search successed event
|
||||
PlaylistSearchSuccessed?.Invoke(this, new PlaylistSearchSuccessedEventArgs { PlaylistService = playlistService });
|
||||
}
|
||||
}
|
||||
|
||||
// HELP BUTTON CLICKED
|
||||
private void HomeOptionsBarPlaylistSearchControlHelpButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Switch info box
|
||||
HomeOptionsBarPlaylistSearchControlInfoBox.IsOpen = !HomeOptionsBarPlaylistSearchControlInfoBox.IsOpen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<PlaylistSearchSuccessedEventArgs> PlaylistSearchSuccessed;
|
||||
public event EventHandler SearchButtonClick;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.HomeOptionsBarVideoSearch"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="100"
|
||||
d:DesignWidth="400">
|
||||
|
||||
|
||||
<Grid x:Name="HomeOptionsBarVideoSearchControlGrid" Margin="10" ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- URL TEXTBOX -->
|
||||
<TextBox x:Name="HomeOptionsBarVideoSearchControlUrlTextBox" x:Uid="HomeOptionsBarVideoSearchControlUrlTextBox" Grid.Column="0" VerticalAlignment="Center"/>
|
||||
|
||||
<!-- SEARCH BUTTON -->
|
||||
<Button x:Name="HomeOptionsBarVideoSearchControlSearchButton" x:Uid="HomeOptionsBarVideoSearchControlSearchButton" Grid.Column="1" Click="HomeOptionsBarVideoSearchControlSearchButton_Click"/>
|
||||
|
||||
<!-- INFO BUTTON -->
|
||||
<Button x:Name="HomeOptionsBarVideoSearchControlHelpButton" x:Uid="HomeOptionsBarVideoSearchControlHelpButton" Grid.Column="2" Content="?" Click="HomeOptionsBarVideoSearchControlHelpButton_Click">
|
||||
<Button.Resources>
|
||||
<muxc:TeachingTip x:Name="HomeOptionsBarVideoSearchControlInfoBox" x:Uid="HomeOptionsBarAddVideoControlInfoBox" Target="{x:Bind HomeOptionsBarVideoSearchControlGrid}"/>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
|
||||
<!-- STATUS CONTROL -->
|
||||
<ContentPresenter x:Name="HomeOptionBarVideoSearchControlStatusControl" Margin="-10,0,0,0" Grid.Column="3"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,159 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Services.Sources;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class HomeOptionsBarVideoSearch : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
// RESOURCES
|
||||
private static readonly ResourceDictionary IconRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") };
|
||||
|
||||
// SEARCHING STATUS CONTROLS
|
||||
private static readonly Microsoft.UI.Xaml.Controls.ProgressRing HomeOptionsBarVideoSearchStatusProgressRing = new Microsoft.UI.Xaml.Controls.ProgressRing { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), IsActive = true };
|
||||
private static readonly Image HomeOptionsBarVideoSearchStatusErrorImage = new Image { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), Source = (SvgImageSource)IconRes["ErrorIcon"] };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeOptionsBarVideoSearch()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// SEARCH BUTTON CLICKED
|
||||
private async void HomeOptionsBarVideoSearchControlSearchButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Invoke search button click event
|
||||
SearchButtonClick?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
// Close info box
|
||||
HomeOptionsBarVideoSearchControlInfoBox.IsOpen = false;
|
||||
|
||||
// Set SearchingStatusControl
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = HomeOptionsBarVideoSearchStatusProgressRing;
|
||||
|
||||
// Parse url
|
||||
IVideo videoService = Source.GetVideo(HomeOptionsBarVideoSearchControlUrlTextBox.Text);
|
||||
|
||||
// Check url
|
||||
if (videoService == null)
|
||||
{
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = HomeOptionsBarVideoSearchStatusErrorImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get metadata and streams
|
||||
try
|
||||
{
|
||||
await videoService.GetMetadataAsync(CancellationToken);
|
||||
await videoService.GetStreamsAsync(CancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = null;
|
||||
return;
|
||||
}
|
||||
catch (MediaNotFoundException)
|
||||
{
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = HomeOptionsBarVideoSearchStatusErrorImage;
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotFoundException)
|
||||
{
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = HomeOptionsBarVideoSearchStatusErrorImage;
|
||||
ContentDialog twitchAccessTokenNotFoundErrorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarVideoSearchingErrorDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarVideoSearchingTwitchAccessTokenNotFoundErrorDialogDescription"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await twitchAccessTokenNotFoundErrorDialog.ShowAsync();
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotValidException)
|
||||
{
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = HomeOptionsBarVideoSearchStatusErrorImage;
|
||||
ContentDialog twitchAccessTokenNotValidErrorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarVideoSearchingErrorDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarVideoSearchingTwitchAccessTokenNotValidErrorDialogDescription"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await twitchAccessTokenNotValidErrorDialog.ShowAsync();
|
||||
return;
|
||||
}
|
||||
catch (WebException wex)
|
||||
{
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = HomeOptionsBarVideoSearchStatusErrorImage;
|
||||
if (wex.Response is null)
|
||||
{
|
||||
ContentDialog internetAccessErrorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarVideoSearchingErrorDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeOptionsBarVideoSearchingInternetConnectionErrorDialogDescription"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await internetAccessErrorDialog.ShowAsync();
|
||||
return;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
|
||||
// Set searching status control to done (null)
|
||||
HomeOptionBarVideoSearchControlStatusControl.Content = null;
|
||||
|
||||
// Invoke search successed event
|
||||
VideoSearchSuccessed?.Invoke(this, new VideoSearchSuccessedEventArgs { Video = videoService });
|
||||
}
|
||||
}
|
||||
|
||||
// HELP BUTTON CLICKED
|
||||
private void HomeOptionsBarVideoSearchControlHelpButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Switch info box
|
||||
HomeOptionsBarVideoSearchControlInfoBox.IsOpen = !HomeOptionsBarVideoSearchControlInfoBox.IsOpen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<VideoSearchSuccessedEventArgs> VideoSearchSuccessed;
|
||||
public event EventHandler SearchButtonClick;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class HomeSerialAddingVideoPanel : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeSerialAddingVideoPanel(IVideo videoService)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set video service object
|
||||
VideoService = videoService;
|
||||
|
||||
// Set metadata
|
||||
ThumbnailImage = VideoService.Metadata.Thumbnail != null ? new BitmapImage { UriSource = VideoService.Metadata.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{VideoService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Title = VideoService.Metadata.Title;
|
||||
Author = VideoService.Metadata.Author;
|
||||
Views = VideoService.Metadata.Views.ToString();
|
||||
Date = VideoService.Metadata.Date.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern);
|
||||
Duration = TimeSpanCustomFormat.ToOptTHBaseMMSS(VideoService.Metadata.Duration);
|
||||
|
||||
// Set video options control
|
||||
HomeVideoAddingOptionsControl = new HomeAddingVideoOptions(VideoService);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// BASE VIDEO DATA
|
||||
public IVideo VideoService { get; set; }
|
||||
|
||||
// VIDEO DATA
|
||||
private ImageSource ThumbnailImage { get; set; }
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Title { get; set; }
|
||||
private string Author { get; set; }
|
||||
private string Views { get; set; }
|
||||
private string Date { get; set; }
|
||||
private string Duration { get; set; }
|
||||
|
||||
// OPTIONS CONTROL
|
||||
public HomeAddingVideoOptions HomeVideoAddingOptionsControl { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// ON CONTROL LOADING
|
||||
private async void HomeSerialAddingVideoPanel_Loading(FrameworkElement sender, object args)
|
||||
{
|
||||
await HomeVideoAddingOptionsControl.Init();
|
||||
HomeSerialAddingVideoExpander.Content = HomeVideoAddingOptionsControl;
|
||||
}
|
||||
|
||||
// SOURCE BUTTON CLICKED
|
||||
private async void HomeSerialAddingVideoPanelSourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Launch the website
|
||||
await Windows.System.Launcher.LaunchUriAsync(VideoService.Url);
|
||||
}
|
||||
|
||||
// DELETE BUTTON CLICKED
|
||||
private void HomeSerialAddingVideoPanelDeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DeleteRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler DeleteRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using Windows.ApplicationModel.ExtendedExecution;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class HomeTaskPanel : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private static readonly ResourceDictionary IconsRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") };
|
||||
private static readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeTaskPanel(TaskData taskData)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set task data
|
||||
Data = taskData;
|
||||
|
||||
// Set video status
|
||||
Status = Core.Enums.TaskStatus.Idle;
|
||||
|
||||
// Create video cancellation token
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Set thumbnail image
|
||||
ThumbnailImage = Data.VideoService.Metadata.Thumbnail != null ? new BitmapImage { UriSource = Data.VideoService.Metadata.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
|
||||
// Set source icon
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Data.VideoService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
|
||||
// Set duration
|
||||
TimeSpan newDuration = Data.TaskOptions.TrimEnd.Subtract(Data.TaskOptions.TrimStart);
|
||||
Duration = TimeSpanCustomFormat.ToOptTHBaseMMSS(newDuration);
|
||||
if (Data.VideoService.Metadata.Duration > newDuration) Duration += $" ({TimeSpanCustomFormat.ToOptTHBaseMMSS(Data.TaskOptions.TrimStart, Data.TaskOptions.TrimEnd)} - {TimeSpanCustomFormat.ToOptTHBaseMMSS(Data.TaskOptions.TrimEnd, Data.TaskOptions.TrimStart)})";
|
||||
|
||||
// Set media type
|
||||
MediaTypeQuality += ResourceLoader.GetForCurrentView().GetString($"MediaType{Data.TaskOptions.MediaType}Text");
|
||||
if (Data.TaskOptions.MediaType != MediaType.OnlyAudio) MediaTypeQuality += $" ({Data.TaskOptions.Stream.Height}p{(Data.TaskOptions.Stream.FrameRate > 0 ? Data.TaskOptions.Stream.FrameRate.ToString() : "N/A")})";
|
||||
|
||||
// Set file
|
||||
File += $@"{(Data.TaskOptions.Location != null ? Data.TaskOptions.Location.Path : $@"{UserDataPaths.GetDefault().Downloads}\VDownload")}\{Data.TaskOptions.Filename}.{Data.TaskOptions.Extension.ToString().ToLower()}";
|
||||
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateIdleIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextIdle");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// TASK STATUS
|
||||
public Core.Enums.TaskStatus Status { get; set; }
|
||||
|
||||
// TASK CANCELLATION TOKEN
|
||||
public CancellationTokenSource CancellationTokenSource { get; set; }
|
||||
|
||||
// TASK DATA
|
||||
private TaskData Data { get; set; }
|
||||
|
||||
// TASK PANEL DATA
|
||||
private ImageSource ThumbnailImage { get; set; }
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Title { get; set; }
|
||||
private string Author { get; set; }
|
||||
private string Duration { get; set; }
|
||||
private string MediaTypeQuality { get; set; }
|
||||
private string File { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region METHODS
|
||||
|
||||
public async Task Start(bool delayWhenOnMeteredConnection)
|
||||
{
|
||||
// Change icon
|
||||
HomeTaskPanelStartStopButton.Icon = new SymbolIcon(Symbol.Stop);
|
||||
|
||||
// Create cancellation token
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Scheduling
|
||||
if (Data.TaskOptions.Schedule > 0)
|
||||
{
|
||||
DateTime ScheduledDateTime = DateTime.Now.AddMinutes(Data.TaskOptions.Schedule);
|
||||
|
||||
// Set task status
|
||||
Status = Core.Enums.TaskStatus.Scheduled;
|
||||
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateScheduledIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextScheduled")} ({ScheduledDateTime.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern)} {ScheduledDateTime.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortTimePattern)})";
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
while (DateTime.Now < ScheduledDateTime && !CancellationTokenSource.IsCancellationRequested) await Task.Delay(100);
|
||||
}
|
||||
|
||||
// Set task status
|
||||
Status = Core.Enums.TaskStatus.Waiting;
|
||||
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateWaitingIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextWaiting");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Visible;
|
||||
|
||||
// Wait in queue
|
||||
await HomeMain.WaitInQueue(delayWhenOnMeteredConnection, CancellationTokenSource.Token);
|
||||
if (!CancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
// Set task status
|
||||
Status = Core.Enums.TaskStatus.InProgress;
|
||||
|
||||
// Get task unique ID
|
||||
string uniqueID = TaskId.Get();
|
||||
|
||||
// Get temporary folder
|
||||
StorageFolder tempFolder;
|
||||
if ((bool)Config.GetValue("custom_temp_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_temp_location"))
|
||||
tempFolder = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("custom_temp_location");
|
||||
else
|
||||
tempFolder = ApplicationData.Current.TemporaryFolder;
|
||||
tempFolder = await tempFolder.CreateFolderAsync(uniqueID);
|
||||
|
||||
bool endedWithError = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Throw exception if cancellation requested
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
// Start stopwatch
|
||||
Stopwatch taskStopwatch = Stopwatch.StartNew();
|
||||
|
||||
// Set progress event handlers
|
||||
Data.VideoService.DownloadingProgressChanged += (s, a) =>
|
||||
{
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDownloadingIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextDownloading")} ({Math.Round(a.Progress)}%)";
|
||||
HomeTaskPanelStateProgressBar.Value = a.Progress;
|
||||
};
|
||||
Data.VideoService.ProcessingProgressChanged += (s, a) =>
|
||||
{
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateProcessingIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextProcessing")} ({Math.Round(a.Progress)}%)";
|
||||
HomeTaskPanelStateProgressBar.Value = a.Progress;
|
||||
};
|
||||
|
||||
// Request extended session
|
||||
ExtendedExecutionSession session = new ExtendedExecutionSession { Reason = ExtendedExecutionReason.Unspecified };
|
||||
await session.RequestExtensionAsync();
|
||||
|
||||
// Start task
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
StorageFile tempOutputFile = await Data.VideoService.DownloadAndTranscodeAsync(tempFolder, Data.TaskOptions.Stream, Data.TaskOptions.Extension, Data.TaskOptions.MediaType, Data.TaskOptions.TrimStart, Data.TaskOptions.TrimEnd, CancellationTokenSource.Token);
|
||||
|
||||
// Dispose session
|
||||
session.Dispose();
|
||||
|
||||
// Cancel if requested
|
||||
CancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateFinalizingIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextFinalizing");
|
||||
HomeTaskPanelStateProgressBar.IsIndeterminate = true;
|
||||
|
||||
// Move to output location
|
||||
string filename = $"{Data.TaskOptions.Filename}.{Data.TaskOptions.Extension.ToString().ToLower()}";
|
||||
CreationCollisionOption collisionOption = (bool)Config.GetValue("replace_output_file_if_exists") ? CreationCollisionOption.ReplaceExisting : CreationCollisionOption.GenerateUniqueName;
|
||||
StorageFile outputFile = await (Data.TaskOptions.Location != null ? Data.TaskOptions.Location.CreateFileAsync(filename, collisionOption): DownloadsFolder.CreateFileAsync(filename, collisionOption));
|
||||
await tempOutputFile.MoveAndReplaceAsync(outputFile);
|
||||
|
||||
// Stop stopwatch
|
||||
taskStopwatch.Stop();
|
||||
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateDoneIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextDone")} ({(Math.Floor(taskStopwatch.Elapsed.TotalHours) > 0 ? $"{ Math.Floor(taskStopwatch.Elapsed.TotalHours):0}:" : "")}{taskStopwatch.Elapsed.Minutes:00}:{taskStopwatch.Elapsed.Seconds:00})";
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
// Show notification
|
||||
if ((bool)Config.GetValue("show_notification_when_task_ended_successfully"))
|
||||
new ToastContentBuilder()
|
||||
.AddText(ResourceLoader.GetForCurrentView().GetString("NotificationTaskEndedSuccessfullyHeader"))
|
||||
.AddText($"\"{Title}\" - {ResourceLoader.GetForCurrentView().GetString("NotificationTaskEndedSuccessfullyDescription")}")
|
||||
.Show();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextCancelled");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
string errorInfo;
|
||||
if (ex.Response is null) errorInfo = ResourceLoader.GetForCurrentView().GetString("TaskErrorInternetConnection");
|
||||
else throw ex;
|
||||
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateErrorIcon"];
|
||||
HomeTaskPanelStateText.Text = $"{ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextError")} ({errorInfo})";
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
// Show notification
|
||||
if ((bool)Config.GetValue("show_notification_when_task_ended_unsuccessfully"))
|
||||
new ToastContentBuilder()
|
||||
.AddText(ResourceLoader.GetForCurrentView().GetString("NotificationTaskEndedUnsuccessfullyHeader"))
|
||||
.AddText($"\"{Title}\" - {ResourceLoader.GetForCurrentView().GetString("NotificationTaskEndedUnsuccessfullyDescription")} ({errorInfo})")
|
||||
.Show();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Set video status
|
||||
Status = Core.Enums.TaskStatus.Idle;
|
||||
|
||||
// Change icon
|
||||
HomeTaskPanelStartStopButton.Icon = new SymbolIcon(Symbol.Download);
|
||||
|
||||
if (!endedWithError || (bool)Config.GetValue("delete_task_temp_when_ended_with_error"))
|
||||
{
|
||||
// Delete temporary files
|
||||
await tempFolder.DeleteAsync();
|
||||
|
||||
// Dispose unique id
|
||||
TaskId.Dispose(uniqueID);
|
||||
}
|
||||
|
||||
if (!endedWithError && !CancellationTokenSource.IsCancellationRequested && (bool)Config.GetValue("remove_task_when_successfully_ended"))
|
||||
{
|
||||
// Remove task when successfully ended
|
||||
TaskRemovingRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set state controls
|
||||
HomeTaskPanelStateIcon.Source = (SvgImageSource)IconsRes["StateCancelledIcon"];
|
||||
HomeTaskPanelStateText.Text = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelStateTextCancelled");
|
||||
HomeTaskPanelStateProgressBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
// Change icon
|
||||
HomeTaskPanelStartStopButton.Icon = new SymbolIcon(Symbol.Download);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// SOURCE BUTTON CLICKED
|
||||
private async void HomeTaskPanelSourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Launch the website
|
||||
await Windows.System.Launcher.LaunchUriAsync(Data.VideoService.Url);
|
||||
}
|
||||
|
||||
// START STOP BUTTON CLICKED
|
||||
private async void HomeTaskPanelStartStopButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Status == Core.Enums.TaskStatus.InProgress || Status == Core.Enums.TaskStatus.Waiting || Status == Core.Enums.TaskStatus.Scheduled) CancellationTokenSource.Cancel();
|
||||
else
|
||||
{
|
||||
bool delay = (bool)Config.GetValue("delay_task_when_queued_task_starts_on_metered_network");
|
||||
if (NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)
|
||||
{
|
||||
ContentDialogResult dialogResult = await new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelTaskStartMeteredConnectionDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelTaskStartMeteredConnectionDialogDescription"),
|
||||
PrimaryButtonText = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelTaskStartMeteredConnectionDialogStartAndDelayText"),
|
||||
SecondaryButtonText = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelTaskStartMeteredConnectionDialogStartWithoutDelayText"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("HomeTaskPanelTaskStartMeteredConnectionDialogCancel"),
|
||||
}.ShowAsync();
|
||||
switch (dialogResult)
|
||||
{
|
||||
case ContentDialogResult.Primary: delay = true; break;
|
||||
case ContentDialogResult.Secondary: delay = false; break;
|
||||
case ContentDialogResult.None: return;
|
||||
}
|
||||
}
|
||||
await Start(delay);
|
||||
}
|
||||
}
|
||||
|
||||
// REMOVE BUTTON CLICKED
|
||||
private void HomeTaskPanelRemoveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Status == Core.Enums.TaskStatus.InProgress || Status == Core.Enums.TaskStatus.Waiting || Status == Core.Enums.TaskStatus.Scheduled) CancellationTokenSource.Cancel();
|
||||
TaskRemovingRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler TaskRemovingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.HomeTasksListPlaceholder"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Images.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Viewbox Width="250">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.Row="0" Source="{StaticResource HomeTasksListPlaceholderImage}" Width="120"/>
|
||||
<TextBlock Grid.Row="1" x:Uid="HomeTasksListPlaceholderTextBlock" Foreground="{StaticResource HomeTasksListPlaceholderTextColor}"/>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</UserControl>
|
||||
@@ -1,12 +0,0 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class HomeTasksListPlaceholder : UserControl
|
||||
{
|
||||
public HomeTasksListPlaceholder()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
VDownload/Views/Home/Controls/PlaylistSearchControl.xaml
Normal file
32
VDownload/Views/Home/Controls/PlaylistSearchControl.xaml
Normal file
@@ -0,0 +1,32 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.PlaylistSearchControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="100"
|
||||
d:DesignWidth="400">
|
||||
|
||||
|
||||
<Grid x:Name="Grid" Margin="10" ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="UrlTextBox" x:Uid="Home_OptionsBar_PlaylistSearchControl_UrlTextBox" Grid.Column="0" VerticalAlignment="Center"/>
|
||||
<muxc:NumberBox x:Name="MaxVideosNumberBox" Grid.Column="1" VerticalAlignment="Center" SpinButtonPlacementMode="Compact" Minimum="0" LostFocus="MaxVideosNumberBox_LostFocus"/>
|
||||
<Button x:Name="SearchButton" x:Uid="Home_OptionsBar_PlaylistSearchControl_SearchButton" Grid.Column="2" Click="SearchButton_Click"/>
|
||||
<Button x:Name="HelpButton" Grid.Column="3" Content="?" Click="HelpButton_Click">
|
||||
<Button.Resources>
|
||||
<muxc:TeachingTip x:Name="InfoBox" x:Uid="Home_OptionsBar_PlaylistSearchControl_InfoBox" Target="{x:Bind Grid}"/>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<ContentPresenter x:Name="StatusControlPresenter" Margin="-10,0,0,0" Grid.Column="4"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
147
VDownload/Views/Home/Controls/PlaylistSearchControl.xaml.cs
Normal file
147
VDownload/Views/Home/Controls/PlaylistSearchControl.xaml.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Services.Sources;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class PlaylistSearchControl : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private static readonly ResourceDictionary IconRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") };
|
||||
|
||||
private static readonly Microsoft.UI.Xaml.Controls.ProgressRing StatusControlProgressRing = new Microsoft.UI.Xaml.Controls.ProgressRing { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), IsActive = true };
|
||||
private static readonly Image StatusControlErrorImage = new Image { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), Source = (SvgImageSource)IconRes["ErrorIcon"] };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public PlaylistSearchControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set default max videos
|
||||
MaxVideosNumberBox.Value = (int)Config.GetValue("default_max_playlist_videos");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private void MaxVideosNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(MaxVideosNumberBox.Value)) MaxVideosNumberBox.Value = (int)Config.GetValue("default_max_playlist_videos");
|
||||
}
|
||||
|
||||
private async void SearchButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
async Task ShowDialog(string localErrorKey)
|
||||
{
|
||||
ContentDialog errorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_OptionsBar_PlaylistSearchControl_Base_Title"),
|
||||
Content = ResourceLoader.GetForCurrentView("DialogResources").GetString($"Home_OptionsBar_PlaylistSearchControl_{localErrorKey}_Content"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CloseButtonText"),
|
||||
};
|
||||
await errorDialog.ShowAsync();
|
||||
StatusControlPresenter.Content = StatusControlErrorImage;
|
||||
}
|
||||
|
||||
SearchButtonClicked?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
InfoBox.IsOpen = false;
|
||||
|
||||
StatusControlPresenter.Content = StatusControlProgressRing;
|
||||
|
||||
MaxVideosNumberBox_LostFocus(sender, e);
|
||||
IPlaylist playlist = Source.GetPlaylist(UrlTextBox.Text);
|
||||
if (playlist == null)
|
||||
{
|
||||
StatusControlPresenter.Content = StatusControlErrorImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
await playlist.GetMetadataAsync(CancellationToken);
|
||||
await playlist.GetVideosAsync((int)Math.Round(MaxVideosNumberBox.Value), CancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
StatusControlPresenter.Content = null;
|
||||
return;
|
||||
}
|
||||
catch (MediaNotFoundException)
|
||||
{
|
||||
StatusControlPresenter.Content = StatusControlErrorImage;
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotFoundException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotFound");
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotValidException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotValid");
|
||||
return;
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
await ShowDialog("InternetNotAvailable");
|
||||
return;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
|
||||
StatusControlPresenter.Content = null;
|
||||
|
||||
SearchingSuccessed?.Invoke(this, new PlaylistSearchSuccessedEventArgs(playlist));
|
||||
}
|
||||
}
|
||||
|
||||
private void HelpButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
InfoBox.IsOpen = !InfoBox.IsOpen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<PlaylistSearchSuccessedEventArgs> SearchingSuccessed;
|
||||
public event EventHandler SearchButtonClicked;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,36 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.HomePlaylistAddingPanel"
|
||||
x:Class="VDownload.Views.Home.Controls.SerialVideoAddingControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:local="using:VDownload.Views.Home.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
Loading="HomePlaylistAddingPanel_Loading"
|
||||
mc:Ignorable="d">
|
||||
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400"
|
||||
Loading="SerialVideoAddingControl_Loading">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid Padding="10" RowSpacing="10">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- METADATA PANEL -->
|
||||
<Grid VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="12" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold" Text="{x:Bind Name}"/>
|
||||
<AppBarButton Grid.Column="1" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="HomePlaylistAddingPanelSourceButton_Click"/>
|
||||
<AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Click="HomePlaylistAddingPanelAddButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Grid.Row="1" Margin="0,0,0,60">
|
||||
<StackPanel x:Name="HomePlaylistAddingPanelVideosList" Spacing="10"/>
|
||||
<ScrollViewer Grid.Row="0" Margin="0,0,0,60">
|
||||
<StackPanel x:Name="VideosStackPanel" x:FieldModifier="public" Spacing="10"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid Grid.Row="1" ColumnSpacing="10">
|
||||
<Grid Grid.Row="0" ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -54,24 +39,24 @@
|
||||
<muxc:Expander.Header>
|
||||
<StackPanel Margin="-5,0,-10,0" Spacing="10" Orientation="Horizontal">
|
||||
<Image Height="18" Source="{ThemeResource ApplyToAllOptionsIcon}"/>
|
||||
<TextBlock x:Uid="HomePlaylistAddingPanelApplyToAllOptionsHeaderTextBlock"/>
|
||||
<TextBlock x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_HeaderTextBlock"/>
|
||||
</StackPanel>
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<StackPanel Spacing="10">
|
||||
<cc:SettingControl x:Name="HomePlaylistAddingApplyToAllLocationSettingControl" x:Uid="HomePlaylistAddingApplyToAllLocationSettingControl" Icon="{ThemeResource LocationIcon}">
|
||||
<cc:SettingControl x:Name="ApplyToAllLocationSettingControl" x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl" Icon="{ThemeResource LocationIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<StackPanel Spacing="10" Orientation="Horizontal">
|
||||
<Button x:Uid="HomePlaylistAddingApplyToAllLocationBrowseButton" Click="HomePlaylistAddingApplyToAllLocationBrowseButton_Click"/>
|
||||
<Button x:Uid="HomePlaylistAddingApplyToAllApplyButton" Click="HomePlaylistAddingApplyToAllApplyLocationButton_Click"/>
|
||||
<Button x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_LocationSettingControl_BrowseButton" Click="ApplyToAllLocationBrowseButton_Click"/>
|
||||
<Button x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_Base_ApplyButton" Click="ApplyToAllApplyLocationButton_Click"/>
|
||||
</StackPanel>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
<cc:SettingControl x:Uid="HomePlaylistAddingApplyToAllScheduleSettingControl" Icon="{ThemeResource ScheduleIcon}">
|
||||
<cc:SettingControl x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_ScheduleSettingControl" Icon="{ThemeResource ScheduleIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<StackPanel Spacing="10" Orientation="Horizontal">
|
||||
<muxc:NumberBox x:Name="HomePlaylistAddingApplyToAllScheduleNumberBox" MaxWidth="100" Value="0" Minimum="0" SpinButtonPlacementMode="Compact" LostFocus="HomePlaylistAddingApplyToAllScheduleNumberBox_LostFocus"/>
|
||||
<Button x:Uid="HomePlaylistAddingApplyToAllApplyButton" Click="HomePlaylistAddingApplyToAllApplyScheduleButton_Click"/>
|
||||
<muxc:NumberBox x:Name="ApplyToAllScheduleNumberBox" MaxWidth="100" Value="0" Minimum="0" SpinButtonPlacementMode="Compact" LostFocus="ApplyToAllScheduleNumberBox_LostFocus"/>
|
||||
<Button x:Uid="Home_Adding_Base_SerialVideoAddingControl_ApplyToAllOptions_Base_ApplyButton" Click="ApplyToAllApplyScheduleButton_Click"/>
|
||||
</StackPanel>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
@@ -87,8 +72,8 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" Height="18" Source="{ThemeResource FilterIcon}"/>
|
||||
<StackPanel Grid.Column="1" Margin="0,0,20,0" Spacing="10" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock x:Uid="HomePlaylistAddingPanelFilterHeaderTextBlock"/>
|
||||
<TextBlock x:Name="HomePlaylistAddingPanelFilterHeaderCountTextBlock" FontWeight="Light"/>
|
||||
<TextBlock x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_Header_TextBlock"/>
|
||||
<TextBlock x:Name="FilterHeaderCountTextBlock" FontWeight="Light"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</muxc:Expander.Header>
|
||||
@@ -106,51 +91,51 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" x:Uid="HomePlaylistAddingPanelFilterTitleTextBlock" FontWeight="SemiBold"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" x:Name="HomePlaylistAddingPanelFilterTitleTextBox" LostFocus="HomePlaylistAddingPanelFilterTitleAndAuthorTextBox_LostFocus"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" x:Uid="HomePlaylistAddingPanelFilterAuthorTextBlock" FontWeight="SemiBold"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" x:Name="HomePlaylistAddingPanelFilterAuthorTextBox" LostFocus="HomePlaylistAddingPanelFilterTitleAndAuthorTextBox_LostFocus"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" x:Uid="HomePlaylistAddingPanelFilterViewsTextBlock" FontWeight="SemiBold"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBlock" FontWeight="SemiBold"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" x:Name="FilterTitleTextBox" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_TitleTextBox" LostFocus="FilterTitleAndAuthorTextBox_LostFocus"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBlock" FontWeight="SemiBold"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" x:Name="FilterAuthorTextBox" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_AuthorTextBox" LostFocus="FilterTitleAndAuthorTextBox_LostFocus"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_ViewsTextBlock" FontWeight="SemiBold"/>
|
||||
<Grid Grid.Row="2" Grid.Column="1" ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<muxc:NumberBox Grid.Column="0" x:Name="HomePlaylistAddingPanelFilterMinViewsNumberBox" SpinButtonPlacementMode="Compact" LostFocus="HomePlaylistAddingPanelFilterMinViewsNumberBox_LostFocus"/>
|
||||
<muxc:NumberBox Grid.Column="0" x:Name="FilterMinViewsNumberBox" SpinButtonPlacementMode="Compact" LostFocus="FilterMinViewsNumberBox_LostFocus"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="-"/>
|
||||
<muxc:NumberBox Grid.Column="2" x:Name="HomePlaylistAddingPanelFilterMaxViewsNumberBox" SpinButtonPlacementMode="Compact" LostFocus="HomePlaylistAddingPanelFilterMaxViewsNumberBox_LostFocus"/>
|
||||
<muxc:NumberBox Grid.Column="2" x:Name="FilterMaxViewsNumberBox" SpinButtonPlacementMode="Compact" LostFocus="FilterMaxViewsNumberBox_LostFocus"/>
|
||||
</Grid>
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" x:Uid="HomePlaylistAddingPanelFilterDateTextBlock" FontWeight="SemiBold"/>
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_DateTextBlock" FontWeight="SemiBold"/>
|
||||
<Grid Grid.Row="3" Grid.Column="1" ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CalendarDatePicker x:Name="HomePlaylistAddingPanelFilterMinDateDatePicker" Grid.Column="0" LostFocus="HomePlaylistAddingPanelFilterMinAndMaxDateDatePicker_LostFocus"/>
|
||||
<CalendarDatePicker x:Name="FilterMinDateDatePicker" Grid.Column="0" LostFocus="FilterMinAndMaxDateDatePicker_LostFocus"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="-"/>
|
||||
<CalendarDatePicker x:Name="HomePlaylistAddingPanelFilterMaxDateDatePicker" Grid.Column="2" LostFocus="HomePlaylistAddingPanelFilterMinAndMaxDateDatePicker_LostFocus"/>
|
||||
<CalendarDatePicker x:Name="FilterMaxDateDatePicker" Grid.Column="2" LostFocus="FilterMinAndMaxDateDatePicker_LostFocus"/>
|
||||
</Grid>
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" x:Uid="HomePlaylistAddingPanelFilterDurationTextBlock" FontWeight="SemiBold"/>
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_DurationTextBlock" FontWeight="SemiBold"/>
|
||||
<Grid Grid.Row="4" Grid.Column="1" ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.Column="0" x:Name="HomePlaylistAddingPanelFilterMinDurationTextBox" LostFocus="HomePlaylistAddingPanelFilterMinDurationTextBox_LostFocus"/>
|
||||
<TextBox Grid.Column="0" x:Name="FilterMinDurationTextBox" LostFocus="FilterMinDurationTextBox_LostFocus"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="-"/>
|
||||
<TextBox Grid.Column="2" x:Name="HomePlaylistAddingPanelFilterMaxDurationTextBox" LostFocus="HomePlaylistAddingPanelFilterMaxDurationTextBox_LostFocus"/>
|
||||
<TextBox Grid.Column="2" x:Name="FilterMaxDurationTextBox" LostFocus="FilterMaxDurationTextBox_LostFocus"/>
|
||||
</Grid>
|
||||
<TextBlock x:Name="HomePlaylistAddingPanelFilterRemovedTextBlock" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" x:Uid="HomePlaylistAddingPanelFilterRemovedTextBlock" FontWeight="SemiBold" Visibility="Collapsed"/>
|
||||
<TextBlock x:Name="FilterRemovedTextBlock" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_TextBlock" FontWeight="SemiBold" Visibility="Collapsed"/>
|
||||
<Grid Grid.Row="5" Grid.Column="1" ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" x:Name="HomePlaylistAddingPanelFilterRemovedCountTextBlock" VerticalAlignment="Center" Visibility="Collapsed"/>
|
||||
<Button Grid.Column="1" x:Uid="HomePlaylistAddingPanelFilterRemovedRestoreButton" x:Name="HomePlaylistAddingPanelFilterRemovedRestoreButton" Visibility="Collapsed" Click="HomePlaylistAddingPanelFilterRemovedRestoreButton_Click"/>
|
||||
<TextBlock Grid.Column="0" x:Name="FilterRemovedCountTextBlock" VerticalAlignment="Center" Visibility="Collapsed"/>
|
||||
<Button Grid.Column="1" x:Name="FilterRemovedRestoreButton" x:Uid="Home_Adding_Base_SerialVideoAddingControl_Filter_Removed_RestoreButton" Visibility="Collapsed" Click="FilterRemovedRestoreButton_Click"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</muxc:Expander.Content>
|
||||
346
VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml.cs
Normal file
346
VDownload/Views/Home/Controls/SerialVideoAddingControl.xaml.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.RegularExpressions;
|
||||
using VDownload.Converters;
|
||||
using VDownload.Core.Extensions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class SerialVideoAddingControl : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public SerialVideoAddingControl(IVideo[] videos)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Videos = videos;
|
||||
MinViews = Videos.Min(video => video.Views);
|
||||
MaxViews = Videos.Max(video => video.Views);
|
||||
MinDate = Videos.Min(video => video.Date);
|
||||
MaxDate = Videos.Max(video => video.Date);
|
||||
MinDuration = Videos.Min(video => video.Duration);
|
||||
MaxDuration = Videos.Max(video => video.Duration);
|
||||
|
||||
FilterMinViewsNumberBox.Minimum = FilterMaxViewsNumberBox.Minimum = FilterMinViewsNumberBox.Value = MinViews;
|
||||
FilterMinViewsNumberBox.Maximum = FilterMaxViewsNumberBox.Maximum = FilterMaxViewsNumberBox.Value = MaxViews;
|
||||
|
||||
FilterMinDateDatePicker.MinDate = FilterMaxDateDatePicker.MinDate = (DateTimeOffset)(FilterMinDateDatePicker.Date = MinDate);
|
||||
FilterMinDateDatePicker.MaxDate = FilterMaxDateDatePicker.MaxDate = (DateTimeOffset)(FilterMaxDateDatePicker.Date = MaxDate);
|
||||
|
||||
TextBoxExtensions.SetMask(FilterMinDurationTextBox, (string)new TimeSpanToTextBoxMaskConverter().Convert(MaxDuration, null, null, null));
|
||||
TextBoxExtensions.SetMask(FilterMaxDurationTextBox, (string)new TimeSpanToTextBoxMaskConverter().Convert(MaxDuration, null, null, null));
|
||||
HashSet<int> maskElements = new HashSet<int>();
|
||||
foreach (TimeSpan ts in new List<TimeSpan> { MinDuration, MaxDuration })
|
||||
{
|
||||
if (Math.Floor(ts.TotalHours) > 0) maskElements.Add(int.Parse(Math.Floor(ts.TotalHours).ToString()[0].ToString()));
|
||||
if (Math.Floor(ts.TotalMinutes) > 0)
|
||||
{
|
||||
if (Math.Floor(ts.TotalHours) > 0) maskElements.Add(5);
|
||||
else maskElements.Add(int.Parse(ts.Minutes.ToString()[0].ToString()));
|
||||
}
|
||||
if (Math.Floor(ts.TotalMinutes) > 0) maskElements.Add(5);
|
||||
else maskElements.Add(int.Parse(ts.Seconds.ToString()[0].ToString()));
|
||||
}
|
||||
List<string> maskElementsString = new List<string>();
|
||||
foreach (int i in maskElements)
|
||||
{
|
||||
if (i != 9) maskElementsString.Add($"{i}:[0-{i}]");
|
||||
}
|
||||
TextBoxExtensions.SetCustomMask(FilterMinDurationTextBox, string.Join(',', maskElementsString));
|
||||
TextBoxExtensions.SetCustomMask(FilterMaxDurationTextBox, string.Join(',', maskElementsString));
|
||||
FilterMinDurationTextBox.Text = MinDuration.ToStringOptTHMMBaseSS(MaxDuration);
|
||||
FilterMaxDurationTextBox.Text = MaxDuration.ToStringOptTHMMBaseSS();
|
||||
|
||||
ApplyToAllSchedule = 0;
|
||||
|
||||
foreach (IVideo video in Videos)
|
||||
{
|
||||
SerialVideoAddingVideoControl videoControl = new SerialVideoAddingVideoControl(video);
|
||||
videoControl.DeleteRequested += (s, a) =>
|
||||
{
|
||||
DeletedVideos.Add(videoControl);
|
||||
FilterRemovedTextBlock.Visibility = Visibility.Visible;
|
||||
FilterRemovedCountTextBlock.Visibility = Visibility.Visible;
|
||||
FilterRemovedRestoreButton.Visibility = Visibility.Visible;
|
||||
FilterRemovedCountTextBlock.Text = $"{DeletedVideos.Count}";
|
||||
FilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("Home_Adding_Base_SerialVideoAddingControl_Filter_Header_CountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : "";
|
||||
VideosStackPanel.Children.Remove(videoControl);
|
||||
};
|
||||
VideosStackPanel.Children.Add(videoControl);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private IVideo[] Videos { get; set; }
|
||||
|
||||
public StorageFolder ApplyToAllLocation { get; set; }
|
||||
public double ApplyToAllSchedule { get; set; }
|
||||
|
||||
private List<SerialVideoAddingVideoControl> DeletedVideos { get; set; } = new List<SerialVideoAddingVideoControl>();
|
||||
private List<SerialVideoAddingVideoControl> HiddenVideos { get; set; } = new List<SerialVideoAddingVideoControl>();
|
||||
|
||||
private long MinViews { get; set; }
|
||||
private long MaxViews { get; set; }
|
||||
private DateTime MinDate { get; set; }
|
||||
private DateTime MaxDate { get; set; }
|
||||
private TimeSpan MinDuration { get; set; }
|
||||
private TimeSpan MaxDuration { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private async void SerialVideoAddingControl_Loading(FrameworkElement sender, object args)
|
||||
{
|
||||
if (!(bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("last_media_location"))
|
||||
{
|
||||
ApplyToAllLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("last_media_location");
|
||||
ApplyToAllLocationSettingControl.Description = ApplyToAllLocation.Path;
|
||||
}
|
||||
else if ((bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_media_location"))
|
||||
{
|
||||
ApplyToAllLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("selected_media_location");
|
||||
ApplyToAllLocationSettingControl.Description = ApplyToAllLocation.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyToAllLocation = null;
|
||||
ApplyToAllLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload";
|
||||
}
|
||||
}
|
||||
|
||||
private async void ApplyToAllLocationBrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FolderPicker picker = new FolderPicker
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.Downloads
|
||||
};
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
StorageFolder selectedFolder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (selectedFolder != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await (await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync();
|
||||
StorageApplicationPermissions.FutureAccessList.AddOrReplace("last_media_location", selectedFolder);
|
||||
ApplyToAllLocation = selectedFolder;
|
||||
ApplyToAllLocationSettingControl.Description = ApplyToAllLocation.Path;
|
||||
}
|
||||
catch (UnauthorizedAccessException) { }
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyToAllApplyLocationButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (SerialVideoAddingVideoControl videoPanel in VideosStackPanel.Children)
|
||||
{
|
||||
videoPanel.OptionsControl.ChangeLocation(ApplyToAllLocation);
|
||||
}
|
||||
foreach (SerialVideoAddingVideoControl videoPanel in DeletedVideos)
|
||||
{
|
||||
videoPanel.OptionsControl.ChangeLocation(ApplyToAllLocation);
|
||||
}
|
||||
foreach (SerialVideoAddingVideoControl videoPanel in HiddenVideos)
|
||||
{
|
||||
videoPanel.OptionsControl.ChangeLocation(ApplyToAllLocation);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyToAllScheduleNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(ApplyToAllScheduleNumberBox.Value))
|
||||
{
|
||||
ApplyToAllScheduleNumberBox.Value = ApplyToAllSchedule;
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyToAllSchedule = ApplyToAllScheduleNumberBox.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyToAllApplyScheduleButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (SerialVideoAddingVideoControl videoPanel in VideosStackPanel.Children)
|
||||
{
|
||||
videoPanel.OptionsControl.ChangeSchedule(ApplyToAllSchedule);
|
||||
}
|
||||
foreach (SerialVideoAddingVideoControl videoPanel in DeletedVideos)
|
||||
{
|
||||
videoPanel.OptionsControl.ChangeSchedule(ApplyToAllSchedule);
|
||||
}
|
||||
foreach (SerialVideoAddingVideoControl videoPanel in HiddenVideos)
|
||||
{
|
||||
videoPanel.OptionsControl.ChangeSchedule(ApplyToAllSchedule);
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterTitleAndAuthorTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
private void FilterMinViewsNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(FilterMinViewsNumberBox.Value))
|
||||
{
|
||||
FilterMinViewsNumberBox.Value = MinViews;
|
||||
}
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
private void FilterMaxViewsNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(FilterMaxViewsNumberBox.Value))
|
||||
{
|
||||
FilterMaxViewsNumberBox.Value = MaxViews;
|
||||
}
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
private void FilterMinAndMaxDateDatePicker_LostFocus(object sender, RoutedEventArgs args)
|
||||
{
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
private void FilterMinDurationTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!FilterMinDurationTextBox.Text.Contains('_'))
|
||||
{
|
||||
string[] segments = FilterMinDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < MinDuration || parsedTimeSpan > MaxDuration)
|
||||
{
|
||||
FilterMinDurationTextBox.Text = MinDuration.ToStringOptTHMMBaseSS(MaxDuration);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FilterMinDurationTextBox.Text = MinDuration.ToStringOptTHMMBaseSS(MaxDuration);
|
||||
}
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
private void FilterMaxDurationTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!FilterMaxDurationTextBox.Text.Contains('_'))
|
||||
{
|
||||
string[] segments = FilterMaxDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < MinDuration || parsedTimeSpan > MaxDuration)
|
||||
{
|
||||
FilterMaxDurationTextBox.Text = MaxDuration.ToStringOptTHMMBaseSS();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FilterMaxDurationTextBox.Text = MaxDuration.ToStringOptTHMMBaseSS();
|
||||
}
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
private void FilterRemovedRestoreButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (SerialVideoAddingVideoControl videoControl in DeletedVideos)
|
||||
{
|
||||
VideosStackPanel.Children.Add(videoControl);
|
||||
}
|
||||
FilterRemovedTextBlock.Visibility = Visibility.Collapsed;
|
||||
FilterRemovedCountTextBlock.Visibility = Visibility.Collapsed;
|
||||
FilterRemovedRestoreButton.Visibility = Visibility.Collapsed;
|
||||
FilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterHeaderCountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : "";
|
||||
DeletedVideos.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PRIVATE METHODS
|
||||
|
||||
private void FilterChanged()
|
||||
{
|
||||
string[] minSegments = FilterMinDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(minSegments.ElementAtOrDefault(0), out int minSeconds);
|
||||
int.TryParse(minSegments.ElementAtOrDefault(1), out int minMinutes);
|
||||
int.TryParse(minSegments.ElementAtOrDefault(2), out int minHours);
|
||||
TimeSpan minDuration = new TimeSpan(minHours, minMinutes, minSeconds);
|
||||
|
||||
string[] maxSegments = FilterMaxDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(maxSegments.ElementAtOrDefault(0), out int maxSeconds);
|
||||
int.TryParse(maxSegments.ElementAtOrDefault(1), out int maxMinutes);
|
||||
int.TryParse(maxSegments.ElementAtOrDefault(2), out int maxHours);
|
||||
TimeSpan maxDuration = new TimeSpan(maxHours, maxMinutes, maxSeconds);
|
||||
|
||||
Regex titleRegex = new Regex("");
|
||||
Regex authorRegex = new Regex("");
|
||||
try
|
||||
{
|
||||
titleRegex = new Regex(FilterTitleTextBox.Text);
|
||||
authorRegex = new Regex(FilterAuthorTextBox.Text);
|
||||
}
|
||||
catch (ArgumentException) { }
|
||||
|
||||
List<SerialVideoAddingVideoControl> allVideos = new List<SerialVideoAddingVideoControl>();
|
||||
allVideos.AddRange((IEnumerable<SerialVideoAddingVideoControl>)(VideosStackPanel.Children.ToList()));
|
||||
allVideos.AddRange(HiddenVideos);
|
||||
VideosStackPanel.Children.Clear();
|
||||
HiddenVideos.Clear();
|
||||
foreach (SerialVideoAddingVideoControl videoControl in allVideos)
|
||||
{
|
||||
if (
|
||||
!titleRegex.IsMatch(videoControl.Video.Title) ||
|
||||
!authorRegex.IsMatch(videoControl.Video.Author) ||
|
||||
FilterMinViewsNumberBox.Value > videoControl.Video.Views ||
|
||||
FilterMaxViewsNumberBox.Value < videoControl.Video.Views ||
|
||||
FilterMinDateDatePicker.Date > videoControl.Video.Date ||
|
||||
FilterMaxDateDatePicker.Date < videoControl.Video.Date ||
|
||||
minDuration > videoControl.Video.Duration ||
|
||||
maxDuration < videoControl.Video.Duration
|
||||
) HiddenVideos.Add(videoControl);
|
||||
else VideosStackPanel.Children.Add(videoControl);
|
||||
}
|
||||
FilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterHeaderCountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.HomeSerialAddingVideoPanel"
|
||||
x:Class="VDownload.Views.Home.Controls.SerialVideoAddingVideoControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
@@ -8,10 +8,10 @@
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
xmlns:ex="using:Microsoft.Toolkit.Uwp.UI"
|
||||
Loading="HomeSerialAddingVideoPanel_Loading"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
d:DesignWidth="400"
|
||||
Loading="SerialVideoAddingVideoControl_Loading">
|
||||
|
||||
|
||||
<UserControl.Resources>
|
||||
@@ -27,7 +27,7 @@
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<muxc:Expander x:Name="HomeSerialAddingVideoExpander" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Background="{ThemeResource HomePlaylistAddingVideoPanelContentBackgroundColor}">
|
||||
<muxc:Expander x:Name="Expander" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Background="{ThemeResource HomePlaylistAddingVideoPanelContentBackgroundColor}">
|
||||
<muxc:Expander.Header>
|
||||
<Grid Margin="-5,10,-15,10" ColumnSpacing="10">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -67,8 +67,8 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="15" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind Title}"/>
|
||||
<AppBarButton Grid.Row="0" Grid.Column="2" Margin="0,-4,0,-4" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="HomeSerialAddingVideoPanelSourceButton_Click"/>
|
||||
<AppBarButton Grid.Row="1" Grid.Column="2" Margin="0,-4,0,-4" Width="40" Height="48" Icon="Delete" Click="HomeSerialAddingVideoPanelDeleteButton_Click"/>
|
||||
<AppBarButton Grid.Row="0" Grid.Column="2" Margin="0,-4,0,-4" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="SourceButton_Click"/>
|
||||
<AppBarButton Grid.Row="1" Grid.Column="2" Margin="0,-4,0,-4" Width="40" Height="48" Icon="Delete" Click="HideButton_Click"/>
|
||||
</Grid>
|
||||
</muxc:Expander.Header>
|
||||
</muxc:Expander>
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using VDownload.Core.Extensions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class SerialVideoAddingVideoControl : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public SerialVideoAddingVideoControl(IVideo video)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Video = video;
|
||||
|
||||
ThumbnailImage = Video.Thumbnail != null ? new BitmapImage { UriSource = Video.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Video.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Title = Video.Title;
|
||||
Author = Video.Author;
|
||||
Views = Video.Views.ToString();
|
||||
Date = Video.Date.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern);
|
||||
Duration = Video.Duration.ToStringOptTHBaseMMSS();
|
||||
|
||||
OptionsControl = new VideoAddingOptionsControl(Video);
|
||||
Expander.Content = OptionsControl;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public IVideo Video { get; set; }
|
||||
|
||||
private ImageSource ThumbnailImage { get; set; }
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Title { get; set; }
|
||||
private string Author { get; set; }
|
||||
private string Views { get; set; }
|
||||
private string Date { get; set; }
|
||||
private string Duration { get; set; }
|
||||
|
||||
public VideoAddingOptionsControl OptionsControl { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private async void SerialVideoAddingVideoControl_Loading(FrameworkElement sender, object args)
|
||||
{
|
||||
await OptionsControl.Init();
|
||||
}
|
||||
|
||||
private async void SourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Windows.System.Launcher.LaunchUriAsync(Video.Url);
|
||||
}
|
||||
|
||||
private void HideButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DeleteRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENTS
|
||||
|
||||
public event EventHandler DeleteRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
23
VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml
Normal file
23
VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml
Normal file
@@ -0,0 +1,23 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.SubscriptionsLoadControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="100"
|
||||
d:DesignWidth="400">
|
||||
|
||||
<Grid Margin="10" ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<muxc:ProgressRing x:Name="ProgressRing" Grid.Column="0" Width="15" Height="15" Margin="15,5,5,5" IsActive="True"/>
|
||||
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" VerticalAlignment="Center"/>
|
||||
<Button x:Name="LoadVideosButton" x:Uid="Home_OptionsBar_SubscriptionsLoadControl_LoadVideosButton" Grid.Column="2" Visibility="Collapsed" Click="LoadVideosButton_Click"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
183
VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml.cs
Normal file
183
VDownload/Views/Home/Controls/SubscriptionsLoadControl.xaml.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Services.Sources;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class SubscriptionsLoadControl : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public SubscriptionsLoadControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
InfoTextBlock.Text = ResourceLoader.GetForCurrentView().GetString("Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Loading");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public async Task StartLoading()
|
||||
{
|
||||
List<IVideo> loadedVideos = new List<IVideo>();
|
||||
(Subscription Subscription, StorageFile SubscriptionFile)[] subscriptions = await SubscriptionsCollectionManagement.GetSubscriptionsAsync();
|
||||
foreach ((Subscription Subscription, StorageFile SubscriptionFile) subscription in subscriptions)
|
||||
{
|
||||
try
|
||||
{
|
||||
loadedVideos.AddRange(await subscription.Subscription.GetNewVideosAsync(CancellationToken));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (MediaNotFoundException)
|
||||
{
|
||||
await ShowDialog("MediaNotFound", subscription.Subscription.Playlist.Name);
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotFoundException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotFound");
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotValidException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotValid");
|
||||
return;
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
await ShowDialog("InternetNotAvailable");
|
||||
return;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
|
||||
ProgressRing.Visibility = Visibility.Collapsed;
|
||||
InfoTextBlock.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Found")}: {loadedVideos.Count}";
|
||||
if (loadedVideos.Count > 0)
|
||||
{
|
||||
LoadVideosButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private async void LoadVideosButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ProgressRing.Visibility = Visibility.Visible;
|
||||
LoadVideosButton.IsEnabled = false;
|
||||
|
||||
List<IVideo> loadedVideos = new List<IVideo>();
|
||||
(Subscription Subscription, StorageFile SubscriptionFile)[] subscriptions = await SubscriptionsCollectionManagement.GetSubscriptionsAsync();
|
||||
foreach ((Subscription Subscription, StorageFile SubscriptionFile) subscription in subscriptions)
|
||||
{
|
||||
try
|
||||
{
|
||||
loadedVideos.AddRange(await subscription.Subscription.GetNewVideosAndUpdateAsync(CancellationToken));
|
||||
await SubscriptionsCollectionManagement.UpdateSubscriptionFileAsync(subscription.Subscription, subscription.SubscriptionFile);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (MediaNotFoundException)
|
||||
{
|
||||
await ShowDialog("MediaNotFound", subscription.Subscription.Playlist.Name);
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotFoundException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotFound");
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotValidException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotValid");
|
||||
return;
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
await ShowDialog("InternetNotAvailable");
|
||||
return;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
|
||||
LoadingSuccessed.Invoke(this, new SubscriptionLoadSuccessedEventArgs(loadedVideos.ToArray()));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PRIVATE METHODS
|
||||
|
||||
private async Task ShowDialog(string localErrorKey, string playlistName = null)
|
||||
{
|
||||
StringBuilder content = new StringBuilder(ResourceLoader.GetForCurrentView("DialogResources").GetString($"Home_OptionsBar_SubscriptionsLoadControl_{localErrorKey}_Content"));
|
||||
if (!string.IsNullOrEmpty(playlistName))
|
||||
{
|
||||
content.Append($" {playlistName}");
|
||||
}
|
||||
ContentDialog errorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_OptionsBar_SubscriptionsLoadControl_Base_Title"),
|
||||
Content = content.ToString(),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CloseButtonText"),
|
||||
};
|
||||
await errorDialog.ShowAsync();
|
||||
ProgressRing.Visibility = Visibility.Collapsed;
|
||||
InfoTextBlock.Text = $"{ResourceLoader.GetForCurrentView().GetString("Home_OptionsBar_SubscriptionsLoadControl_InfoTextBlock_Found")}: 0";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENTS
|
||||
|
||||
public event EventHandler<SubscriptionLoadSuccessedEventArgs> LoadingSuccessed;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
76
VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml
Normal file
76
VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml
Normal file
@@ -0,0 +1,76 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.VideoAddingOptionsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:ex="using:Microsoft.Toolkit.Uwp.UI"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<StackPanel Spacing="30">
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_HeaderTextBlock" Margin="0,0,0,10" FontWeight="SemiBold"/>
|
||||
<cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_MediaTypeSettingControl" Margin="0,0,0,10" Icon="{ThemeResource MediaTypeIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<ComboBox x:Name="MediaTypeSettingControlComboBox" Width="150" SelectionChanged="MediaTypeSettingControlComboBox_SelectionChanged"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
<cc:SettingControl x:Name="QualitySettingControl" Margin="0,0,0,10" x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_QualitySettingControl" Icon="{ThemeResource QualityIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<ComboBox x:Name="QualitySettingControlComboBox" Width="150" SelectionChanged="QualitySettingControlComboBox_SelectionChanged"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
<cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_DownloadingOptions_TrimSettingControl" Icon="{ThemeResource TrimIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBox x:Name="TrimSettingControlStartTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" LostFocus="TrimSettingControlStartTextBox_LostFocus"/>
|
||||
<TextBlock VerticalAlignment="Center" Text="-"/>
|
||||
<TextBox x:Name="TrimSettingControlEndTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind Video.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" LostFocus="TrimSettingControlEndTextBox_LostFocus"/>
|
||||
</StackPanel>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_HeaderTextBlock" FontWeight="SemiBold"/>
|
||||
<cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileSettingControl" Icon="{ThemeResource FileIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<Grid ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="FileSettingControlFilenameTextBox" Grid.Column="0" MaxWidth="300" HorizontalAlignment="Right" IsSpellCheckEnabled="False" LostFocus="FileSettingControlFilenameTextBox_LostFocus"/>
|
||||
<ComboBox x:Name="FileSettingControlFileExtensionComboBox" Grid.Column="1" HorizontalAlignment="Stretch" SelectionChanged="FileSettingControlFileExtensionComboBox_SelectionChanged"/>
|
||||
</Grid>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
<cc:SettingControl x:Name="FileLocationSettingControl" x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl" Icon="{ThemeResource LocationIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<Button x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_FileOptions_FileLocationSettingControl_BrowseButton" Click="FileSettingControlFileLocationBrowseButton_Click"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_HeaderTextBlock" FontWeight="SemiBold"/>
|
||||
<cc:SettingControl x:Uid="Home_Adding_Base_DownloadingTaskOptionsControl_TaskOptions_ScheduleSettingControl" Icon="{ThemeResource ScheduleIcon}">
|
||||
<cc:SettingControl.SettingContent>
|
||||
<muxc:NumberBox x:Name="ScheduleSettingControlNumberBox" MaxWidth="100" Value="0" Minimum="0" SpinButtonPlacementMode="Compact" LostFocus="ScheduleSettingControlNumberBox_LostFocus"/>
|
||||
</cc:SettingControl.SettingContent>
|
||||
</cc:SettingControl>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
280
VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml.cs
Normal file
280
VDownload/Views/Home/Controls/VideoAddingOptionsControl.xaml.cs
Normal file
@@ -0,0 +1,280 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Controls;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Extensions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class VideoAddingOptionsControl : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public VideoAddingOptionsControl(IVideo video)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Video = video;
|
||||
|
||||
foreach (string mediaType in Enum.GetNames(typeof(MediaType)))
|
||||
{
|
||||
MediaTypeSettingControlComboBox.Items.Add(ResourceLoader.GetForCurrentView().GetString($"Base_MediaType_{mediaType}Text"));
|
||||
}
|
||||
MediaTypeSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_media_type");
|
||||
|
||||
foreach (BaseStream stream in Video.BaseStreams)
|
||||
{
|
||||
QualitySettingControlComboBox.Items.Add(stream.ToString());
|
||||
}
|
||||
QualitySettingControlComboBox.SelectedIndex = 0;
|
||||
|
||||
TrimStart = TimeSpan.Zero;
|
||||
TrimSettingControlStartTextBox.Text = TrimStart.ToStringOptTHMMBaseSS(Video.Duration);
|
||||
|
||||
TrimEnd = Video.Duration;
|
||||
TrimSettingControlEndTextBox.Text = TrimEnd.ToStringOptTHMMBaseSS();
|
||||
|
||||
string temporaryFilename = (string)Config.GetValue("default_filename");
|
||||
Dictionary<string, string> filenameStandardTemplates = new Dictionary<string, string>()
|
||||
{
|
||||
{ "<title>", Video.Title },
|
||||
{ "<author>", Video.Author },
|
||||
{ "<views>", Video.Views.ToString() },
|
||||
{ "<id>", Video.ID },
|
||||
};
|
||||
foreach (KeyValuePair<string, string> template in filenameStandardTemplates)
|
||||
{
|
||||
temporaryFilename = temporaryFilename.Replace(template.Key, template.Value);
|
||||
}
|
||||
Dictionary<Regex, IFormattable> filenameFormatTemplates = new Dictionary<Regex, IFormattable>()
|
||||
{
|
||||
{ new Regex(@"<date_pub:(?<format>.*)>"), Video.Date },
|
||||
{ new Regex(@"<date_now:(?<format>.*)>"), DateTime.Now },
|
||||
{ new Regex(@"<duration:(?<format>.*)>"), Video.Duration },
|
||||
};
|
||||
foreach (KeyValuePair<Regex, IFormattable> template in filenameFormatTemplates)
|
||||
{
|
||||
foreach (Match templateMatch in template.Key.Matches(temporaryFilename))
|
||||
{
|
||||
temporaryFilename = temporaryFilename.Replace(templateMatch.Value, template.Value.ToString(templateMatch.Groups["format"].Value, null));
|
||||
}
|
||||
}
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
temporaryFilename = temporaryFilename.Replace(c, ' ');
|
||||
}
|
||||
Filename = temporaryFilename;
|
||||
FileSettingControlFilenameTextBox.Text = Filename;
|
||||
|
||||
Schedule = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private IVideo Video { get; set; }
|
||||
|
||||
public MediaType MediaType { get; private set; }
|
||||
public BaseStream Stream { get; private set; }
|
||||
public TimeSpan TrimStart { get; private set; }
|
||||
public TimeSpan TrimEnd { get; private set; }
|
||||
public string Filename { get; private set; }
|
||||
public MediaFileExtension FileExtension { get; private set; }
|
||||
public StorageFolder FileLocation { get; private set; }
|
||||
public double Schedule { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public void ChangeLocation(StorageFolder location)
|
||||
{
|
||||
FileLocation = location;
|
||||
FileLocationSettingControl.Description = FileLocation.Path ?? $@"{UserDataPaths.GetDefault().Downloads}\VDownload";
|
||||
}
|
||||
|
||||
public void ChangeSchedule(double schedule)
|
||||
{
|
||||
Schedule = schedule;
|
||||
ScheduleSettingControlNumberBox.Value = Schedule;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
if (!(bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("last_media_location"))
|
||||
{
|
||||
FileLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("last_media_location");
|
||||
FileLocationSettingControl.Description = FileLocation.Path;
|
||||
}
|
||||
else if ((bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_media_location"))
|
||||
{
|
||||
FileLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("selected_media_location");
|
||||
FileLocationSettingControl.Description = FileLocation.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
FileLocation = null;
|
||||
FileLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private void MediaTypeSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
MediaType = (MediaType)((ComboBox)sender).SelectedIndex;
|
||||
if (((ComboBox)sender).SelectedIndex == (int)MediaType.OnlyAudio)
|
||||
{
|
||||
QualitySettingControl.Visibility = Visibility.Collapsed;
|
||||
QualitySettingControlComboBox.SelectedIndex = Video.BaseStreams.Count() - 1;
|
||||
|
||||
FileSettingControlFileExtensionComboBox.Items.Clear();
|
||||
foreach (AudioFileExtension extension in Enum.GetValues(typeof(AudioFileExtension)))
|
||||
{
|
||||
FileSettingControlFileExtensionComboBox.Items.Add(extension);
|
||||
}
|
||||
FileSettingControlFileExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_audio_extension") - 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
QualitySettingControl.Visibility = Visibility.Visible;
|
||||
QualitySettingControlComboBox.SelectedIndex = 0;
|
||||
|
||||
FileSettingControlFileExtensionComboBox.Items.Clear();
|
||||
foreach (VideoFileExtension extension in Enum.GetValues(typeof(VideoFileExtension)))
|
||||
{
|
||||
FileSettingControlFileExtensionComboBox.Items.Add(extension);
|
||||
}
|
||||
FileSettingControlFileExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_video_extension");
|
||||
}
|
||||
}
|
||||
|
||||
private void QualitySettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
Stream = Video.BaseStreams[((ComboBox)sender).SelectedIndex];
|
||||
}
|
||||
|
||||
private void TrimSettingControlStartTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!((TextBox)sender).Text.Contains('_'))
|
||||
{
|
||||
string[] segments = ((TextBox)sender).Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < Video.Duration && parsedTimeSpan > TimeSpan.Zero) TrimStart = parsedTimeSpan;
|
||||
else
|
||||
{
|
||||
TrimStart = TimeSpan.Zero;
|
||||
((TextBox)sender).Text = TrimStart.ToStringOptTHMMBaseSS(Video.Duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TrimSettingControlEndTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!((TextBox)sender).Text.Contains('_'))
|
||||
{
|
||||
string[] segments = ((TextBox)sender).Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < Video.Duration && parsedTimeSpan > TimeSpan.Zero) TrimEnd = parsedTimeSpan;
|
||||
else
|
||||
{
|
||||
TrimEnd = Video.Duration;
|
||||
((TextBox)sender).Text = TrimEnd.ToStringOptTHMMBaseSS();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FileSettingControlFilenameTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
((TextBox)sender).Text = ((TextBox)sender).Text.Replace(c, ' ');
|
||||
}
|
||||
Filename = ((TextBox)sender).Text;
|
||||
}
|
||||
|
||||
private void FileSettingControlFileExtensionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
FileExtension = (MediaFileExtension)((ComboBox)sender).SelectedIndex + (MediaType == MediaType.OnlyAudio ? 3 : 0);
|
||||
}
|
||||
|
||||
private async void FileSettingControlFileLocationBrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FolderPicker picker = new FolderPicker
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.Downloads
|
||||
};
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
StorageFolder selectedFolder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (selectedFolder != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await(await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync();
|
||||
StorageApplicationPermissions.FutureAccessList.AddOrReplace("last_media_location", selectedFolder);
|
||||
FileLocation = selectedFolder;
|
||||
FileLocationSettingControl.Description = FileLocation.Path;
|
||||
}
|
||||
catch (UnauthorizedAccessException) { }
|
||||
}
|
||||
}
|
||||
|
||||
private void ScheduleSettingControlNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (((NumberBox)sender).Value == double.NaN) ((NumberBox)sender).Value = 0;
|
||||
Schedule = ((NumberBox)sender).Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
30
VDownload/Views/Home/Controls/VideoSearchControl.xaml
Normal file
30
VDownload/Views/Home/Controls/VideoSearchControl.xaml
Normal file
@@ -0,0 +1,30 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.Controls.VideoSearchControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="100"
|
||||
d:DesignWidth="400">
|
||||
|
||||
|
||||
<Grid x:Name="Grid" Margin="10" ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="UrlTextBox" x:Uid="Home_OptionsBar_VideoSearchControl_UrlTextBox" Grid.Column="0" VerticalAlignment="Center"/>
|
||||
<Button x:Name="SearchButton" x:Uid="Home_OptionsBar_VideoSearchControl_SearchButton" Grid.Column="1" Click="SearchButton_Click"/>
|
||||
<Button x:Name="HelpButton" Grid.Column="2" Content="?" Click="HelpButton_Click">
|
||||
<Button.Resources>
|
||||
<muxc:TeachingTip x:Name="InfoBox" x:Uid="Home_OptionsBar_VideoSearchControl_InfoBox" Target="{x:Bind Grid}"/>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<ContentPresenter x:Name="StatusControlPresenter" Margin="-10,0,0,0" Grid.Column="3"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
140
VDownload/Views/Home/Controls/VideoSearchControl.xaml.cs
Normal file
140
VDownload/Views/Home/Controls/VideoSearchControl.xaml.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Services.Sources;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home.Controls
|
||||
{
|
||||
public sealed partial class VideoSearchControl : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private static readonly ResourceDictionary IconRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml") };
|
||||
|
||||
private static readonly Microsoft.UI.Xaml.Controls.ProgressRing StatusControlProgressRing = new Microsoft.UI.Xaml.Controls.ProgressRing { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), IsActive = true };
|
||||
private static readonly Image StatusControlErrorImage = new Image { Width = 15, Height = 15, Margin = new Thickness(15, 5, 5, 5), Source = (SvgImageSource)IconRes["ErrorIcon"] };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public VideoSearchControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private async void SearchButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
async Task ShowDialog(string localErrorKey)
|
||||
{
|
||||
ContentDialog errorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_OptionsBar_VideoSearchControl_Base_Title"),
|
||||
Content = ResourceLoader.GetForCurrentView("DialogResources").GetString($"Home_OptionsBar_VideoSearchControl_{localErrorKey}_Content"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CloseButtonText"),
|
||||
};
|
||||
await errorDialog.ShowAsync();
|
||||
StatusControlPresenter.Content = StatusControlErrorImage;
|
||||
}
|
||||
|
||||
SearchButtonClicked.Invoke(this, EventArgs.Empty);
|
||||
|
||||
InfoBox.IsOpen = false;
|
||||
|
||||
StatusControlPresenter.Content = StatusControlProgressRing;
|
||||
|
||||
IVideo video = Source.GetVideo(UrlTextBox.Text);
|
||||
|
||||
if (video == null)
|
||||
{
|
||||
StatusControlPresenter.Content = StatusControlErrorImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
await video.GetMetadataAsync(CancellationToken);
|
||||
await video.GetStreamsAsync(CancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
StatusControlPresenter.Content = null;
|
||||
return;
|
||||
}
|
||||
catch (MediaNotFoundException)
|
||||
{
|
||||
StatusControlPresenter.Content = StatusControlErrorImage;
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotFoundException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotFound");
|
||||
return;
|
||||
}
|
||||
catch (TwitchAccessTokenNotValidException)
|
||||
{
|
||||
await ShowDialog("TwitchAccessTokenNotValid");
|
||||
return;
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
await ShowDialog("InternetNotAvailable");
|
||||
return;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
|
||||
StatusControlPresenter.Content = null;
|
||||
|
||||
SearchingSuccessed?.Invoke(this, new VideoSearchSuccessedEventArgs(video));
|
||||
}
|
||||
}
|
||||
|
||||
private void HelpButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Switch info box
|
||||
InfoBox.IsOpen = !InfoBox.IsOpen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<VideoSearchSuccessedEventArgs> SearchingSuccessed;
|
||||
public event EventHandler SearchButtonClicked;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
<Page
|
||||
x:Class="VDownload.Views.Home.HomeMain"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
mc:Ignorable="d"
|
||||
Background="Transparent">
|
||||
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="Compact">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="HomeOptionBarButtonsStackPanel.(Grid.ColumnSpan)" Value="4"/>
|
||||
<Setter Target="HomeOptionBarButtonsStackPanel.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeOptionsBarAddingControl.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeOptionsBarAddingControl.(Grid.ColumnSpan)" Value="3"/>
|
||||
<Setter Target="HomeOptionsBarSearchingStatusControl.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeOptionsBarSearchingStatusControl.(Grid.Column)" Value="3"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger x:Name="HomeNormalStateAdaptiveTrigger" MinWindowWidth="800"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="HomeOptionBarButtonsStackPanel.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeOptionBarButtonsStackPanel.(Grid.Column)" Value="3"/>
|
||||
<Setter Target="HomeOptionsBarAddingControl.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="HomeOptionsBarAddingControl.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeOptionsBarSearchingStatusControl.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="HomeOptionsBarSearchingStatusControl.(Grid.Column)" Value="1"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="HomeTasksListRow" Height="1*"/>
|
||||
<RowDefinition x:Name="HomeOptionBarAndAddingPanelRow" Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- VIDEOS LIST -->
|
||||
<ScrollViewer Margin="0,0,0,10" CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<ContentControl x:Name="HomeTasksListParent" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- OPTIONS BAR AND ADDING PANEL -->
|
||||
<Grid Grid.Row="1" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource HomeBackgroundColor}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ContentControl x:Name="HomeAddingPanel" Grid.Row="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/> <!-- Adding Panel -->
|
||||
<Grid x:Name="HomeOptionsBar" Grid.Row="1" Background="{ThemeResource HomeOptionBarBackgroundColor}"> <!-- Options Bar -->
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter x:Name="HomeOptionsBarAddingControl" Grid.Row="1" Grid.Column="0"/>
|
||||
<ContentPresenter x:Name="HomeOptionsBarSearchingStatusControl" Grid.Row="1" Grid.Column="1"/>
|
||||
<StackPanel x:Name="HomeOptionBarButtonsStackPanel" Grid.Row="1" Grid.Column="3" Margin="3,0,3,0" HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<AppBarButton x:Name="HomeOptionsBarLoadSubscripionsButton" x:Uid="HomeOptionsBarLoadSubscripionsButton" Icon="Favorite" Width="120"/>
|
||||
<AppBarSeparator VerticalAlignment="Center" Height="50"/>
|
||||
<AppBarToggleButton x:Name="HomeOptionsBarPlaylistSearchButton" x:Uid="HomeOptionsBarPlaylistSearchButton" Icon="List" Width="85" Checked="HomeOptionsBarPlaylistSearchButton_Checked" Unchecked="HomeOptionsBarAddingButtons_Unchecked"/>
|
||||
<AppBarToggleButton x:Name="HomeOptionsBarVideoSearchButton" x:Uid="HomeOptionsBarVideoSearchButton" Icon="Video" Width="75" Checked="HomeOptionsBarVideoSearchButton_Checked" Unchecked="HomeOptionsBarAddingButtons_Unchecked"/>
|
||||
<AppBarSeparator VerticalAlignment="Center" Height="50"/>
|
||||
<AppBarButton x:Name="HomeOptionsBarDownloadAllButton" x:Uid="HomeOptionsBarDownloadAllButton" Icon="Download" Width="90" Click="HomeOptionsBarDownloadAllButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,256 +0,0 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using VDownload.Views.Home.Controls;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class HomeMain : Page
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeMain()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set cancellation token
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// CANCELLATON TOKEN
|
||||
private CancellationTokenSource SearchingCancellationToken { get; set; }
|
||||
|
||||
// HOME TASKS LIST
|
||||
private static ContentControl HomeTasksListCurrentParent = null;
|
||||
private static StackPanel HomeTasksList = null;
|
||||
public static List<HomeTaskPanel> TasksList = new List<HomeTaskPanel>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// ON NAVIGATED TO
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
// Set current panel
|
||||
HomeTasksListCurrentParent = HomeTasksListParent;
|
||||
|
||||
// Detach task panels from old task list
|
||||
if (HomeTasksList != null) HomeTasksList.Children.Clear();
|
||||
|
||||
// Create new task list
|
||||
HomeTasksList = new StackPanel { Spacing = 10 };
|
||||
|
||||
// Attach task panels to new task list
|
||||
if (TasksList.Count > 0)
|
||||
{
|
||||
foreach (HomeTaskPanel homeVideoPanel in TasksList) HomeTasksList.Children.Add(homeVideoPanel);
|
||||
HomeTasksListCurrentParent.Content = HomeTasksList;
|
||||
}
|
||||
else
|
||||
{
|
||||
HomeTasksListCurrentParent.Content = new HomeTasksListPlaceholder();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SEARCH VIDEO BUTTON CHECKED
|
||||
private void HomeOptionsBarVideoSearchButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Uncheck playlist button
|
||||
HomeOptionsBarPlaylistSearchButton.IsChecked = false;
|
||||
|
||||
// Create video adding control
|
||||
HomeOptionsBarVideoSearch homeOptionsBarSearchVideoControl = new HomeOptionsBarVideoSearch();
|
||||
homeOptionsBarSearchVideoControl.VideoSearchSuccessed += HomeOptionsBarVideoSearchControl_VideoSearchSuccessed;
|
||||
homeOptionsBarSearchVideoControl.SearchButtonClick += (s, a) =>
|
||||
{
|
||||
SearchingCancellationToken.Cancel();
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
homeOptionsBarSearchVideoControl.CancellationToken = SearchingCancellationToken.Token;
|
||||
};
|
||||
HomeOptionsBarAddingControl.Content = homeOptionsBarSearchVideoControl;
|
||||
}
|
||||
|
||||
// VIDEO SEARCH SUCCESSED
|
||||
private void HomeOptionsBarVideoSearchControl_VideoSearchSuccessed(object sender, VideoSearchSuccessedEventArgs e)
|
||||
{
|
||||
// Set UI
|
||||
HomeOptionBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
HomeTasksListRow.Height = new GridLength(0);
|
||||
|
||||
// Open adding panel
|
||||
HomeVideoAddingPanel addingPanel = new HomeVideoAddingPanel(e.Video);
|
||||
addingPanel.TasksAddingRequested += HomeTasksAddingRequest;
|
||||
HomeAddingPanel.Content = addingPanel;
|
||||
}
|
||||
|
||||
|
||||
// SEARCH PLAYLIST BUTTON CHECKED
|
||||
private void HomeOptionsBarPlaylistSearchButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Uncheck video button
|
||||
HomeOptionsBarVideoSearchButton.IsChecked = false;
|
||||
|
||||
// Create playlist adding control
|
||||
HomeOptionsBarPlaylistSearch homeOptionsBarPlaylistSearchControl = new HomeOptionsBarPlaylistSearch();
|
||||
homeOptionsBarPlaylistSearchControl.PlaylistSearchSuccessed += HomeOptionsBarPlaylistSearchControl_PlaylistSearchSuccessed;
|
||||
homeOptionsBarPlaylistSearchControl.SearchButtonClick += (s, a) =>
|
||||
{
|
||||
SearchingCancellationToken.Cancel();
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
homeOptionsBarPlaylistSearchControl.CancellationToken = SearchingCancellationToken.Token;
|
||||
};
|
||||
HomeOptionsBarAddingControl.Content = homeOptionsBarPlaylistSearchControl;
|
||||
}
|
||||
|
||||
// PLAYLIST SEARCH SUCCESSED
|
||||
private void HomeOptionsBarPlaylistSearchControl_PlaylistSearchSuccessed(object sender, PlaylistSearchSuccessedEventArgs e)
|
||||
{
|
||||
// Set UI
|
||||
HomeOptionBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
HomeTasksListRow.Height = new GridLength(0);
|
||||
|
||||
// Open adding panel
|
||||
HomePlaylistAddingPanel addingPanel = new HomePlaylistAddingPanel(e.PlaylistService);
|
||||
addingPanel.TasksAddingRequested += HomeTasksAddingRequest;
|
||||
HomeAddingPanel.Content = addingPanel;
|
||||
}
|
||||
|
||||
|
||||
// TASK ADDING REQUEST
|
||||
private void HomeTasksAddingRequest(object sender, TasksAddingRequestedEventArgs e)
|
||||
{
|
||||
// Replace placeholder
|
||||
HomeTasksListCurrentParent.Content = HomeTasksList;
|
||||
|
||||
// Uncheck button
|
||||
switch (e.RequestSource)
|
||||
{
|
||||
case TaskAddingRequestSource.Video: HomeOptionsBarVideoSearchButton.IsChecked = false; break;
|
||||
case TaskAddingRequestSource.Playlist: HomeOptionsBarPlaylistSearchButton.IsChecked = false; break;
|
||||
}
|
||||
|
||||
// Create video tasks
|
||||
foreach (TaskData taskData in e.TaskData)
|
||||
{
|
||||
HomeTaskPanel taskPanel = new HomeTaskPanel(taskData);
|
||||
|
||||
taskPanel.TaskRemovingRequested += (s, a) =>
|
||||
{
|
||||
// Remove task from tasks lists
|
||||
TasksList.Remove(taskPanel);
|
||||
HomeTasksList.Children.Remove(taskPanel);
|
||||
if (TasksList.Count <= 0) HomeTasksListCurrentParent.Content = new HomeTasksListPlaceholder();
|
||||
};
|
||||
|
||||
// Add task to tasks lists
|
||||
HomeTasksList.Children.Add(taskPanel);
|
||||
TasksList.Add(taskPanel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TASK ADDING CANCELLED
|
||||
private void HomeSearchingCancelled()
|
||||
{
|
||||
// Cancel searching operations
|
||||
SearchingCancellationToken.Cancel();
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
|
||||
// Set grid dimensions
|
||||
HomeOptionBarAndAddingPanelRow.Height = GridLength.Auto;
|
||||
HomeTasksListRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
|
||||
// Clear panels
|
||||
HomeAddingPanel.Content = null;
|
||||
HomeOptionsBarAddingControl.Content = null;
|
||||
HomeOptionsBarSearchingStatusControl.Content = null;
|
||||
}
|
||||
|
||||
|
||||
// ADDING BUTTONS UNCHECKED
|
||||
private void HomeOptionsBarAddingButtons_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
HomeSearchingCancelled();
|
||||
}
|
||||
|
||||
|
||||
// DOWNLOAD ALL BUTTON CLICKED
|
||||
private async void HomeOptionsBarDownloadAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
HomeTaskPanel[] idleTasks = TasksList.Where((HomeTaskPanel video) => video.Status == Core.Enums.TaskStatus.Idle).ToArray();
|
||||
if (idleTasks.Count() > 0)
|
||||
{
|
||||
bool delay = (bool)Config.GetValue("delay_task_when_queued_task_starts_on_metered_network");
|
||||
if (NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)
|
||||
{
|
||||
ContentDialogResult dialogResult = await new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("HomeDownloadAllButtonMeteredConnectionDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("HomeDownloadAllButtonMeteredConnectionDialogDescription"),
|
||||
PrimaryButtonText = ResourceLoader.GetForCurrentView().GetString("HomeDownloadAllButtonMeteredConnectionDialogStartAndDelayText"),
|
||||
SecondaryButtonText = ResourceLoader.GetForCurrentView().GetString("HomeDownloadAllButtonMeteredConnectionDialogStartWithoutDelayText"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("HomeDownloadAllButtonMeteredConnectionDialogCancel"),
|
||||
}.ShowAsync();
|
||||
switch (dialogResult)
|
||||
{
|
||||
case ContentDialogResult.Primary: delay = true; break;
|
||||
case ContentDialogResult.Secondary: delay = false; break;
|
||||
case ContentDialogResult.None: return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (HomeTaskPanel videoPanel in idleTasks)
|
||||
{
|
||||
await Task.Delay(1);
|
||||
|
||||
#pragma warning disable CS4014
|
||||
videoPanel.Start(delay);
|
||||
#pragma warning restore CS4014
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region METHODS
|
||||
|
||||
// WAIT IN QUEUE
|
||||
public static async Task WaitInQueue(bool delayWhenOnMeteredConnection, CancellationToken token)
|
||||
{
|
||||
while ((TasksList.Where((HomeTaskPanel task) => task.Status == Core.Enums.TaskStatus.InProgress).Count() >= (int)Config.GetValue("max_active_video_task") || (delayWhenOnMeteredConnection && NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)) && !token.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,425 +0,0 @@
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Converters;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using VDownload.Views.Home.Controls;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class HomePlaylistAddingPanel : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomePlaylistAddingPanel(IPlaylist playlistService)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set playlist service object
|
||||
PlaylistService = playlistService;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// BASE PLAYLIST DATA
|
||||
private IPlaylist PlaylistService { get; set; }
|
||||
|
||||
// PLAYLIST DATA
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Name { get; set; }
|
||||
|
||||
// APPLY TO ALL OPTIONS
|
||||
public StorageFolder ATLLocation { get; set; }
|
||||
public double ATLSchedule { get; set; }
|
||||
|
||||
// DELETED VIDEOS
|
||||
public List<HomeSerialAddingVideoPanel> DeletedVideos = new List<HomeSerialAddingVideoPanel>();
|
||||
public List<HomeSerialAddingVideoPanel> HiddenVideos = new List<HomeSerialAddingVideoPanel>();
|
||||
|
||||
// FILTER MIN MAX
|
||||
private long MinViews { get; set; }
|
||||
private long MaxViews { get; set; }
|
||||
private DateTime MinDate { get; set; }
|
||||
private DateTime MaxDate { get; set; }
|
||||
private TimeSpan MinDuration { get; set; }
|
||||
private TimeSpan MaxDuration { get; set; }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
// ON CONTROL LOADING
|
||||
private async void HomePlaylistAddingPanel_Loading(FrameworkElement sender, object args)
|
||||
{
|
||||
// Set metadata
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{PlaylistService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Name = PlaylistService.Name;
|
||||
|
||||
// Add videos to list and search mins and maxes
|
||||
MinViews = PlaylistService.Videos[0].Metadata.Views;
|
||||
MaxViews = PlaylistService.Videos[0].Metadata.Views;
|
||||
MinDate = PlaylistService.Videos[0].Metadata.Date;
|
||||
MaxDate = PlaylistService.Videos[0].Metadata.Date;
|
||||
MinDuration = PlaylistService.Videos[0].Metadata.Duration;
|
||||
MaxDuration = PlaylistService.Videos[0].Metadata.Duration;
|
||||
foreach (IVideo video in PlaylistService.Videos)
|
||||
{
|
||||
// Set mins and maxes
|
||||
if (video.Metadata.Views < MinViews) MinViews = video.Metadata.Views;
|
||||
if (video.Metadata.Views > MaxViews) MaxViews = video.Metadata.Views;
|
||||
if (video.Metadata.Date < MinDate) MinDate = video.Metadata.Date;
|
||||
if (video.Metadata.Date > MaxDate) MaxDate = video.Metadata.Date;
|
||||
if (video.Metadata.Duration < MinDuration) MinDuration = video.Metadata.Duration;
|
||||
if (video.Metadata.Duration > MaxDuration) MaxDuration = video.Metadata.Duration;
|
||||
|
||||
// Add videos to list
|
||||
HomeSerialAddingVideoPanel videoPanel = new HomeSerialAddingVideoPanel(video);
|
||||
videoPanel.DeleteRequested += (s, a) =>
|
||||
{
|
||||
DeletedVideos.Add(videoPanel);
|
||||
HomePlaylistAddingPanelFilterRemovedTextBlock.Visibility = Visibility.Visible;
|
||||
HomePlaylistAddingPanelFilterRemovedCountTextBlock.Visibility = Visibility.Visible;
|
||||
HomePlaylistAddingPanelFilterRemovedRestoreButton.Visibility = Visibility.Visible;
|
||||
HomePlaylistAddingPanelFilterRemovedCountTextBlock.Text = $"{DeletedVideos.Count}";
|
||||
HomePlaylistAddingPanelFilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterHeaderCountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : "";
|
||||
HomePlaylistAddingPanelVideosList.Children.Remove(videoPanel);
|
||||
};
|
||||
HomePlaylistAddingPanelVideosList.Children.Add(videoPanel);
|
||||
}
|
||||
|
||||
// Set apply to all location option
|
||||
if (!(bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("last_media_location"))
|
||||
{
|
||||
ATLLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("last_media_location");
|
||||
HomePlaylistAddingApplyToAllLocationSettingControl.Description = ATLLocation.Path;
|
||||
}
|
||||
else if ((bool)Config.GetValue("custom_media_location") && StorageApplicationPermissions.FutureAccessList.ContainsItem("custom_media_location"))
|
||||
{
|
||||
ATLLocation = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync("selected_media_location");
|
||||
HomePlaylistAddingApplyToAllLocationSettingControl.Description = ATLLocation.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
ATLLocation = null;
|
||||
HomePlaylistAddingApplyToAllLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload";
|
||||
}
|
||||
|
||||
// Set apply to all schedule option
|
||||
ATLSchedule = 0;
|
||||
|
||||
// Set title and author filter option
|
||||
HomePlaylistAddingPanelFilterTitleTextBox.PlaceholderText = ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterTitleTextBoxPlaceholderText");
|
||||
HomePlaylistAddingPanelFilterAuthorTextBox.PlaceholderText = ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterAuthorTextBoxPlaceholderText");
|
||||
|
||||
// Set views filter option
|
||||
HomePlaylistAddingPanelFilterMinViewsNumberBox.Minimum = HomePlaylistAddingPanelFilterMaxViewsNumberBox.Minimum = HomePlaylistAddingPanelFilterMinViewsNumberBox.Value = MinViews;
|
||||
HomePlaylistAddingPanelFilterMinViewsNumberBox.Maximum = HomePlaylistAddingPanelFilterMaxViewsNumberBox.Maximum = HomePlaylistAddingPanelFilterMaxViewsNumberBox.Value = MaxViews;
|
||||
|
||||
// Set date filter option
|
||||
HomePlaylistAddingPanelFilterMinDateDatePicker.MinDate = HomePlaylistAddingPanelFilterMaxDateDatePicker.MinDate = (DateTimeOffset)(HomePlaylistAddingPanelFilterMinDateDatePicker.Date = MinDate);
|
||||
HomePlaylistAddingPanelFilterMinDateDatePicker.MaxDate = HomePlaylistAddingPanelFilterMaxDateDatePicker.MaxDate = (DateTimeOffset)(HomePlaylistAddingPanelFilterMaxDateDatePicker.Date = MaxDate);
|
||||
|
||||
// Set duration filter option
|
||||
TextBoxExtensions.SetMask(HomePlaylistAddingPanelFilterMinDurationTextBox, (string)new TimeSpanToTextBoxMaskConverter().Convert(MaxDuration, null, null, null));
|
||||
TextBoxExtensions.SetMask(HomePlaylistAddingPanelFilterMaxDurationTextBox, (string)new TimeSpanToTextBoxMaskConverter().Convert(MaxDuration, null, null, null));
|
||||
HashSet<int> maskElements = new HashSet<int>();
|
||||
foreach (TimeSpan ts in new List<TimeSpan> { MinDuration, MaxDuration })
|
||||
{
|
||||
if (Math.Floor(ts.TotalHours) > 0) maskElements.Add(int.Parse(Math.Floor(ts.TotalHours).ToString()[0].ToString()));
|
||||
if (Math.Floor(ts.TotalMinutes) > 0)
|
||||
{
|
||||
if (Math.Floor(ts.TotalHours) > 0) maskElements.Add(5);
|
||||
else maskElements.Add(int.Parse(ts.Minutes.ToString()[0].ToString()));
|
||||
}
|
||||
if (Math.Floor(ts.TotalMinutes) > 0) maskElements.Add(5);
|
||||
else maskElements.Add(int.Parse(ts.Seconds.ToString()[0].ToString()));
|
||||
}
|
||||
List<string> maskElementsString = new List<string>();
|
||||
foreach (int i in maskElements)
|
||||
{
|
||||
if (i != 9) maskElementsString.Add($"{i}:[0-{i}]");
|
||||
}
|
||||
TextBoxExtensions.SetCustomMask(HomePlaylistAddingPanelFilterMinDurationTextBox, string.Join(',', maskElementsString));
|
||||
TextBoxExtensions.SetCustomMask(HomePlaylistAddingPanelFilterMaxDurationTextBox, string.Join(',', maskElementsString));
|
||||
HomePlaylistAddingPanelFilterMinDurationTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(MinDuration, MaxDuration);
|
||||
HomePlaylistAddingPanelFilterMaxDurationTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(MaxDuration);
|
||||
}
|
||||
|
||||
// FILTER CHANGED
|
||||
private void FilterChanged()
|
||||
{
|
||||
// Min duration
|
||||
string[] minSegments = HomePlaylistAddingPanelFilterMinDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(minSegments.ElementAtOrDefault(0), out int minSeconds);
|
||||
int.TryParse(minSegments.ElementAtOrDefault(1), out int minMinutes);
|
||||
int.TryParse(minSegments.ElementAtOrDefault(2), out int minHours);
|
||||
TimeSpan minDuration = new TimeSpan(minHours, minMinutes, minSeconds);
|
||||
|
||||
// Max duration
|
||||
string[] maxSegments = HomePlaylistAddingPanelFilterMaxDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(maxSegments.ElementAtOrDefault(0), out int maxSeconds);
|
||||
int.TryParse(maxSegments.ElementAtOrDefault(1), out int maxMinutes);
|
||||
int.TryParse(maxSegments.ElementAtOrDefault(2), out int maxHours);
|
||||
TimeSpan maxDuration = new TimeSpan(maxHours, maxMinutes, maxSeconds);
|
||||
|
||||
// Title and author regex
|
||||
Regex titleRegex = new Regex("");
|
||||
Regex authorRegex = new Regex("");
|
||||
try
|
||||
{
|
||||
titleRegex = new Regex(HomePlaylistAddingPanelFilterTitleTextBox.Text);
|
||||
authorRegex = new Regex(HomePlaylistAddingPanelFilterAuthorTextBox.Text);
|
||||
}
|
||||
catch (ArgumentException) { }
|
||||
|
||||
List<HomeSerialAddingVideoPanel> allVideos = new List<HomeSerialAddingVideoPanel>();
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in HomePlaylistAddingPanelVideosList.Children) allVideos.Add(videoPanel);
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in HiddenVideos) allVideos.Add(videoPanel);
|
||||
HomePlaylistAddingPanelVideosList.Children.Clear();
|
||||
HiddenVideos.Clear();
|
||||
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in allVideos)
|
||||
{
|
||||
if (
|
||||
!titleRegex.IsMatch(videoPanel.VideoService.Metadata.Title) ||
|
||||
!authorRegex.IsMatch(videoPanel.VideoService.Metadata.Author) ||
|
||||
HomePlaylistAddingPanelFilterMinViewsNumberBox.Value > videoPanel.VideoService.Metadata.Views ||
|
||||
HomePlaylistAddingPanelFilterMaxViewsNumberBox.Value < videoPanel.VideoService.Metadata.Views ||
|
||||
HomePlaylistAddingPanelFilterMinDateDatePicker.Date > videoPanel.VideoService.Metadata.Date ||
|
||||
HomePlaylistAddingPanelFilterMaxDateDatePicker.Date < videoPanel.VideoService.Metadata.Date ||
|
||||
minDuration > videoPanel.VideoService.Metadata.Duration ||
|
||||
maxDuration < videoPanel.VideoService.Metadata.Duration
|
||||
) HiddenVideos.Add(videoPanel);
|
||||
else HomePlaylistAddingPanelVideosList.Children.Add(videoPanel);
|
||||
}
|
||||
|
||||
HomePlaylistAddingPanelFilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterHeaderCountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : "";
|
||||
}
|
||||
|
||||
// ATL LOCATION BROWSE BUTTON CLICKED
|
||||
private async void HomePlaylistAddingApplyToAllLocationBrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FolderPicker picker = new FolderPicker
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.Downloads
|
||||
};
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
StorageFolder selectedFolder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (selectedFolder != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await(await selectedFolder.CreateFileAsync("VDownloadLocationAccessTest")).DeleteAsync();
|
||||
StorageApplicationPermissions.FutureAccessList.AddOrReplace("last_media_location", selectedFolder);
|
||||
ATLLocation = selectedFolder;
|
||||
HomePlaylistAddingApplyToAllLocationSettingControl.Description = ATLLocation.Path;
|
||||
}
|
||||
catch (UnauthorizedAccessException) { }
|
||||
}
|
||||
}
|
||||
|
||||
// APPLY ATL LOCATION BUTTON CLICKED
|
||||
private void HomePlaylistAddingApplyToAllApplyLocationButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in HomePlaylistAddingPanelVideosList.Children)
|
||||
{
|
||||
videoPanel.HomeVideoAddingOptionsControl.ChangeLocation(ATLLocation);
|
||||
}
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in DeletedVideos)
|
||||
{
|
||||
videoPanel.HomeVideoAddingOptionsControl.ChangeLocation(ATLLocation);
|
||||
}
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in HiddenVideos)
|
||||
{
|
||||
videoPanel.HomeVideoAddingOptionsControl.ChangeLocation(ATLLocation);
|
||||
}
|
||||
}
|
||||
|
||||
// ATL SCHEDULE NUMBERBOX VALUE CHANGED
|
||||
private void HomePlaylistAddingApplyToAllScheduleNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(HomePlaylistAddingApplyToAllScheduleNumberBox.Value)) HomePlaylistAddingApplyToAllScheduleNumberBox.Value = ATLSchedule;
|
||||
else ATLSchedule = HomePlaylistAddingApplyToAllScheduleNumberBox.Value;
|
||||
}
|
||||
|
||||
// APPLY ATL SCHEDULE BUTTON CLICKED
|
||||
private void HomePlaylistAddingApplyToAllApplyScheduleButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in HomePlaylistAddingPanelVideosList.Children)
|
||||
{
|
||||
videoPanel.HomeVideoAddingOptionsControl.ChangeSchedule(ATLSchedule);
|
||||
}
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in DeletedVideos)
|
||||
{
|
||||
videoPanel.HomeVideoAddingOptionsControl.ChangeSchedule(ATLSchedule);
|
||||
}
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in HiddenVideos)
|
||||
{
|
||||
videoPanel.HomeVideoAddingOptionsControl.ChangeSchedule(ATLSchedule);
|
||||
}
|
||||
}
|
||||
|
||||
// TITLE AND AUTHOR FILTERS TEXTBOXS TEXT CHANGED
|
||||
private void HomePlaylistAddingPanelFilterTitleAndAuthorTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
// MIN VIEWS FILTERS NUMBERBOX VALUE CHANGED
|
||||
private void HomePlaylistAddingPanelFilterMinViewsNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(HomePlaylistAddingPanelFilterMinViewsNumberBox.Value)) HomePlaylistAddingPanelFilterMinViewsNumberBox.Value = MinViews;
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
// MAX VIEWS FILTERS NUMBERBOX VALUE CHANGED
|
||||
private void HomePlaylistAddingPanelFilterMaxViewsNumberBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (double.IsNaN(HomePlaylistAddingPanelFilterMaxViewsNumberBox.Value)) HomePlaylistAddingPanelFilterMaxViewsNumberBox.Value = MaxViews;
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
// MIN AND MAX DATE FILTERS DATEPICKER VALUE CHANGED
|
||||
private void HomePlaylistAddingPanelFilterMinAndMaxDateDatePicker_LostFocus(object sender, RoutedEventArgs args)
|
||||
{
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
// MIN DURATION FILTERS TEXTBOX VALUE CHANGED
|
||||
private void HomePlaylistAddingPanelFilterMinDurationTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!HomePlaylistAddingPanelFilterMinDurationTextBox.Text.Contains('_'))
|
||||
{
|
||||
string[] segments = HomePlaylistAddingPanelFilterMinDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < MinDuration || parsedTimeSpan > MaxDuration)
|
||||
{
|
||||
HomePlaylistAddingPanelFilterMinDurationTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(MinDuration, MaxDuration);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HomePlaylistAddingPanelFilterMinDurationTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(MinDuration, MaxDuration);
|
||||
}
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
// MAX DURATION FILTERS TEXTBOX VALUE CHANGED
|
||||
private void HomePlaylistAddingPanelFilterMaxDurationTextBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!HomePlaylistAddingPanelFilterMaxDurationTextBox.Text.Contains('_'))
|
||||
{
|
||||
string[] segments = HomePlaylistAddingPanelFilterMaxDurationTextBox.Text.Split(':').Reverse().ToArray();
|
||||
int.TryParse(segments.ElementAtOrDefault(0), out int seconds);
|
||||
int.TryParse(segments.ElementAtOrDefault(1), out int minutes);
|
||||
int.TryParse(segments.ElementAtOrDefault(2), out int hours);
|
||||
|
||||
TimeSpan parsedTimeSpan = new TimeSpan(hours, minutes, seconds);
|
||||
|
||||
if (parsedTimeSpan < MinDuration || parsedTimeSpan > MaxDuration)
|
||||
{
|
||||
HomePlaylistAddingPanelFilterMaxDurationTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(MaxDuration);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HomePlaylistAddingPanelFilterMaxDurationTextBox.Text = TimeSpanCustomFormat.ToOptTHMMBaseSS(MaxDuration);
|
||||
}
|
||||
FilterChanged();
|
||||
}
|
||||
|
||||
// RESTORE REMOVED VIDEOS BUTTON CLICKED
|
||||
private void HomePlaylistAddingPanelFilterRemovedRestoreButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in DeletedVideos)
|
||||
{
|
||||
HomePlaylistAddingPanelVideosList.Children.Add(videoPanel);
|
||||
}
|
||||
HomePlaylistAddingPanelFilterRemovedTextBlock.Visibility = Visibility.Collapsed;
|
||||
HomePlaylistAddingPanelFilterRemovedCountTextBlock.Visibility = Visibility.Collapsed;
|
||||
HomePlaylistAddingPanelFilterRemovedRestoreButton.Visibility = Visibility.Collapsed;
|
||||
HomePlaylistAddingPanelFilterHeaderCountTextBlock.Text = HiddenVideos.Count + DeletedVideos.Count > 0 ? $"{ResourceLoader.GetForCurrentView().GetString("HomePlaylistAddingPanelFilterHeaderCountTextBlockPrefix")}: {HiddenVideos.Count + DeletedVideos.Count}" : "";
|
||||
DeletedVideos.Clear();
|
||||
}
|
||||
|
||||
// SOURCE BUTTON CLICKED
|
||||
private async void HomePlaylistAddingPanelSourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Launch the website
|
||||
await Windows.System.Launcher.LaunchUriAsync(PlaylistService.Url);
|
||||
}
|
||||
|
||||
// ADD BUTTON CLICKED
|
||||
private void HomePlaylistAddingPanelAddButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Pack tasks data
|
||||
List<TaskData> taskDataList = new List<TaskData>();
|
||||
foreach (HomeSerialAddingVideoPanel videoPanel in HomePlaylistAddingPanelVideosList.Children)
|
||||
{
|
||||
TaskData taskData = new TaskData
|
||||
{
|
||||
VideoService = videoPanel.VideoService,
|
||||
TaskOptions = new TaskOptions()
|
||||
{
|
||||
MediaType = videoPanel.HomeVideoAddingOptionsControl.MediaType,
|
||||
Stream = videoPanel.HomeVideoAddingOptionsControl.Stream,
|
||||
TrimStart = videoPanel.HomeVideoAddingOptionsControl.TrimStart,
|
||||
TrimEnd = videoPanel.HomeVideoAddingOptionsControl.TrimEnd,
|
||||
Filename = videoPanel.HomeVideoAddingOptionsControl.Filename,
|
||||
Extension = videoPanel.HomeVideoAddingOptionsControl.Extension,
|
||||
Location = videoPanel.HomeVideoAddingOptionsControl.Location,
|
||||
Schedule = videoPanel.HomeVideoAddingOptionsControl.Schedule,
|
||||
}
|
||||
};
|
||||
taskDataList.Add(taskData);
|
||||
}
|
||||
|
||||
// Request tasks adding
|
||||
TasksAddingRequestedEventArgs eventArgs = new TasksAddingRequestedEventArgs
|
||||
{
|
||||
TaskData = taskDataList.ToArray(),
|
||||
RequestSource = TaskAddingRequestSource.Playlist
|
||||
};
|
||||
TasksAddingRequested?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<TasksAddingRequestedEventArgs> TasksAddingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.HomeVideoAddingPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ex="using:Microsoft.Toolkit.Uwp.UI"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
Loading="HomeVideoAddingPanel_Loading"
|
||||
mc:Ignorable="d">
|
||||
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<x:String x:Key="MetadataIconSize">16</x:String>
|
||||
<x:String x:Key="MetadataTextSize">12</x:String>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid Padding="10" RowSpacing="40" VerticalAlignment="Stretch">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState>
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.Width" Value="NaN"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.Column)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.Column)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.Column)" Value="3"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.Column)" Value="3"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataR1.Height" Value="Auto"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataR2.Height" Value="Auto"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataC1.Width" Value="1*"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataC2.Width" Value="1*"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState>
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="600"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelThumbnailImage.Width" Value="250"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.Column)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelTitleAndDetailedMetadataGrid.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelAuthorText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelViewsText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDateText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.Row)" Value="3"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.Row)" Value="3"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="HomeVideoAddingPanelDurationText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataR1.Height" Value="1*"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataR2.Height" Value="1*"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataC1.Width" Value="Auto"/>
|
||||
<Setter Target="HomeVideoAddingPanelDMetadataC2.Width" Value="Auto"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- METADATA PANEL -->
|
||||
<Grid Grid.Row="0" ColumnSpacing="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="HomeVideoAddingPanelThumbnailImage" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="2" Source="{x:Bind ThumbnailImage}"/> <!-- Thumbnail image -->
|
||||
<Grid x:Name="HomeVideoAddingPanelTitleAndDetailedMetadataGrid" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" FontSize="18" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind Title}"/>
|
||||
<AppBarButton Grid.Column="1" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="HomeVideoAddingPanelSourceButton_Click"/>
|
||||
<AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Click="HomeVideoAddingPanelAddButton_Click"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" ColumnSpacing="10" RowSpacing="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="HomeVideoAddingPanelDMetadataR1" Height="1*"/>
|
||||
<RowDefinition x:Name="HomeVideoAddingPanelDMetadataR2" Height="1*"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="HomeVideoAddingPanelDMetadataC1" Width="Auto"/>
|
||||
<ColumnDefinition x:Name="HomeVideoAddingPanelDMetadataC2" Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="HomeVideoAddingPanelAuthorIcon" Grid.Row="0" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource AuthorIcon}"/>
|
||||
<TextBlock x:Name="HomeVideoAddingPanelAuthorText" Grid.Row="0" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Author}"/>
|
||||
<Image x:Name="HomeVideoAddingPanelViewsIcon" Grid.Row="1" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource ViewsIcon}"/>
|
||||
<TextBlock x:Name="HomeVideoAddingPanelViewsText" Grid.Row="1" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Views}"/>
|
||||
<Image x:Name="HomeVideoAddingPanelDateIcon" Grid.Row="2" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DateIcon}"/>
|
||||
<TextBlock x:Name="HomeVideoAddingPanelDateText" Grid.Row="2" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Date}"/>
|
||||
<Image x:Name="HomeVideoAddingPanelDurationIcon" Grid.Row="3" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DurationIcon}"/>
|
||||
<TextBlock x:Name="HomeVideoAddingPanelDurationText" Grid.Row="3" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Duration}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Options PANEL -->
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<ContentControl x:Name="HomeVideoAddingOptionsControlParent" HorizontalContentAlignment="Stretch"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,136 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using VDownload.Views.Home.Controls;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class HomeVideoAddingPanel : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public HomeVideoAddingPanel(IVideo videoService)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set video service object
|
||||
VideoService = videoService;
|
||||
|
||||
// Set metadata
|
||||
ThumbnailImage = VideoService.Metadata.Thumbnail != null ? new BitmapImage { UriSource = VideoService.Metadata.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{VideoService.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Title = VideoService.Metadata.Title;
|
||||
Author = VideoService.Metadata.Author;
|
||||
Views = VideoService.Metadata.Views.ToString();
|
||||
Date = VideoService.Metadata.Date.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern);
|
||||
Duration = TimeSpanCustomFormat.ToOptTHBaseMMSS(VideoService.Metadata.Duration);
|
||||
|
||||
// Set video options control
|
||||
HomeVideoAddingOptionsControl = new HomeAddingVideoOptions(VideoService);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
// BASE VIDEO DATA
|
||||
private IVideo VideoService { get; set; }
|
||||
|
||||
// VIDEO DATA
|
||||
private ImageSource ThumbnailImage { get; set; }
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Title { get; set; }
|
||||
private string Author { get; set; }
|
||||
private string Views { get; set; }
|
||||
private string Date { get; set; }
|
||||
private string Duration { get; set; }
|
||||
|
||||
// OPTIONS CONTROL
|
||||
private HomeAddingVideoOptions HomeVideoAddingOptionsControl { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
private async void HomeVideoAddingPanel_Loading(FrameworkElement sender, object args)
|
||||
{
|
||||
await HomeVideoAddingOptionsControl.Init();
|
||||
HomeVideoAddingOptionsControlParent.Content = HomeVideoAddingOptionsControl;
|
||||
}
|
||||
|
||||
// SOURCE BUTTON CLICKED
|
||||
public async void HomeVideoAddingPanelSourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Launch the website
|
||||
await Windows.System.Launcher.LaunchUriAsync(VideoService.Url);
|
||||
}
|
||||
|
||||
// ADD BUTTON CLICKED
|
||||
public void HomeVideoAddingPanelAddButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Pack task data
|
||||
TaskData taskData = new TaskData
|
||||
{
|
||||
VideoService = VideoService,
|
||||
TaskOptions = new TaskOptions()
|
||||
{
|
||||
MediaType = HomeVideoAddingOptionsControl.MediaType,
|
||||
Stream = HomeVideoAddingOptionsControl.Stream,
|
||||
TrimStart = HomeVideoAddingOptionsControl.TrimStart,
|
||||
TrimEnd = HomeVideoAddingOptionsControl.TrimEnd,
|
||||
Filename = HomeVideoAddingOptionsControl.Filename,
|
||||
Extension = HomeVideoAddingOptionsControl.Extension,
|
||||
Location = HomeVideoAddingOptionsControl.Location,
|
||||
Schedule = HomeVideoAddingOptionsControl.Schedule,
|
||||
}
|
||||
};
|
||||
|
||||
// Request task adding
|
||||
TasksAddingRequestedEventArgs eventArgs = new TasksAddingRequestedEventArgs
|
||||
{
|
||||
TaskData = new TaskData[] { taskData },
|
||||
RequestSource = TaskAddingRequestSource.Video
|
||||
};
|
||||
TasksAddingRequested?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<TasksAddingRequestedEventArgs> TasksAddingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
109
VDownload/Views/Home/MainPage.xaml
Normal file
109
VDownload/Views/Home/MainPage.xaml
Normal file
@@ -0,0 +1,109 @@
|
||||
<Page
|
||||
x:Class="VDownload.Views.Home.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
mc:Ignorable="d"
|
||||
Background="Transparent">
|
||||
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Images.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="Compact">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="OptionsBarButtonsStackPanel.(Grid.ColumnSpan)" Value="4"/>
|
||||
<Setter Target="OptionsBarButtonsStackPanel.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="SearchControlPresenter.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="SearchControlPresenter.(Grid.ColumnSpan)" Value="3"/>
|
||||
<Setter Target="OptionsBarSearchingStatusControl.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="OptionsBarSearchingStatusControl.(Grid.Column)" Value="3"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger x:Name="NormalStateAdaptiveTrigger" MinWindowWidth="800"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="OptionsBarButtonsStackPanel.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="OptionsBarButtonsStackPanel.(Grid.Column)" Value="3"/>
|
||||
<Setter Target="SearchControlPresenter.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="SearchControlPresenter.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="SearchingStatusControlPresenter.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="SearchingStatusControlPresenter.(Grid.Column)" Value="1"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="DownloadTaskControlsStackPanelRow" Height="1*"/>
|
||||
<RowDefinition x:Name="OptionsBarAndAddingPanelRow" Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ScrollViewer Margin="0,0,0,10" CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<cc:PlaceholderableStackPanel x:Name="DownloadTaskControlsStackPanel" Spacing="10">
|
||||
<cc:PlaceholderableStackPanel.Placeholder>
|
||||
<Viewbox Width="250">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.Row="0" Source="{StaticResource HomeTasksListPlaceholderImage}" Width="120"/>
|
||||
<TextBlock Grid.Row="1" x:Uid="Home_DownloadTaskControlsStackPanel_Placeholder_TextBlock" Foreground="{StaticResource HomeTasksListPlaceholderTextColor}"/>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</cc:PlaceholderableStackPanel.Placeholder>
|
||||
</cc:PlaceholderableStackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid Grid.Row="1" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource HomeBackgroundColor}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ContentPresenter x:Name="AddingPanelPresenter" Grid.Row="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
|
||||
<Grid x:Name="OptionsBar" Grid.Row="1" Background="{ThemeResource HomeOptionBarBackgroundColor}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter x:Name="SearchControlPresenter" Grid.Row="1" Grid.Column="0"/>
|
||||
<ContentPresenter x:Name="SearchingStatusControlPresenter" Grid.Row="1" Grid.Column="1"/>
|
||||
<StackPanel x:Name="OptionsBarButtonsStackPanel" Grid.Row="1" Grid.Column="3" Margin="3,0,3,0" HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<AppBarToggleButton x:Name="LoadSubscripionsButton" x:Uid="Home_OptionsBar_LoadSubscripionsButton" Icon="Favorite" Width="120" Checked="LoadSubscripionsButton_Checked" Unchecked="AddingButtons_Unchecked"/>
|
||||
<AppBarSeparator VerticalAlignment="Center" Height="50"/>
|
||||
<AppBarToggleButton x:Name="PlaylistSearchButton" x:Uid="Home_OptionsBar_PlaylistSearchButton" Icon="List" Width="85" Checked="PlaylistSearchButton_Checked" Unchecked="AddingButtons_Unchecked"/>
|
||||
<AppBarToggleButton x:Name="VideoSearchButton" x:Uid="Home_OptionsBar_VideoSearchButton" Icon="Video" Width="75" Checked="VideoSearchButton_Checked" Unchecked="AddingButtons_Unchecked"/>
|
||||
<AppBarSeparator VerticalAlignment="Center" Height="50"/>
|
||||
<AppBarButton x:Name="DownloadAllButton" x:Uid="Home_OptionsBar_DownloadAllButton" Icon="Download" Width="90" Click="DownloadAllButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
207
VDownload/Views/Home/MainPage.xaml.cs
Normal file
207
VDownload/Views/Home/MainPage.xaml.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using Microsoft.Toolkit.Uwp.Connectivity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Exceptions;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using VDownload.Views.Home.Controls;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private CancellationTokenSource SearchingCancellationToken { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
foreach (DownloadTask downloadTask in DownloadTasksCollectionManagement.DownloadTasksCollection.Values)
|
||||
{
|
||||
DownloadTaskControl downloadTaskControl = new DownloadTaskControl(downloadTask);
|
||||
downloadTaskControl.RemovingRequested += (s, a) =>
|
||||
{
|
||||
DownloadTaskControlsStackPanel.Remove(downloadTaskControl);
|
||||
DownloadTasksCollectionManagement.Remove(downloadTask.Id);
|
||||
};
|
||||
DownloadTaskControlsStackPanel.Add(downloadTaskControl);
|
||||
}
|
||||
}
|
||||
|
||||
private void VideoSearchButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PlaylistSearchButton.IsChecked = false;
|
||||
LoadSubscripionsButton.IsChecked = false;
|
||||
|
||||
VideoSearchControl videoSearchControl = new VideoSearchControl();
|
||||
videoSearchControl.SearchingSuccessed += (s, a) =>
|
||||
{
|
||||
OptionsBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
DownloadTaskControlsStackPanelRow.Height = new GridLength(0);
|
||||
|
||||
VideoAddingPanel videoAddingPanel = new VideoAddingPanel(a.Video);
|
||||
videoAddingPanel.TasksAddingRequested += DownloadTasksAddingRequest;
|
||||
AddingPanelPresenter.Content = videoAddingPanel;
|
||||
};
|
||||
videoSearchControl.SearchButtonClicked += (s, a) =>
|
||||
{
|
||||
SearchingCancellationToken.Cancel();
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
videoSearchControl.CancellationToken = SearchingCancellationToken.Token;
|
||||
};
|
||||
SearchControlPresenter.Content = videoSearchControl;
|
||||
}
|
||||
|
||||
private void PlaylistSearchButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
VideoSearchButton.IsChecked = false;
|
||||
LoadSubscripionsButton.IsChecked = false;
|
||||
|
||||
PlaylistSearchControl playlistSearchControl = new PlaylistSearchControl();
|
||||
playlistSearchControl.SearchingSuccessed += (s, a) =>
|
||||
{
|
||||
OptionsBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
DownloadTaskControlsStackPanelRow.Height = new GridLength(0);
|
||||
|
||||
PlaylistAddingPanel playlistAddingPanel = new PlaylistAddingPanel(a.Playlist);
|
||||
playlistAddingPanel.TasksAddingRequested += DownloadTasksAddingRequest;
|
||||
AddingPanelPresenter.Content = playlistAddingPanel;
|
||||
};
|
||||
playlistSearchControl.SearchButtonClicked += (s, a) =>
|
||||
{
|
||||
SearchingCancellationToken.Cancel();
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
playlistSearchControl.CancellationToken = SearchingCancellationToken.Token;
|
||||
};
|
||||
SearchControlPresenter.Content = playlistSearchControl;
|
||||
}
|
||||
|
||||
private async void LoadSubscripionsButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
VideoSearchButton.IsChecked = false;
|
||||
PlaylistSearchButton.IsChecked = false;
|
||||
|
||||
SubscriptionsLoadControl subscriptionsLoadControl = new SubscriptionsLoadControl();
|
||||
SearchingCancellationToken.Cancel();
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
subscriptionsLoadControl.CancellationToken = SearchingCancellationToken.Token;
|
||||
subscriptionsLoadControl.LoadingSuccessed += (s, a) =>
|
||||
{
|
||||
OptionsBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
DownloadTaskControlsStackPanelRow.Height = new GridLength(0);
|
||||
|
||||
SubscriptionsAddingPanel subscriptionsAddingPanel = new SubscriptionsAddingPanel(a.Videos);
|
||||
subscriptionsAddingPanel.TasksAddingRequested += DownloadTasksAddingRequest;
|
||||
AddingPanelPresenter.Content = subscriptionsAddingPanel;
|
||||
};
|
||||
SearchControlPresenter.Content = subscriptionsLoadControl;
|
||||
await subscriptionsLoadControl.StartLoading();
|
||||
}
|
||||
|
||||
private void AddingButtons_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SearchingCancellationToken.Cancel();
|
||||
SearchingCancellationToken = new CancellationTokenSource();
|
||||
|
||||
OptionsBarAndAddingPanelRow.Height = GridLength.Auto;
|
||||
DownloadTaskControlsStackPanelRow.Height = new GridLength(1, GridUnitType.Star);
|
||||
|
||||
AddingPanelPresenter.Content = null;
|
||||
SearchControlPresenter.Content = null;
|
||||
SearchingStatusControlPresenter.Content = null;
|
||||
}
|
||||
|
||||
private void DownloadTasksAddingRequest(object sender, DownloadTasksAddingRequestedEventArgs e)
|
||||
{
|
||||
switch (e.RequestSource)
|
||||
{
|
||||
case DownloadTasksAddingRequestSource.Video: VideoSearchButton.IsChecked = false; break;
|
||||
case DownloadTasksAddingRequestSource.Playlist: PlaylistSearchButton.IsChecked = false; break;
|
||||
case DownloadTasksAddingRequestSource.Subscriptions: LoadSubscripionsButton.IsChecked = false; break;
|
||||
}
|
||||
|
||||
foreach (DownloadTask downloadTask in e.DownloadTasks)
|
||||
{
|
||||
DownloadTaskControl downloadTaskControl = new DownloadTaskControl(downloadTask);
|
||||
DownloadTasksCollectionManagement.Add(downloadTask, downloadTask.Id);
|
||||
downloadTaskControl.RemovingRequested += (s, a) =>
|
||||
{
|
||||
DownloadTaskControlsStackPanel.Remove(downloadTaskControl);
|
||||
DownloadTasksCollectionManagement.Remove(downloadTask.Id);
|
||||
};
|
||||
|
||||
DownloadTaskControlsStackPanel.Add(downloadTaskControl);
|
||||
}
|
||||
}
|
||||
|
||||
private async void DownloadAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DownloadTask[] idleDownloadTasks = DownloadTasksCollectionManagement.DownloadTasksCollection.Values.Where((task) => task.Status == DownloadTaskStatus.Idle).ToArray();
|
||||
if (idleDownloadTasks.Count() > 0)
|
||||
{
|
||||
bool delay = (bool)Config.GetValue("delay_task_when_queued_task_starts_on_metered_network");
|
||||
if (NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)
|
||||
{
|
||||
ContentDialogResult dialogResult = await new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_Title"),
|
||||
Content = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_Content"),
|
||||
PrimaryButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_StartWithDelayButtonText"),
|
||||
SecondaryButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Home_DownloadAll_MeteredConnection_StartWithoutDelayButtonText"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView("DialogResources").GetString("Base_CancelButtonText"),
|
||||
}.ShowAsync();
|
||||
switch (dialogResult)
|
||||
{
|
||||
case ContentDialogResult.Primary: delay = true; break;
|
||||
case ContentDialogResult.Secondary: delay = false; break;
|
||||
case ContentDialogResult.None: return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DownloadTask downloadTask in idleDownloadTasks)
|
||||
{
|
||||
await Task.Delay(1);
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
downloadTask.Run(delay);
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
40
VDownload/Views/Home/PlaylistAddingPanel.xaml
Normal file
40
VDownload/Views/Home/PlaylistAddingPanel.xaml
Normal file
@@ -0,0 +1,40 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.PlaylistAddingPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid x:Name="Base" Padding="10" RowSpacing="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="12" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold" Text="{x:Bind Name}"/>
|
||||
<AppBarButton Grid.Column="1" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="SourceButton_Click"/>
|
||||
<AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Background="{ThemeResource SystemAccentColor}" Foreground="{ThemeResource SystemAltHighColor}" Click="AddButton_Click"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
103
VDownload/Views/Home/PlaylistAddingPanel.xaml.cs
Normal file
103
VDownload/Views/Home/PlaylistAddingPanel.xaml.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Converters;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using VDownload.Views.Home.Controls;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class PlaylistAddingPanel : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public PlaylistAddingPanel(IPlaylist playlist)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Playlist = playlist;
|
||||
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Playlist.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Name = Playlist.Name;
|
||||
|
||||
SerialVideoAddingControl = new SerialVideoAddingControl(playlist.Videos);
|
||||
Grid.SetRow(SerialVideoAddingControl, 1);
|
||||
Base.Children.Add(SerialVideoAddingControl);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private IPlaylist Playlist { get; set; }
|
||||
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Name { get; set; }
|
||||
|
||||
private SerialVideoAddingControl SerialVideoAddingControl { get; set;}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private async void SourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Windows.System.Launcher.LaunchUriAsync(Playlist.Url);
|
||||
}
|
||||
|
||||
private void AddButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
List<DownloadTask> tasksList = new List<DownloadTask>();
|
||||
foreach (SerialVideoAddingVideoControl videoControl in SerialVideoAddingControl.VideosStackPanel.Children)
|
||||
{
|
||||
TrimData trim = new TrimData
|
||||
{
|
||||
Start = videoControl.OptionsControl.TrimStart,
|
||||
End = videoControl.OptionsControl.TrimEnd,
|
||||
};
|
||||
OutputFile file;
|
||||
if (videoControl.OptionsControl.FileLocation is null)
|
||||
{
|
||||
file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension, videoControl.OptionsControl.FileLocation);
|
||||
}
|
||||
string id = DownloadTasksCollectionManagement.GenerateID();
|
||||
tasksList.Add(new DownloadTask(id, videoControl.Video, videoControl.OptionsControl.MediaType, videoControl.OptionsControl.Stream, trim, file, videoControl.OptionsControl.Schedule));
|
||||
}
|
||||
|
||||
TasksAddingRequested?.Invoke(this, new DownloadTasksAddingRequestedEventArgs(tasksList.ToArray(), DownloadTasksAddingRequestSource.Playlist));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENTS
|
||||
|
||||
public event EventHandler<DownloadTasksAddingRequestedEventArgs> TasksAddingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
40
VDownload/Views/Home/SubscriptionsAddingPanel.xaml
Normal file
40
VDownload/Views/Home/SubscriptionsAddingPanel.xaml
Normal file
@@ -0,0 +1,40 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.SubscriptionsAddingPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Colors.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid x:Name="Base" Padding="10" RowSpacing="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" x:Uid="Home_Adding_Subscriptions_HeaderTextBlock" Margin="12" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold"/>
|
||||
<AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Background="{ThemeResource SystemAccentColor}" Foreground="{ThemeResource SystemAltHighColor}" Click="AddButton_Click"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
92
VDownload/Views/Home/SubscriptionsAddingPanel.xaml.cs
Normal file
92
VDownload/Views/Home/SubscriptionsAddingPanel.xaml.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Converters;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using VDownload.Views.Home.Controls;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class SubscriptionsAddingPanel : UserControl
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public SubscriptionsAddingPanel(IVideo[] videos)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Videos = videos;
|
||||
|
||||
SerialVideoAddingControl = new SerialVideoAddingControl(Videos);
|
||||
Grid.SetRow(SerialVideoAddingControl, 1);
|
||||
Base.Children.Add(SerialVideoAddingControl);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private IVideo[] Videos { get; set; }
|
||||
|
||||
private SerialVideoAddingControl SerialVideoAddingControl { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS VOIDS
|
||||
|
||||
private void AddButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
List<DownloadTask> tasksList = new List<DownloadTask>();
|
||||
foreach (SerialVideoAddingVideoControl videoControl in SerialVideoAddingControl.VideosStackPanel.Children)
|
||||
{
|
||||
TrimData trim = new TrimData
|
||||
{
|
||||
Start = videoControl.OptionsControl.TrimStart,
|
||||
End = videoControl.OptionsControl.TrimEnd,
|
||||
};
|
||||
OutputFile file;
|
||||
if (videoControl.OptionsControl.FileLocation is null)
|
||||
{
|
||||
file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
file = new OutputFile(videoControl.OptionsControl.Filename, videoControl.OptionsControl.FileExtension, videoControl.OptionsControl.FileLocation);
|
||||
}
|
||||
string id = DownloadTasksCollectionManagement.GenerateID();
|
||||
tasksList.Add(new DownloadTask(id, videoControl.Video, videoControl.OptionsControl.MediaType, videoControl.OptionsControl.Stream, trim, file, videoControl.OptionsControl.Schedule));
|
||||
}
|
||||
|
||||
TasksAddingRequested?.Invoke(this, new DownloadTasksAddingRequestedEventArgs(tasksList.ToArray(), DownloadTasksAddingRequestSource.Playlist));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
public event EventHandler<DownloadTasksAddingRequestedEventArgs> TasksAddingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
204
VDownload/Views/Home/VideoAddingPanel.xaml
Normal file
204
VDownload/Views/Home/VideoAddingPanel.xaml
Normal file
@@ -0,0 +1,204 @@
|
||||
<UserControl
|
||||
x:Class="VDownload.Views.Home.VideoAddingPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Home"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ex="using:Microsoft.Toolkit.Uwp.UI"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
Loading="VideoAddingPanel_Loading"
|
||||
mc:Ignorable="d">
|
||||
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<x:String x:Key="MetadataIconSize">16</x:String>
|
||||
<x:String x:Key="MetadataTextSize">12</x:String>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid Padding="10" RowSpacing="40" VerticalAlignment="Stretch">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState>
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Thumbnail.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="Thumbnail.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="Thumbnail.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="Thumbnail.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="Thumbnail.Width" Value="NaN"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="AuthorIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="AuthorIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="AuthorIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="AuthorIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="AuthorText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="AuthorText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="AuthorText.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="AuthorText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="AuthorText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="ViewsIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="ViewsIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="ViewsIcon.(Grid.Column)" Value="1"/>
|
||||
<Setter Target="ViewsIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="ViewsText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="ViewsText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="ViewsText.(Grid.Column)" Value="1"/>
|
||||
<Setter Target="ViewsText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="ViewsText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="DateIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="DateIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="DateIcon.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="DateIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="DateText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="DateText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="DateText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="DateText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="DateText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="DurationIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="DurationIcon.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="DurationIcon.(Grid.Column)" Value="3"/>
|
||||
<Setter Target="DurationIcon.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="DurationText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="DurationText.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="DurationText.(Grid.Column)" Value="3"/>
|
||||
<Setter Target="DurationText.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="DurationText.HorizontalAlignment" Value="Center"/>
|
||||
<Setter Target="MetadataR1.Height" Value="Auto"/>
|
||||
<Setter Target="MetadataR2.Height" Value="Auto"/>
|
||||
<Setter Target="MetadataC1.Width" Value="1*"/>
|
||||
<Setter Target="MetadataC2.Width" Value="1*"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState>
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="600"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Thumbnail.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="Thumbnail.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="Thumbnail.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="Thumbnail.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="Thumbnail.Width" Value="250"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.Column)" Value="1"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.ColumnSpan)" Value="1"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="TitleAndMetadataGrid.(Grid.RowSpan)" Value="2"/>
|
||||
<Setter Target="AuthorIcon.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="AuthorIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="AuthorIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="AuthorIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="AuthorText.(Grid.Row)" Value="0"/>
|
||||
<Setter Target="AuthorText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="AuthorText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="AuthorText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="AuthorText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="ViewsIcon.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="ViewsIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="ViewsIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="ViewsIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="ViewsText.(Grid.Row)" Value="1"/>
|
||||
<Setter Target="ViewsText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="ViewsText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="ViewsText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="ViewsText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="DateIcon.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="DateIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="DateIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="DateIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="DateText.(Grid.Row)" Value="2"/>
|
||||
<Setter Target="DateText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="DateText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="DateText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="DateText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="DurationIcon.(Grid.Row)" Value="3"/>
|
||||
<Setter Target="DurationIcon.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="DurationIcon.(Grid.Column)" Value="0"/>
|
||||
<Setter Target="DurationIcon.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="DurationText.(Grid.Row)" Value="3"/>
|
||||
<Setter Target="DurationText.(Grid.RowSpan)" Value="1"/>
|
||||
<Setter Target="DurationText.(Grid.Column)" Value="2"/>
|
||||
<Setter Target="DurationText.(Grid.ColumnSpan)" Value="2"/>
|
||||
<Setter Target="DurationText.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="MetadataR1.Height" Value="1*"/>
|
||||
<Setter Target="MetadataR2.Height" Value="1*"/>
|
||||
<Setter Target="MetadataC1.Width" Value="Auto"/>
|
||||
<Setter Target="MetadataC2.Width" Value="Auto"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" ColumnSpacing="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="Thumbnail" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="2" Source="{x:Bind ThumbnailImage}"/>
|
||||
<Grid x:Name="TitleAndMetadataGrid" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" FontSize="18" VerticalAlignment="Center" FontWeight="SemiBold" Text="{x:Bind Title}"/>
|
||||
<AppBarButton Grid.Column="1" Width="40" Height="48" Icon="{x:Bind SourceImage}" Click="SourceButton_Click"/>
|
||||
<AppBarButton Grid.Column="2" Width="40" Height="48" Icon="Add" Background="{ThemeResource SystemAccentColor}" Foreground="{ThemeResource SystemAltHighColor}" Click="AddButton_Click"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" ColumnSpacing="10" RowSpacing="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="MetadataR1" Height="1*"/>
|
||||
<RowDefinition x:Name="MetadataR2" Height="1*"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="MetadataC1" Width="Auto"/>
|
||||
<ColumnDefinition x:Name="MetadataC2" Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="AuthorIcon" Grid.Row="0" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource AuthorIcon}"/>
|
||||
<TextBlock x:Name="AuthorText" Grid.Row="0" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Author}"/>
|
||||
<Image x:Name="ViewsIcon" Grid.Row="1" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource ViewsIcon}"/>
|
||||
<TextBlock x:Name="ViewsText" Grid.Row="1" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Views}"/>
|
||||
<Image x:Name="DateIcon" Grid.Row="2" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DateIcon}"/>
|
||||
<TextBlock x:Name="DateText" Grid.Row="2" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Date}"/>
|
||||
<Image x:Name="DurationIcon" Grid.Row="3" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="2" Width="{StaticResource MetadataIconSize}" Source="{ThemeResource DurationIcon}"/>
|
||||
<TextBlock x:Name="DurationText" Grid.Row="3" Grid.RowSpan="1" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="{StaticResource MetadataTextSize}" Text="{x:Bind Duration}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<ContentPresenter x:Name="OptionsControlPresenter" HorizontalContentAlignment="Stretch"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
122
VDownload/Views/Home/VideoAddingPanel.xaml.cs
Normal file
122
VDownload/Views/Home/VideoAddingPanel.xaml.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Enums;
|
||||
using VDownload.Core.Extensions;
|
||||
using VDownload.Core.EventArgs;
|
||||
using VDownload.Core.Interfaces;
|
||||
using VDownload.Core.Services;
|
||||
using VDownload.Core.Structs;
|
||||
using VDownload.Views.Home.Controls;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace VDownload.Views.Home
|
||||
{
|
||||
public sealed partial class VideoAddingPanel : UserControl
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private readonly ResourceDictionary ImagesRes = new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Images.xaml") };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public VideoAddingPanel(IVideo video)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Video = video;
|
||||
|
||||
ThumbnailImage = Video.Thumbnail != null ? new BitmapImage { UriSource = Video.Thumbnail } : (BitmapImage)ImagesRes["UnknownThumbnailImage"];
|
||||
SourceImage = new BitmapIcon { UriSource = new Uri($"ms-appx:///Assets/Sources/{Video.GetType().Namespace.Split(".").Last()}.png"), ShowAsMonochrome = false };
|
||||
Title = Video.Title;
|
||||
Author = Video.Author;
|
||||
Views = Video.Views.ToString();
|
||||
Date = Video.Date.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern);
|
||||
Duration = Video.Duration.ToStringOptTHBaseMMSS();
|
||||
|
||||
OptionsControl = new VideoAddingOptionsControl(Video);
|
||||
OptionsControlPresenter.Content = OptionsControl;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
private IVideo Video { get; set; }
|
||||
|
||||
// VIDEO DATA
|
||||
private ImageSource ThumbnailImage { get; set; }
|
||||
private IconElement SourceImage { get; set; }
|
||||
private string Title { get; set; }
|
||||
private string Author { get; set; }
|
||||
private string Views { get; set; }
|
||||
private string Date { get; set; }
|
||||
private string Duration { get; set; }
|
||||
|
||||
private VideoAddingOptionsControl OptionsControl { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENT HANDLERS
|
||||
|
||||
private async void VideoAddingPanel_Loading(FrameworkElement sender, object args)
|
||||
{
|
||||
await OptionsControl.Init();
|
||||
}
|
||||
|
||||
private async void SourceButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Windows.System.Launcher.LaunchUriAsync(Video.Url);
|
||||
}
|
||||
|
||||
private void AddButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TrimData trim = new TrimData
|
||||
{
|
||||
Start = OptionsControl.TrimStart,
|
||||
End = OptionsControl.TrimEnd,
|
||||
};
|
||||
OutputFile file;
|
||||
if (OptionsControl.FileLocation is null)
|
||||
{
|
||||
file = new OutputFile(OptionsControl.Filename, OptionsControl.FileExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
file = new OutputFile(OptionsControl.Filename, OptionsControl.FileExtension, OptionsControl.FileLocation);
|
||||
}
|
||||
string id = DownloadTasksCollectionManagement.GenerateID();
|
||||
DownloadTask downloadTask = new DownloadTask(id, Video, OptionsControl.MediaType, OptionsControl.Stream, trim, file, OptionsControl.Schedule);
|
||||
TasksAddingRequested?.Invoke(this, new DownloadTasksAddingRequestedEventArgs(new DownloadTask[] { downloadTask }, DownloadTasksAddingRequestSource.Video));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region EVENTS
|
||||
|
||||
public event EventHandler<DownloadTasksAddingRequestedEventArgs> TasksAddingRequested;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
|
||||
<Grid>
|
||||
<!-- TITLE BAR -->
|
||||
<Border x:Name="AppTitleBar"
|
||||
IsHitTestVisible="True"
|
||||
VerticalAlignment="Top"
|
||||
@@ -45,26 +44,25 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- NAVIGATION PANEL AND CONTENT -->
|
||||
<muxc:NavigationView x:Name="MainPageNavigationPanel" Background="Transparent" IsTitleBarAutoPaddingEnabled="False" IsBackButtonVisible="Collapsed" PaneDisplayMode="LeftCompact" Canvas.ZIndex="0" ItemInvoked="MainPageNavigationPanel_ItemInvoked">
|
||||
<muxc:NavigationView x:Name="NavigationPanel" Background="Transparent" IsTitleBarAutoPaddingEnabled="False" IsBackButtonVisible="Collapsed" PaneDisplayMode="LeftCompact" Canvas.ZIndex="0" ItemInvoked="NavigationPanel_ItemInvoked">
|
||||
<muxc:NavigationView.MenuItems>
|
||||
<muxc:NavigationViewItem x:Uid="MainPageNavigationPanelHomeItem" Tag="home" Icon="Video" Content="Home"/>
|
||||
<muxc:NavigationViewItem x:Uid="Main_NavigationPanel_Home" Tag="home" Icon="Video"/>
|
||||
<muxc:NavigationViewItemSeparator/>
|
||||
<muxc:NavigationViewItem x:Uid="MainPageNavigationPanelSubscriptionsItem" Tag="subscriptions" Icon="Favorite" Content="Subscriptions"/>
|
||||
<muxc:NavigationViewItem x:Uid="Main_NavigationPanel_Subscriptions" Tag="subscriptions" Icon="Favorite"/>
|
||||
</muxc:NavigationView.MenuItems>
|
||||
<muxc:NavigationView.FooterMenuItems>
|
||||
<muxc:NavigationViewItem x:Uid="MainPageNavigationPanelAboutItem" Tag="about" Content="About">
|
||||
<muxc:NavigationViewItem x:Uid="Main_NavigationPanel_About" Tag="about">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<PathIcon Data="M3331.25,1.389c-1833.78,-0 -3325.69,1492.85 -3325.69,3327.78c-0,1834.92 1491.92,3327.78 3325.69,3327.78c1833.78,0 3325.69,-1492.85 3325.69,-3327.78c0,-1834.92 -1491.92,-3327.78 -3325.69,-3327.78Zm0,499.167c1564.18,-0 2826.84,1263.45 2826.84,2828.61c0,1565.15 -1262.66,2828.61 -2826.84,2828.61c-1564.18,-0 -2826.84,-1263.46 -2826.84,-2828.61c-0,-1565.16 1262.66,-2828.61 2826.84,-2828.61Zm0,1164.72c-182.442,-0 -332.569,150.221 -332.569,332.778c-0,182.556 150.127,332.777 332.569,332.777c182.442,0 332.569,-150.221 332.569,-332.777c0,-182.557 -150.127,-332.778 -332.569,-332.778Zm-3.897,1161.15c-136.709,2.137 -247.491,116.362 -245.53,253.158l-0,1830.28c-0.017,1.177 -0.025,2.353 -0.025,3.53c-0,136.931 112.607,249.608 249.452,249.608c136.845,0 249.452,-112.677 249.452,-249.608c0,-1.177 -0.008,-2.353 -0.025,-3.53l0,-1830.28c0.017,-1.193 0.026,-2.386 0.026,-3.58c-0,-136.931 -112.607,-249.608 -249.452,-249.608c-1.3,-0 -2.599,0.01 -3.898,0.03Z"/>
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
<muxc:NavigationViewItem x:Uid="MainPageNavigationPanelSourcesItem" Tag="sources" Content="Sources">
|
||||
<muxc:NavigationViewItem x:Uid="Main_NavigationPanel_Sources" Tag="sources">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<PathIcon Data="M959.759,-4.167c-33.26,0 -65.978,1.941 -98.383,5.426c-64.583,6.944 -127.292,20.746 -187.509,40.509c-302.561,99.296 -541.778,351.698 -635.887,670.935c-18.731,63.536 -31.812,129.701 -38.394,197.844c-0.011,0.119 0.012,0.242 0,0.362c-3.279,34.075 -5.142,68.472 -5.142,103.443l0,4629.63c0,559.218 435.309,1018.52 965.315,1018.52l4738.82,0c530.005,0 965.314,-459.301 965.314,-1018.52l-0,-4629.63c-0,-35.074 -1.801,-69.606 -5.142,-103.805c-6.582,-68.143 -19.663,-134.308 -38.393,-197.844c-94.109,-319.237 -333.327,-571.639 -635.887,-670.935c-60.218,-19.763 -122.926,-33.565 -187.51,-40.509c-0.113,-0.012 -0.23,0.012 -0.343,-0c-32.302,-3.501 -64.914,-5.426 -98.039,-5.426l-4738.82,0Zm-0,555.556c145.323,-0 263.267,124.444 263.267,277.778c0,153.333 -117.944,277.777 -263.267,277.777c-145.324,0 -263.268,-124.444 -263.268,-277.777c0,-153.334 117.944,-277.778 263.268,-277.778Zm877.558,-0c145.324,-0 263.268,124.444 263.268,277.778c-0,153.333 -117.944,277.777 -263.268,277.777c-145.323,0 -263.267,-124.444 -263.267,-277.777c-0,-153.334 117.944,-277.778 263.267,-277.778Zm1053.07,-0l2808.19,-0c145.323,-0 263.267,124.444 263.267,277.778c0,153.333 -117.944,277.777 -263.267,277.777l-2808.19,0c-145.323,0 -263.267,-124.444 -263.267,-277.777c-0,-153.334 117.944,-277.778 263.267,-277.778Zm-2369.41,1111.11l5616.37,0l-0,3981.48c-0,258.931 -193.374,462.963 -438.779,462.963l-4738.82,0c-245.405,0 -438.779,-204.032 -438.779,-462.963l-0,-3981.48Zm2247.03,1111.11c-166.006,3.577 -316.401,144.126 -316.401,339.265l0,1543.69c0,260.371 267.48,423.128 482.657,294.054l1285.49,-771.484c216.757,-130.185 216.757,-458.831 0,-588.831l-1285.49,-771.485c-53.794,-32.268 -110.92,-46.403 -166.256,-45.211Z"/>
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
</muxc:NavigationView.FooterMenuItems>
|
||||
<Frame x:Name="MainPageContentFrame" CornerRadius="5" Margin="10"/>
|
||||
<Frame x:Name="ContentFrame" CornerRadius="5" Margin="10"/>
|
||||
</muxc:NavigationView>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -8,6 +8,21 @@ namespace VDownload.Views
|
||||
{
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private readonly Dictionary<string, Type> Pages = new Dictionary<string, Type>()
|
||||
{
|
||||
{"home", typeof(Home.MainPage)},
|
||||
{"subscriptions", typeof(Subscriptions.MainPage)},
|
||||
{"about", typeof(About.MainPage)},
|
||||
{"sources", typeof(Sources.MainPage)},
|
||||
{"settings", typeof(Settings.MainPage)},
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public MainPage()
|
||||
@@ -20,34 +35,28 @@ namespace VDownload.Views
|
||||
Window.Current.SetTitleBar(AppTitleBar);
|
||||
|
||||
// Navigate to home page
|
||||
MainPageContentFrame.Navigate(Pages["home"]);
|
||||
MainPageNavigationPanel.SelectedItem = MainPageNavigationPanel.MenuItems[0];
|
||||
ContentFrame.Navigate(Pages["home"]);
|
||||
NavigationPanel.SelectedItem = NavigationPanel.MenuItems[0];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region NAVIGATION PANEL
|
||||
#region EVENT HANDLERS
|
||||
|
||||
// PAGES DICTIONARY
|
||||
private readonly Dictionary<string, Type> Pages = new Dictionary<string, Type>()
|
||||
private void NavigationPanel_ItemInvoked(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs args)
|
||||
{
|
||||
{"home", typeof(Home.HomeMain)},
|
||||
{"subscriptions", typeof(Subscriptions.MainPage)},
|
||||
{"about", typeof(About.AboutMain)},
|
||||
{"sources", typeof(Sources.MainPage)},
|
||||
{"settings", typeof(Settings.SettingsMain)},
|
||||
};
|
||||
|
||||
// ON ITEM INVOKED
|
||||
private void MainPageNavigationPanel_ItemInvoked(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs args)
|
||||
{
|
||||
if (args.IsSettingsInvoked) MainPageContentFrame.Navigate(Pages["settings"], args.RecommendedNavigationTransitionInfo);
|
||||
else if (args.InvokedItemContainer != null) MainPageContentFrame.Navigate(Pages[args.InvokedItemContainer.Tag.ToString()], args.RecommendedNavigationTransitionInfo);
|
||||
if (args.IsSettingsInvoked)
|
||||
{
|
||||
ContentFrame.Navigate(Pages["settings"], args.RecommendedNavigationTransitionInfo);
|
||||
}
|
||||
else if (args.InvokedItemContainer != null)
|
||||
{
|
||||
ContentFrame.Navigate(Pages[args.InvokedItemContainer.Tag.ToString()], args.RecommendedNavigationTransitionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
27
VDownload/Views/Settings/MainPage.xaml
Normal file
27
VDownload/Views/Settings/MainPage.xaml
Normal file
@@ -0,0 +1,27 @@
|
||||
<Page
|
||||
x:Class="VDownload.Views.Settings.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:VDownload.Views.Settings"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:cc="using:VDownload.Controls"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid Padding="20" RowSpacing="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock x:Uid="Settings_HeaderTextBlock" Grid.Row="0" FontSize="28" FontWeight="SemiBold"/>
|
||||
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<StackPanel VerticalAlignment="Stretch" Spacing="10">
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</Page>
|
||||
@@ -15,9 +15,9 @@ using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace VDownload.Views.Settings
|
||||
{
|
||||
public sealed partial class SettingsMain : Page
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
public SettingsMain()
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
@@ -5,7 +5,9 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VDownload.Core.Structs;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.UI.WindowManagement;
|
||||
using Windows.UI.Xaml;
|
||||
@@ -39,24 +41,103 @@ namespace VDownload.Views.Sources
|
||||
await Task.WhenAll(checkingTasks);
|
||||
}
|
||||
|
||||
private async void TwitchSettingControlLoginButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
async Task ShowDialog(string localErrorKey, string unknownErrorInfo = null)
|
||||
{
|
||||
StringBuilder content = new StringBuilder(ResourceLoader.GetForCurrentView("DialogResources").GetString($"Sources_TwitchLogin_{localErrorKey}_Content"));
|
||||
if (!string.IsNullOrEmpty(unknownErrorInfo))
|
||||
{
|
||||
content.Append($" {unknownErrorInfo}");
|
||||
}
|
||||
ContentDialog errorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView("DialogResources").GetString("Sources_TwitchLogin_Base_Title"),
|
||||
Content = content.ToString(),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("Base_CloseButtonText"),
|
||||
};
|
||||
await errorDialog.ShowAsync();
|
||||
}
|
||||
|
||||
TwitchAccessTokenValidationData accessTokenValidation = TwitchAccessTokenValidationData.Null;
|
||||
try
|
||||
{
|
||||
string accessToken = await Core.Services.Sources.Twitch.Helpers.Authorization.ReadAccessTokenAsync();
|
||||
accessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Authorization.ValidateAccessTokenAsync(accessToken);
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
goto Check;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
|
||||
if (accessTokenValidation.IsValid)
|
||||
{
|
||||
await Core.Services.Sources.Twitch.Helpers.Authorization.RevokeAccessTokenAsync(accessTokenValidation.AccessToken);
|
||||
await Core.Services.Sources.Twitch.Helpers.Authorization.DeleteAccessTokenAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
string response = await LoginToTwitch();
|
||||
accessTokenValidation = TwitchAccessTokenValidationData.Null;
|
||||
try
|
||||
{
|
||||
accessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Authorization.ValidateAccessTokenAsync(response);
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
await ShowDialog("InternetNotAvailable");
|
||||
goto Check;
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
if (accessTokenValidation.IsValid)
|
||||
{
|
||||
await Core.Services.Sources.Twitch.Helpers.Authorization.SaveAccessTokenAsync(accessTokenValidation.AccessToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dictionary<string, string> errorCodes = new Dictionary<string, string>
|
||||
{
|
||||
{"The user denied you access", string.Empty},
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(response))
|
||||
{
|
||||
if (errorCodes.ContainsKey(response))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(errorCodes[response]))
|
||||
{
|
||||
await ShowDialog(errorCodes[response]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ShowDialog("Unknown", response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Check: await CheckTwitch();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PRIVATE METHODS
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task CheckTwitch()
|
||||
{
|
||||
try
|
||||
{
|
||||
string twitchAccessToken = await Core.Services.Sources.Twitch.Helpers.Auth.ReadAccessTokenAsync();
|
||||
#pragma warning disable IDE0042 // Deconstruct variable declaration
|
||||
(bool IsValid, string Login, DateTime? ExpirationDate) twitchAccessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Auth.ValidateAccessTokenAsync(twitchAccessToken);
|
||||
#pragma warning restore IDE0042 // Deconstruct variable declaration
|
||||
string twitchAccessToken = await Core.Services.Sources.Twitch.Helpers.Authorization.ReadAccessTokenAsync();
|
||||
TwitchAccessTokenValidationData twitchAccessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Authorization.ValidateAccessTokenAsync(twitchAccessToken);
|
||||
if (twitchAccessTokenValidation.IsValid)
|
||||
{
|
||||
TwitchSettingControl.Description = $"{ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_LoggedIn")} {twitchAccessTokenValidation.Login}";
|
||||
@@ -66,18 +147,17 @@ namespace VDownload.Views.Sources
|
||||
{
|
||||
if (twitchAccessToken != null)
|
||||
{
|
||||
TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_AccessTokenExpired");
|
||||
TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_InvalidAccessToken");
|
||||
}
|
||||
else if (twitchAccessTokenValidation.ExpirationDate < DateTime.Now)
|
||||
else
|
||||
{
|
||||
TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_NotLoggedIn");
|
||||
}
|
||||
Debug.WriteLine(twitchAccessTokenValidation.ExpirationDate.Value.ToString("dd.MM.yyyy"));
|
||||
TwitchSettingControlLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_LoginButton_Content_NotLoggedIn");
|
||||
}
|
||||
TwitchSettingControlLoginButton.IsEnabled = true;
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (WebException)
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
{
|
||||
@@ -89,122 +169,54 @@ namespace VDownload.Views.Sources
|
||||
}
|
||||
}
|
||||
|
||||
#region TWITCH
|
||||
|
||||
|
||||
|
||||
// TWITCH LOGIN BUTTON CLICKED
|
||||
private async void TwitchSettingControlLoginButton_Click(object sender, RoutedEventArgs e)
|
||||
private async Task<string> LoginToTwitch()
|
||||
{
|
||||
try
|
||||
{
|
||||
string accessToken = await Core.Services.Sources.Twitch.Helpers.Auth.ReadAccessTokenAsync();
|
||||
var accessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Auth.ValidateAccessTokenAsync(accessToken);
|
||||
if (accessTokenValidation.IsValid)
|
||||
{
|
||||
// Revoke access token
|
||||
await Core.Services.Sources.Twitch.Helpers.Auth.RevokeAccessTokenAsync(accessToken);
|
||||
|
||||
// Delete access token
|
||||
await Core.Services.Sources.Twitch.Helpers.Auth.DeleteAccessTokenAsync();
|
||||
|
||||
// Update Twitch SettingControl
|
||||
TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_NotLoggedIn");
|
||||
TwitchSettingControlLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_LoginButton_Content_NotLoggedIn");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Open new window
|
||||
AppWindow TwitchAuthWindow = await AppWindow.TryCreateAsync();
|
||||
TwitchAuthWindow.Title = "Twitch Authentication";
|
||||
string response = string.Empty;
|
||||
AppWindow TwitchAuthWindow = await AppWindow.TryCreateAsync();
|
||||
TwitchAuthWindow.Title = "Twitch Authentication";
|
||||
|
||||
#pragma warning disable CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.
|
||||
WebView2 TwitchAuthWebView = new WebView2();
|
||||
await TwitchAuthWebView.EnsureCoreWebView2Async();
|
||||
TwitchAuthWebView.Source = Core.Services.Sources.Twitch.Helpers.Auth.AuthorizationUrl;
|
||||
ElementCompositionPreview.SetAppWindowContent(TwitchAuthWindow, TwitchAuthWebView);
|
||||
|
||||
// NavigationStarting event (only when redirected)
|
||||
TwitchAuthWebView.NavigationStarting += async (s, a) =>
|
||||
{
|
||||
if (new Uri(a.Uri).Host == Core.Services.Sources.Twitch.Helpers.Auth.RedirectUrl.Host)
|
||||
{
|
||||
// Close window
|
||||
await TwitchAuthWindow.CloseAsync();
|
||||
WebView2 TwitchAuthWebView = new WebView2();
|
||||
await TwitchAuthWebView.EnsureCoreWebView2Async();
|
||||
TwitchAuthWebView.Source = Core.Services.Sources.Twitch.Helpers.Authorization.AuthorizationUrl;
|
||||
|
||||
// Get response
|
||||
string response = a.Uri.Replace(Core.Services.Sources.Twitch.Helpers.Auth.RedirectUrl.OriginalString, "");
|
||||
ElementCompositionPreview.SetAppWindowContent(TwitchAuthWindow, TwitchAuthWebView);
|
||||
|
||||
if (response[1] == '#')
|
||||
{
|
||||
// Get access token
|
||||
accessToken = response.Split('&')[0].Replace("/#access_token=", "");
|
||||
|
||||
// Check token
|
||||
accessTokenValidation = await Core.Services.Sources.Twitch.Helpers.Auth.ValidateAccessTokenAsync(accessToken);
|
||||
|
||||
// Save token
|
||||
await Core.Services.Sources.Twitch.Helpers.Auth.SaveAccessTokenAsync(accessToken);
|
||||
|
||||
// Update Twitch SettingControl
|
||||
TwitchSettingControl.Description = $"{ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_Description_LoggedIn")} {accessTokenValidation.Login}";
|
||||
TwitchSettingControlLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("Sources_TwitchSettingControl_LoginButton_Content_LoggedIn");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignored errors
|
||||
string[] ignoredErrors = new[]
|
||||
{
|
||||
"The user denied you access",
|
||||
};
|
||||
|
||||
// Errors translation
|
||||
Dictionary<string, string> errorsTranslation = new Dictionary<string, string>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
// Get error info
|
||||
string errorInfo = (response.Split('&')[1].Replace("error_description=", "")).Replace('+', ' ');
|
||||
if (!ignoredErrors.Contains(errorInfo))
|
||||
{
|
||||
// Error
|
||||
ContentDialog loginErrorDialog = new ContentDialog
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogTitle"),
|
||||
Content = errorsTranslation.Keys.Contains(errorInfo) ? errorsTranslation[errorInfo] : $"{ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogDescriptionUnknown")} ({errorInfo})",
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await loginErrorDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
#pragma warning restore CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.
|
||||
|
||||
await TwitchAuthWindow.TryShowAsync();
|
||||
|
||||
// Clear cache
|
||||
TwitchAuthWebView.CoreWebView2.CookieManager.DeleteAllCookies();
|
||||
}
|
||||
}
|
||||
catch (WebException wex)
|
||||
TwitchAuthWebView.NavigationStarting += async (s, a) =>
|
||||
{
|
||||
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
|
||||
if (new Uri(a.Uri).Host == Core.Services.Sources.Twitch.Helpers.Authorization.RedirectUrl.Host)
|
||||
{
|
||||
TwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionInternetConnectionError");
|
||||
TwitchSettingControlLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextNotLoggedIn");
|
||||
TwitchSettingControlLoginButton.IsEnabled = false;
|
||||
ContentDialog internetAccessErrorDialog = new ContentDialog
|
||||
string login_response = a.Uri.Replace(Core.Services.Sources.Twitch.Helpers.Authorization.RedirectUrl.OriginalString, "");
|
||||
|
||||
if (login_response[1] == '#')
|
||||
{
|
||||
Title = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogTitle"),
|
||||
Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogDescriptionInternetConnectionError"),
|
||||
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
|
||||
};
|
||||
await internetAccessErrorDialog.ShowAsync();
|
||||
response = login_response.Split('&')[0].Replace("/#access_token=", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
response = (login_response.Split('&')[1].Replace("error_description=", "")).Replace('+', ' ');
|
||||
}
|
||||
|
||||
await TwitchAuthWindow.CloseAsync();
|
||||
}
|
||||
};
|
||||
|
||||
await TwitchAuthWindow.TryShowAsync();
|
||||
|
||||
try
|
||||
{
|
||||
while (TwitchAuthWindow.IsVisible)
|
||||
{
|
||||
await Task.Delay(1);
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
|
||||
TwitchAuthWebView.CoreWebView2.CookieManager.DeleteAllCookies();
|
||||
#pragma warning restore CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user