1.0-dev7 (Video adding panel added)

This commit is contained in:
2022-02-26 14:32:34 +01:00
Unverified
parent 96a7953500
commit f571a42995
95 changed files with 1436 additions and 227 deletions

View File

@@ -2,11 +2,11 @@
{
public enum AudioFileExtension
{
MP3 = 4,
FLAC = 5,
WAV = 6,
M4A = 7,
ALAC = 8,
WMA = 9,
MP3 = 3,
FLAC = 4,
WAV = 5,
M4A = 6,
ALAC = 7,
WMA = 8,
}
}

View File

@@ -0,0 +1,8 @@
namespace VDownload.Core.Enums
{
public enum DefaultLocationType
{
Last,
Selected
}
}

View File

@@ -2,8 +2,8 @@
{
public enum MediaType
{
AudioVideo,
OnlyAudio,
OnlyVideo,
AudioVideo = 0,
OnlyAudio = 1,
OnlyVideo = 2,
}
}

View File

@@ -0,0 +1,8 @@
namespace VDownload.Core.Enums
{
public enum PlaylistSource
{
TwitchChannel,
Null
}
}

View File

@@ -2,8 +2,8 @@
{
public enum VideoFileExtension
{
MP4 = 1,
WMV = 2,
HEVC = 3,
MP4 = 0,
WMV = 1,
HEVC = 2,
}
}

View File

@@ -0,0 +1,9 @@
namespace VDownload.Core.Enums
{
public enum VideoSource
{
TwitchVod,
TwitchClip,
Null
}
}

View File

@@ -0,0 +1,20 @@
using System;
using VDownload.Core.Enums;
using VDownload.Core.Interfaces;
using VDownload.Core.Objects;
using Windows.Storage;
namespace VDownload.Core.EventArgsObjects
{
public class VideoAddEventArgs : EventArgs
{
public IVideoService VideoService { get; set; }
public MediaType MediaType { get; set; }
public Stream 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; }
}
}

View File

@@ -1,8 +1,9 @@
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;
namespace VDownload.Core.Interfaces
{
internal interface IPlaylistService
public interface IPlaylistService
{
#region PROPERTIES
@@ -15,9 +16,9 @@ namespace VDownload.Core.Interfaces
#region METHODS
Task GetMetadataAsync();
Task GetMetadataAsync(CancellationToken cancellationToken = default);
Task GetVideosAsync(int numberOfVideos);
Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default);
#endregion
}

View File

@@ -12,13 +12,16 @@ namespace VDownload.Core.Interfaces
{
#region PROPERTIES
// VIDEO PROPERTIES
string ID { get; }
Uri VideoUrl { get; }
string Title { get; }
string Author { get; }
DateTime Date { get; }
TimeSpan Duration { get; }
long Views { get; }
Uri Thumbnail { get; }
Stream[] Streams { get; }
#endregion
@@ -27,10 +30,10 @@ namespace VDownload.Core.Interfaces
#region METHODS
// GET VIDEO METADATA
Task GetMetadataAsync();
Task GetMetadataAsync(CancellationToken cancellationToken = default);
// GET VIDEO STREAMS
Task GetStreamsAsync();
Task GetStreamsAsync(CancellationToken cancellationToken = default);
// DOWNLOAD VIDEO
Task<StorageFile> DownloadAndTranscodeAsync(StorageFolder downloadingFolder, Stream audioVideoStream, MediaFileExtension extension, MediaType mediaType, TimeSpan trimStart, TimeSpan trimEnd, CancellationToken cancellationToken = default);

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using VDownload.Core.Enums;
using Windows.Media.Editing;
using Windows.Storage;
@@ -24,6 +25,11 @@ namespace VDownload.Core.Services
{ "media_transcoding_use_mrfcrf444_algorithm", true },
{ "media_editing_algorithm", (int)MediaTrimmingPreference.Fast },
{ "default_max_playlist_videos", 0 },
{ "default_media_type", (int)MediaType.AudioVideo },
{ "default_filename", "[<date_pub:yyyy.MM.dd>] <title>" },
{ "default_video_extension", (int)VideoFileExtension.MP4 },
{ "default_audio_extension", (int)AudioFileExtension.MP3 },
{ "default_location_type", (int)DefaultLocationType.Last },
};
#endregion

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using VDownload.Core.Enums;
namespace VDownload.Core.Services
{
public class Source
{
#region CONSTANTS
private static readonly (Regex Regex, VideoSource Type)[] VideoSources = new (Regex Regex, VideoSource Type)[]
{
(new Regex(@"^https://www.twitch.tv/videos/(?<id>\d+)"), VideoSource.TwitchVod),
(new Regex(@"^https://www.twitch.tv/\S+/clip/(?<id>[^?]+)"), VideoSource.TwitchClip),
(new Regex(@"^https://clips.twitch.tv/(?<id>[^?]+)"), VideoSource.TwitchClip),
};
private static readonly (Regex Regex, PlaylistSource Type)[] PlaylistSources = new (Regex Regex, PlaylistSource Type)[]
{
(new Regex(@"^https://www.twitch.tv/(?<id>[^?]+)"), PlaylistSource.TwitchChannel),
};
#endregion
#region METHODS
public static (VideoSource Type, string ID) GetVideoSource(string url)
{
foreach ((Regex Regex, VideoSource Type) Source in VideoSources)
{
Match sourceMatch = Source.Regex.Match(url);
if (sourceMatch.Success) return (Source.Type, sourceMatch.Groups["id"].Value);
}
return (VideoSource.Null, null);
}
public static (PlaylistSource Type, string ID) GetPlaylistSource(string url)
{
foreach ((Regex Regex, PlaylistSource Type) Source in PlaylistSources)
{
Match sourceMatch = Source.Regex.Match(url);
if (sourceMatch.Success) return (Source.Type, sourceMatch.Groups["id"].Value);
}
return (PlaylistSource.Null, null);
}
#endregion
}
}

View File

@@ -100,9 +100,15 @@ namespace VDownload.Core.Services.Sources.Twitch
return (true, login, expirationDate);
}
catch (WebException)
catch (WebException wex)
{
return (false, null, null);
if (wex.Response != null)
{
JObject wexInfo = JObject.Parse(new StreamReader(wex.Response.GetResponseStream()).ReadToEnd());
if ((int)wexInfo["status"] == 401) return (false, null, null);
else throw;
}
else throw;
}
}

View File

@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VDownload.Core.Exceptions;
using VDownload.Core.Interfaces;
@@ -37,8 +38,11 @@ namespace VDownload.Core.Services.Sources.Twitch
#region STANDARD METHODS
// GET CHANNEL METADATA
public async Task GetMetadataAsync()
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
{
// Set cancellation token
cancellationToken.ThrowIfCancellationRequested();
// Get access token
string accessToken = await Auth.ReadAccessTokenAsync();
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
@@ -62,8 +66,11 @@ namespace VDownload.Core.Services.Sources.Twitch
}
// GET CHANNEL VIDEOS
public async Task GetVideosAsync(int numberOfVideos)
public async Task GetVideosAsync(int numberOfVideos, CancellationToken cancellationToken = default)
{
// Set cancellation token
cancellationToken.ThrowIfCancellationRequested();
// Get access token
string accessToken = await Auth.ReadAccessTokenAsync();
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();

View File

@@ -41,6 +41,7 @@ namespace VDownload.Core.Services.Sources.Twitch
#region PROPERTIES
public string ID { get; private set; }
public Uri VideoUrl { get; private set; }
public string Title { get; private set; }
public string Author { get; private set; }
public DateTime Date { get; private set; }
@@ -56,8 +57,11 @@ namespace VDownload.Core.Services.Sources.Twitch
#region STANDARD METHODS
// GET CLIP METADATA
public async Task GetMetadataAsync()
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
{
// Set cancellation token
cancellationToken.ThrowIfCancellationRequested();
// Get access token
string accessToken = await Auth.ReadAccessTokenAsync();
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
@@ -75,6 +79,9 @@ namespace VDownload.Core.Services.Sources.Twitch
client.QueryString.Add("id", ID);
JToken response = JObject.Parse(await client.DownloadStringTaskAsync("https://api.twitch.tv/helix/clips")).GetValue("data")[0];
// Create unified video url
VideoUrl = new Uri($"https://clips.twitch.tv/{ID}");
// Set parameters
Title = (string)response["title"];
Author = (string)response["broadcaster_name"];
@@ -84,8 +91,11 @@ namespace VDownload.Core.Services.Sources.Twitch
Thumbnail = new Uri((string)response["thumbnail_url"]);
}
public async Task GetStreamsAsync()
public async Task GetStreamsAsync(CancellationToken cancellationToken = default)
{
// Set cancellation token
cancellationToken.ThrowIfCancellationRequested();
// Create client
WebClient client = new WebClient { Encoding = Encoding.UTF8 };
client.Headers.Add("Client-ID", Auth.GQLApiClientID);

View File

@@ -20,16 +20,8 @@ namespace VDownload.Core.Services.Sources.Twitch
{
#region CONSTANTS
// METADATA TIME FORMATS
private static readonly string[] TimeFormats = new[]
{
@"h\hm\ms\s",
@"m\ms\s",
@"s\s",
};
// STREAMS RESPONSE REGULAR EXPRESSIONS
private static readonly Regex L2Regex = new Regex(@"^#EXT-X-STREAM-INF:BANDWIDTH=\d+,CODECS=""(?<video_codec>\S+),(?<audio_codec>\S+)"",RESOLUTION=(?<width>\d+)x(?<height>\d+),VIDEO=""\w+"",FRAME-RATE=(?<frame_rate>\d+.\d+)");
private static readonly Regex L2Regex = new Regex(@"^#EXT-X-STREAM-INF:BANDWIDTH=\d+,CODECS=""(?<video_codec>\S+),(?<audio_codec>\S+)"",RESOLUTION=(?<width>\d+)x(?<height>\d+),VIDEO=""\w+""(,FRAME-RATE=(?<frame_rate>\d+.\d+))?");
// CHUNK RESPONSE REGULAR EXPRESSION
private static readonly Regex ChunkRegex = new Regex(@"#EXTINF:(?<duration>\d+.\d+),\n(?<filename>\S+.ts)");
@@ -52,6 +44,7 @@ namespace VDownload.Core.Services.Sources.Twitch
#region PROPERTIES
public string ID { get; private set; }
public Uri VideoUrl { get; private set; }
public string Title { get; private set; }
public string Author { get; private set; }
public DateTime Date { get; private set; }
@@ -67,8 +60,11 @@ namespace VDownload.Core.Services.Sources.Twitch
#region STANDARD METHODS
// GET VOD METADATA
public async Task GetMetadataAsync()
public async Task GetMetadataAsync(CancellationToken cancellationToken = default)
{
// Set cancellation token
cancellationToken.ThrowIfCancellationRequested();
// Get access token
string accessToken = await Auth.ReadAccessTokenAsync();
if (accessToken == null) throw new TwitchAccessTokenNotFoundException();
@@ -91,18 +87,24 @@ namespace VDownload.Core.Services.Sources.Twitch
}
internal void GetMetadataAsync(JToken response)
{
// Create unified video url
VideoUrl = new Uri($"https://www.twitch.tv/videos/{ID}");
// Set parameters
Title = ((string)response["title"]).Replace("\n", "");
Author = (string)response["user_name"];
Date = Convert.ToDateTime(response["created_at"]);
Duration = TimeSpan.ParseExact((string)response["duration"], TimeFormats, null);
Duration = ParseDuration((string)response["duration"]);
Views = (long)response["view_count"];
Thumbnail = (string)response["thumbnail_url"] == string.Empty ? null : new Uri((string)response["thumbnail_url"]);
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()
public async Task GetStreamsAsync(CancellationToken cancellationToken = default)
{
// Set cancellation token
cancellationToken.ThrowIfCancellationRequested();
// Create client
WebClient client = new WebClient();
client.Headers.Add("Client-Id", Auth.GQLApiClientID);
@@ -126,7 +128,7 @@ namespace VDownload.Core.Services.Sources.Twitch
Uri url = new Uri(response[i + 2]);
int width = int.Parse(line2.Groups["width"].Value);
int height = int.Parse(line2.Groups["height"].Value);
int frameRate = (int)Math.Round(double.Parse(line2.Groups["frame_rate"].Value));
int frameRate = line2.Groups["frame_rate"].Value != string.Empty ? (int)Math.Round(double.Parse(line2.Groups["frame_rate"].Value)) : 0;
string videoCodec = line2.Groups["video_codec"].Value;
string audioCodec = line2.Groups["audio_codec"].Value;
@@ -161,12 +163,7 @@ namespace VDownload.Core.Services.Sources.Twitch
List<(Uri ChunkUrl, TimeSpan ChunkDuration)> chunksList = await ExtractChunksFromM3U8Async(audioVideoStream.Url);
// Passive trim
if ((bool)Config.GetValue("twitch_vod_passive_trim"))
{
var trimResult = PassiveVideoTrim(chunksList, trimStart, trimEnd, Duration);
trimStart = trimResult.TrimStart;
trimEnd = trimResult.TrimEnd;
}
if ((bool)Config.GetValue("twitch_vod_passive_trim")) (trimStart, trimEnd) = PassiveVideoTrim(chunksList, trimStart, trimEnd, Duration);
// Download
StorageFile rawFile = await downloadingFolder.CreateFileAsync("raw.ts");
@@ -301,6 +298,20 @@ namespace VDownload.Core.Services.Sources.Twitch
});
}
// PARSE DURATION TO SECONDS
private static TimeSpan ParseDuration(string duration)
{
char[] separators = { 'h', 'm', 's' };
string[] durationParts = duration.Split(separators, StringSplitOptions.RemoveEmptyEntries).Reverse().ToArray();
TimeSpan timeSpan = new TimeSpan(
durationParts.Count() > 2 ? int.Parse(durationParts[2]) : 0,
durationParts.Count() > 1 ? int.Parse(durationParts[1]) : 0,
int.Parse(durationParts[0]));
return timeSpan;
}
#endregion

View File

@@ -121,10 +121,14 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Enums\AudioFileExtension.cs" />
<Compile Include="Enums\DefaultLocationType.cs" />
<Compile Include="Enums\MediaFileExtension.cs" />
<Compile Include="Enums\MediaType.cs" />
<Compile Include="Enums\PlaylistSource.cs" />
<Compile Include="Enums\StreamType.cs" />
<Compile Include="Enums\VideoFileExtension.cs" />
<Compile Include="Enums\VideoSource.cs" />
<Compile Include="EventArgsObjects\VideoAddEventArgs.cs" />
<Compile Include="EventArgsObjects\VideoSearchEventArgs.cs" />
<Compile Include="EventArgsObjects\PlaylistSearchEventArgs.cs" />
<Compile Include="Exceptions\TwitchAccessTokenNotFoundException.cs" />
@@ -136,6 +140,7 @@
<Compile Include="Objects\Stream.cs" />
<Compile Include="Services\Config.cs" />
<Compile Include="Services\MediaProcessor.cs" />
<Compile Include="Services\Source.cs" />
<Compile Include="Services\Sources\Twitch\Auth.cs" />
<Compile Include="Services\Sources\Twitch\Channel.cs" />
<Compile Include="Services\Sources\Twitch\Clip.cs" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,5 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600">
<g transform="matrix(1.1995,0,0,1.1995,-161.207,-161.6)">
<path d="M800,133.333C616.497,133.333 466.667,283.164 466.667,466.667C466.667,650.169 616.497,800 800,800C983.503,800 1133.33,650.169 1133.33,466.667C1133.33,283.164 983.503,133.333 800,133.333ZM800,233.333C929.459,233.333 1033.33,337.208 1033.33,466.667C1033.33,596.125 929.459,700 800,700C670.541,700 566.667,596.125 566.667,466.667C566.667,337.208 670.541,233.333 800,233.333ZM399.284,933.333C326.623,933.333 266.667,993.29 266.667,1065.95L266.667,1116.67C266.667,1236.81 342.642,1329.74 443.555,1385.42C544.467,1441.09 672.27,1466.67 800,1466.67C927.73,1466.67 1055.53,1441.09 1156.44,1385.42C1242.49,1337.94 1307.25,1262.09 1325.39,1166.67L1333.4,1166.67L1333.4,1065.95C1333.4,993.29 1273.38,933.333 1200.72,933.333L399.284,933.333ZM399.284,1033.33L1200.72,1033.33C1219.32,1033.33 1233.4,1047.34 1233.4,1065.95L1233.4,1066.67L1233.33,1066.67L1233.33,1116.67C1233.33,1196.53 1188.48,1253.59 1108.14,1297.92C1027.8,1342.24 913.937,1366.67 800,1366.67C686.063,1366.67 572.2,1342.24 491.862,1297.92C411.524,1253.59 366.667,1196.53 366.667,1116.67L366.667,1065.95C366.667,1047.34 380.678,1033.33 399.284,1033.33Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,5 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600">
<g transform="matrix(1.1995,0,0,1.1995,-161.207,-161.6)">
<path d="M800,133.333C616.497,133.333 466.667,283.164 466.667,466.667C466.667,650.169 616.497,800 800,800C983.503,800 1133.33,650.169 1133.33,466.667C1133.33,283.164 983.503,133.333 800,133.333ZM800,233.333C929.459,233.333 1033.33,337.208 1033.33,466.667C1033.33,596.125 929.459,700 800,700C670.541,700 566.667,596.125 566.667,466.667C566.667,337.208 670.541,233.333 800,233.333ZM399.284,933.333C326.623,933.333 266.667,993.29 266.667,1065.95L266.667,1116.67C266.667,1236.81 342.642,1329.74 443.555,1385.42C544.467,1441.09 672.27,1466.67 800,1466.67C927.73,1466.67 1055.53,1441.09 1156.44,1385.42C1242.49,1337.94 1307.25,1262.09 1325.39,1166.67L1333.4,1166.67L1333.4,1065.95C1333.4,993.29 1273.38,933.333 1200.72,933.333L399.284,933.333ZM399.284,1033.33L1200.72,1033.33C1219.32,1033.33 1233.4,1047.34 1233.4,1065.95L1233.4,1066.67L1233.33,1066.67L1233.33,1116.67C1233.33,1196.53 1188.48,1253.59 1108.14,1297.92C1027.8,1342.24 913.937,1366.67 800,1366.67C686.063,1366.67 572.2,1342.24 491.862,1297.92C411.524,1253.59 366.667,1196.53 366.667,1116.67L366.667,1065.95C366.667,1047.34 380.678,1033.33 399.284,1033.33Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.3325,0,0,1.3325,-266.5,-266.5)">
<path d="M416.667,200C297.602,200 200,297.602 200,416.667L200,1183.33C200,1302.4 297.602,1400 416.667,1400L1183.33,1400C1302.4,1400 1400,1302.4 1400,1183.33L1400,416.667C1400,297.602 1302.4,200 1183.33,200L416.667,200ZM416.667,300L1183.33,300C1248.34,300 1300,351.665 1300,416.667L1300,466.667L300,466.667L300,416.667C300,351.665 351.665,300 416.667,300ZM300,566.667L1300,566.667L1300,1183.33C1300,1248.34 1248.34,1300 1183.33,1300L416.667,1300C351.665,1300 300,1248.34 300,1183.33L300,566.667ZM516.667,700C470.951,700 433.333,737.618 433.333,783.333C433.333,829.049 470.951,866.667 516.667,866.667C562.382,866.667 600,829.049 600,783.333C600,737.618 562.382,700 516.667,700ZM800,700C754.285,700 716.667,737.618 716.667,783.333C716.667,829.049 754.285,866.667 800,866.667C845.715,866.667 883.333,829.049 883.333,783.333C883.333,737.618 845.715,700 800,700ZM1083.33,700C1037.62,700 1000,737.618 1000,783.333C1000,829.049 1037.62,866.667 1083.33,866.667C1129.05,866.667 1166.67,829.049 1166.67,783.333C1166.67,737.618 1129.05,700 1083.33,700ZM516.667,1000C470.951,1000 433.333,1037.62 433.333,1083.33C433.333,1129.05 470.951,1166.67 516.667,1166.67C562.382,1166.67 600,1129.05 600,1083.33C600,1037.62 562.382,1000 516.667,1000ZM800,1000C754.285,1000 716.667,1037.62 716.667,1083.33C716.667,1129.05 754.285,1166.67 800,1166.67C845.715,1166.67 883.333,1129.05 883.333,1083.33C883.333,1037.62 845.715,1000 800,1000Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.3325,0,0,1.3325,-266.5,-266.5)">
<path d="M416.667,200C297.602,200 200,297.602 200,416.667L200,1183.33C200,1302.4 297.602,1400 416.667,1400L1183.33,1400C1302.4,1400 1400,1302.4 1400,1183.33L1400,416.667C1400,297.602 1302.4,200 1183.33,200L416.667,200ZM416.667,300L1183.33,300C1248.34,300 1300,351.665 1300,416.667L1300,466.667L300,466.667L300,416.667C300,351.665 351.665,300 416.667,300ZM300,566.667L1300,566.667L1300,1183.33C1300,1248.34 1248.34,1300 1183.33,1300L416.667,1300C351.665,1300 300,1248.34 300,1183.33L300,566.667ZM516.667,700C470.951,700 433.333,737.618 433.333,783.333C433.333,829.049 470.951,866.667 516.667,866.667C562.382,866.667 600,829.049 600,783.333C600,737.618 562.382,700 516.667,700ZM800,700C754.285,700 716.667,737.618 716.667,783.333C716.667,829.049 754.285,866.667 800,866.667C845.715,866.667 883.333,829.049 883.333,783.333C883.333,737.618 845.715,700 800,700ZM1083.33,700C1037.62,700 1000,737.618 1000,783.333C1000,829.049 1037.62,866.667 1083.33,866.667C1129.05,866.667 1166.67,829.049 1166.67,783.333C1166.67,737.618 1129.05,700 1083.33,700ZM516.667,1000C470.951,1000 433.333,1037.62 433.333,1083.33C433.333,1129.05 470.951,1166.67 516.667,1166.67C562.382,1166.67 600,1129.05 600,1083.33C600,1037.62 562.382,1000 516.667,1000ZM800,1000C754.285,1000 716.667,1037.62 716.667,1083.33C716.667,1129.05 754.285,1166.67 800,1166.67C845.715,1166.67 883.333,1129.05 883.333,1083.33C883.333,1037.62 845.715,1000 800,1000Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-163.7,-163.7)">
<path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM782.552,399.284C755.148,399.712 732.94,422.595 733.333,450L733.333,850C733.336,877.428 755.905,899.997 783.333,900L1050,900C1050.24,900.003 1050.47,900.005 1050.71,900.005C1078.14,900.005 1100.71,877.432 1100.71,850C1100.71,822.568 1078.14,799.995 1050.71,799.995C1050.47,799.995 1050.24,799.997 1050,800L833.333,800L833.333,450C833.337,449.761 833.338,449.522 833.338,449.283C833.338,421.851 810.765,399.278 783.333,399.278C783.073,399.278 782.813,399.28 782.552,399.284Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-163.7,-163.7)">
<path d="M800,133.333C432.402,133.333 133.333,432.402 133.333,800C133.333,1167.6 432.402,1466.67 800,1466.67C1167.6,1466.67 1466.67,1167.6 1466.67,800C1466.67,432.402 1167.6,133.333 800,133.333ZM800,233.333C1113.55,233.333 1366.67,486.446 1366.67,800C1366.67,1113.55 1113.55,1366.67 800,1366.67C486.446,1366.67 233.333,1113.55 233.333,800C233.333,486.446 486.446,233.333 800,233.333ZM782.552,399.284C755.148,399.712 732.94,422.595 733.333,450L733.333,850C733.336,877.428 755.905,899.997 783.333,900L1050,900C1050.24,900.003 1050.47,900.005 1050.71,900.005C1078.14,900.005 1100.71,877.432 1100.71,850C1100.71,822.568 1078.14,799.995 1050.71,799.995C1050.47,799.995 1050.24,799.997 1050,800L833.333,800L833.333,450C833.337,449.761 833.338,449.522 833.338,449.283C833.338,421.851 810.765,399.278 783.333,399.278C783.073,399.278 782.813,399.28 782.552,399.284Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 6667 6667" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M5899.29,2.12l757.274,756.327l-5905.76,5897.97l-757.274,-756.328l5905.76,-5897.97Z" style="fill:#f44336;fill-rule:nonzero;"/><path d="M6656.01,5900.61l-757.241,756.057l-5904.98,-5899.05l757.241,-756.057l5904.98,5899.05Z" style="fill:#f44336;fill-rule:nonzero;"/></svg>

After

Width:  |  Height:  |  Size: 721 B

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-160.6,-160.7)">
<path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 911 B

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.20025,0,0,1.20025,-160.6,-160.7)">
<path d="M416.667,133.333C334.417,133.333 266.667,201.083 266.667,283.333L266.667,1316.67C266.667,1398.92 334.417,1466.67 416.667,1466.67L1183.33,1466.67C1265.58,1466.67 1333.33,1398.92 1333.33,1316.67L1333.33,616.667C1333.33,603.412 1328.06,590.687 1318.68,581.315L885.352,147.982C875.98,138.609 863.255,133.336 850,133.333L416.667,133.333ZM416.667,233.333L800,233.333L800,516.667C800,598.917 867.75,666.667 950,666.667L1233.33,666.667L1233.33,1316.67C1233.33,1344.88 1211.55,1366.67 1183.33,1366.67L416.667,1366.67C388.45,1366.67 366.667,1344.88 366.667,1316.67L366.667,283.333C366.667,255.117 388.45,233.333 416.667,233.333ZM900,304.036L1162.63,566.667L950,566.667C921.783,566.667 900,544.883 900,516.667L900,304.036Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 911 B

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.19956,0,0,1.19956,-159.645,-159.608)">
<path d="M800,133.333C487.642,133.333 233.333,387.642 233.333,700C233.333,840.226 284.777,968.828 369.271,1067.71L369.531,1067.97L369.727,1068.23C369.727,1068.23 610.791,1343.3 696.615,1425.19C754.086,1479.99 845.849,1479.99 903.32,1425.19C1001.15,1331.89 1230.34,1068.1 1230.34,1068.1L1230.47,1067.9L1230.66,1067.71C1315.23,968.827 1366.67,840.226 1366.67,700C1366.67,387.642 1112.36,133.333 800,133.333ZM800,233.333C1058.31,233.333 1266.67,441.692 1266.67,700C1266.67,815.84 1224.45,921.086 1154.62,1002.73C1154.17,1003.25 919.668,1271.45 834.31,1352.87C814.514,1371.74 785.421,1371.74 765.625,1352.87C694.286,1284.79 446.037,1003.56 445.313,1002.73L445.247,1002.67C375.511,921.028 333.333,815.808 333.333,700C333.333,441.692 541.692,233.333 800,233.333ZM800,500C737.5,500 684.294,525.238 650.13,563.672C615.967,602.106 600,651.389 600,700C600,748.611 615.967,797.894 650.13,836.328C684.294,874.762 737.5,900 800,900C862.5,900 915.706,874.762 949.87,836.328C984.033,797.894 1000,748.611 1000,700C1000,651.389 984.033,602.106 949.87,563.672C915.706,525.238 862.5,500 800,500ZM800,600C837.5,600 859.294,612.262 875.13,630.078C890.967,647.894 900,673.611 900,700C900,726.389 890.967,752.106 875.13,769.922C859.294,787.738 837.5,800 800,800C762.5,800 740.706,787.738 724.87,769.922C709.033,752.106 700,726.389 700,700C700,673.611 709.033,647.894 724.87,630.078C740.706,612.262 762.5,600 800,600Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.19956,0,0,1.19956,-159.645,-159.608)">
<path d="M800,133.333C487.642,133.333 233.333,387.642 233.333,700C233.333,840.226 284.777,968.828 369.271,1067.71L369.531,1067.97L369.727,1068.23C369.727,1068.23 610.791,1343.3 696.615,1425.19C754.086,1479.99 845.849,1479.99 903.32,1425.19C1001.15,1331.89 1230.34,1068.1 1230.34,1068.1L1230.47,1067.9L1230.66,1067.71C1315.23,968.827 1366.67,840.226 1366.67,700C1366.67,387.642 1112.36,133.333 800,133.333ZM800,233.333C1058.31,233.333 1266.67,441.692 1266.67,700C1266.67,815.84 1224.45,921.086 1154.62,1002.73C1154.17,1003.25 919.668,1271.45 834.31,1352.87C814.514,1371.74 785.421,1371.74 765.625,1352.87C694.286,1284.79 446.037,1003.56 445.313,1002.73L445.247,1002.67C375.511,921.028 333.333,815.808 333.333,700C333.333,441.692 541.692,233.333 800,233.333ZM800,500C737.5,500 684.294,525.238 650.13,563.672C615.967,602.106 600,651.389 600,700C600,748.611 615.967,797.894 650.13,836.328C684.294,874.762 737.5,900 800,900C862.5,900 915.706,874.762 949.87,836.328C984.033,797.894 1000,748.611 1000,700C1000,651.389 984.033,602.106 949.87,563.672C915.706,525.238 862.5,500 800,500ZM800,600C837.5,600 859.294,612.262 875.13,630.078C890.967,647.894 900,673.611 900,700C900,726.389 890.967,752.106 875.13,769.922C859.294,787.738 837.5,800 800,800C762.5,800 740.706,787.738 724.87,769.922C709.033,752.106 700,726.389 700,700C700,673.611 709.033,647.894 724.87,630.078C740.706,612.262 762.5,600 800,600Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.23077,0,0,1.23077,-204.462,-187.385)">
<path d="M350,200C249.341,200 166.667,282.674 166.667,383.333L166.667,1216.67C166.667,1317.33 249.341,1400 350,1400L1283.33,1400C1383.99,1400 1466.67,1317.33 1466.67,1216.67L1466.67,383.333C1466.67,282.674 1383.99,200 1283.33,200L350,200ZM350,300L1283.33,300C1329.94,300 1366.67,336.726 1366.67,383.333L1366.67,1216.67C1366.67,1263.27 1329.94,1300 1283.33,1300L350,1300C303.392,1300 266.667,1263.27 266.667,1216.67L266.667,383.333C266.667,336.726 303.392,300 350,300ZM366.667,350C348.267,350 333.333,364.933 333.333,383.333L333.333,416.667C333.333,435.067 348.267,450 366.667,450L400,450C418.4,450 433.333,435.067 433.333,416.667L433.333,383.333C433.333,364.933 418.4,350 400,350L366.667,350ZM1233.33,350C1214.93,350 1200,364.933 1200,383.333L1200,416.667C1200,435.067 1214.93,450 1233.33,450L1266.67,450C1285.07,450 1300,435.067 1300,416.667L1300,383.333C1300,364.933 1285.07,350 1266.67,350L1233.33,350ZM683.333,533.398C668.77,533.398 654.043,537.201 640.951,544.922C615.478,559.97 600,587.195 600,616.732L600,983.398C600,1012.87 615.401,1039.96 640.885,1055.01C666.823,1070.43 697.284,1070.78 723.503,1056.38L1056.84,873.047L1056.9,872.982C1083.33,858.401 1100,830.405 1100,800.065C1100,769.725 1083.33,741.665 1056.9,727.083L1056.84,727.018L723.438,543.685L723.372,543.62C710.907,536.807 697.1,533.398 683.333,533.398ZM366.667,550C348.267,550 333.333,564.933 333.333,583.333L333.333,616.667C333.333,635.067 348.267,650 366.667,650L400,650C418.4,650 433.333,635.067 433.333,616.667L433.333,583.333C433.333,564.933 418.4,550 400,550L366.667,550ZM1233.33,550C1214.93,550 1200,564.933 1200,583.333L1200,616.667C1200,635.067 1214.93,650 1233.33,650L1266.67,650C1285.07,650 1300,635.067 1300,616.667L1300,583.333C1300,564.933 1285.07,550 1266.67,550L1233.33,550ZM700,644.857L982.096,800L700,955.208L700,644.857ZM366.667,750C348.267,750 333.333,764.933 333.333,783.333L333.333,816.667C333.333,835.067 348.267,850 366.667,850L400,850C418.4,850 433.333,835.067 433.333,816.667L433.333,783.333C433.333,764.933 418.4,750 400,750L366.667,750ZM1233.33,750C1214.93,750 1200,764.933 1200,783.333L1200,816.667C1200,835.067 1214.93,850 1233.33,850L1266.67,850C1285.07,850 1300,835.067 1300,816.667L1300,783.333C1300,764.933 1285.07,750 1266.67,750L1233.33,750ZM366.667,950C348.267,950 333.333,964.933 333.333,983.333L333.333,1016.67C333.333,1035.07 348.267,1050 366.667,1050L400,1050C418.4,1050 433.333,1035.07 433.333,1016.67L433.333,983.333C433.333,964.933 418.4,950 400,950L366.667,950ZM1233.33,950C1214.93,950 1200,964.933 1200,983.333L1200,1016.67C1200,1035.07 1214.93,1050 1233.33,1050L1266.67,1050C1285.07,1050 1300,1035.07 1300,1016.67L1300,983.333C1300,964.933 1285.07,950 1266.67,950L1233.33,950ZM366.667,1150C348.267,1150 333.333,1164.93 333.333,1183.33L333.333,1216.67C333.333,1235.07 348.267,1250 366.667,1250L400,1250C418.4,1250 433.333,1235.07 433.333,1216.67L433.333,1183.33C433.333,1164.93 418.4,1150 400,1150L366.667,1150ZM1233.33,1150C1214.93,1150 1200,1164.93 1200,1183.33L1200,1216.67C1200,1235.07 1214.93,1250 1233.33,1250L1266.67,1250C1285.07,1250 1300,1235.07 1300,1216.67L1300,1183.33C1300,1164.93 1285.07,1150 1266.67,1150L1233.33,1150Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.23077,0,0,1.23077,-204.462,-187.385)">
<path d="M350,200C249.341,200 166.667,282.674 166.667,383.333L166.667,1216.67C166.667,1317.33 249.341,1400 350,1400L1283.33,1400C1383.99,1400 1466.67,1317.33 1466.67,1216.67L1466.67,383.333C1466.67,282.674 1383.99,200 1283.33,200L350,200ZM350,300L1283.33,300C1329.94,300 1366.67,336.726 1366.67,383.333L1366.67,1216.67C1366.67,1263.27 1329.94,1300 1283.33,1300L350,1300C303.392,1300 266.667,1263.27 266.667,1216.67L266.667,383.333C266.667,336.726 303.392,300 350,300ZM366.667,350C348.267,350 333.333,364.933 333.333,383.333L333.333,416.667C333.333,435.067 348.267,450 366.667,450L400,450C418.4,450 433.333,435.067 433.333,416.667L433.333,383.333C433.333,364.933 418.4,350 400,350L366.667,350ZM1233.33,350C1214.93,350 1200,364.933 1200,383.333L1200,416.667C1200,435.067 1214.93,450 1233.33,450L1266.67,450C1285.07,450 1300,435.067 1300,416.667L1300,383.333C1300,364.933 1285.07,350 1266.67,350L1233.33,350ZM683.333,533.398C668.77,533.398 654.043,537.201 640.951,544.922C615.478,559.97 600,587.195 600,616.732L600,983.398C600,1012.87 615.401,1039.96 640.885,1055.01C666.823,1070.43 697.284,1070.78 723.503,1056.38L1056.84,873.047L1056.9,872.982C1083.33,858.401 1100,830.405 1100,800.065C1100,769.725 1083.33,741.665 1056.9,727.083L1056.84,727.018L723.438,543.685L723.372,543.62C710.907,536.807 697.1,533.398 683.333,533.398ZM366.667,550C348.267,550 333.333,564.933 333.333,583.333L333.333,616.667C333.333,635.067 348.267,650 366.667,650L400,650C418.4,650 433.333,635.067 433.333,616.667L433.333,583.333C433.333,564.933 418.4,550 400,550L366.667,550ZM1233.33,550C1214.93,550 1200,564.933 1200,583.333L1200,616.667C1200,635.067 1214.93,650 1233.33,650L1266.67,650C1285.07,650 1300,635.067 1300,616.667L1300,583.333C1300,564.933 1285.07,550 1266.67,550L1233.33,550ZM700,644.857L982.096,800L700,955.208L700,644.857ZM366.667,750C348.267,750 333.333,764.933 333.333,783.333L333.333,816.667C333.333,835.067 348.267,850 366.667,850L400,850C418.4,850 433.333,835.067 433.333,816.667L433.333,783.333C433.333,764.933 418.4,750 400,750L366.667,750ZM1233.33,750C1214.93,750 1200,764.933 1200,783.333L1200,816.667C1200,835.067 1214.93,850 1233.33,850L1266.67,850C1285.07,850 1300,835.067 1300,816.667L1300,783.333C1300,764.933 1285.07,750 1266.67,750L1233.33,750ZM366.667,950C348.267,950 333.333,964.933 333.333,983.333L333.333,1016.67C333.333,1035.07 348.267,1050 366.667,1050L400,1050C418.4,1050 433.333,1035.07 433.333,1016.67L433.333,983.333C433.333,964.933 418.4,950 400,950L366.667,950ZM1233.33,950C1214.93,950 1200,964.933 1200,983.333L1200,1016.67C1200,1035.07 1214.93,1050 1233.33,1050L1266.67,1050C1285.07,1050 1300,1035.07 1300,1016.67L1300,983.333C1300,964.933 1285.07,950 1266.67,950L1233.33,950ZM366.667,1150C348.267,1150 333.333,1164.93 333.333,1183.33L333.333,1216.67C333.333,1235.07 348.267,1250 366.667,1250L400,1250C418.4,1250 433.333,1235.07 433.333,1216.67L433.333,1183.33C433.333,1164.93 418.4,1150 400,1150L366.667,1150ZM1233.33,1150C1214.93,1150 1200,1164.93 1200,1183.33L1200,1216.67C1200,1235.07 1214.93,1250 1233.33,1250L1266.67,1250C1285.07,1250 1300,1235.07 1300,1216.67L1300,1183.33C1300,1164.93 1285.07,1150 1266.67,1150L1233.33,1150Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.14286,0,0,1.14286,-114.286,-114.286)">
<path d="M283.333,233.333C182.674,233.333 100,316.008 100,416.667L100,1183.33C100,1283.99 182.674,1366.67 283.333,1366.67L1316.67,1366.67C1417.33,1366.67 1500,1283.99 1500,1183.33L1500,416.667C1500,316.008 1417.33,233.333 1316.67,233.333L283.333,233.333ZM283.333,333.333L716.667,333.333C763.274,333.333 800,370.059 800,416.667L800,716.667C800,763.274 763.274,800 716.667,800L283.333,800C236.726,800 200,763.274 200,716.667L200,416.667C200,410.841 200.597,405.139 201.693,399.674C209.359,361.422 242.552,333.333 283.333,333.333ZM879.557,333.333L1016.67,333.333C1063.27,333.333 1100,370.059 1100,416.667L1100,950C1100,996.608 1063.27,1033.33 1016.67,1033.33L283.333,1033.33C236.726,1033.33 200,996.608 200,950L200,879.557C225.076,892.51 253.36,900 283.333,900L716.667,900C817.326,900 900,817.326 900,716.667L900,416.667C900,386.693 892.51,358.409 879.557,333.333ZM1179.56,333.333L1316.67,333.333C1363.27,333.333 1400,370.059 1400,416.667L1400,1183.33C1400,1229.94 1363.27,1266.67 1316.67,1266.67L283.333,1266.67C236.726,1266.67 200,1229.94 200,1183.33L200,1112.89C225.076,1125.84 253.36,1133.33 283.333,1133.33L1016.67,1133.33C1117.33,1133.33 1200,1050.66 1200,950L1200,416.667C1200,386.693 1192.51,358.409 1179.56,333.333Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.14286,0,0,1.14286,-114.286,-114.286)">
<path d="M283.333,233.333C182.674,233.333 100,316.008 100,416.667L100,1183.33C100,1283.99 182.674,1366.67 283.333,1366.67L1316.67,1366.67C1417.33,1366.67 1500,1283.99 1500,1183.33L1500,416.667C1500,316.008 1417.33,233.333 1316.67,233.333L283.333,233.333ZM283.333,333.333L716.667,333.333C763.274,333.333 800,370.059 800,416.667L800,716.667C800,763.274 763.274,800 716.667,800L283.333,800C236.726,800 200,763.274 200,716.667L200,416.667C200,410.841 200.597,405.139 201.693,399.674C209.359,361.422 242.552,333.333 283.333,333.333ZM879.557,333.333L1016.67,333.333C1063.27,333.333 1100,370.059 1100,416.667L1100,950C1100,996.608 1063.27,1033.33 1016.67,1033.33L283.333,1033.33C236.726,1033.33 200,996.608 200,950L200,879.557C225.076,892.51 253.36,900 283.333,900L716.667,900C817.326,900 900,817.326 900,716.667L900,416.667C900,386.693 892.51,358.409 879.557,333.333ZM1179.56,333.333L1316.67,333.333C1363.27,333.333 1400,370.059 1400,416.667L1400,1183.33C1400,1229.94 1363.27,1266.67 1316.67,1266.67L283.333,1266.67C236.726,1266.67 200,1229.94 200,1183.33L200,1112.89C225.076,1125.84 253.36,1133.33 283.333,1133.33L1016.67,1133.33C1117.33,1133.33 1200,1050.66 1200,950L1200,416.667C1200,386.693 1192.51,358.409 1179.56,333.333Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.11651,0,0,1.11651,-114.651,-186.605)">
<path d="M283.333,233.333C182.233,233.333 100,315.6 100,416.667L100,1183.33C100,1284.4 182.233,1366.67 283.333,1366.67L733.333,1366.67C733.333,1330.87 741.718,1297.03 756.185,1266.67L283.333,1266.67C237.4,1266.67 200,1229.3 200,1183.33L200,416.667C200,370.7 237.4,333.333 283.333,333.333L1316.67,333.333C1362.6,333.333 1400,370.7 1400,416.667L1400,701.693C1418.73,704.393 1437.12,711.141 1453.32,723.307C1479.05,742.607 1494.56,770.5 1498.83,800L1500,800L1500,416.667C1500,315.6 1417.77,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1386.2,765.625C1369.3,764.93 1353.15,772.857 1343.36,786.654L1158.98,1032.42L955.273,784.896C945.741,772.99 931.267,766.078 916.016,766.146C888.67,766.271 866.239,788.805 866.239,816.15C866.239,827.969 870.429,839.413 878.06,848.438L1097.2,1114.78L1025.72,1210.03C1007.54,1203.65 987.694,1200 966.667,1200C915.278,1200 870.405,1221.07 841.797,1253.26C813.189,1285.44 800,1326.39 800,1366.67C800,1406.94 813.189,1447.89 841.797,1480.08C870.405,1512.26 915.278,1533.33 966.667,1533.33C1018.06,1533.33 1062.93,1512.26 1091.54,1480.08C1120.14,1447.89 1133.33,1406.94 1133.33,1366.67C1133.33,1333.5 1123.82,1300.16 1104.69,1271.35L1162.57,1194.21L1227.41,1273.05C1209.06,1301.47 1200,1334.16 1200,1366.67C1200,1406.94 1213.19,1447.89 1241.8,1480.08C1270.4,1512.26 1315.28,1533.33 1366.67,1533.33C1418.06,1533.33 1462.93,1512.26 1491.54,1480.08C1520.14,1447.89 1533.33,1406.94 1533.33,1366.67C1533.33,1326.39 1520.14,1285.44 1491.54,1253.26C1462.93,1221.07 1418.06,1200 1366.67,1200C1344.9,1200 1324.42,1203.94 1305.73,1210.74L1224.35,1111.91L1423.31,846.68C1430.33,837.839 1434.15,826.876 1434.15,815.588C1434.15,788.931 1412.83,766.72 1386.2,765.625ZM283.333,1100C264.933,1100 250,1114.93 250,1133.33L250,1166.67C250,1185.07 264.933,1200 283.333,1200L316.667,1200C335.067,1200 350,1185.07 350,1166.67L350,1133.33C350,1114.93 335.067,1100 316.667,1100L283.333,1100ZM483.333,1100C464.933,1100 450,1114.93 450,1133.33L450,1166.67C450,1185.07 464.933,1200 483.333,1200L516.667,1200C535.067,1200 550,1185.07 550,1166.67L550,1133.33C550,1114.93 535.067,1100 516.667,1100L483.333,1100ZM683.333,1100C664.933,1100 650,1114.93 650,1133.33L650,1166.67C650,1185.07 664.933,1200 683.333,1200L716.667,1200C735.067,1200 750,1185.07 750,1166.67L750,1133.33C750,1114.93 735.067,1100 716.667,1100L683.333,1100ZM966.667,1300C979.861,1300 989.903,1302.06 997.852,1305.53C1003.13,1312.89 1010.31,1318.67 1018.62,1322.27C1027.7,1333.76 1033.33,1349.82 1033.33,1366.67C1033.33,1384.72 1027.08,1402.11 1016.8,1413.67C1006.52,1425.24 993.056,1433.33 966.667,1433.33C940.278,1433.33 926.817,1425.24 916.536,1413.67C906.256,1402.11 900,1384.72 900,1366.67C900,1348.61 906.256,1331.23 916.536,1319.66C926.817,1308.1 940.278,1300 966.667,1300ZM1366.67,1300C1393.06,1300 1406.52,1308.1 1416.8,1319.66C1427.08,1331.23 1433.33,1348.61 1433.33,1366.67C1433.33,1384.72 1427.08,1402.11 1416.8,1413.67C1406.52,1425.24 1393.06,1433.33 1366.67,1433.33C1340.28,1433.33 1326.82,1425.24 1316.54,1413.67C1306.26,1402.11 1300,1384.72 1300,1366.67C1300,1349.41 1305.81,1332.89 1315.3,1321.35C1322.81,1317.95 1329.37,1312.74 1334.38,1306.18C1342.54,1302.35 1352.73,1300 1366.67,1300Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,3 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><g transform="matrix(1.11651,0,0,1.11651,-114.651,-186.605)">
<path d="M283.333,233.333C182.233,233.333 100,315.6 100,416.667L100,1183.33C100,1284.4 182.233,1366.67 283.333,1366.67L733.333,1366.67C733.333,1330.87 741.718,1297.03 756.185,1266.67L283.333,1266.67C237.4,1266.67 200,1229.3 200,1183.33L200,416.667C200,370.7 237.4,333.333 283.333,333.333L1316.67,333.333C1362.6,333.333 1400,370.7 1400,416.667L1400,701.693C1418.73,704.393 1437.12,711.141 1453.32,723.307C1479.05,742.607 1494.56,770.5 1498.83,800L1500,800L1500,416.667C1500,315.6 1417.77,233.333 1316.67,233.333L283.333,233.333ZM283.333,400C264.933,400 250,414.933 250,433.333L250,466.667C250,485.067 264.933,500 283.333,500L316.667,500C335.067,500 350,485.067 350,466.667L350,433.333C350,414.933 335.067,400 316.667,400L283.333,400ZM483.333,400C464.933,400 450,414.933 450,433.333L450,466.667C450,485.067 464.933,500 483.333,500L516.667,500C535.067,500 550,485.067 550,466.667L550,433.333C550,414.933 535.067,400 516.667,400L483.333,400ZM683.333,400C664.933,400 650,414.933 650,433.333L650,466.667C650,485.067 664.933,500 683.333,500L716.667,500C735.067,500 750,485.067 750,466.667L750,433.333C750,414.933 735.067,400 716.667,400L683.333,400ZM883.333,400C864.933,400 850,414.933 850,433.333L850,466.667C850,485.067 864.933,500 883.333,500L916.667,500C935.067,500 950,485.067 950,466.667L950,433.333C950,414.933 935.067,400 916.667,400L883.333,400ZM1083.33,400C1064.93,400 1050,414.933 1050,433.333L1050,466.667C1050,485.067 1064.93,500 1083.33,500L1116.67,500C1135.07,500 1150,485.067 1150,466.667L1150,433.333C1150,414.933 1135.07,400 1116.67,400L1083.33,400ZM1283.33,400C1264.93,400 1250,414.933 1250,433.333L1250,466.667C1250,485.067 1264.93,500 1283.33,500L1316.67,500C1335.07,500 1350,485.067 1350,466.667L1350,433.333C1350,414.933 1335.07,400 1316.67,400L1283.33,400ZM1386.2,765.625C1369.3,764.93 1353.15,772.857 1343.36,786.654L1158.98,1032.42L955.273,784.896C945.741,772.99 931.267,766.078 916.016,766.146C888.67,766.271 866.239,788.805 866.239,816.15C866.239,827.969 870.429,839.413 878.06,848.438L1097.2,1114.78L1025.72,1210.03C1007.54,1203.65 987.694,1200 966.667,1200C915.278,1200 870.405,1221.07 841.797,1253.26C813.189,1285.44 800,1326.39 800,1366.67C800,1406.94 813.189,1447.89 841.797,1480.08C870.405,1512.26 915.278,1533.33 966.667,1533.33C1018.06,1533.33 1062.93,1512.26 1091.54,1480.08C1120.14,1447.89 1133.33,1406.94 1133.33,1366.67C1133.33,1333.5 1123.82,1300.16 1104.69,1271.35L1162.57,1194.21L1227.41,1273.05C1209.06,1301.47 1200,1334.16 1200,1366.67C1200,1406.94 1213.19,1447.89 1241.8,1480.08C1270.4,1512.26 1315.28,1533.33 1366.67,1533.33C1418.06,1533.33 1462.93,1512.26 1491.54,1480.08C1520.14,1447.89 1533.33,1406.94 1533.33,1366.67C1533.33,1326.39 1520.14,1285.44 1491.54,1253.26C1462.93,1221.07 1418.06,1200 1366.67,1200C1344.9,1200 1324.42,1203.94 1305.73,1210.74L1224.35,1111.91L1423.31,846.68C1430.33,837.839 1434.15,826.876 1434.15,815.588C1434.15,788.931 1412.83,766.72 1386.2,765.625ZM283.333,1100C264.933,1100 250,1114.93 250,1133.33L250,1166.67C250,1185.07 264.933,1200 283.333,1200L316.667,1200C335.067,1200 350,1185.07 350,1166.67L350,1133.33C350,1114.93 335.067,1100 316.667,1100L283.333,1100ZM483.333,1100C464.933,1100 450,1114.93 450,1133.33L450,1166.67C450,1185.07 464.933,1200 483.333,1200L516.667,1200C535.067,1200 550,1185.07 550,1166.67L550,1133.33C550,1114.93 535.067,1100 516.667,1100L483.333,1100ZM683.333,1100C664.933,1100 650,1114.93 650,1133.33L650,1166.67C650,1185.07 664.933,1200 683.333,1200L716.667,1200C735.067,1200 750,1185.07 750,1166.67L750,1133.33C750,1114.93 735.067,1100 716.667,1100L683.333,1100ZM966.667,1300C979.861,1300 989.903,1302.06 997.852,1305.53C1003.13,1312.89 1010.31,1318.67 1018.62,1322.27C1027.7,1333.76 1033.33,1349.82 1033.33,1366.67C1033.33,1384.72 1027.08,1402.11 1016.8,1413.67C1006.52,1425.24 993.056,1433.33 966.667,1433.33C940.278,1433.33 926.817,1425.24 916.536,1413.67C906.256,1402.11 900,1384.72 900,1366.67C900,1348.61 906.256,1331.23 916.536,1319.66C926.817,1308.1 940.278,1300 966.667,1300ZM1366.67,1300C1393.06,1300 1406.52,1308.1 1416.8,1319.66C1427.08,1331.23 1433.33,1348.61 1433.33,1366.67C1433.33,1384.72 1427.08,1402.11 1416.8,1413.67C1406.52,1425.24 1393.06,1433.33 1366.67,1433.33C1340.28,1433.33 1326.82,1425.24 1316.54,1413.67C1306.26,1402.11 1300,1384.72 1300,1366.67C1300,1349.41 1305.81,1332.89 1315.3,1321.35C1322.81,1317.95 1329.37,1312.74 1334.38,1306.18C1342.54,1302.35 1352.73,1300 1366.67,1300Z" style="fill-rule:nonzero;"/>
</g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><path d="M799.544,300C422.224,300 89.762,561.531 1.563,904.232C0.52,908.294 -0.008,912.472 -0.008,916.667C-0.008,944.1 22.566,966.675 50,966.675C72.756,966.675 92.779,951.143 98.438,929.102C174.438,633.803 468.865,400 799.544,400C1130.22,400 1425.58,633.885 1501.56,929.102C1507.22,951.143 1527.24,966.675 1550,966.675C1577.43,966.675 1600.01,944.1 1600.01,916.667C1600.01,912.472 1599.48,908.294 1598.44,904.232C1510.22,561.448 1176.87,300 799.544,300ZM800.065,566.667C622.73,566.667 477.93,711.467 477.93,888.802C477.93,1066.14 622.73,1211 800.065,1211C977.401,1211 1122.27,1066.14 1122.27,888.802C1122.27,711.467 977.401,566.667 800.065,566.667ZM800.065,666.667C923.357,666.667 1022.27,765.511 1022.27,888.802C1022.27,1012.09 923.357,1111 800.065,1111C676.774,1111 577.93,1012.09 577.93,888.802C577.93,765.511 676.774,666.667 800.065,666.667Z"/></svg>

After

Width:  |  Height:  |  Size: 934 B

View File

@@ -0,0 +1 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1600"><path d="M799.544,300C422.224,300 89.762,561.531 1.563,904.232C0.52,908.294 -0.008,912.472 -0.008,916.667C-0.008,944.1 22.566,966.675 50,966.675C72.756,966.675 92.779,951.143 98.438,929.102C174.438,633.803 468.865,400 799.544,400C1130.22,400 1425.58,633.885 1501.56,929.102C1507.22,951.143 1527.24,966.675 1550,966.675C1577.43,966.675 1600.01,944.1 1600.01,916.667C1600.01,912.472 1599.48,908.294 1598.44,904.232C1510.22,561.448 1176.87,300 799.544,300ZM800.065,566.667C622.73,566.667 477.93,711.467 477.93,888.802C477.93,1066.14 622.73,1211 800.065,1211C977.401,1211 1122.27,1066.14 1122.27,888.802C1122.27,711.467 977.401,566.667 800.065,566.667ZM800.065,666.667C923.357,666.667 1022.27,765.511 1022.27,888.802C1022.27,1012.09 923.357,1111 800.065,1111C676.774,1111 577.93,1012.09 577.93,888.802C577.93,765.511 676.774,666.667 800.065,666.667Z"/></svg>

After

Width:  |  Height:  |  Size: 934 B

BIN
VDownload/Assets/Twitch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

View File

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

View File

@@ -20,21 +20,24 @@
<SolidColorBrush x:Key="SettingControlBackgroundColor" Color="#FBFBFB"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Padding="18" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource SettingControlBackgroundColor}">
<Grid MinHeight="65" Padding="18" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource SettingControlBackgroundColor}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="18"/>
<ColumnDefinition/>
<ColumnDefinition Width="18"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="18"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!-- ICON -->
<ContentPresenter Grid.Column="0" VerticalAlignment="Center" Width="20" Content="{x:Bind Icon, Mode=OneWay}"/>
<Image Grid.Column="0" VerticalAlignment="Center" Width="20" Source="{x:Bind Icon, Mode=OneWay}"/>
<!-- TITLE & DESCRIPTION -->
<Grid Grid.Column="2" VerticalAlignment="Center">
@@ -43,10 +46,10 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{x:Bind Title, Mode=OneWay}"/>
<TextBlock Grid.Row="1" FontSize="12" Foreground="{ThemeResource SystemBaseMediumColor}" TextWrapping="Wrap" Text="{x:Bind Description, Mode=OneWay}"/>
<TextBlock Grid.Row="1" Visibility="{x:Bind Description, Converter={StaticResource StringToVisibilityConverter}}" FontSize="12" Foreground="{ThemeResource SystemBaseMediumColor}" TextWrapping="Wrap" Text="{x:Bind Description, Mode=OneWay}"/>
</Grid>
<!-- SETTING CONTROL -->
<ContentPresenter Grid.Column="4" VerticalAlignment="Center" Content="{x:Bind SettingContent, Mode=OneWay}"/>
<ContentPresenter Grid.Column="4" HorizontalAlignment="Right" VerticalAlignment="Center" Content="{x:Bind SettingContent, Mode=OneWay}"/>
</Grid>
</UserControl>

View File

@@ -21,10 +21,10 @@ namespace VDownload.Controls
#region PROPERTIES
// ICON
public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(IconElement), typeof(SettingControl), new PropertyMetadata(null));
public IconElement Icon
public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(ImageSource), typeof(SettingControl), new PropertyMetadata(null));
public ImageSource Icon
{
get => (IconElement)GetValue(IconProperty);
get => (ImageSource)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}

View File

@@ -0,0 +1,26 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace VDownload.Converters
{
public class StringToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (string.IsNullOrEmpty((string)value))
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using Windows.UI.Xaml.Data;
namespace VDownload.Converters
{
public class TimeSpanToTextBoxMaskConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
string mask = string.Empty;
if (Math.Floor(((TimeSpan)value).TotalHours) > 0) mask += $"{Math.Floor(((TimeSpan)value).TotalHours).ToString()[0]}{new string('9', Math.Floor(((TimeSpan)value).TotalHours).ToString().Length - 1)}:";
if (Math.Floor(((TimeSpan)value).TotalMinutes) > 0) mask += Math.Floor(((TimeSpan)value).TotalHours) > 0 ? $"59:" : $"{((TimeSpan)value).Minutes.ToString()[0]}{new string('9', ((TimeSpan)value).Minutes.ToString().Length - 1)}:";
mask += Math.Floor(((TimeSpan)value).TotalMinutes) > 0 ? $"59" : $"{((TimeSpan)value).Seconds.ToString()[0]}{new string('9', ((TimeSpan)value).Seconds.ToString().Length - 1)}";
return mask;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Windows.UI.Xaml.Data;
namespace VDownload.Converters
{
public class TimeSpanToTextBoxMaskElementsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
HashSet<int> maskElements = new HashSet<int>();
if (Math.Floor(((TimeSpan)value).TotalHours) > 0) maskElements.Add(int.Parse(Math.Floor(((TimeSpan)value).TotalHours).ToString()[0].ToString()));
if (Math.Floor(((TimeSpan)value).TotalMinutes) > 0)
{
if (Math.Floor(((TimeSpan)value).TotalHours) > 0) maskElements.Add(5);
else maskElements.Add(int.Parse(((TimeSpan)value).Minutes.ToString()[0].ToString()));
}
if (Math.Floor(((TimeSpan)value).TotalMinutes) > 0) maskElements.Add(5);
else maskElements.Add(int.Parse(((TimeSpan)value).Seconds.ToString()[0].ToString()));
List<string> maskElementsString = new List<string>();
foreach (int i in maskElements)
{
if (i != 9) maskElementsString.Add($"{i}:[0-{i}]");
}
return string.Join(',', maskElementsString);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,8 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:VDownload.Converters">
<converters:StringToVisibilityConverter x:Key="StringToVisibilityConverter"/>
<converters:TimeSpanToTextBoxMaskConverter x:Key="TimeSpanToTextBoxMaskConverter"/>
<converters:TimeSpanToTextBoxMaskElementsConverter x:Key="TimeSpanToTextBoxMaskElementsConverter"/>
</ResourceDictionary>

View File

@@ -0,0 +1,29 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SvgImageSource x:Key="AuthorIcon" UriSource="ms-appx:///Assets/Icons/AuthorLight.svg"/>
<SvgImageSource x:Key="ViewsIcon" UriSource="ms-appx:///Assets/Icons/ViewsLight.svg"/>
<SvgImageSource x:Key="DateIcon" UriSource="ms-appx:///Assets/Icons/DateLight.svg"/>
<SvgImageSource x:Key="DurationIcon" UriSource="ms-appx:///Assets/Icons/DurationLight.svg"/>
<SvgImageSource x:Key="MediaTypeIcon" UriSource="ms-appx:///Assets/Icons/MediaTypeLight.svg"/>
<SvgImageSource x:Key="QualityIcon" UriSource="ms-appx:///Assets/Icons/QualityLight.svg"/>
<SvgImageSource x:Key="TrimIcon" UriSource="ms-appx:///Assets/Icons/TrimLight.svg"/>
<SvgImageSource x:Key="FileIcon" UriSource="ms-appx:///Assets/Icons/FileLight.svg"/>
<SvgImageSource x:Key="LocationIcon" UriSource="ms-appx:///Assets/Icons/LocationLight.svg"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SvgImageSource x:Key="AuthorIcon" UriSource="ms-appx:///Assets/Icons/AuthorDark.svg"/>
<SvgImageSource x:Key="ViewsIcon" UriSource="ms-appx:///Assets/Icons/ViewsDark.svg"/>
<SvgImageSource x:Key="DateIcon" UriSource="ms-appx:///Assets/Icons/DateDark.svg"/>
<SvgImageSource x:Key="DurationIcon" UriSource="ms-appx:///Assets/Icons/DurationDark.svg"/>
<SvgImageSource x:Key="MediaTypeIcon" UriSource="ms-appx:///Assets/Icons/MediaTypeDark.svg"/>
<SvgImageSource x:Key="QualityIcon" UriSource="ms-appx:///Assets/Icons/QualityDark.svg"/>
<SvgImageSource x:Key="TrimIcon" UriSource="ms-appx:///Assets/Icons/TrimDark.svg"/>
<SvgImageSource x:Key="FileIcon" UriSource="ms-appx:///Assets/Icons/FileDark.svg"/>
<SvgImageSource x:Key="LocationIcon" UriSource="ms-appx:///Assets/Icons/LocationDark.svg"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<SvgImageSource x:Key="ErrorIcon" UriSource="ms-appx:///Assets/Icons/Error.svg"/>
</ResourceDictionary>

View File

@@ -117,6 +117,9 @@
<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>
<data name="HomeOptionsBarAddPlaylistButton.Label" xml:space="preserve">
<value>Add playlist</value>
</data>
@@ -169,6 +172,54 @@ The number in the numberbox indicades how many videos will be got from playlist.
<data name="HomeOptionsBarLoadSubscripionsButton.Width" xml:space="preserve">
<value>120</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="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="HomeVideoAddingDownloadingOptionsHeaderTextBlock.Text" xml:space="preserve">
<value>Downloading options</value>
</data>
<data name="HomeVideoAddingFileOptionsHeaderTextBlock.Text" xml:space="preserve">
<value>File options</value>
</data>
<data name="HomeVideoAddingFileSettingControl.Title" xml:space="preserve">
<value>File</value>
</data>
<data name="HomeVideoAddingLocationBrowseButton.Content" xml:space="preserve">
<value>Browse</value>
</data>
<data name="HomeVideoAddingLocationSettingControl.Title" xml:space="preserve">
<value>Location</value>
</data>
<data name="HomeVideoAddingMediaTypeSettingControl.Title" xml:space="preserve">
<value>Media type</value>
</data>
<data name="HomeVideoAddingQualitySettingControl.Title" xml:space="preserve">
<value>Quality</value>
</data>
<data name="HomeVideoAddingTrimSettingControl.Title" xml:space="preserve">
<value>Trim</value>
</data>
<data name="MainPageNavigationPanelHomeItem.Content" xml:space="preserve">
<value>Home</value>
</data>
@@ -178,6 +229,15 @@ The number in the numberbox indicades how many videos will be got from playlist.
<data name="MainPageNavigationPanelSubscriptionsItem.Content" xml:space="preserve">
<value>Subscriptions</value>
</data>
<data name="MediaTypeAudioVideoText" xml:space="preserve">
<value>Audio &amp; 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="SourcesMainPageHeaderText.Text" xml:space="preserve">
<value>Sources</value>
</data>
@@ -190,6 +250,9 @@ The number in the numberbox indicades how many videos will be got from playlist.
<data name="SourcesTwitchLoginButtonTextNotLoggedIn" xml:space="preserve">
<value>Sign in</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>
@@ -202,6 +265,9 @@ The number in the numberbox indicades how many videos will be got from playlist.
<data name="SourcesTwitchSettingControl.Title" xml:space="preserve">
<value>Twitch</value>
</data>
<data name="SourcesTwitchSettingControlDescriptionInternetConnectionError" xml:space="preserve">
<value>Unable to connect to Twitch servers. Check your internet connection.</value>
</data>
<data name="SourcesTwitchSettingControlDescriptionLoggedIn" xml:space="preserve">
<value>Logged in as</value>
</data>

View File

@@ -119,6 +119,9 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.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>
@@ -134,6 +137,12 @@
<Compile Include="Views\Home\HomeMain.xaml.cs">
<DependentUpon>HomeMain.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Home\HomeVideoAddingPanel.xaml.cs">
<DependentUpon>HomeVideoAddingPanel.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Home\HomeVideoPanel.xaml.cs">
<DependentUpon>HomeVideoPanel.xaml</DependentUpon>
</Compile>
<Compile Include="Views\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
@@ -153,46 +162,25 @@
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Assets\Icons\Add\NotFound.png" />
<Content Include="Assets\Icons\Add\Start.png" />
<Content Include="Assets\Icons\Error.png" />
<Content Include="Assets\Icons\MainPage\About.png" />
<Content Include="Assets\Icons\MainPage\Sources.png" />
<Content Include="Assets\Icons\Sources\Twitch.png" />
<Content Include="Assets\Icons\Sources\Unknown.png" />
<Content Include="Assets\Icons\Sources\Youtube.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Author.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Cancelled.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Date.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Done.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Downloading.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Duration.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Error.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Finalizing.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Idle.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\MediaType.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\File.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Transcoding.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Quality.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Trim.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Views.png" />
<Content Include="Assets\Icons\VideoMetadata\Dark\Waiting.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Author.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Cancelled.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Date.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Done.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Downloading.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Duration.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Error.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Finalizing.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Idle.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\MediaType.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\File.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Transcoding.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Quality.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Trim.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Views.png" />
<Content Include="Assets\Icons\VideoMetadata\Light\Waiting.png" />
<Content Include="Assets\Icons\AuthorDark.svg" />
<Content Include="Assets\Icons\AuthorLight.svg" />
<Content Include="Assets\Icons\DateDark.svg" />
<Content Include="Assets\Icons\DateLight.svg" />
<Content Include="Assets\Icons\DurationDark.svg" />
<Content Include="Assets\Icons\DurationLight.svg" />
<Content Include="Assets\Icons\Error.svg" />
<Content Include="Assets\Icons\FileDark.svg" />
<Content Include="Assets\Icons\FileLight.svg" />
<Content Include="Assets\Icons\LocationDark.svg" />
<Content Include="Assets\Icons\LocationLight.svg" />
<Content Include="Assets\Icons\MediaTypeDark.svg" />
<Content Include="Assets\Icons\MediaTypeLight.svg" />
<Content Include="Assets\Icons\QualityDark.svg" />
<Content Include="Assets\Icons\QualityLight.svg" />
<Content Include="Assets\Icons\TrimDark.svg" />
<Content Include="Assets\Icons\TrimLight.svg" />
<Content Include="Assets\Icons\ViewsDark.svg" />
<Content Include="Assets\Icons\ViewsLight.svg" />
<Content Include="Assets\Logo\Logo.png" />
<Content Include="Assets\Logo\LargeTile.scale-100.png" />
<Content Include="Assets\Logo\LargeTile.scale-125.png" />
@@ -244,13 +232,22 @@
<Content Include="Assets\Logo\Wide310x150Logo.scale-150.png" />
<Content Include="Assets\Logo\Wide310x150Logo.scale-200.png" />
<Content Include="Assets\Logo\Wide310x150Logo.scale-400.png" />
<Content Include="Assets\Other\UnknownThumbnail.png" />
<Content Include="Assets\Twitch.png" />
<Content Include="Assets\UnknownThumbnail.png" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Resources\Converters.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Resources\Icons.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\About\AboutMain.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -271,6 +268,14 @@
<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\HomeVideoPanel.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>

View File

@@ -15,12 +15,17 @@
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="HomeMainOptionBarBackgroundColor" Color="#202020"/>
<SolidColorBrush x:Key="HomeMainOptionBarBackgroundColor" Color="#1B1B1B"/>
<SolidColorBrush x:Key="HomeMainAddingPanelBackgroundColor" Color="#212121"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="HomeMainOptionBarBackgroundColor" Color="#F5F5F5"/>
<SolidColorBrush x:Key="HomeMainAddingPanelBackgroundColor" Color="#F5F5F5"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///Resources/Icons.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
@@ -33,10 +38,12 @@
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="HomeOptionBarButtonsStackPanel.(Grid.ColumnSpan)" Value="3"/>
<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">
@@ -45,40 +52,45 @@
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="HomeOptionBarButtonsStackPanel.(Grid.ColumnSpan)" Value="1"/>
<Setter Target="HomeOptionBarButtonsStackPanel.(Grid.Column)" Value="2"/>
<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 Height="1*"/>
<RowDefinition Height="Auto"/>
<RowDefinition x:Name="HomeVideosListRow" Height="1*"/>
<RowDefinition x:Name="HomeOptionBarAndAddingPanelRow" Height="Auto"/>
</Grid.RowDefinitions>
<!-- VIDEOS LIST -->
<StackPanel/>
<!-- OPTIONS BAR AND ADDING PANEL -->
<Grid Grid.Row="1" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource HomeMainOptionBarBackgroundColor}">
<Grid Grid.Row="1" CornerRadius="{ThemeResource ControlCornerRadius}" Background="{ThemeResource HomeMainAddingPanelBackgroundColor}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="HomeOptionsBar" Grid.Row="1"> <!-- Options Bar -->
<ContentControl x:Name="HomeAddingPanel" Grid.Row="0" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"/> <!-- Adding Panel -->
<Grid x:Name="HomeOptionsBar" Grid.Row="1" Background="{ThemeResource HomeMainOptionBarBackgroundColor}"> <!-- 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"/>
<StackPanel x:Name="HomeOptionBarButtonsStackPanel" Grid.Row="1" Grid.Column="2" Margin="3,0,3,0" HorizontalAlignment="Center" Orientation="Horizontal">
<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" Label="Load subscriptions" Width="120"/>
<AppBarSeparator VerticalAlignment="Center" Height="50"/>
<AppBarToggleButton x:Name="HomeOptionsBarAddPlaylistButton" x:Uid="HomeOptionsBarAddPlaylistButton" Icon="List" Label="Add playlist" Width="85" Checked="HomeOptionsBarAddPlaylistButton_Checked" Unchecked="HomeOptionsBarAddingButtons_Unchecked"/>

View File

@@ -1,7 +1,16 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using VDownload.Core.Enums;
using VDownload.Core.EventArgsObjects;
using VDownload.Core.Exceptions;
using VDownload.Core.Interfaces;
using VDownload.Core.Services;
using Windows.ApplicationModel.Resources;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
namespace VDownload.Views.Home
{
@@ -18,6 +27,18 @@ namespace VDownload.Views.Home
#region PROPERTIES
// SEARCHING STATUS CONTROLS
private readonly Microsoft.UI.Xaml.Controls.ProgressRing HomeOptionsBarSearchingStatusProgressRing = new Microsoft.UI.Xaml.Controls.ProgressRing { Width = 15, Height = 15, Margin = new Thickness(5), IsActive = true};
private readonly Image HomeOptionsBarSearchingStatusErrorImage = new Image { Width = 15, Height = 15, Margin = new Thickness(5), Source = (SvgImageSource)new ResourceDictionary { Source = new Uri("ms-appx:///Resources/Icons.xaml")}["ErrorIcon"] };
CancellationTokenSource SearchingCancellationToken = new CancellationTokenSource();
#endregion
#region BUTTONS EVENTS
// ADD VIDEO BUTTON CHECKED
@@ -33,9 +54,102 @@ namespace VDownload.Views.Home
}
// ADD VIDEO SEARCH BUTTON CLICKED
private void HomeOptionsBarAddVideoControl_SearchButtonClicked(object sender, VideoSearchEventArgs e)
private async void HomeOptionsBarAddVideoControl_SearchButtonClicked(object sender, VideoSearchEventArgs e)
{
HomeAddingPanel.Content = null;
// Cancel previous operations
SearchingCancellationToken.Cancel();
SearchingCancellationToken = new CancellationTokenSource();
// Set SearchingStatusControl
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusProgressRing;
// Parse url
(VideoSource Type, string ID) source = Source.GetVideoSource(e.Phrase);
// Check url
if (source.Type == VideoSource.Null)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
}
else
{
// Select video service
IVideoService videoService = null;
switch (source.Type)
{
case VideoSource.TwitchVod: videoService = new Core.Services.Sources.Twitch.Vod(source.ID); break;
case VideoSource.TwitchClip: videoService = new Core.Services.Sources.Twitch.Clip(source.ID); break;
}
// Get metadata and streams
try
{
await videoService.GetMetadataAsync(SearchingCancellationToken.Token);
await videoService.GetStreamsAsync(SearchingCancellationToken.Token);
}
catch (OperationCanceledException)
{
HomeOptionsBarSearchingStatusControl.Content = null;
return;
}
catch (TwitchAccessTokenNotFoundException)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
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)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
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)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
if (wex.Response == 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)
HomeOptionsBarSearchingStatusControl.Content = null;
HomeOptionBarAndAddingPanelRow.Height = new GridLength(1, GridUnitType.Star);
HomeVideosListRow.Height = new GridLength(0);
HomeVideoAddingPanel addingPanel = new HomeVideoAddingPanel(videoService);
addingPanel.VideoAddRequest += HomeVideoAddingPanel_VideoAddRequest;
HomeAddingPanel.Content = addingPanel;
}
}
private void HomeVideoAddingPanel_VideoAddRequest(object sender, VideoAddEventArgs e)
{
HomeOptionsBarAddVideoButton.IsChecked = false;
}
@@ -52,16 +166,99 @@ namespace VDownload.Views.Home
}
// ADD PLAYLIST SEARCH BUTTON CLICKED
private void HomeOptionsBarAddPlaylistControl_SearchButtonClicked(object sender, PlaylistSearchEventArgs e)
private async void HomeOptionsBarAddPlaylistControl_SearchButtonClicked(object sender, PlaylistSearchEventArgs e)
{
// Cancel previous operations
SearchingCancellationToken.Cancel();
// Set SearchingStatusControl
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusProgressRing;
// Parse url
(PlaylistSource Type, string ID) source = Source.GetPlaylistSource(e.Phrase);
// Check url
if (source.Type == PlaylistSource.Null)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
}
else
{
// Select video service
IPlaylistService playlistService = null;
switch (source.Type)
{
case PlaylistSource.TwitchChannel: playlistService = new Core.Services.Sources.Twitch.Channel(source.ID); break;
}
// Get metadata and streams
try
{
await playlistService.GetMetadataAsync(SearchingCancellationToken.Token);
await playlistService.GetVideosAsync(e.Count, SearchingCancellationToken.Token);
}
catch (OperationCanceledException)
{
HomeOptionsBarSearchingStatusControl.Content = null;
return;
}
catch (TwitchAccessTokenNotFoundException)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
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)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
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 wex)
{
HomeOptionsBarSearchingStatusControl.Content = HomeOptionsBarSearchingStatusErrorImage;
if (wex.Response == null)
{
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;
}
}
}
// ADDING BUTTONS UNCHECKED
private void HomeOptionsBarAddingButtons_Unchecked(object sender, RoutedEventArgs e)
{
// Cancel searching operations
SearchingCancellationToken.Cancel();
SearchingCancellationToken = new CancellationTokenSource();
HomeOptionBarAndAddingPanelRow.Height = GridLength.Auto;
HomeVideosListRow.Height = new GridLength(1, GridUnitType.Star);
HomeAddingPanel.Content = null;
HomeOptionsBarAddingControl.Content = null;
HomeOptionsBarSearchingStatusControl.Content = null;
}
#endregion

View File

@@ -0,0 +1,249 @@
<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"
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>
<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>
<!-- PROPERTIES PANEL -->
<ScrollViewer Grid.Row="1">
<StackPanel Spacing="30">
<StackPanel>
<TextBlock x:Uid="HomeVideoAddingDownloadingOptionsHeaderTextBlock" Margin="0,0,0,10" FontWeight="SemiBold"/>
<cc:SettingControl x:Uid="HomeVideoAddingMediaTypeSettingControl" Margin="0,0,0,10" Icon="{ThemeResource MediaTypeIcon}">
<cc:SettingControl.SettingContent>
<ComboBox x:Name="HomeVideoAddingMediaTypeSettingControlComboBox" Width="150" SelectionChanged="HomeVideoAddingMediaTypeSettingControlComboBox_SelectionChanged"/>
</cc:SettingControl.SettingContent>
</cc:SettingControl>
<cc:SettingControl x:Name="HomeVideoAddingQualitySettingControl" Margin="0,0,0,10" x:Uid="HomeVideoAddingQualitySettingControl" Icon="{ThemeResource QualityIcon}">
<cc:SettingControl.SettingContent>
<ComboBox x:Name="HomeVideoAddingQualitySettingControlComboBox" Width="150" SelectionChanged="HomeVideoAddingQualitySettingControlComboBox_SelectionChanged"/>
</cc:SettingControl.SettingContent>
</cc:SettingControl>
<cc:SettingControl x:Uid="HomeVideoAddingTrimSettingControl" Icon="{ThemeResource TrimIcon}">
<cc:SettingControl.SettingContent>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBox x:Name="HomeVideoAddingTrimStartTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind VideoData.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind VideoData.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" TextChanged="HomeVideoAddingTrimStartTextBox_TextChanged"/>
<TextBlock VerticalAlignment="Center" Text="-"/>
<TextBox x:Name="HomeVideoAddingTrimEndTextBox" ex:TextBoxExtensions.CustomMask="{x:Bind VideoData.Duration, Converter={StaticResource TimeSpanToTextBoxMaskElementsConverter}}" ex:TextBoxExtensions.Mask="{x:Bind VideoData.Duration, Converter={StaticResource TimeSpanToTextBoxMaskConverter}}" TextChanged="HomeVideoAddingTrimEndTextBox_TextChanged"/>
</StackPanel>
</cc:SettingControl.SettingContent>
</cc:SettingControl>
</StackPanel>
<StackPanel Spacing="10">
<TextBlock x:Uid="HomeVideoAddingFileOptionsHeaderTextBlock" FontWeight="SemiBold"/>
<cc:SettingControl x:Uid="HomeVideoAddingFileSettingControl" Icon="{ThemeResource FileIcon}">
<cc:SettingControl.SettingContent>
<Grid ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="HomeVideoAddingFilenameTextBox" Grid.Column="0" HorizontalAlignment="Stretch" MinWidth="150" MaxWidth="300" IsSpellCheckEnabled="False" TextChanged="HomeVideoAddingFilenameTextBox_TextChanged"/>
<ComboBox x:Name="HomeVideoAddingExtensionComboBox" Grid.Column="1" HorizontalAlignment="Stretch" SelectionChanged="HomeVideoAddingExtensionComboBox_SelectionChanged"/>
</Grid>
</cc:SettingControl.SettingContent>
</cc:SettingControl>
<cc:SettingControl x:Name="HomeVideoAddingLocationSettingControl" x:Uid="HomeVideoAddingLocationSettingControl" Icon="{ThemeResource LocationIcon}">
<cc:SettingControl.SettingContent>
<Button x:Uid="HomeVideoAddingLocationBrowseButton" Click="HomeVideoAddingLocationBrowseButton_Click"/>
</cc:SettingControl.SettingContent>
</cc:SettingControl>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,307 @@
using Microsoft.Toolkit.Uwp.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using VDownload.Core.Enums;
using VDownload.Core.EventArgsObjects;
using VDownload.Core.Interfaces;
using VDownload.Core.Objects;
using VDownload.Core.Services;
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;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace VDownload.Views.Home
{
public sealed partial class HomeVideoAddingPanel : UserControl
{
#region CONSTRUCTORS
public HomeVideoAddingPanel(IVideoService videoData)
{
this.InitializeComponent();
// Set video service object
VideoData = videoData;
// Set metadata
ThumbnailImage = new BitmapImage { UriSource = VideoData.Thumbnail ?? new Uri("ms-appx:///Assets/UnknownThumbnail.png") };
SourceImage = new BitmapIcon { ShowAsMonochrome = false, UriSource = new Uri($"ms-appx:///Assets/{VideoData.GetType().Namespace.Split(".").Last()}.png") };
Title = VideoData.Title;
Author = VideoData.Author;
Views = VideoData.Views.ToString();
Date = VideoData.Date.ToString(CultureInfo.InstalledUICulture.DateTimeFormat.ShortDatePattern);
Duration = $"{Math.Floor(VideoData.Duration.TotalHours):00}:{VideoData.Duration.Minutes:00}:{VideoData.Duration.Seconds:00}";
// Set media type
foreach (string mediaType in Enum.GetNames(typeof(MediaType)))
{
HomeVideoAddingMediaTypeSettingControlComboBox.Items.Add(ResourceLoader.GetForCurrentView().GetString($"MediaType{mediaType}Text"));
}
HomeVideoAddingMediaTypeSettingControlComboBox.SelectedIndex = (int)Config.GetValue("default_media_type");
// Set quality
foreach (Stream stream in VideoData.Streams)
{
HomeVideoAddingQualitySettingControlComboBox.Items.Add($"{stream.Height}p{(stream.FrameRate > 0 ? stream.FrameRate.ToString() : "N/A")}");
}
HomeVideoAddingQualitySettingControlComboBox.SelectedIndex = 0;
// Set trim start
if (Math.Floor(VideoData.Duration.TotalHours) > 0) HomeVideoAddingTrimStartTextBox.Text += $"{new string('0', Math.Floor(VideoData.Duration.TotalHours).ToString().Length)}:";
if (Math.Floor(VideoData.Duration.TotalMinutes) > 0) HomeVideoAddingTrimStartTextBox.Text += Math.Floor(VideoData.Duration.TotalHours) > 0 ? "00:" : $"{new string('0', VideoData.Duration.Minutes.ToString().Length)}:";
HomeVideoAddingTrimStartTextBox.Text += Math.Floor(VideoData.Duration.TotalMinutes) > 0 ? "00" : $"{new string('0', VideoData.Duration.Seconds.ToString().Length)}";
// Set trim end
if (Math.Floor(VideoData.Duration.TotalHours) > 0) HomeVideoAddingTrimEndTextBox.Text += $"{Math.Floor(VideoData.Duration.TotalHours)}:";
if (Math.Floor(VideoData.Duration.TotalMinutes) > 0) HomeVideoAddingTrimEndTextBox.Text += Math.Floor(VideoData.Duration.TotalHours) > 0 ? $"{VideoData.Duration.Minutes:00}:" : $"{VideoData.Duration.Minutes}:";
HomeVideoAddingTrimEndTextBox.Text += Math.Floor(VideoData.Duration.TotalMinutes) > 0 ? $"{VideoData.Duration.Seconds:00}" : $"{VideoData.Duration.Seconds}";
// Set filename
string temporaryFilename = (string)Config.GetValue("default_filename");
Dictionary<string, string> filenameStandardTemplates = new Dictionary<string, string>()
{
{ "<title>", VideoData.Title },
{ "<author>", VideoData.Author },
{ "<views>", VideoData.Views.ToString() },
{ "<id>", VideoData.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>.*)>"), VideoData.Date },
{ new Regex(@"<date_now:(?<format>.*)>"), DateTime.Now },
{ new Regex(@"<duration:(?<format>.*)>"), VideoData.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));
HomeVideoAddingFilenameTextBox.Text = temporaryFilename;
// Set location
if ((DefaultLocationType)Config.GetValue("default_location_type") == DefaultLocationType.Last && StorageApplicationPermissions.FutureAccessList.ContainsItem("last_media_location"))
{
Task<StorageFolder> task = StorageApplicationPermissions.FutureAccessList.GetFolderAsync("last_media_location").AsTask();
Location = task.Result;
HomeVideoAddingLocationSettingControl.Description = Location.Path;
}
else if ((DefaultLocationType)Config.GetValue("default_location_type") == DefaultLocationType.Selected && StorageApplicationPermissions.FutureAccessList.ContainsItem("selected_media_location"))
{
Task<StorageFolder> task = StorageApplicationPermissions.FutureAccessList.GetFolderAsync("selected_media_location").AsTask();
Location = task.Result;
HomeVideoAddingLocationSettingControl.Description = Location.Path;
}
else
{
Location = null;
HomeVideoAddingLocationSettingControl.Description = $@"{UserDataPaths.GetDefault().Downloads}\VDownload";
}
}
#endregion
#region PROPERTIES
// BASE VIDEO DATA
private IVideoService VideoData { 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; }
// VIDEO OPTIONS
private MediaType MediaType { get; set; }
private Stream Stream { get; set; }
private TimeSpan TrimStart { get; set; }
private TimeSpan TrimEnd { get; set; }
private string Filename { get; set; }
private MediaFileExtension Extension { get; set; }
private StorageFolder Location { get; set; }
#endregion
#region EVENT HANDLERS VOIDS
// MEDIA TYPE COMBOBOX SELECTION CHANGED
private void HomeVideoAddingMediaTypeSettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MediaType = (MediaType)HomeVideoAddingMediaTypeSettingControlComboBox.SelectedIndex;
if (HomeVideoAddingMediaTypeSettingControlComboBox.SelectedIndex == (int)MediaType.OnlyAudio)
{
HomeVideoAddingQualitySettingControl.Visibility = Visibility.Collapsed;
HomeVideoAddingQualitySettingControlComboBox.SelectedIndex = VideoData.Streams.Count() - 1;
HomeVideoAddingExtensionComboBox.Items.Clear();
foreach (AudioFileExtension extension in Enum.GetValues(typeof(AudioFileExtension)))
{
HomeVideoAddingExtensionComboBox.Items.Add(extension);
}
HomeVideoAddingExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_audio_extension") - 3;
}
else
{
HomeVideoAddingQualitySettingControl.Visibility = Visibility.Visible;
HomeVideoAddingQualitySettingControlComboBox.SelectedIndex = 0;
HomeVideoAddingExtensionComboBox.Items.Clear();
foreach (VideoFileExtension extension in Enum.GetValues(typeof(VideoFileExtension)))
{
HomeVideoAddingExtensionComboBox.Items.Add(extension);
}
HomeVideoAddingExtensionComboBox.SelectedIndex = (int)Config.GetValue("default_video_extension");
}
}
// QUALITY COMBOBOX SELECTION CHANGED
private void HomeVideoAddingQualitySettingControlComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Stream = VideoData.Streams[HomeVideoAddingQualitySettingControlComboBox.SelectedIndex];
}
// TRIM START TEXTBOX TEXT CHANGED
private void HomeVideoAddingTrimStartTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (!HomeVideoAddingTrimStartTextBox.Text.Contains('_'))
{
string[] segments = HomeVideoAddingTrimStartTextBox.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 < VideoData.Duration && parsedTimeSpan > new TimeSpan(0)) TrimStart = parsedTimeSpan;
else
{
TrimStart = new TimeSpan(0);
string newText = string.Empty;
if (Math.Floor(VideoData.Duration.TotalHours) > 0) newText += $"{new string('0', Math.Floor(VideoData.Duration.TotalHours).ToString().Length)}:";
if (Math.Floor(VideoData.Duration.TotalMinutes) > 0) newText += Math.Floor(VideoData.Duration.TotalHours) > 0 ? "00:" : $"{new string('0', VideoData.Duration.Minutes.ToString().Length)}:";
newText += Math.Floor(VideoData.Duration.TotalMinutes) > 0 ? "00" : $"{new string('0', VideoData.Duration.Seconds.ToString().Length)}";
if (newText != HomeVideoAddingTrimStartTextBox.Text) HomeVideoAddingTrimStartTextBox.Text = newText;
}
}
}
// TRIM END TEXTBOX TEXT CHANGED
private void HomeVideoAddingTrimEndTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (!HomeVideoAddingTrimEndTextBox.Text.Contains('_'))
{
string[] segments = HomeVideoAddingTrimEndTextBox.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 < VideoData.Duration && parsedTimeSpan > new TimeSpan(0)) TrimEnd = parsedTimeSpan;
else
{
TrimEnd = VideoData.Duration;
string newText = string.Empty;
if (Math.Floor(VideoData.Duration.TotalHours) > 0) newText += $"{Math.Floor(VideoData.Duration.TotalHours)}:";
if (Math.Floor(VideoData.Duration.TotalMinutes) > 0) newText += Math.Floor(VideoData.Duration.TotalHours) > 0 ? $"{TrimEnd.Minutes:00}:" : $"{TrimEnd.Minutes}:";
newText += Math.Floor(VideoData.Duration.TotalMinutes) > 0 ? $"{TrimEnd.Seconds:00}" : $"{TrimEnd.Seconds}";
if (newText != HomeVideoAddingTrimEndTextBox.Text) HomeVideoAddingTrimEndTextBox.Text = newText;
}
}
}
// FILENAME TEXTBOX TEXT CHANGED
private void HomeVideoAddingFilenameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
string oldFilename = HomeVideoAddingFilenameTextBox.Text;
string newFilename = oldFilename;
foreach (char c in System.IO.Path.GetInvalidFileNameChars()) newFilename = newFilename.Replace(c, ' ');
if (oldFilename != newFilename) HomeVideoAddingFilenameTextBox.Text = newFilename;
}
// EXTENSION COMBOBOX SELECTION CHANGED
private void HomeVideoAddingExtensionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Extension = (MediaFileExtension)HomeVideoAddingExtensionComboBox.SelectedIndex + (MediaType == MediaType.OnlyAudio ? 3 : 0);
}
// LOCATION BROWSE BUTTON CLICKED
private async void HomeVideoAddingLocationBrowseButton_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);
Location = selectedFolder;
HomeVideoAddingLocationSettingControl.Description = Location.Path;
}
catch (UnauthorizedAccessException) { }
}
}
// SOURCE BUTTON CLICKED
public async void HomeVideoAddingPanelSourceButton_Click(object sender, RoutedEventArgs e)
{
// Launch the website
await Windows.System.Launcher.LaunchUriAsync(VideoData.VideoUrl);
}
// ADD BUTTON CLICKED
public void HomeVideoAddingPanelAddButton_Click(object sender, RoutedEventArgs e)
{
VideoAddEventArgs args = new VideoAddEventArgs
{
VideoService = VideoData,
MediaType = MediaType,
Stream = Stream,
TrimStart = TrimStart,
TrimEnd = TrimEnd,
Filename = Filename,
Extension = Extension,
Location = Location,
};
VideoAddRequest?.Invoke(this, args);
}
#endregion
#region EVENT HANDLERS
public event EventHandler<VideoAddEventArgs> VideoAddRequest;
#endregion
}
}

View File

@@ -0,0 +1,15 @@
<UserControl
x:Class="VDownload.Views.Home.HomeVideoPanel"
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">
<Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace VDownload.Views.Home
{
public sealed partial class HomeVideoPanel : UserControl
{
public HomeVideoPanel()
{
this.InitializeComponent();
}
}
}

View File

@@ -50,12 +50,12 @@
<muxc:NavigationView.FooterMenuItems>
<muxc:NavigationViewItem x:Uid="MainPageNavigationPanelAboutItem" Tag="about" Content="About">
<muxc:NavigationViewItem.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/Icons/MainPage/About.png"/>
<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.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/Icons/MainPage/Sources.png"/>
<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>

View File

@@ -24,10 +24,10 @@
<StackPanel Grid.Row="2" Spacing="10">
<cc:SettingControl x:Name="SourcesTwitchSettingControl" x:Uid="SourcesTwitchSettingControl" Grid.Row="0" Title="Twitch"> <!-- Twitch -->
<cc:SettingControl.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/Icons/Sources/Twitch.png" ShowAsMonochrome="False"/>
<BitmapImage UriSource="ms-appx:///Assets/Twitch.png"/>
</cc:SettingControl.Icon>
<cc:SettingControl.SettingContent>
<Button x:Name="SourcesTwitchLoginButton" Click="SourcesTwitchLoginButton_Click"/>
<Button x:Name="SourcesTwitchLoginButton" IsEnabled="False" Click="SourcesTwitchLoginButton_Click"/>
</cc:SettingControl.SettingContent>
</cc:SettingControl>
</StackPanel>

View File

@@ -1,25 +1,14 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI;
using Windows.UI.WindowManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace VDownload.Views.Sources
@@ -43,133 +32,144 @@ namespace VDownload.Views.Sources
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
// Check Twitch
string twitchAccessToken = await Core.Services.Sources.Twitch.Auth.ReadAccessTokenAsync();
var twitchAccessTokenValidation = await Core.Services.Sources.Twitch.Auth.ValidateAccessTokenAsync(twitchAccessToken);
if (twitchAccessToken != null && twitchAccessTokenValidation.IsValid)
try
{
Debug.WriteLine("Twitch authentication status: LOGGED_IN");
Debug.WriteLine(twitchAccessTokenValidation.ExpirationDate.Value.ToString("dd.MM.yyyy"));
SourcesTwitchSettingControl.Description = $"{ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionLoggedIn")} {twitchAccessTokenValidation.Login}";
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextLoggedIn");
string twitchAccessToken = await Core.Services.Sources.Twitch.Auth.ReadAccessTokenAsync();
(bool IsValid, string Login, DateTime? ExpirationDate) twitchAccessTokenValidation = await Core.Services.Sources.Twitch.Auth.ValidateAccessTokenAsync(twitchAccessToken);
if (twitchAccessToken != null && twitchAccessTokenValidation.IsValid)
{
SourcesTwitchSettingControl.Description = $"{ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionLoggedIn")} {twitchAccessTokenValidation.Login}";
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextLoggedIn");
SourcesTwitchLoginButton.IsEnabled = true;
}
else if (twitchAccessToken == null || !twitchAccessTokenValidation.IsValid)
{
if (twitchAccessToken != null) await Core.Services.Sources.Twitch.Auth.DeleteAccessTokenAsync();
SourcesTwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionNotLoggedIn");
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextNotLoggedIn");
SourcesTwitchLoginButton.IsEnabled = true;
}
}
else if (twitchAccessToken == null || !twitchAccessTokenValidation.IsValid)
catch (WebException wex)
{
if (twitchAccessToken != null)
if (wex.Response == null)
{
Debug.WriteLine("Twitch authentication status: ACCESS_TOKEN_READ_BUT_NOT_VALID");
await Core.Services.Sources.Twitch.Auth.DeleteAccessTokenAsync();
SourcesTwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionInternetConnectionError");
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextNotLoggedIn");
}
else
{
Debug.WriteLine("Twitch authentication status: ACCESS_TOKEN_NOT_FOUND");
}
SourcesTwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionNotLoggedIn");
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextNotLoggedIn");
else throw;
}
}
// TWITCH LOGIN BUTTON CLICKED
private async void SourcesTwitchLoginButton_Click(object sender, RoutedEventArgs e)
{
string accessToken = await Core.Services.Sources.Twitch.Auth.ReadAccessTokenAsync();
var accessTokenValidation = await Core.Services.Sources.Twitch.Auth.ValidateAccessTokenAsync(accessToken);
if (accessToken != null && accessTokenValidation.IsValid)
try
{
Debug.WriteLine("Log out from Twitch (revoke and delete access token)");
// Revoke access token
await Core.Services.Sources.Twitch.Auth.RevokeAccessTokenAsync(accessToken);
Debug.WriteLine($"Twitch access token ({accessToken}) revoked successfully");
// Delete access token
await Core.Services.Sources.Twitch.Auth.DeleteAccessTokenAsync();
Debug.WriteLine($"Twitch access token ({accessToken}) deleted successfully");
// Update Twitch SettingControl
Debug.WriteLine("Twitch authentication status: ACCESS_TOKEN_NOT_FOUND");
SourcesTwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionNotLoggedIn");
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextNotLoggedIn");
}
else
{
Debug.WriteLine("Log in to Twitch (get, validate and save access token)");
// Open new window
AppWindow TwitchAuthWindow = await AppWindow.TryCreateAsync();
TwitchAuthWindow.Title = "Twitch Authentication";
WebView2 TwitchAuthWebView = new WebView2();
await TwitchAuthWebView.EnsureCoreWebView2Async();
TwitchAuthWebView.Source = Core.Services.Sources.Twitch.Auth.AuthorizationUrl;
ElementCompositionPreview.SetAppWindowContent(TwitchAuthWindow, TwitchAuthWebView);
TwitchAuthWindow.TryShowAsync();
// NavigationStarting event (only when redirected)
TwitchAuthWebView.NavigationStarting += async (s, a) =>
string accessToken = await Core.Services.Sources.Twitch.Auth.ReadAccessTokenAsync();
var accessTokenValidation = await Core.Services.Sources.Twitch.Auth.ValidateAccessTokenAsync(accessToken);
if (accessToken != null && accessTokenValidation.IsValid)
{
Debug.WriteLine($"TwitchAuthWebView redirected to {a.Uri}");
if (new Uri(a.Uri).Host == Core.Services.Sources.Twitch.Auth.RedirectUrl.Host)
// Revoke access token
await Core.Services.Sources.Twitch.Auth.RevokeAccessTokenAsync(accessToken);
// Delete access token
await Core.Services.Sources.Twitch.Auth.DeleteAccessTokenAsync();
// Update Twitch SettingControl
SourcesTwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionNotLoggedIn");
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextNotLoggedIn");
}
else
{
// Open new window
AppWindow TwitchAuthWindow = await AppWindow.TryCreateAsync();
TwitchAuthWindow.Title = "Twitch Authentication";
WebView2 TwitchAuthWebView = new WebView2();
await TwitchAuthWebView.EnsureCoreWebView2Async();
TwitchAuthWebView.Source = Core.Services.Sources.Twitch.Auth.AuthorizationUrl;
ElementCompositionPreview.SetAppWindowContent(TwitchAuthWindow, TwitchAuthWebView);
TwitchAuthWindow.TryShowAsync();
// NavigationStarting event (only when redirected)
TwitchAuthWebView.NavigationStarting += async (s, a) =>
{
// Close window
await TwitchAuthWindow.CloseAsync();
// Get response
string response = a.Uri.Replace(Core.Services.Sources.Twitch.Auth.RedirectUrl.OriginalString, "");
if (response[1] == '#')
if (new Uri(a.Uri).Host == Core.Services.Sources.Twitch.Auth.RedirectUrl.Host)
{
// Get access token
accessToken = response.Split('&')[0].Replace("/#access_token=", "");
Debug.WriteLine($"Twitch access token got successfully ({accessToken})");
// Close window
await TwitchAuthWindow.CloseAsync();
// Check token
accessTokenValidation = await Core.Services.Sources.Twitch.Auth.ValidateAccessTokenAsync(accessToken);
Debug.WriteLine("Twitch access token validated successfully");
// Get response
string response = a.Uri.Replace(Core.Services.Sources.Twitch.Auth.RedirectUrl.OriginalString, "");
// Save token
await Core.Services.Sources.Twitch.Auth.SaveAccessTokenAsync(accessToken);
Debug.WriteLine("Twitch access token saved successfully");
// Update Twitch SettingControl
Debug.WriteLine("Twitch authentication status: LOGGED_IN");
SourcesTwitchSettingControl.Description = $"{ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionLoggedIn")} {accessTokenValidation.Login}";
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextLoggedIn");
}
else
{
// Ignored errors
string[] ignoredErrors = new[]
if (response[1] == '#')
{
// Get access token
accessToken = response.Split('&')[0].Replace("/#access_token=", "");
// Check token
accessTokenValidation = await Core.Services.Sources.Twitch.Auth.ValidateAccessTokenAsync(accessToken);
// Save token
await Core.Services.Sources.Twitch.Auth.SaveAccessTokenAsync(accessToken);
// Update Twitch SettingControl
SourcesTwitchSettingControl.Description = $"{ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionLoggedIn")} {accessTokenValidation.Login}";
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextLoggedIn");
}
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
// Errors translation
Dictionary<string, string> errorsTranslation = new Dictionary<string, string>
{
Title = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogTitle"),
Content = errorsTranslation.Keys.Contains(errorInfo) ? errorsTranslation[errorInfo] : $"{ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogDescriptionUnknown")} ({errorInfo})",
CloseButtonText = "OK",
};
await loginErrorDialog.ShowAsync();
}
Debug.WriteLine($"Log in to Twitch failed ({errorInfo})");
}
// Clear cache
TwitchAuthWebView.CoreWebView2.CookieManager.DeleteAllCookies();
}
};
};
// 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();
}
}
// Clear cache
TwitchAuthWebView.CoreWebView2.CookieManager.DeleteAllCookies();
}
};
}
}
catch (WebException wex)
{
if (wex.Response == null)
{
SourcesTwitchSettingControl.Description = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchSettingControlDescriptionInternetConnectionError");
SourcesTwitchLoginButton.Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginButtonTextNotLoggedIn");
SourcesTwitchLoginButton.IsEnabled = false;
ContentDialog internetAccessErrorDialog = new ContentDialog
{
Title = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogTitle"),
Content = ResourceLoader.GetForCurrentView().GetString("SourcesTwitchLoginErrorDialogDescriptionInternetConnectionError"),
CloseButtonText = ResourceLoader.GetForCurrentView().GetString("CloseErrorDialogButtonText"),
};
await internetAccessErrorDialog.ShowAsync();
}
else throw;
}
}