eea3b11e23
- Added ability to delete comments. Fixes Related Work Items: #228, #234 - Refactored Video page Related Work Items: #183, #186 - Player refactoring - Chat Related Work Items: #183, #185 - New Logo draft - Improving thumbnails quality - Seek slider fixes Related Work Items: #235, #237 - Regions and languagaes improved. Video cards duration display fixed. Added settings updating system Related Work Items: #236 - Items cards' context menu. Fixed duplicating authors' name on comments replies. Another version of logo Related Work Items: #226 - Context menu for cards done Related Work Items: #226 - Live stats update done Related Work Items: #183, #186 - Settings recovery system improvements - Missync fixed. New logo. Supporting all existing qualities Related Work Items: #207 - Added support of livestreams. Chat messages avatars added. Formatting live chat messages added. Few player fixes Related Work Items: #183, #184 - Refactored player. Refactored changelog system. Players isn't localized Related Work Items: #161, #211, #238 - Player fixes. Localization updates. Tiles updated Related Work Items: #161 - Menu render fix Related Work Items: #239 - Fullscreen mode fixes Related Work Items: #240 - Manifest changes - Ads development - Video cards fixes. Localization fixes. CardAdvert created Related Work Items: #244, #245 - Localization fixed. Player fixes. Menu fixes Related Work Items: #243, #245 - Menu displaying fixes - Fixes Related Work Items: #241, #242, #249, #250 - SafeSearch filter fixed - Video page fixes Related Work Items: #249 - Patchnote updated - Version updated
350 lines
13 KiB
C#
350 lines
13 KiB
C#
using FoxTube.Controls.Adverts;
|
|
using FoxTube.Pages;
|
|
using Google.Apis.YouTube.v3;
|
|
using Microsoft.Advertising.WinRT.UI;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Web;
|
|
using System.Xml;
|
|
using Windows.ApplicationModel.Core;
|
|
using Windows.ApplicationModel.DataTransfer;
|
|
using Windows.ApplicationModel.Resources;
|
|
using Windows.Storage;
|
|
using Windows.Storage.Streams;
|
|
using Windows.System;
|
|
using Windows.UI;
|
|
using Windows.UI.Xaml;
|
|
using Windows.UI.Xaml.Controls;
|
|
using Windows.UI.Xaml.Documents;
|
|
using Windows.UI.Xaml.Media;
|
|
using YoutubeExplode.Models.MediaStreams;
|
|
|
|
namespace FoxTube
|
|
{
|
|
public static class Methods
|
|
{
|
|
private static ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods");
|
|
public static CommentsPage CommentsPage { get; set; }
|
|
|
|
public static bool NeedToResponse { get; set; } = false;
|
|
public static MainPage MainPage
|
|
{
|
|
get { return (Window.Current.Content as Frame).Content as MainPage; }
|
|
}
|
|
|
|
public static void CloseApp()
|
|
{
|
|
CoreApplication.Exit();
|
|
}
|
|
|
|
public static Uri ToUri(this string url)
|
|
{
|
|
return new Uri(url);
|
|
}
|
|
|
|
public static string GetChars(this string str, int count)
|
|
{
|
|
try
|
|
{
|
|
string s = "";
|
|
for (int i = 0; i < count; i++)
|
|
s += str[i];
|
|
return s;
|
|
}
|
|
catch
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public static List<object> ToReversedList(this Array array)
|
|
{
|
|
List<object> list = new List<object>();
|
|
foreach (object i in array)
|
|
list.Add(i);
|
|
list.Reverse();
|
|
return list;
|
|
}
|
|
|
|
public static void ForEach<T>(this IEnumerable<T> array, Action<T> action)
|
|
{
|
|
array.ToList().ForEach(action);
|
|
}
|
|
|
|
public static T Find<T>(this IEnumerable<T> array, Predicate<T> match)
|
|
{
|
|
return array.ToList().Find(match);
|
|
}
|
|
|
|
public static List<T> FindAll<T>(this IEnumerable<T> array, Predicate<T> match)
|
|
{
|
|
return array.ToList().FindAll(match);
|
|
}
|
|
|
|
public static string ReplaceInvalidChars(this string str, char newValue)
|
|
{
|
|
foreach (char i in Path.GetInvalidFileNameChars())
|
|
str = str.Replace(i, newValue);
|
|
return str;
|
|
}
|
|
|
|
public static string Last(this string[] arr)
|
|
{
|
|
return arr[arr.Length - 1];
|
|
}
|
|
|
|
public static TimeSpan GetDuration(this string str)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToTimeSpan(str);
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
TimeSpan time = XmlConvert.ToTimeSpan("PT" + str.Split('T')[1]);
|
|
TimeSpan date = TimeSpan.FromDays(int.Parse(str.Split('W')[0].Replace("P", "")) * 7);
|
|
date.Add(time);
|
|
|
|
return date;
|
|
}
|
|
catch
|
|
{
|
|
return TimeSpan.FromMilliseconds(0);
|
|
}
|
|
}
|
|
|
|
public static string GetAgo(DateTime dateTime)
|
|
{
|
|
TimeSpan span = DateTime.Now - dateTime;
|
|
|
|
if (span.TotalMinutes < 1)
|
|
return resources.GetString("/Methods/now");
|
|
else if (Math.Round(span.TotalMinutes) == 1)
|
|
return resources.GetString("/Methods/oneMinute");
|
|
else if (span.TotalMinutes < 60)
|
|
return Math.Round(span.TotalMinutes) + " " + resources.GetString("/Methods/minutes");
|
|
else if (Math.Round(span.TotalHours) == 1)
|
|
return resources.GetString("/Methods/oneHr");
|
|
else if (span.TotalHours < 24)
|
|
return Math.Round(span.TotalHours) + " " + resources.GetString("/Methods/hrs");
|
|
else if (Math.Round(span.TotalDays) == 1)
|
|
return resources.GetString("/Methods/oneDay");
|
|
else if (span.TotalDays < 7)
|
|
return Math.Round(span.TotalDays) + " " + resources.GetString("/Methods/days");
|
|
else if (Math.Round(span.TotalDays) == 7)
|
|
return resources.GetString("/Methods/oneWeek");
|
|
else if (span.TotalDays < 30)
|
|
return Math.Round(span.TotalDays / 7) + " " + resources.GetString("/Methods/weeks");
|
|
else if (Math.Round(span.TotalDays) == 30)
|
|
return resources.GetString("/Methods/oneMonth");
|
|
else if (Math.Round(span.TotalDays) < 365)
|
|
return Math.Round(span.TotalDays / 30) + " " + resources.GetString("/Methods/months");
|
|
else if (Math.Round(span.TotalDays / 365) == 365)
|
|
return resources.GetString("/Methods/oneYear");
|
|
else
|
|
return Math.Round(span.TotalDays / 365) + " " + resources.GetString("/Methods/years");
|
|
}
|
|
|
|
public static void FormatText(ref TextBlock block, string text)
|
|
{
|
|
block.Inlines.Clear();
|
|
|
|
Regex regx = new Regex(@"(http(s)?://[\S]+|www.[\S]+|[\S]+@[\S]+)", RegexOptions.IgnoreCase);
|
|
Regex isWWW = new Regex(@"(http[s]?://[\S]+|www.[\S]+)");
|
|
Regex isEmail = new Regex(@"[\S]+@[\S]+");
|
|
foreach (string item in regx.Split(text))
|
|
{
|
|
if (isWWW.IsMatch(item))
|
|
{
|
|
try
|
|
{
|
|
Hyperlink link = new Hyperlink();
|
|
link.Click += (s, arg) => { ProcessLink(item); };
|
|
link.Inlines.Add(new Run { Text = item });
|
|
block.Inlines.Add(link);
|
|
}
|
|
catch
|
|
{
|
|
block.Inlines.Add(new Run { Text = item });
|
|
}
|
|
}
|
|
else if (isEmail.IsMatch(item))
|
|
{
|
|
try
|
|
{
|
|
Hyperlink link = new Hyperlink { NavigateUri = new Uri($"mailto:{item}"), Foreground = new SolidColorBrush(Colors.Red) };
|
|
link.Inlines.Add(new Run { Text = item });
|
|
block.Inlines.Add(link);
|
|
}
|
|
catch
|
|
{
|
|
block.Inlines.Add(new Run { Text = item });
|
|
}
|
|
}
|
|
else if (item == "s")
|
|
continue;
|
|
else
|
|
block.Inlines.Add(new Run { Text = item });
|
|
}
|
|
}
|
|
|
|
public static string GetVideoQualityLabel(this VideoQuality quality)
|
|
{
|
|
switch (quality)
|
|
{
|
|
case VideoQuality.High1080:
|
|
return "1080p";
|
|
case VideoQuality.High1440:
|
|
return "1440p";
|
|
case VideoQuality.High2160:
|
|
return "2160p";
|
|
case VideoQuality.High2880:
|
|
return "2880p";
|
|
case VideoQuality.High3072:
|
|
return "3072p";
|
|
case VideoQuality.High4320:
|
|
return "4320p";
|
|
case VideoQuality.High720:
|
|
return "720p";
|
|
case VideoQuality.Low144:
|
|
return "144p";
|
|
case VideoQuality.Low240:
|
|
return "240p";
|
|
case VideoQuality.Medium360:
|
|
return "360p";
|
|
case VideoQuality.Medium480:
|
|
return "480p";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
/*public static string QualityToString(YouTubeQuality quality)
|
|
{
|
|
switch(quality)
|
|
{
|
|
case YouTubeQuality.NotAvailable:
|
|
return "N/A";
|
|
case YouTubeQuality.Quality1080P:
|
|
return "1080p";
|
|
case YouTubeQuality.Quality144P:
|
|
return "144p";
|
|
case YouTubeQuality.Quality2160P:
|
|
return "2160p";
|
|
case YouTubeQuality.Quality240P:
|
|
return "240p";
|
|
case YouTubeQuality.Quality270P:
|
|
return "270p";
|
|
case YouTubeQuality.Quality360P:
|
|
return "360p";
|
|
case YouTubeQuality.Quality480P:
|
|
return "480p";
|
|
case YouTubeQuality.Quality520P:
|
|
return "520p";
|
|
case YouTubeQuality.Quality720P:
|
|
return "720p";
|
|
case YouTubeQuality.QualityHigh:
|
|
return "[Audio only] High quality";
|
|
case YouTubeQuality.QualityLow:
|
|
return "[Audio only] Low quality";
|
|
case YouTubeQuality.QualityMedium:
|
|
return "[Audio only] Medium quality";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}*/
|
|
|
|
public async static void ProcessLink(string url)
|
|
{
|
|
try
|
|
{
|
|
Debug.WriteLine($"Processing link: {url}");
|
|
if (url.Contains("youtube.com/") || url.Contains("youtu.be/"))
|
|
{
|
|
Debug.WriteLine("This is an internal youtube link");
|
|
url = url.Replace("https://", "").Replace("http://", "").Replace("wwww.", "").Replace("//", "");
|
|
Debug.WriteLine($"Prepared link: {url}");
|
|
|
|
if (url.Contains("/playlist"))
|
|
{
|
|
Debug.WriteLine($"This is a playlist link. ID: {HttpUtility.ParseQueryString(url).Get("list")}");
|
|
MainPage.GoToPlaylist(HttpUtility.ParseQueryString(url).Get("list"));
|
|
}
|
|
else if (url.Contains("youtu.be/"))
|
|
{
|
|
Debug.WriteLine($"This is obfuscated video link. Video ID: {url.Split('/')[1]}");
|
|
MainPage.GoToVideo(url.Split('/')[1]);
|
|
}
|
|
else if (url.Contains("/watch"))
|
|
{
|
|
Debug.WriteLine($"This is regular video link. Video ID: {HttpUtility.ParseQueryString(url).Get("v")}");
|
|
MainPage.GoToVideo(HttpUtility.ParseQueryString(url).Get(0), HttpUtility.ParseQueryString(url).Get("list"));
|
|
}
|
|
else if (url.Contains("/v/"))
|
|
{
|
|
Debug.WriteLine($"This is video link. ID: {url.Split('/')[2].Split('?')[0]}");
|
|
MainPage.GoToVideo(url.Split('/')[2].Split('?')[0]);
|
|
}
|
|
else if (url.Contains("/channel/"))
|
|
{
|
|
Debug.WriteLine($"This is channel link. ID: {url.Split('/')[2]}");
|
|
MainPage.GoToChannel(url.Split('/')[2]);
|
|
}
|
|
else if (url.Contains("/user/"))
|
|
{
|
|
Debug.WriteLine($"This is channel link with username. Username: {url.Split('/')[2]}");
|
|
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("id");
|
|
Debug.WriteLine(request.ForUsername = url.Split('/')[2]);
|
|
request.MaxResults = 1;
|
|
MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id);
|
|
}
|
|
else if (url.Contains("/c/"))
|
|
{
|
|
Debug.WriteLine($"This is channel link with custom url. Custom name: {url.Split('/')[2]}");
|
|
SearchResource.ListRequest request = SecretsVault.Service.Search.List("id");
|
|
Debug.WriteLine(request.Q = url.Split('/')[2]);
|
|
request.MaxResults = 1;
|
|
MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id.ChannelId);
|
|
}
|
|
else
|
|
throw new Exception();
|
|
}
|
|
else
|
|
throw new Exception();
|
|
}
|
|
catch
|
|
{
|
|
await Launcher.LaunchUriAsync(new Uri(url));
|
|
}
|
|
}
|
|
|
|
public static async void Share(DataRequestedEventArgs args, string thumbnail, string title, string url, string type)
|
|
{
|
|
DataRequest request = args.Request;
|
|
request.Data.Properties.Title = title;
|
|
request.Data.Properties.Description = $"{resources.GetString("/Methods/sharing")} {type}";
|
|
|
|
request.Data.SetText(title + "\n" + "#YouTube #FoxTube #SharedWithFoxTube");
|
|
request.Data.SetWebLink(url.ToUri());
|
|
|
|
DataRequestDeferral deferral = request.GetDeferral();
|
|
try
|
|
{
|
|
StorageFile thumbnailFile = await StorageFile.CreateStreamedFileFromUriAsync("tempThumb.jpg", thumbnail.ToUri(), null);
|
|
request.Data.Properties.Thumbnail = RandomAccessStreamReference.CreateFromFile(thumbnailFile);
|
|
StorageFile imageFile = thumbnailFile;
|
|
|
|
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageFile));
|
|
}
|
|
finally
|
|
{
|
|
deferral.Complete();
|
|
}
|
|
}
|
|
}
|
|
}
|