From d6d37151b83c6d84fd0b585d60793fa4251387f1 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Fri, 19 Jul 2019 19:24:20 +0300 Subject: [PATCH] Fresh start --- FoxTube.Background/BackgroundProcessor.cs | 161 +----- FoxTube.Background/FoxTube.Background.csproj | 1 - FoxTube.Background/ResourceCreators.cs | 197 ------- FoxTube/App.xaml.cs | 54 +- FoxTube/Assets/FoxGame.png | Bin 31160 -> 0 bytes FoxTube/Assets/LockScreenLogo.scale-200.png | Bin 1430 -> 0 bytes FoxTube/Assets/StoreLogo.backup.png | Bin 1451 -> 0 bytes FoxTube/Classes/AdaptiveCommandBar.cs | 10 - FoxTube/Classes/DownloadAgent.cs | 74 --- FoxTube/Classes/Extensions.cs | 34 -- FoxTube/Classes/HistorySet.cs | 87 ---- FoxTube/Classes/InboxItem.cs | 42 -- FoxTube/Classes/ManifestGenerator.cs | 304 ----------- FoxTube/Classes/Methods.cs | 319 ------------ FoxTube/Classes/Navigation.cs | 36 -- FoxTube/Classes/Processes.cs | 299 ----------- FoxTube/Classes/QualityComparer.cs | 30 -- FoxTube/Classes/SearchParameters.cs | 156 ------ FoxTube/Classes/SecretsVault.cs | 275 ---------- FoxTube/Classes/SettingsStorage.cs | 286 ----------- FoxTube/Classes/StreamInfo.cs | 42 -- FoxTube/Controls/Adverts/CardAdvert.xaml | 53 -- FoxTube/Controls/Adverts/CardAdvert.xaml.cs | 64 --- FoxTube/Controls/Adverts/ChatAdvert.xaml | 48 -- FoxTube/Controls/Adverts/ChatAdvert.xaml.cs | 60 --- FoxTube/Controls/Adverts/CommentAdvert.xaml | 29 -- .../Controls/Adverts/CommentAdvert.xaml.cs | 53 -- FoxTube/Controls/Adverts/PlayerAdvert.xaml | 98 ---- FoxTube/Controls/Adverts/PlayerAdvert.xaml.cs | 73 --- FoxTube/Controls/ChannelCard.xaml | 77 --- FoxTube/Controls/ChannelCard.xaml.cs | 129 ----- FoxTube/Controls/CommentCard.xaml | 97 ---- FoxTube/Controls/CommentCard.xaml.cs | 337 ------------ FoxTube/Controls/Common/AccountManager.xaml | 35 -- .../Controls/Common/AccountManager.xaml.cs | 48 -- FoxTube/Controls/Common/AddToPlaylist.cs | 129 ----- .../Controls/Common/CreateAndAddPlaylist.xaml | 22 - .../Common/CreateAndAddPlaylist.xaml.cs | 75 --- FoxTube/Controls/Common/DownloadSelector.cs | 47 -- FoxTube/Controls/ContentFrame.xaml | 14 - FoxTube/Controls/ContentFrame.xaml.cs | 38 -- FoxTube/Controls/DownloadItem.xaml | 47 -- FoxTube/Controls/DownloadItem.xaml.cs | 286 ----------- FoxTube/Controls/Player/LiveCaptions.xaml | 18 - FoxTube/Controls/Player/LiveCaptions.xaml.cs | 74 --- FoxTube/Controls/Player/PlayerControls.cs | 456 ----------------- FoxTube/Controls/Player/PlayerMain.cs | 208 -------- FoxTube/Controls/Player/VideoPlayer.xaml | 24 - FoxTube/Controls/Player/VideoPlayer.xaml.cs | 173 ------- FoxTube/Controls/PlaylistCard.xaml | 68 --- FoxTube/Controls/PlaylistCard.xaml.cs | 89 ---- FoxTube/Controls/ShowMore.xaml | 13 - FoxTube/Controls/ShowMore.xaml.cs | 25 - FoxTube/Controls/VideoCard.xaml | 82 --- FoxTube/Controls/VideoCard.xaml.cs | 289 ----------- FoxTube/Controls/VideoPage/Chat.xaml | 84 --- FoxTube/Controls/VideoPage/Chat.xaml.cs | 169 ------ FoxTube/Controls/VideoPage/Comments.xaml | 53 -- FoxTube/Controls/VideoPage/Comments.xaml.cs | 196 ------- FoxTube/Controls/VideoPage/RelatedVideos.xaml | 15 - .../Controls/VideoPage/RelatedVideos.xaml.cs | 35 -- FoxTube/Controls/VideoPage/ReportVideo.xaml | 22 - .../Controls/VideoPage/ReportVideo.xaml.cs | 69 --- FoxTube/Controls/VideoPage/VideoPlaylist.xaml | 35 -- .../Controls/VideoPage/VideoPlaylist.xaml.cs | 124 ----- FoxTube/FoxTube.csproj | 277 +--------- FoxTube/Pages/ChannelPage.xaml | 105 ---- FoxTube/Pages/ChannelPage.xaml.cs | 305 ----------- FoxTube/Pages/Downloads.xaml | 36 -- FoxTube/Pages/Downloads.xaml.cs | 47 -- FoxTube/Pages/History.xaml | 61 --- FoxTube/Pages/History.xaml.cs | 152 ------ FoxTube/Pages/Home.xaml | 62 --- FoxTube/Pages/Home.xaml.cs | 222 -------- FoxTube/Pages/LoadingPage.xaml | 41 -- FoxTube/Pages/LoadingPage.xaml.cs | 94 ---- FoxTube/Pages/MainFrame.xaml | 15 - FoxTube/Pages/MainFrame.xaml.cs | 153 ------ FoxTube/Pages/MainPage.xaml | 94 +--- FoxTube/Pages/MainPage.xaml.cs | 318 +----------- FoxTube/Pages/PlaylistPage.xaml | 86 ---- FoxTube/Pages/PlaylistPage.xaml.cs | 141 ----- FoxTube/Pages/Search.xaml | 80 --- FoxTube/Pages/Search.xaml.cs | 233 --------- FoxTube/Pages/Settings.xaml | 33 -- FoxTube/Pages/Settings.xaml.cs | 53 -- FoxTube/Pages/SettingsPages/About.xaml | 38 -- FoxTube/Pages/SettingsPages/About.xaml.cs | 30 -- FoxTube/Pages/SettingsPages/General.xaml | 51 -- FoxTube/Pages/SettingsPages/General.xaml.cs | 147 ------ FoxTube/Pages/SettingsPages/Inbox.xaml | 108 ---- FoxTube/Pages/SettingsPages/Inbox.xaml.cs | 148 ------ FoxTube/Pages/SettingsPages/Translate.xaml | 68 --- FoxTube/Pages/SettingsPages/Translate.xaml.cs | 221 -------- FoxTube/Pages/Subscriptions.xaml | 29 -- FoxTube/Pages/Subscriptions.xaml.cs | 25 - FoxTube/Pages/VideoGrid.xaml | 20 - FoxTube/Pages/VideoGrid.xaml.cs | 64 --- FoxTube/Pages/VideoPage.xaml | 147 ------ FoxTube/Pages/VideoPage.xaml.cs | 482 ------------------ 100 files changed, 30 insertions(+), 10743 deletions(-) delete mode 100644 FoxTube.Background/ResourceCreators.cs delete mode 100644 FoxTube/Assets/FoxGame.png delete mode 100644 FoxTube/Assets/LockScreenLogo.scale-200.png delete mode 100644 FoxTube/Assets/StoreLogo.backup.png delete mode 100644 FoxTube/Classes/AdaptiveCommandBar.cs delete mode 100644 FoxTube/Classes/DownloadAgent.cs delete mode 100644 FoxTube/Classes/Extensions.cs delete mode 100644 FoxTube/Classes/HistorySet.cs delete mode 100644 FoxTube/Classes/InboxItem.cs delete mode 100644 FoxTube/Classes/ManifestGenerator.cs delete mode 100644 FoxTube/Classes/Methods.cs delete mode 100644 FoxTube/Classes/Navigation.cs delete mode 100644 FoxTube/Classes/Processes.cs delete mode 100644 FoxTube/Classes/QualityComparer.cs delete mode 100644 FoxTube/Classes/SearchParameters.cs delete mode 100644 FoxTube/Classes/SecretsVault.cs delete mode 100644 FoxTube/Classes/SettingsStorage.cs delete mode 100644 FoxTube/Classes/StreamInfo.cs delete mode 100644 FoxTube/Controls/Adverts/CardAdvert.xaml delete mode 100644 FoxTube/Controls/Adverts/CardAdvert.xaml.cs delete mode 100644 FoxTube/Controls/Adverts/ChatAdvert.xaml delete mode 100644 FoxTube/Controls/Adverts/ChatAdvert.xaml.cs delete mode 100644 FoxTube/Controls/Adverts/CommentAdvert.xaml delete mode 100644 FoxTube/Controls/Adverts/CommentAdvert.xaml.cs delete mode 100644 FoxTube/Controls/Adverts/PlayerAdvert.xaml delete mode 100644 FoxTube/Controls/Adverts/PlayerAdvert.xaml.cs delete mode 100644 FoxTube/Controls/ChannelCard.xaml delete mode 100644 FoxTube/Controls/ChannelCard.xaml.cs delete mode 100644 FoxTube/Controls/CommentCard.xaml delete mode 100644 FoxTube/Controls/CommentCard.xaml.cs delete mode 100644 FoxTube/Controls/Common/AccountManager.xaml delete mode 100644 FoxTube/Controls/Common/AccountManager.xaml.cs delete mode 100644 FoxTube/Controls/Common/AddToPlaylist.cs delete mode 100644 FoxTube/Controls/Common/CreateAndAddPlaylist.xaml delete mode 100644 FoxTube/Controls/Common/CreateAndAddPlaylist.xaml.cs delete mode 100644 FoxTube/Controls/Common/DownloadSelector.cs delete mode 100644 FoxTube/Controls/ContentFrame.xaml delete mode 100644 FoxTube/Controls/ContentFrame.xaml.cs delete mode 100644 FoxTube/Controls/DownloadItem.xaml delete mode 100644 FoxTube/Controls/DownloadItem.xaml.cs delete mode 100644 FoxTube/Controls/Player/LiveCaptions.xaml delete mode 100644 FoxTube/Controls/Player/LiveCaptions.xaml.cs delete mode 100644 FoxTube/Controls/Player/PlayerControls.cs delete mode 100644 FoxTube/Controls/Player/PlayerMain.cs delete mode 100644 FoxTube/Controls/Player/VideoPlayer.xaml delete mode 100644 FoxTube/Controls/Player/VideoPlayer.xaml.cs delete mode 100644 FoxTube/Controls/PlaylistCard.xaml delete mode 100644 FoxTube/Controls/PlaylistCard.xaml.cs delete mode 100644 FoxTube/Controls/ShowMore.xaml delete mode 100644 FoxTube/Controls/ShowMore.xaml.cs delete mode 100644 FoxTube/Controls/VideoCard.xaml delete mode 100644 FoxTube/Controls/VideoCard.xaml.cs delete mode 100644 FoxTube/Controls/VideoPage/Chat.xaml delete mode 100644 FoxTube/Controls/VideoPage/Chat.xaml.cs delete mode 100644 FoxTube/Controls/VideoPage/Comments.xaml delete mode 100644 FoxTube/Controls/VideoPage/Comments.xaml.cs delete mode 100644 FoxTube/Controls/VideoPage/RelatedVideos.xaml delete mode 100644 FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs delete mode 100644 FoxTube/Controls/VideoPage/ReportVideo.xaml delete mode 100644 FoxTube/Controls/VideoPage/ReportVideo.xaml.cs delete mode 100644 FoxTube/Controls/VideoPage/VideoPlaylist.xaml delete mode 100644 FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs delete mode 100644 FoxTube/Pages/ChannelPage.xaml delete mode 100644 FoxTube/Pages/ChannelPage.xaml.cs delete mode 100644 FoxTube/Pages/Downloads.xaml delete mode 100644 FoxTube/Pages/Downloads.xaml.cs delete mode 100644 FoxTube/Pages/History.xaml delete mode 100644 FoxTube/Pages/History.xaml.cs delete mode 100644 FoxTube/Pages/Home.xaml delete mode 100644 FoxTube/Pages/Home.xaml.cs delete mode 100644 FoxTube/Pages/LoadingPage.xaml delete mode 100644 FoxTube/Pages/LoadingPage.xaml.cs delete mode 100644 FoxTube/Pages/MainFrame.xaml delete mode 100644 FoxTube/Pages/MainFrame.xaml.cs delete mode 100644 FoxTube/Pages/PlaylistPage.xaml delete mode 100644 FoxTube/Pages/PlaylistPage.xaml.cs delete mode 100644 FoxTube/Pages/Search.xaml delete mode 100644 FoxTube/Pages/Search.xaml.cs delete mode 100644 FoxTube/Pages/Settings.xaml delete mode 100644 FoxTube/Pages/Settings.xaml.cs delete mode 100644 FoxTube/Pages/SettingsPages/About.xaml delete mode 100644 FoxTube/Pages/SettingsPages/About.xaml.cs delete mode 100644 FoxTube/Pages/SettingsPages/General.xaml delete mode 100644 FoxTube/Pages/SettingsPages/General.xaml.cs delete mode 100644 FoxTube/Pages/SettingsPages/Inbox.xaml delete mode 100644 FoxTube/Pages/SettingsPages/Inbox.xaml.cs delete mode 100644 FoxTube/Pages/SettingsPages/Translate.xaml delete mode 100644 FoxTube/Pages/SettingsPages/Translate.xaml.cs delete mode 100644 FoxTube/Pages/Subscriptions.xaml delete mode 100644 FoxTube/Pages/Subscriptions.xaml.cs delete mode 100644 FoxTube/Pages/VideoGrid.xaml delete mode 100644 FoxTube/Pages/VideoGrid.xaml.cs delete mode 100644 FoxTube/Pages/VideoPage.xaml delete mode 100644 FoxTube/Pages/VideoPage.xaml.cs diff --git a/FoxTube.Background/BackgroundProcessor.cs b/FoxTube.Background/BackgroundProcessor.cs index 83ff2c7..7bfe196 100644 --- a/FoxTube.Background/BackgroundProcessor.cs +++ b/FoxTube.Background/BackgroundProcessor.cs @@ -1,171 +1,12 @@ -using Google.Apis.Auth.OAuth2; -using Google.Apis.Oauth2.v2; -using Google.Apis.Services; -using Google.Apis.YouTube.v3; -using Google.Apis.YouTube.v3.Data; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -using Windows.ApplicationModel.Background; -using Windows.Storage; -using Windows.UI.Notifications; +using Windows.ApplicationModel.Background; namespace FoxTube.Background { public sealed class BackgroundProcessor : IBackgroundTask { - private DateTime lastCheck; - private readonly ApplicationDataContainer settings = ApplicationData.Current.RoamingSettings; - dynamic prefs; - BackgroundTaskDeferral def; - public async void Run(IBackgroundTaskInstance taskInstance) { - try - { - def = taskInstance.GetDeferral(); - if (settings.Values["lastCheck"] == null) - { - settings.Values["lastCheck"] = DateTime.Now.ToString(); - def.Complete(); - return; - } - else - lastCheck = DateTime.Parse(settings.Values["lastCheck"] as string); - - prefs = JsonConvert.DeserializeObject(settings.Values["settings"] as string); - if ((bool)prefs.devNotifications) - CheckAnnouncements(); - if ((bool)prefs.videoNotifications && (bool)prefs.hasAccount) - await CheckAccount(); - } - finally - { - settings.Values["lastCheck"] = DateTime.Now.ToString(); - def.Complete(); - } - } - - async Task CheckAccount() - { - try - { - UserCredential Credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( - new ClientSecrets - { - ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", - ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_" - }, - new[] - { - Oauth2Service.Scope.UserinfoProfile, - Oauth2Service.Scope.UserinfoEmail, - YouTubeService.Scope.YoutubeForceSsl, - YouTubeService.Scope.Youtube, - YouTubeService.Scope.YoutubeUpload, - YouTubeService.Scope.YoutubeReadonly, - YouTubeService.Scope.Youtubepartner - }, - "user", - CancellationToken.None); - - if (Credential == null) - return; - - YouTubeService service = new YouTubeService(new BaseClientService.Initializer - { - HttpClientInitializer = Credential, - ApplicationName = "FoxTube" - }); - - List subscriptions = new List(); - List results = new List(); - - SubscriptionsResource.ListRequest subRequest = service.Subscriptions.List("snippet"); - subRequest.Mine = true; - subRequest.MaxResults = 50; - subRequest.Order = SubscriptionsResource.ListRequest.OrderEnum.Relevance; - SubscriptionListResponse subResponse; - string nextToken = null; - - do - { - subRequest.PageToken = nextToken; - subResponse = await subRequest.ExecuteAsync(); - foreach (Subscription s in subResponse.Items) - subscriptions.Add(s); - nextToken = subResponse.NextPageToken; - - } while (!string.IsNullOrWhiteSpace(nextToken)); - - foreach (Subscription item in subscriptions) - { - SearchResource.ListRequest request = service.Search.List("snippet"); - request.PublishedAfter = lastCheck; - request.ChannelId = item.Snippet.ResourceId.ChannelId; - request.Type = "video"; - request.MaxResults = 5; - SearchListResponse response = await request.ExecuteAsync(); - - foreach (SearchResult i in response.Items) - { - results.Add(i); - - if (i.Snippet.LiveBroadcastContent == "live") - ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetStreamToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, item.Snippet.Thumbnails.Default__.Url)); - else if (i.Snippet.LiveBroadcastContent == "upcoming") - ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetUpcomingToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, item.Snippet.Thumbnails.Default__.Url)); - else - ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetVideoToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, item.Snippet.Thumbnails.Default__.Url)); - } - } - - results.OrderBy(i => i.Snippet.PublishedAt); - - TileUpdater updater = TileUpdateManager.CreateTileUpdaterForApplication(); - updater.EnableNotificationQueue(true); - updater.Clear(); - for (int i = 0; i < 5 && i < results.Count; i++) - updater.Update(Tiles.GetTileLayout(System.Security.SecurityElement.Escape(results[i].Snippet.Title.ConvertEscapeSymbols()), System.Security.SecurityElement.Escape(results[i].Snippet.ChannelTitle), results[i].Snippet.Thumbnails.Medium.Url.Replace("&", "%26"), subscriptions.Find(x => x.Snippet.ResourceId.ChannelId == results[i].Snippet.ChannelId).Snippet.Thumbnails.Medium.Url)); - } - catch { } - } - - async void CheckAnnouncements() - { - try - { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(await new HttpClient().GetStringAsync("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml")); - XmlElement item = doc["posts"].FirstChild as XmlElement; - - DateTime date = DateTime.Parse(item.GetAttribute("time")); - if (date > lastCheck && date < DateTime.Now) - ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetInternalToast(item["id"].InnerText, - item["header"][(string)prefs.language].InnerText, - item["content"][(string)prefs.language].InnerText, - item["thumbnail"].InnerText, - item["avatar"].InnerText)); - } - catch { } - } - } - - public static class Ext - { - public static string ConvertEscapeSymbols(this string str) - { - return str.Replace(""", "\"").Replace("'", "'").Replace("<", "<").Replace(">", ">").Replace("&", "&").Replace(""", "\"").Replace("'", "'").Replace("<", "<").Replace(">", ">").Replace("&", "&"); } } } diff --git a/FoxTube.Background/FoxTube.Background.csproj b/FoxTube.Background/FoxTube.Background.csproj index 4dbe1ff..3eec541 100644 --- a/FoxTube.Background/FoxTube.Background.csproj +++ b/FoxTube.Background/FoxTube.Background.csproj @@ -108,7 +108,6 @@ - diff --git a/FoxTube.Background/ResourceCreators.cs b/FoxTube.Background/ResourceCreators.cs deleted file mode 100644 index b5fd2a0..0000000 --- a/FoxTube.Background/ResourceCreators.cs +++ /dev/null @@ -1,197 +0,0 @@ -using Google.Apis.YouTube.v3.Data; -using Microsoft.Toolkit.Uwp.Notifications; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using Windows.Data.Xml.Dom; -using Windows.Storage; -using Windows.UI.Notifications; - -namespace FoxTube.Background -{ - public static class Notification - { - private static readonly Dictionary languagePack = LoadPack(); - - private static Dictionary LoadPack() - { - dynamic saved = JsonConvert.DeserializeObject(ApplicationData.Current.RoamingSettings.Values["settings"] as string); - string hl = saved.language; - if (hl == "ru-RU") - return new Dictionary() - { - { "addLater", "Посмотреть позже" }, - { "changelog", "Список изменений" }, - { "changelogHeader", "Что нового в версии" }, - { "videoContent", "загрузил новое видео" }, - { "live", "ПРЯМОЙ ЭФИР" }, - { "upcoming", "Запланирован" }, - { "liveContent", "начал прямой эфир" }, - { "upcomingContent", "запланировал прямой эфир" }, - { "goChannel", "Открыть канал" } - }; - else - return new Dictionary() - { - { "addLater", "Add to Watch later" }, - { "changelog", "Changelog" }, - { "changelogHeader", "What's new in version" }, - { "videoContent", "uploaded a new video" }, - { "live", "LIVE" }, - { "upcoming", "Upcoming" }, - { "liveContent", "started live broadcast" }, - { "upcomingContent", "planned live broadcast" }, - { "goChannel", "Go to channel" } - }; - } - - public static ToastNotification GetChangelogToast(string version) - { - XmlDocument template = new XmlDocument(); - - template.LoadXml($@" - - - - - {languagePack["changelog"]} - {languagePack["changelogHeader"]} {version} - - - "); - - return new ToastNotification(template); - } - - public static ToastNotification GetVideoToast(string id, string channelId, string title, string channel, string thumbnail, DateTimeOffset timeStamp, string avatar) - { - XmlDocument template = new XmlDocument(); - string ts = $"{timeStamp.Year}-{timeStamp.Month:00}-{timeStamp.Day:00}T{timeStamp.Hour:00}:{timeStamp.Minute:00}:{timeStamp.Second:00}Z"; - template.LoadXml($@" - - - - - {System.Security.SecurityElement.Escape(title)} - {System.Security.SecurityElement.Escape(channel)} {languagePack["videoContent"]} - - - - - - - - "); - - return new ToastNotification(template); - } - public static ToastNotification GetStreamToast(string id, string channelId, string title, string channel, string thumbnail, DateTimeOffset timeStamp, string avatar) - { - XmlDocument template = new XmlDocument(); - string ts = $"{timeStamp.Year}-{timeStamp.Month:00}-{timeStamp.Day:00}T{timeStamp.Hour:00}:{timeStamp.Minute:00}:{timeStamp.Second:00}Z"; - template.LoadXml($@" - - - - - 🔴 [{languagePack["live"]}] {System.Security.SecurityElement.Escape(title)} - {System.Security.SecurityElement.Escape(channel)} {languagePack["liveContent"]} - - - - - - - "); - - return new ToastNotification(template); - } - public static ToastNotification GetUpcomingToast(string id, string channelId, string title, string channel, string thumbnail, DateTimeOffset timeStamp, string avatar) - { - XmlDocument template = new XmlDocument(); - string ts = $"{timeStamp.Year}-{timeStamp.Month:00}-{timeStamp.Day:00}T{timeStamp.Hour:00}:{timeStamp.Minute:00}:{timeStamp.Second:00}Z"; - template.LoadXml($@" - - - - - 🔴 [{languagePack["upcoming"]}] {System.Security.SecurityElement.Escape(title)} - {System.Security.SecurityElement.Escape(channel)} {languagePack["upcomingContent"]} - - - - - - - "); - - return new ToastNotification(template); - } - - public static ToastNotification GetInternalToast(string id, string header, string content, string thumbnail, string avatar) - { - XmlDocument template = new XmlDocument(); - - template.LoadXml($@" - - - - - {header} - {content} - - - "); - - return new ToastNotification(template); - } - } - - public static class Tiles - { - public static TileNotification GetTileLayout(string title, string channel, string thumbnail, string avatar) - { - XmlDocument doc = new XmlDocument(); - - doc.LoadXml($@" - - - - - - {channel} - {title} - - - - - - - - - - {channel} - {title} - - - - - - - - - - - - {channel} - {title} - - - - - "); - - return new TileNotification(doc); - } - } -} diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index 0d49758..c0f2ed0 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -1,8 +1,6 @@ -using FoxTube.Classes; -using Microsoft.AppCenter.Analytics; -using System.Collections.Generic; +using FoxTube.Pages; +using System.Diagnostics; using Windows.ApplicationModel.Activation; -using Windows.Globalization; using Windows.UI.Notifications; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -13,32 +11,10 @@ namespace FoxTube { public App() { - SettingsStorage.LoadData(); - - switch (SettingsStorage.Theme) - { - case 0: - RequestedTheme = ApplicationTheme.Light; - break; - case 1: - RequestedTheme = ApplicationTheme.Dark; - break; - } - ApplicationLanguages.PrimaryLanguageOverride = SettingsStorage.Language; - InitializeComponent(); - Suspending += (s, e) => Processes.SuspendApp(); - UnhandledException += (s, e) => Analytics.TrackEvent("The app crashed", new Dictionary() - { - { "Exception", e.Exception.GetType().ToString() }, - { "Details", e.Message }, - { "StackTrace", e.Exception.StackTrace } - }); - - Processes.InitializeApp(); } - protected override void OnLaunched(LaunchActivatedEventArgs e) + /*protected override void OnLaunched(LaunchActivatedEventArgs e) { if (!(Window.Current.Content is Frame)) Window.Current.Content = new Frame(); @@ -50,19 +26,11 @@ namespace FoxTube Window.Current.Activate(); } - } + }*/ protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { - var deferral = args.TaskInstance.GetDeferral(); base.OnBackgroundActivated(args); - - if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" || !(args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details)) - return; - - Processes.ProcessToast(details.Argument); - - deferral.Complete(); } protected override void OnActivated(IActivatedEventArgs e) @@ -70,26 +38,18 @@ namespace FoxTube base.OnActivated(e); //TODO: Check this shit - /*if (!(Window.Current.Content is Frame rootFrame)) + if (!(Window.Current.Content is Frame rootFrame)) { rootFrame = new Frame(); - rootFrame.NavigationFailed += OnNavigationFailed; - Window.Current.Content = rootFrame; } if (rootFrame.Content == null) rootFrame.Navigate(typeof(MainPage)); - Window.Current.Activate();*/ + Window.Current.Activate(); - if (e.Kind != ActivationKind.ToastNotification) - return; - - if (SecretsVault.IsAuthorized) - Processes.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); - else - SecretsVault.AuthorizationStateChanged += (arg) => Processes.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); + Debug.WriteLine("Hello, World"); } } } diff --git a/FoxTube/Assets/FoxGame.png b/FoxTube/Assets/FoxGame.png deleted file mode 100644 index bccd1462295023f5f1b92d208636108f75715938..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31160 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 yGvCsSX*m diff --git a/FoxTube/Assets/StoreLogo.backup.png b/FoxTube/Assets/StoreLogo.backup.png deleted file mode 100644 index 7385b56c0e4d3c6b0efe3324aa1194157d837826..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1451 zcmaJ>eN5D57_Z|bH;{0+1#mbl)eTU3{h)Wf7EZV?;HD@XL@{B`Ui%(2aMxQ~xdXSv z5nzWi(LW)U2=Vc-cY@s7nPt{i0hc6!7xN4NNHI#EQl>YNBy8l4%x9gr_W-j zEZMQmmTIy(>;lblRfh`dIyTgc9W5d!VP$L4(kKrN1c5G~(O_#xG zAJCNTstD^5SeXFB+&$h=ToJP2H>xr$iqPs-#O*;4(!Fjw25-!gEb*)mU}=)J;Iu>w zxK(5XoD0wrPSKQ~rbL^Cw6O_03*l*}i=ydbu7adJ6y;%@tjFeXIXT+ms30pmbOP%Q zX}S;+LBh8Tea~TSkHzvX6$rYb)+n&{kSbIqh|c7hmlxmwSiq5iVhU#iEQ<>a18|O^Sln-8t&+t`*{qBWo5M?wFM(JuimAOb5!K#D}XbslM@#1ZVz_;!9U zpfEpLAOz=0g@bd6Xj_ILi-x^!M}73h^o@}hM$1jflTs|Yuj9AL@A3<-?MV4!^4q`e z)fO@A;{9K^?W?DbnesnPr6kK>$zaKo&;FhFd(GYFCIU^T+OIMb%Tqo+P%oq(IdX7S zf6+HLO?7o0m+p>~Tp5UrXWh!UH!wZ5kv!E`_w)PTpI(#Iw{AS`gH4^b(bm^ZCq^FZ zY9DD7bH}rq9mg88+KgA$Zp!iWncuU2n1AuIa@=sWvUR-s`Qb{R*kk(SPU^`$6BXz8 zn#7yaFOIK%qGxyi`dYtm#&qqox0$h=pNi#u=M8zUG@bpiZ=3sT=1}Trr}39cC)H|v zbL?W)=&s4zrh)7>L(|cc%$1#!zfL?HjpeP%T+x_a+jZ16b^iKOHxFEX$7d|8${H-* zIrOJ5w&i$>*D>AKaIoYg`;{L@jM((Kt?$N$5OnuPqVvq**Nm}(f0wwOF%iX_Pba;V z;m@wxX&NcV3?<1+u?A{y_DIj7#m3Af1rCE)o`D&Y3}0%7E;iX1yMDiS)sh0wKi!36 zL!Wmq?P^Ku&rK~HJd97KkLTRl>ScGFYZNlYytWnhmuu|)L&ND8_PmkayQb{HOY640 bno1(wj@u8DCVuFR|31B*4ek@pZJqxCDDe1x diff --git a/FoxTube/Classes/AdaptiveCommandBar.cs b/FoxTube/Classes/AdaptiveCommandBar.cs deleted file mode 100644 index 3af6a2d..0000000 --- a/FoxTube/Classes/AdaptiveCommandBar.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Windows.UI.Xaml.Controls; - -namespace FoxTube.Classes -{ - class AdaptiveCommandBar : CommandBar - { - public AdaptiveCommandBar() => - ClosedDisplayMode = SettingsStorage.AppBarClosedMode; - } -} diff --git a/FoxTube/Classes/DownloadAgent.cs b/FoxTube/Classes/DownloadAgent.cs deleted file mode 100644 index 1cfffdf..0000000 --- a/FoxTube/Classes/DownloadAgent.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using Windows.Storage; -using Newtonsoft.Json; -using YoutubeExplode.Models.MediaStreams; -using FoxTube.Controls; -using FoxTube.Pages; -using Microsoft.AppCenter.Analytics; -using YoutubeExplode.Models; - -namespace FoxTube -{ - public static class DownloadAgent - { - public static List Items { get; set; } = new List(); - static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; - public static Downloads Page { get; set; } - public static StorageFolder Downloads { get; set; } - - public static async void Initialize() - { - Items.Clear(); - try - { - Downloads = await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists); - List containers = JsonConvert.DeserializeObject>((string)settings.Values[$"downloads"]); - containers.ForEach(i => Items.Add(new DownloadItem(i))); - } - catch (Exception e) - { - settings.Values["downloads"] = JsonConvert.SerializeObject(new List()); - Analytics.TrackEvent("Failed to load downloads history", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "StackTrace", e.StackTrace } - }); - } - } - - public static void Add((MediaStreamInfo info, Video meta, string qualty) e) => - Items.Insert(0, new DownloadItem(e.info, e.meta, e.qualty)); - - public static void Remove(DownloadItem item) - { - Page?.Remove(item); - Items.Remove(item); - } - - - public static void Cancel(string id) - { - DownloadItem item = Items.Find(i => i.Container.Id == id); - if (item != null) - item.Cancel(); - } - - public static void QuitPrompt() - { - foreach (DownloadItem i in Items.FindAll(i => !i.Container.IsDownloaded)) - { - i.Cancel(); - Items.Remove(i); - } - - List containers = new List(); - Items.ForEach(i => containers.Add(i.Container)); - - string data = JsonConvert.SerializeObject(containers); - - settings.Values[$"downloads"] = data; - } - } -} diff --git a/FoxTube/Classes/Extensions.cs b/FoxTube/Classes/Extensions.cs deleted file mode 100644 index 7efc0f9..0000000 --- a/FoxTube/Classes/Extensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Google.Apis.YouTube.v3.Data; -using System; -using System.Threading.Tasks; - -namespace FoxTube.Classes -{ - public delegate void SubscriptionsChangedEventHandler(string action, Subscription subscription); - public delegate void AuthorizationChangedEventHandler(bool? isAuthorized); - public delegate void MinimodeChangedEventHandler(bool isOn); - public delegate void SimpleEventHandler(); - public delegate void PlaylistItemChangedEventHandler(string id); - public delegate void NavigationEventHanlder(Type sourcePageType, object parameter); - - public static class Extensions - { - public static bool Belongs(this double num, double x1, double x2) - { - return num > x1 && num < x2; - } - } - - public interface INavigationPage - { - object Parameter { get; set; } - } - - public interface ICard - { - Task Initialize(); - void ItemClicked(); - } - - public enum PlayerDisplayState { Normal, Minimized, Compact } -} diff --git a/FoxTube/Classes/HistorySet.cs b/FoxTube/Classes/HistorySet.cs deleted file mode 100644 index 7e95ccc..0000000 --- a/FoxTube/Classes/HistorySet.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using Windows.Storage; - -namespace FoxTube -{ - public class HistoryItem - { - public string Id { get; set; } - public TimeSpan LeftOn { get; set; } = TimeSpan.FromSeconds(0); - } - - public static class HistorySet - { - public static List Items { get; set; } = new List(); - - public static void Update(HistoryItem item) - { - if (!SecretsVault.IsAuthorized) - return; - - Items.RemoveAll(i => i.Id == item.Id); - - Items.Insert(0, item); - if (Items.Count > 200) - Items.RemoveRange(200, Items.Count - 200); - Save(); - } - - public static void Delete(HistoryItem item) - { - if (!SecretsVault.IsAuthorized) - return; - - Items.Remove(item); - Save(); - } - - public static void Clear() - { - if (!SecretsVault.IsAuthorized) - return; - - Items.Clear(); - Save(); - } - - public static void Save() - { - List[] parts = new List[4] - { - new List(), new List(), - new List(), new List() - }; - - foreach(HistoryItem i in Items) - if (parts[0].Count < 50) - parts[0].Add(i); - else - { - if (parts[1].Count < 50) - parts[1].Add(i); - else - { - if (parts[2].Count < 50) - parts[2].Add(i); - else - parts[3].Add(i); - } - } - - for(int k = 0; k < parts.Length; k++) - ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}-level{k}"] = JsonConvert.SerializeObject(parts[k]); - } - - public static void Load() - { - try - { - for (int k = 0; k < 4; k++) - Items.AddRange(JsonConvert.DeserializeObject>(ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}-level{k}"] as string)); - } - catch { } - } - } -} diff --git a/FoxTube/Classes/InboxItem.cs b/FoxTube/Classes/InboxItem.cs deleted file mode 100644 index f2def2f..0000000 --- a/FoxTube/Classes/InboxItem.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using Windows.ApplicationModel.Resources; - -namespace FoxTube.Classes -{ - public enum InboxItemType { Default, PatchNote } - - public class InboxItem - { - public InboxItemType Type { get; set; } = InboxItemType.Default; - public DateTime TimeStamp { get; set; } - public string TimeStampString => Type == InboxItemType.PatchNote ? TimeStamp.ToShortDateString() : TimeStamp.ToString(); - - public string Subject { get; set; } - public string Content { get; set; } - public string Id { get; set; } - - private ResourceLoader resources = ResourceLoader.GetForCurrentView("Inbox"); - - public string Icon => Type == InboxItemType.PatchNote ? "\xE728" : "\xE119"; - public string Subtitle => Type == InboxItemType.PatchNote ? resources.GetString("changelog") : resources.GetString("dev"); - - public string Title => Type == InboxItemType.PatchNote ? $"{resources.GetString("whatsnew")}{Id}" : Subject; - - public InboxItem(string version, string content, DateTime timeStamp) - { - Type = InboxItemType.PatchNote; - Content = content; - TimeStamp = timeStamp; - Id = version; - } - - public InboxItem(string title, string content, DateTime timeStamp, string id, string header) - { - Type = InboxItemType.Default; - Content = header + "\n\n" + content; - Subject = title; - TimeStamp = timeStamp; - Id = id; - } - } -} diff --git a/FoxTube/Classes/ManifestGenerator.cs b/FoxTube/Classes/ManifestGenerator.cs deleted file mode 100644 index c187b16..0000000 --- a/FoxTube/Classes/ManifestGenerator.cs +++ /dev/null @@ -1,304 +0,0 @@ -using AngleSharp.Html.Dom; -using AngleSharp.Html.Parser; -using Google.Apis.YouTube.v3.Data; -using Microsoft.AppCenter.Analytics; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Xml; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using YoutubeExplode.Models.MediaStreams; - -namespace FoxTube.Classes -{ - public static class ManifestGenerator - { - static readonly StorageFolder roaming = ApplicationData.Current.RoamingFolder; - public static async Task GetManifest(Video meta, VideoStreamInfo requestedQuality, MediaStreamInfoSet list) - { - StorageFile manifest; - try { manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.ReplaceExisting); } - catch { manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.GenerateUniqueName); } - - try - { - XmlDocument doc = new XmlDocument(); - - XmlElement mpd = doc.CreateElement("MPD"); - mpd.SetAttribute("mediaPresentationDuration", meta.ContentDetails.Duration); - mpd.SetAttribute("minBufferTime", "PT2S"); - mpd.SetAttribute("profiles", "urn:mpeg:dash:profile:isoff-on-demand:2011"); - mpd.SetAttribute("type", "static"); - - XmlElement period = doc.CreateElement("Period"); - - XmlElement videoSet = doc.CreateElement("AdaptationSet"); - XmlElement videoMeta = doc.CreateElement("ContentComponent"); - videoMeta.SetAttribute("contentType", "video"); - videoMeta.SetAttribute("id", "1"); - videoSet.AppendChild(videoMeta); - - StreamInfo streamInfo = await GetInfoAsync(meta, requestedQuality); - - foreach (var i in streamInfo.Video) - videoSet.AppendChild(GetVideoPresentation(doc, i)); - - XmlElement audioSet = doc.CreateElement("AdaptationSet"); - XmlElement audioMeta = doc.CreateElement("ContentComponent"); - audioMeta.SetAttribute("contentType", "audio"); - audioMeta.SetAttribute("id", "2"); - audioSet.AppendChild(audioMeta); - - foreach (var i in streamInfo.Audio) - audioSet.AppendChild(GetAudioPresentation(doc, i)); - - doc.AppendChild(mpd); - mpd.AppendChild(period); - period.AppendChild(videoSet); - period.AppendChild(audioSet); - - doc.Save(await manifest.OpenStreamForWriteAsync()); - - return $"ms-appdata:///roaming/{manifest.Name}".ToUri(); - } - catch (Exception e) - { - Analytics.TrackEvent("Failed to generate manifest", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "Video ID", meta.Id }, - { "Requested quality", requestedQuality.VideoQualityLabel }, - { "StackTrace", e.StackTrace } - }); - return null; - } - } - - static XmlElement GetVideoPresentation(XmlDocument doc, StreamInfo.VideoInfo info) - { - XmlElement representation = doc.CreateElement("Representation"); - representation.SetAttribute("bandwidth", GetBandwidth(info.Label)); - representation.SetAttribute("id", info.Itag); - representation.SetAttribute("mimeType", info.MimeType); - representation.SetAttribute("codecs", info.Codecs); - representation.SetAttribute("fps", info.Fps); - representation.SetAttribute("height", info.Height); - representation.SetAttribute("width", info.Width); - - XmlElement baseUrl = doc.CreateElement("BaseURL"); - baseUrl.InnerText = info.Url; - representation.AppendChild(baseUrl); - - XmlElement segmentBase = doc.CreateElement("SegmentBase"); - segmentBase.SetAttribute("indexRange", info.IndexRange); - representation.AppendChild(segmentBase); - - XmlElement initialization = doc.CreateElement("Initialization"); - initialization.SetAttribute("range", info.InitRange); - segmentBase.AppendChild(initialization); - - return representation; - } - - static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info) - { - XmlElement audio = doc.CreateElement("Representation"); - audio.SetAttribute("bandwidth", "200000"); - audio.SetAttribute("id", info.Itag); - audio.SetAttribute("sampleRate", info.SampleRate); - audio.SetAttribute("numChannels", info.ChannelsCount); - audio.SetAttribute("codecs", info.Codecs); - audio.SetAttribute("mimeType", info.MimeType); - - XmlElement audioUrl = doc.CreateElement("BaseURL"); - audioUrl.InnerText = info.Url; - audio.AppendChild(audioUrl); - - XmlElement audioSegmentBase = doc.CreateElement("SegmentBase"); - audioSegmentBase.SetAttribute("indexRange", info.IndexRange); - audio.AppendChild(audioSegmentBase); - - XmlElement audioInit = doc.CreateElement("Initialization"); - audioInit.SetAttribute("range", info.InitRange); - audioSegmentBase.AppendChild(audioInit); - - return audio; - } - - static async Task GetInfoAsync(Video info, VideoStreamInfo requestedQuality) - { - try - { - StreamInfo si = new StreamInfo(); - HttpClient http = new HttpClient(); - - string response = await http.GetStringAsync($"https://youtube.com/watch?v={info.Id}&disable_polymer=true&bpctr=9999999999&hl=en"); - IHtmlDocument videoEmbedPageHtml = new HtmlParser().ParseDocument(response); - - string playerConfigRaw = Regex.Match(videoEmbedPageHtml.Source.Text, - @"ytplayer\.config = (?\{[^\{\}]*(((?\{)[^\{\}]*)+((?\})[^\{\}]*)+)*(?(Open)(?!))\})") - .Groups["Json"].Value; - JToken playerConfigJson = JToken.Parse(playerConfigRaw); - - var playerResponseRaw = playerConfigJson.SelectToken("args.player_response").Value(); - JToken playerResponseJson = JToken.Parse(playerResponseRaw); - string errorReason = playerResponseJson.SelectToken("playabilityStatus.reason")?.Value(); - if (!string.IsNullOrWhiteSpace(errorReason)) - throw new InvalidDataException($"Video [{info.Id}] is unplayable. Reason: {errorReason}"); - - List> adaptiveStreamInfosUrl = playerConfigJson.SelectToken("args.adaptive_fmts")?.Value().Split(',').Select(SplitQuery).ToList(); - List> video = - requestedQuality == null ? - adaptiveStreamInfosUrl.FindAll(i => i.ContainsKey("quality_label")) : - adaptiveStreamInfosUrl.FindAll(i => i.ContainsValue(requestedQuality.VideoQualityLabel)); - List> audio = adaptiveStreamInfosUrl.FindAll(i => i.ContainsKey("audio_sample_rate")); - - foreach (var i in video) - si.Video.Add(new StreamInfo.VideoInfo - { - IndexRange = i["index"], - Url = i["url"], - Itag = i["itag"], - Fps = i["fps"], - Height = i["size"].Split('x')[1], - Width = i["size"].Split('x')[0], - Codecs = i["type"].Split('"')[1], - MimeType = i["type"].Split(';')[0], - Label = i["quality_label"] - }); - - foreach (var i in audio) - si.Audio.Add(new StreamInfo.AudioInfo - { - ChannelsCount = i["audio_channels"], - IndexRange = i["index"], - SampleRate = i["audio_sample_rate"], - Codecs = i["type"].Split('"')[1], - MimeType = i["type"].Split(';')[0], - Url = i["url"], - Itag = i["itag"] - }); - - return si; - } - catch - { - return new StreamInfo(); - } - } - - static Dictionary SplitQuery(string query) - { - Dictionary dic = new Dictionary(StringComparer.OrdinalIgnoreCase); - string[] paramsEncoded = query.TrimStart('?').Split("&"); - foreach (string paramEncoded in paramsEncoded) - { - string param = WebUtility.UrlDecode(paramEncoded); - - // Look for the equals sign - int equalsPos = param.IndexOf('='); - if (equalsPos <= 0) - continue; - - // Get the key and value - string key = param.Substring(0, equalsPos); - string value = equalsPos < param.Length - ? param.Substring(equalsPos + 1) - : string.Empty; - - // Add to dictionary - dic[key] = value; - } - - return dic; - } - - static string GetBandwidth(string quality) - { - string parsed = quality.Split('p')[0]; - switch (quality) - { - case "4320": - return $"16763040‬"; - case "3072": - return $"11920384"; - case "2880": - return $"11175360"; - case "2160": - return $"8381520"; - case "1440": - return $"5587680‬"; - case "1080": - return $"4190760"; - case "720": - return $"2073921"; - case "480": - return $"869460"; - case "360": - return $"686521"; - case "240": - return $"264835"; - case "144": - default: - return $"100000"; - } - } - - public static async Task> ResolveLiveSteream(string url) - { - List list = new List(); - string playlistRaw = await new HttpClient().GetStringAsync(url); - - List streamsRaw = playlistRaw.Split("#EXT-X-STREAM-INF:").ToList(); - streamsRaw.RemoveAt(0); - List> streams = new List>(); - foreach (string i in streamsRaw) - { - Dictionary item = new Dictionary(); - string[] par = i.Split('\n'); - item.Add("URL", par[1]); - par = par[0].Split(','); - foreach (string k in par) - { - string[] pair = k.Split('='); - if (pair.Length < 2) - continue; - item[pair[0]] = pair[1]; - } - streams.Add(item); - } - - foreach (var i in streams) - { - StreamQuality item = new StreamQuality(); - item.Resolution = $"{i["RESOLUTION"].Split('x')[1]}p"; - item.Url = i["URL"].ToUri(); - list.Add(item); - } - - list.Add(new StreamQuality - { - Resolution = ResourceLoader.GetForCurrentView("VideoPage").GetString("/VideoPage/auto"), - Url = url.ToUri() - }); - list.Reverse(); - - return list; - } - - public static async void ClearRoaming() - { - IReadOnlyList items = await roaming.GetFilesAsync(); - foreach (StorageFile f in items) - await f.DeleteAsync(StorageDeleteOption.PermanentDelete); - } - } -} diff --git a/FoxTube/Classes/Methods.cs b/FoxTube/Classes/Methods.cs deleted file mode 100644 index ca0190e..0000000 --- a/FoxTube/Classes/Methods.cs +++ /dev/null @@ -1,319 +0,0 @@ -using FoxTube.Classes; -using FoxTube.Controls.VideoPage; -using Google.Apis.YouTube.v3.Data; -using Microsoft.AppCenter.Analytics; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Mail; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Xml; -using Windows.ApplicationModel.DataTransfer; -using Windows.ApplicationModel.Resources; -using Windows.Storage.Streams; -using Windows.System; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Documents; -using YoutubeExplode; -using YoutubeExplode.Models.MediaStreams; - -namespace FoxTube -{ - public delegate void Event(); - - public delegate void ObjectEventHandler(object sender = null, params object[] args); - - public static class Methods - { - private static readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods"); - - public static Comments CommentsPage { get; set; } - public static MainPage MainPage => (Window.Current.Content as Frame).Content as MainPage; - - public static async Task AddItemToWL(string id) - { - try - { - SecretsVault.RefreshToken(); - - PlaylistItem item = new PlaylistItem - { - Snippet = new PlaylistItemSnippet - { - ResourceId = new ResourceId - { - Kind = "youtube#video", - VideoId = id - }, - PlaylistId = "WL" - } - }; - - await SecretsVault.Service.PlaylistItems.Insert(item, "snippet").ExecuteAsync(); - return true; - } - catch (Exception e) - { - Analytics.TrackEvent("Failed to add video to WL", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "Video ID", id }, - { "StackTrace", e.StackTrace } - }); - - return false; - } - } - - public static Uri ToUri(this string url) - { - try { return string.IsNullOrWhiteSpace(url) ? null : new Uri(url); } - catch { return null; } - } - - public static string GuardFromNull(string str) => - str ?? string.Empty; - - public static void SendMail(string content) - { - MailMessage msg = new MailMessage(); - msg.To.Add("michael.xfox@outlook.com"); - msg.From = new MailAddress(SecretsVault.EmailCredential.UserName); - msg.Subject = "[Automatic message] FoxTube service message"; - msg.Body = content; - - SmtpClient client = new SmtpClient("smtp.gmail.com", 587); - client.EnableSsl = true; - client.Credentials = SecretsVault.EmailCredential; - client.Send(msg); - } - - public static List ToReversedList(this Array array) - { - List list = new List(); - foreach (object i in array) - list.Add(i); - list.Reverse(); - return list; - } - - public static void ForEach(this IEnumerable array, Action action) => - array.ToList().ForEach(action); - - public static T Find(this IEnumerable array, Predicate match) => - array.ToList().Find(match); - - public static List FindAll(this IEnumerable array, Predicate match) => - 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; - } - - [Obsolete("Use *YoutubeExplode.Models.Video instead*")] - 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].Remove('P')) * 7); - date.Add(time); - - return date; - } - catch (Exception e) - { - Analytics.TrackEvent("Failed to parse duration", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "StackTrace", e.StackTrace } - }); - 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(this TextBlock block, string text) - { - block.Inlines.Clear(); - Regex filter = new Regex(@"\b((?:https?://|www\.)\S+)|(\S+@\S+)\b", RegexOptions.IgnoreCase); - Regex link = new Regex(@"\b(?:https?://|www\.)\S+\b", RegexOptions.IgnoreCase); - Regex mail = new Regex(@"\b\S+@\S+\b", RegexOptions.IgnoreCase); - - foreach (string item in filter.Split(text)) - { - if (link.IsMatch(item)) - { - string str = item; - if (!str.Contains("http")) - str = str.Insert(0, "http://"); - - Hyperlink hl = new Hyperlink(); - hl.Click += (s, arg) => ProcessLink(str); - hl.Inlines.Add(new Run { Text = item }); - block.Inlines.Add(hl); - } - else if (mail.IsMatch(item)) - { - Hyperlink hl = new Hyperlink { NavigateUri = $"mailto:{item}".ToUri() }; - hl.Inlines.Add(new Run { Text = item }); - block.Inlines.Add(hl); - } - 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 async static void ProcessLink(string url) - { - try - { - string type; - - if (YoutubeClient.TryParseChannelId(url, out string output)) - { - type = "channel"; - goto LinkFound; - } - else if (YoutubeClient.TryParsePlaylistId(url, out output)) - { - type = "playlist"; - goto LinkFound; - } - else if (YoutubeClient.TryParseUsername(url, out output)) - { - type = "user"; - goto LinkFound; - } - else if (YoutubeClient.TryParseVideoId(url, out output)) - { - type = "video"; - goto LinkFound; - } - - await Launcher.LaunchUriAsync(new Uri(url)); - return; - - LinkFound: - switch (type) - { - case "channel": - Navigation.GoToChannel(output); - break; - case "video": - Navigation.GoToVideo(output); - break; - case "playlist": - Navigation.GoToPlaylist(output); - break; - case "user": - Navigation.GoToChannel(await new YoutubeClient().GetChannelIdAsync(output)); - break; - } - } - catch { } - } - - public static 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()); - - request.Data.Properties.Thumbnail = RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri()); - request.Data.SetBitmap(RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri())); - } - - public static async Task GetHistory() - { - SecretsVault.RefreshToken(); - - return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("HL"); - } - - public static async Task GetLater() - { - SecretsVault.RefreshToken(); - - return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("WL"); - } - } -} diff --git a/FoxTube/Classes/Navigation.cs b/FoxTube/Classes/Navigation.cs deleted file mode 100644 index 0c3c2b6..0000000 --- a/FoxTube/Classes/Navigation.cs +++ /dev/null @@ -1,36 +0,0 @@ -using FoxTube.Pages; -using Google.Apis.YouTube.v3.Data; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace FoxTube.Classes -{ - public static class Navigation - { - public static MainFrame Frame => ((Window.Current.Content as Frame).Content as MainPage).Content; - - public static void GoToSearch(SearchParameters args) => - Frame.NavigateTo(typeof(Search), new object[] { args, Frame.Frame }); - - public static void GoToChannel(string id) => - Frame.NavigateTo(typeof(ChannelPage), id); - - public static void GoToHome() => - Frame.NavigateTo(typeof(Home)); - - public static void GoToVideo(string id, string playlistId = null, bool incognito = false) => - Frame.OpenVideo(id, playlistId, incognito); - - public static void GoToDeveloper(string id) => - Frame.NavigateTo(typeof(Settings), id); - - public static void GoToPlaylist(string id) => - Frame.NavigateTo(typeof(PlaylistPage), id); - - public static void GoToHistory() => - Frame.NavigateTo(typeof(History)); - - public static void GoToDownloads() => - Frame.NavigateTo(typeof(Downloads)); - } -} diff --git a/FoxTube/Classes/Processes.cs b/FoxTube/Classes/Processes.cs deleted file mode 100644 index 4b9475b..0000000 --- a/FoxTube/Classes/Processes.cs +++ /dev/null @@ -1,299 +0,0 @@ -using Microsoft.AppCenter; -using Microsoft.AppCenter.Analytics; -using Microsoft.Services.Store.Engagement; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Xml; -using Windows.ApplicationModel; -using Windows.ApplicationModel.Background; -using Windows.ApplicationModel.DataTransfer; -using Windows.ApplicationModel.Resources; -using Windows.Storage; -using Windows.System; -using Windows.System.Power; -using Windows.UI.Notifications; -using Windows.UI.Popups; -using Windows.UI.Xaml; -using Windows.UI.Core; - -namespace FoxTube.Classes -{ - public static class Processes - { - static Stopwatch sw = new Stopwatch(); - public static async void InitializeApp() => await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.High, () => - { - CheckVersion(); - - RegisterToastTask(); - ActivateBackgoundTask(); - - AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics)); - AppCenter.SetCountryCode(SettingsStorage.Region); - - sw.Start(); - - SecretsVault.Initialize(); - DownloadAgent.Initialize(); - - if (SettingsStorage.ProcessClipboard) - { - Clipboard.ContentChanged += ParseClipboard; - ParseClipboard(); - } - - PromptFeedback(); - }); - - public static void SuspendApp() - { - sw.Stop(); - SettingsStorage.Uptime += sw.Elapsed; - - HistorySet.Save(); - SettingsStorage.SaveData(); - DownloadAgent.QuitPrompt(); - ManifestGenerator.ClearRoaming(); - Analytics.TrackEvent("Session terminated", new Dictionary - { - { "Uptime", sw.Elapsed.ToString() }, - { "Total time", SettingsStorage.Uptime.ToString() } - }); - } - - public static async void ProcessToast(string arg) - { - string[] args = arg.Split('|'); - switch (args[0]) - { - case "changelog": - case "inbox": - Navigation.GoToDeveloper(args[1]); - break; - - case "video": - Navigation.GoToVideo(args[1]); - break; - - case "channel": - Navigation.GoToChannel(args[1]); - break; - - case "download": - Navigation.GoToDownloads(); - break; - case "dcancel": - DownloadAgent.Cancel(args[1]); - break; - case "clipboard": - switch (args[1]) - { - case "video": - Navigation.GoToVideo(args[2]); - break; - case "channel": - Navigation.GoToChannel(args[2]); - break; - case "playlist": - Navigation.GoToPlaylist(args[2]); - break; - } - break; - case "later": - if (SecretsVault.IsAuthorized) - await Methods.AddItemToWL(args[1]); - else - { - SecretsVault.AuthorizationStateChanged += async (e) => - { - if (e.Value) - await Methods.AddItemToWL(args[1]); - }; - SecretsVault.CheckAuthorization(false); - } - break; - } - } - public static async void PromptFeedback() - { - ResourceLoader resources = ResourceLoader.GetForCurrentView("Main"); - - if (SettingsStorage.Uptime.TotalHours >= 12 && SettingsStorage.PromptFeedback) - { - MessageDialog dialog = new MessageDialog(resources.GetString("/Main/feedbackMessage")); - dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptFeedback = false)); - dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater"))); - dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) => - { - SettingsStorage.PromptFeedback = false; - if (StoreServicesFeedbackLauncher.IsSupported()) - await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync(); - else - { - MessageDialog message = new MessageDialog(resources.GetString("/Main/feedbackFail")); - message.Commands.Add(new UICommand(resources.GetString("/Main/sendEmail"), async (c) => await Launcher.LaunchUriAsync("mailto:michael.xfox@outlook.com".ToUri()))); - message.Commands.Add(new UICommand(resources.GetString("/Main/goBack"))); - message.CancelCommandIndex = 1; - message.DefaultCommandIndex = 0; - await message.ShowAsync(); - } - })); - dialog.DefaultCommandIndex = 2; - dialog.CancelCommandIndex = 1; - await dialog.ShowAsync(); - } - - if (SettingsStorage.Uptime.TotalHours >= 24 && SettingsStorage.PromptReview) - { - MessageDialog dialog = new MessageDialog(resources.GetString("/Main/rate")); - dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptReview = false)); - dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater"))); - dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) => - { - SettingsStorage.PromptReview = false; - await Launcher.LaunchUriAsync("ms-windows-store://review/?ProductId=9NCQQXJTDLFH".ToUri()); - })); - dialog.DefaultCommandIndex = 2; - dialog.CancelCommandIndex = 1; - await dialog.ShowAsync(); - } - } - - public static async void ParseClipboard(object sender = null, object e = null) - { - ResourceLoader resources = ResourceLoader.GetForCurrentView("Main"); - - if (Window.Current.CoreWindow.ActivationMode != Windows.UI.Core.CoreWindowActivationMode.ActivatedInForeground || !SettingsStorage.ProcessClipboard) - return; - - try - { - string link = await Clipboard.GetContent().GetTextAsync(); - - if (!link.Contains("youtube") && !link.Contains("youtu.be")) - return; - - string id; - string type = string.Empty; - string name = string.Empty; - - if (YoutubeExplode.YoutubeClient.TryParseChannelId(link, out id)) - { - type = "channel"; - name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title; - } - else if (YoutubeExplode.YoutubeClient.TryParsePlaylistId(link, out id)) - { - type = "playlist"; - name = (await new YoutubeExplode.YoutubeClient().GetPlaylistAsync(id)).Title; - } - else if (YoutubeExplode.YoutubeClient.TryParseUsername(link, out id)) - { - id = await new YoutubeExplode.YoutubeClient().GetChannelIdAsync(id); - type = "channel"; - name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title; - } - else if (YoutubeExplode.YoutubeClient.TryParseVideoId(link, out id)) - { - type = "video"; - name = (await new YoutubeExplode.YoutubeClient().GetVideoAsync(id)).Title; - } - - if(string.IsNullOrWhiteSpace(id)) - return; - - Windows.Data.Xml.Dom.XmlDocument toastXml = new Windows.Data.Xml.Dom.XmlDocument(); - toastXml.LoadXml($@" - - - {resources.GetString("/Main/clipboardHead")} - {name} - {resources.GetString($"/Main/{type}")} - - - - - - "); - ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml)); - } - catch { } - } - - /// - /// Comparing current version with last recorded version. If doesn't match, poping up changelog notification - /// - static async void CheckVersion() - { - PackageVersion ver = Package.Current.Id.Version; - if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}.{ver.Build}") - try - { - XmlDocument changelog = new XmlDocument(); - StorageFile file = await (await Package.Current.InstalledLocation.GetFolderAsync(@"Assets\Data")).GetFileAsync("Patchnotes.xml"); - changelog.Load(await file.OpenStreamForReadAsync()); - - ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(changelog["items"].FirstChild.Attributes["version"].Value)); - - SettingsStorage.Version = $"{ver.Major}.{ver.Minor}.{ver.Build}"; - } - catch (Exception e) - { - Analytics.TrackEvent("Unable to retrieve changelog", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "App version", $"{ver.Major}.{ver.Minor}.{ver.Revision}.{ver.Build}" }, - { "StackTrace", e.StackTrace } - }); - } - } - - /// - /// Initializes background task for processing toast notifications' clicks - /// - static async void RegisterToastTask() - { - const string taskName = "FoxtubeToastBackground"; - if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName))) - return; - - var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync(); - if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser) - return; - - BackgroundTaskBuilder builder = new BackgroundTaskBuilder() { Name = taskName }; - builder.SetTrigger(new ToastNotificationActionTrigger()); - - BackgroundTaskRegistration registration = builder.Register(); - } - - /// - /// Initializes background task for checking user's subscriptions and poping toast notifications when new video is uploaded - /// - static async void ActivateBackgoundTask() - { - const string taskName = "FoxtubeBackgound"; - if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName))) - return; - - var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync(); - var saverRequest = PowerManager.EnergySaverStatus; - if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser || saverRequest == EnergySaverStatus.On) - return; - - BackgroundTaskBuilder builder = new BackgroundTaskBuilder() - { - Name = taskName, - IsNetworkRequested = true, - TaskEntryPoint = "FoxTube.Background.BackgroundProcessor" - }; - builder.SetTrigger(new TimeTrigger(15, false)); - - BackgroundTaskRegistration registration = builder.Register(); - } - } -} diff --git a/FoxTube/Classes/QualityComparer.cs b/FoxTube/Classes/QualityComparer.cs deleted file mode 100644 index 0a3bb90..0000000 --- a/FoxTube/Classes/QualityComparer.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; - -namespace FoxTube.Classes -{ - public class QualityComparer : IComparer - { - public int Compare(string x, string y) - { - string[] xArr = x.Split('p'); - string[] yArr = y.Split('p'); - - int qualityA = int.Parse(xArr[0]); - int qualityB = int.Parse(yArr[0]); - int framerateA = 30; - int framerateB = 30; - - if (!string.IsNullOrWhiteSpace(xArr[1])) - framerateA = int.Parse(xArr[1]); - if (!string.IsNullOrWhiteSpace(yArr[1])) - framerateB = int.Parse(yArr[1]); - - if (qualityA > qualityB) - return 1; - else if (qualityA < qualityB) - return -1; - else - return framerateA - framerateB > 0 ? 1 : -1; - } - } -} diff --git a/FoxTube/Classes/SearchParameters.cs b/FoxTube/Classes/SearchParameters.cs deleted file mode 100644 index bd31364..0000000 --- a/FoxTube/Classes/SearchParameters.cs +++ /dev/null @@ -1,156 +0,0 @@ -using Google.Apis.YouTube.v3.Data; -using System; -using System.Collections.Generic; -using static Google.Apis.YouTube.v3.SearchResource.ListRequest; - -namespace FoxTube -{ - public class SearchParameters - { - public class Filters - { - public static class Enumerations - { - public enum Order { Relevance, Date, Views, Rating, Title } - public enum Type { All, Video, Channel, Playlist } - public enum Date { Any, Hour, Today, Week, Month, Year } - public enum Duration { Any, Long, Medium, Short } - public enum ConversionType { Order, Type, Date, Duration, HD, ThreeD, Captions, LiveEvent, CreativeCommons } - } - - public Enumerations.Order Order { get; set; } = Enumerations.Order.Relevance; - public Enumerations.Date Date { get; set; } = Enumerations.Date.Any; - public Enumerations.Type Type { get; set; } = Enumerations.Type.All; - public Enumerations.Duration Duration { get; set; } = Enumerations.Duration.Any; - public bool HD { get; set; } = false; - public bool Is3D { get; set; } = false; - public bool Captions { get; set; } = false; - public bool LiveEvent { get; set; } = false; - public bool CreativeCommons { get; set; } = false; - - public object GetParameter(Enumerations.ConversionType type) - { - switch(type) - { - case Enumerations.ConversionType.Captions: - if (Captions) - return VideoCaptionEnum.ClosedCaption; - else return VideoCaptionEnum.Any; - case Enumerations.ConversionType.CreativeCommons: - if (CreativeCommons) - return VideoLicenseEnum.CreativeCommon; - else return VideoLicenseEnum.Any; - case Enumerations.ConversionType.Date: - switch(Date) - { - case Enumerations.Date.Any: - return null; - case Enumerations.Date.Hour: - return DateTime.Now.Subtract(TimeSpan.FromHours(1)); - case Enumerations.Date.Today: - return DateTime.Now.Subtract(TimeSpan.FromDays(1)); - case Enumerations.Date.Week: - return DateTime.Now.Subtract(TimeSpan.FromDays(7)); - case Enumerations.Date.Month: - return DateTime.Now.Subtract(TimeSpan.FromDays(31)); - case Enumerations.Date.Year: - return DateTime.Now.Subtract(TimeSpan.FromDays(365)); - } - break; - case Enumerations.ConversionType.Duration: - return (VideoDurationEnum)Duration; - case Enumerations.ConversionType.HD: - if (HD) - return VideoDefinitionEnum.High; - else return VideoDefinitionEnum.Any; - case Enumerations.ConversionType.LiveEvent: - if (LiveEvent) - return EventTypeEnum.Live; - else return null; - case Enumerations.ConversionType.Order: - Dictionary d = new Dictionary() - { - { 0, 2 }, - { 1, 0 }, - { 2, 5 }, - { 3, 1 }, - { 4, 3 } - }; - return (OrderEnum)d[(int)Order]; - case Enumerations.ConversionType.ThreeD: - if (Is3D) - return VideoDimensionEnum.Value3d; - else return VideoDimensionEnum.Any; - case Enumerations.ConversionType.Type: - switch(Type) - { - case Enumerations.Type.All: - return "video,channel,playlist"; - case Enumerations.Type.Channel: - return "channel"; - case Enumerations.Type.Playlist: - return "playlist"; - case Enumerations.Type.Video: - return "video"; - } - break; - } - return null; - } - } - - public string Term { get; private set; } - public string Channel { get; private set; } - public VideoCategory Category { get; set; } - public Filters Filter { get; private set; } = new Filters(); - - public SearchParameters(string term) - { - Term = term; - } - - public SearchParameters(VideoCategory category) - { - Category = category; - Filter = new Filters(); - Filter.Type = Filters.Enumerations.Type.Video; - } - - public SearchParameters(string term, Filters filters) - { - Term = term; - Filter = filters; - } - - public SearchParameters(string term, string channelId) - { - Term = term; - Channel = channelId; - } - - - public SearchParameters(string term, string channelId, Filters filters) - { - Term = term; - Channel = channelId; - Filter = filters; - } - - public override string ToString() - { - return $@"Term: {Term} -Channel id: {Channel} -Category id: {Category} -Filters: - Order: {Filter.Order} - Type: {Filter.Type} - Date: {Filter.Date} - Duration: {Filter.Duration} - HD: {Filter.HD} - 3D: {Filter.Is3D} - Event type: {Filter.LiveEvent} - CC: {Filter.Captions} - License: {Filter.CreativeCommons}"; - } - } -} diff --git a/FoxTube/Classes/SecretsVault.cs b/FoxTube/Classes/SecretsVault.cs deleted file mode 100644 index 5bf49a1..0000000 --- a/FoxTube/Classes/SecretsVault.cs +++ /dev/null @@ -1,275 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Google.Apis.Auth.OAuth2; -using Google.Apis.Services; -using Google.Apis.YouTube.v3; -using Google.Apis.YouTube.v3.Data; -using Windows.Services.Store; -using System.Net.Http; -using Google.Apis.Oauth2.v2.Data; -using Google.Apis.Oauth2.v2; -using static Google.Apis.Auth.OAuth2.UwpCodeReceiver; -using Microsoft.AppCenter.Analytics; -using System.Net; -using FoxTube.Classes; -using System.Linq; - -namespace FoxTube -{ - public static class SecretsVault - { - #region Properties - //Events - public static event AuthorizationChangedEventHandler AuthorizationStateChanged; - public static event SubscriptionsChangedEventHandler SubscriptionsChanged; - public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version or after purchase - - //Properties - public static NetworkCredential EmailCredential => new NetworkCredential("foxtube.bot@xfox111.net", "JkY39w$.7?bT57O,8k3a"); - private static ClientSecrets Secrets => new ClientSecrets - { - ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", - ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_" - }; - private static YouTubeService NoAuthService => new YouTubeService(new BaseClientService.Initializer - { - ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", - ApplicationName = "FoxTube" - }); - private static BaseClientService.Initializer Initializer => new BaseClientService.Initializer - { - HttpClientInitializer = Credential, - ApplicationName = "FoxTube" - }; - public static YouTubeService Service => IsAuthorized ? new YouTubeService(Initializer) : NoAuthService; - - public static HttpClient HttpClient { get; } = new HttpClient(); - private static bool TestAds => true; //TODO: Change this bool - public static string AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh"; - public static string AdUnitId => TestAds ? "test" : "1100037769"; - public static bool AdsDisabled { get; private set; } = true; - - //User info - public static bool IsAuthorized => Credential != null; - private static UserCredential Credential { get; set; } = null; - - public static string AccountId => UserChannel?.Id; - public static Channel UserChannel { get; private set; } - public static Userinfoplus UserInfo { get; private set; } - - public static List Subscriptions { get; } = new List(); - public static YoutubeExplode.Models.Playlist History { get; set; } - public static YoutubeExplode.Models.Playlist WatchLater { get; set; } - #endregion - - #region Methods - public static void RefreshToken() => - Credential?.RefreshTokenAsync(CancellationToken.None); - - /// - /// Subscribes or unsibscribes authorized user from the channel - /// - /// The ID of channel which has to be added/removed - /// Returns 'true' if channel is in subscriptions now; 'false' if it's not - public static async Task ChangeSubscriptionState(string id) - { - if(Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == id) is Subscription subscription) - { - try { await Service.Subscriptions.Delete(subscription.Id).ExecuteAsync(); } - catch { return true; } - - SubscriptionsChanged?.Invoke("remove", subscription); - Subscriptions.Remove(subscription); - - return false; - } - else - { - var request = Service.Subscriptions.Insert(new Subscription - { - Snippet = new SubscriptionSnippet - { - ResourceId = new ResourceId - { - ChannelId = id, - Kind = "youtube#channel" - } - } - }, "snippet"); - - if (!(await request.ExecuteAsync() is Subscription sub)) - return false; - - Subscriptions.Add(sub); - SubscriptionsChanged?.Invoke("add", sub); - - return true; - } - } - - /// - /// Sets up **SecretsVault** - /// - public static void Initialize() - { - CheckAddons(); - CheckAuthorization(); - } - - /// - /// Prompts to add an Youtube account and retrieves its info when successful - /// - /// Loads user's subscriptions if true - public static async void Authorize(bool retrieveSubs = true) - { - #region Retrieving user's credential - try - { - Credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( - Secrets, - new[] - { - Oauth2Service.Scope.UserinfoProfile, - Oauth2Service.Scope.UserinfoEmail, - YouTubeService.Scope.YoutubeForceSsl, - YouTubeService.Scope.YoutubeUpload - }, - "user", - CancellationToken.None); - - await Credential.RefreshTokenAsync(CancellationToken.None); - } - catch (AuthenticateException e) when (e.Message.Contains("UserCancel")) { } - catch(Exception e) - { - AuthorizationStateChanged?.Invoke(null); - Analytics.TrackEvent("Failed to authorize", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "StackTrace", e.StackTrace } - }); - } - - if (Credential == null || !retrieveSubs) - return; - - HttpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Credential.Token.AccessToken); - - SettingsStorage.HasAccount = true; - #endregion - - try - { - #region Retrieving user's data - UserInfo = await new Oauth2Service(Initializer).Userinfo.Get().ExecuteAsync(); - - WatchLater = await Methods.GetLater(); - History = await Methods.GetHistory(); - - SubscriptionsResource.ListRequest subRequest = Service.Subscriptions.List("snippet"); - subRequest.Mine = true; - subRequest.MaxResults = 50; - subRequest.Order = SubscriptionsResource.ListRequest.OrderEnum.Relevance; - SubscriptionListResponse subResponse; - string nextToken = null; - - do - { - subRequest.PageToken = nextToken; - subResponse = await subRequest.ExecuteAsync(); - foreach (Subscription s in subResponse.Items) - Subscriptions.Add(s); - nextToken = subResponse.NextPageToken; - - } while (!string.IsNullOrWhiteSpace(nextToken)); - - ChannelsResource.ListRequest request = Service.Channels.List("snippet,contentDetails"); - request.Mine = true; - UserChannel = (await request.ExecuteAsync()).Items.FirstOrDefault(); - #endregion - - AuthorizationStateChanged?.Invoke(true); - } - catch (Exception e) - { - AuthorizationStateChanged?.Invoke(null); - Methods.SendMail(e.ToString()); - Analytics.TrackEvent("Failed to retrieve user's info", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "StackTrace", e.StackTrace } - }); - } - } - - /// - /// Deauthenticates current user - /// - public static async void Deauthenticate() - { - if (!await Credential.RevokeTokenAsync(CancellationToken.None)) - return; - - Credential = null; - UserChannel = null; - UserInfo = null; - History = null; - WatchLater = null; - Subscriptions.Clear(); - - HttpClient.DefaultRequestHeaders.Authorization = null; - - AuthorizationStateChanged?.Invoke(false); - SettingsStorage.HasAccount = false; - } - - /// - /// Checks if any user has already been logged in. If has, calls *Authorize()* to retrieve his info - /// - /// Loads user's subscriptions if true - public static void CheckAuthorization(bool retrieveSubs = true) - { - if (SettingsStorage.HasAccount) - Authorize(retrieveSubs); - else - AuthorizationStateChanged.Invoke(false); - } - - /// - /// Connects to MS Store and checks if user has bought ad-free - /// - public static async void CheckAddons() - { - try - { - StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" }); - - if (requset.Products["9NP1QK556625"].IsInUserCollection) - return; - - AdsDisabled = false; - Purchased?.Invoke(null, false, requset.Products["9NP1QK556625"].Price.FormattedPrice); - } - catch { } - } - - public static async void GetAdblock() - { - StorePurchaseResult request = await StoreContext.GetDefault().RequestPurchaseAsync("9NP1QK556625"); - - switch (request.Status) - { - case StorePurchaseStatus.AlreadyPurchased: - case StorePurchaseStatus.Succeeded: - Purchased?.Invoke(args: true); - AdsDisabled = true; - break; - } - } - #endregion - } -} diff --git a/FoxTube/Classes/SettingsStorage.cs b/FoxTube/Classes/SettingsStorage.cs deleted file mode 100644 index 52f545c..0000000 --- a/FoxTube/Classes/SettingsStorage.cs +++ /dev/null @@ -1,286 +0,0 @@ -using Microsoft.AppCenter.Analytics; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Windows.ApplicationModel; -using Windows.Storage; -using Windows.UI.Xaml.Controls; - -namespace FoxTube -{ - public enum MatureState { Blocked, Allowed } - - public class SettingsContainer - { - public string videoQuality = "remember"; - public string rememberedQuality = "1080p"; - - public bool videoNotifications = true; - public bool devNotifications = true; - - public bool checkConnection = true; - public bool autoplay = true; - public double volume = 100; - - public string language = GetLanguage(); - public string relevanceLanguage = CultureInfo.InstalledUICulture.TwoLetterISOLanguageName; - public string region = CultureInfo.InstalledUICulture.Name.Split('-')[1]; - public int safeSearch = 0; - - public bool hasAccount = false; - public int theme = 2; - - public TimeSpan uptime = TimeSpan.FromSeconds(0); - public bool promptReview = true; - public bool promptFeedback = true; - - public bool processClipboard = true; - public bool minimizeCommandbar = false; - - private static string GetLanguage() - { - if (new string[] { "ru-RU", "en-US" }.Contains(CultureInfo.InstalledUICulture.Name)) - return CultureInfo.InstalledUICulture.Name; - else if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName)) - return "ru-RU"; - else - return "en-US"; - } - } - - public static class SettingsStorage - { - public static string VideoQuality - { - get { return Container.videoQuality; } - set - { - Container.videoQuality = value; - SaveData(); - } - } - public static string RememberedQuality - { - get { return Container.rememberedQuality; } - set - { - Container.rememberedQuality = value; - SaveData(); - } - } - - public static bool VideoNotifications - { - get { return Container.videoNotifications; } - set - { - Container.videoNotifications = value; - SaveData(); - } - } - public static bool DevNotifications - { - get { return Container.devNotifications; } - set - { - Container.devNotifications = value; - SaveData(); - } - } - - public static bool CheckConnection - { - get { return Container.checkConnection; } - set - { - Container.checkConnection = value; - SaveData(); - } - } - public static bool Autoplay - { - get { return Container.autoplay; } - set - { - Container.autoplay = value; - SaveData(); - } - } - public static double Volume - { - get { return Container.volume; } - set - { - Container.volume = value; - SaveData(); - } - } - - public static string Language - { - get { return Container.language; } - set - { - Container.language = value; - SaveData(); - } - } - public static string RelevanceLanguage - { - get { return Container.relevanceLanguage; } - set - { - Container.relevanceLanguage = value; - SaveData(); - } - } - public static string Region - { - get { return Container.region; } - set - { - Container.region = value; - SaveData(); - } - } - public static int SafeSearch - { - get { return Container.safeSearch; } - set - { - Container.safeSearch = value; - SaveData(); - } - } - public static bool HasAccount - { - get { return Container.hasAccount; } - set - { - Container.hasAccount = value; - SaveData(); - } - } - public static int Theme - { - get { return Container.theme; } - set - { - Container.theme = value; - SaveData(); - } - } - - public static string Version - { - get - { - if (storage.Values["version"] == null) - { - PackageVersion ver = Package.Current.Id.Version; - storage.Values["version"] = $"{ver.Major}.{ver.Minor}"; - return $"{ver.Major}.{ver.Minor}"; - } - else return (string)storage.Values["version"]; - } - set - { - storage.Values["version"] = value; - } - } - public static MatureState Mature - { - get - { - if (storage.Values["mature"] == null) - { - storage.Values["mature"] = (int)MatureState.Blocked; - return MatureState.Blocked; - } - else return (MatureState)storage.Values["mature"]; - } - set - { - storage.Values["mature"] = value; - } - } - - public static TimeSpan Uptime - { - get { return Container.uptime; } - set - { - Container.uptime = value; - SaveData(); - } - } - public static bool PromptReview - { - get { return Container.promptReview; } - set - { - Container.promptReview = value; - SaveData(); - } - } - public static bool PromptFeedback - { - get { return Container.promptFeedback; } - set - { - Container.promptFeedback = value; - SaveData(); - } - } - - public static bool ProcessClipboard - { - get => Container.processClipboard; - set - { - Container.processClipboard = value; - SaveData(); - } - } - - public static AppBarClosedDisplayMode AppBarClosedMode - { - get => Container.minimizeCommandbar ? AppBarClosedDisplayMode.Minimal : AppBarClosedDisplayMode.Compact; - set - { - Container.minimizeCommandbar = value == AppBarClosedDisplayMode.Minimal; - SaveData(); - } - } - - //Settings storage - private static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings; - private static SettingsContainer Container = new SettingsContainer(); - - public static void LoadData() - { - try - { - Container = JsonConvert.DeserializeObject(storage.Values["settings"] as string); - } - catch (Exception e) - { - SaveData(); - if (storage.Values["settings"] != null) - Analytics.TrackEvent("Failed to retrieve settings", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "StackTrace", e.StackTrace } - }); - } - } - - public static void SaveData() - { - storage.Values["settings"] = JsonConvert.SerializeObject(Container); - } - } -} diff --git a/FoxTube/Classes/StreamInfo.cs b/FoxTube/Classes/StreamInfo.cs deleted file mode 100644 index b906d03..0000000 --- a/FoxTube/Classes/StreamInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace FoxTube.Classes -{ - public class StreamInfo - { - public class VideoInfo - { - public string IndexRange { get; set; } - public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}"; - public string Itag { get; set; } - public string Fps { get; set; } - public string Url { get; set; } - public string Codecs { get; set; } - public string MimeType { get; set; } - public string Height { get; set; } - public string Width { get; set; } - public string Label { get; set; } - } - public class AudioInfo - { - public string IndexRange { get; set; } - public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}"; - public string SampleRate { get; set; } - public string ChannelsCount { get; set; } - public string Codecs { get; set; } - public string MimeType { get; set; } - public string Url { get; set; } - public string Itag { get; set; } - } - - public List Video { get; } = new List(); - public List Audio { get; } = new List(); - } - - public class StreamQuality - { - public Uri Url { get; set; } - public string Resolution { get; set; } - } -} diff --git a/FoxTube/Controls/Adverts/CardAdvert.xaml b/FoxTube/Controls/Adverts/CardAdvert.xaml deleted file mode 100644 index 038beec..0000000 --- a/FoxTube/Controls/Adverts/CardAdvert.xaml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs b/FoxTube/Controls/Adverts/CardAdvert.xaml.cs deleted file mode 100644 index 55274af..0000000 --- a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Linq; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Microsoft.Advertising.WinRT.UI; -using Windows.UI.Xaml.Media.Imaging; -using Microsoft.Toolkit.Uwp.UI.Controls; - -namespace FoxTube.Controls.Adverts -{ - /// - /// Advert which is looks similar to video cards - /// - public sealed partial class CardAdvert : UserControl - { - readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); - NativeAdV2 advert; - public CardAdvert() - { - InitializeComponent(); - manager.AdReady += AdReady; - manager.ErrorOccurred += ErrorOccurred; - manager.RequestAd(); - } - - void ErrorOccurred(object sender, NativeAdErrorEventArgs e) - { - (Parent as AdaptiveGridView)?.Items.Remove(this); - System.Diagnostics.Debug.WriteLine("Error has occured while loading ad"); - } - - void AdReady(object sender, NativeAdReadyEventArgs e) - { - advert = e.NativeAd; - Initialize(); - e.NativeAd.RegisterAdContainer(grid); - } - - void Initialize() - { - title.Text = advert.Title; - image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri()); - - icon.ProfilePicture = advert.AdIcon == null ? null : advert.AdIcon.Source; - - sponsor.Text = Methods.GuardFromNull(advert.SponsoredBy); - - if (string.IsNullOrWhiteSpace(advert.CallToActionText)) - (info.Parent as FrameworkElement).Visibility = Visibility.Collapsed; - else - info.Text = advert.CallToActionText; - - desc.Text = string.Empty; - - if (!string.IsNullOrWhiteSpace(advert.Price)) - desc.Text += advert.Price; - - if (!string.IsNullOrWhiteSpace(advert.Rating)) - desc.Text += " " + advert.Rating; - } - - void Image_ImageOpened(object sender, RoutedEventArgs e) => - show.Begin(); - } -} diff --git a/FoxTube/Controls/Adverts/ChatAdvert.xaml b/FoxTube/Controls/Adverts/ChatAdvert.xaml deleted file mode 100644 index 01b673d..0000000 --- a/FoxTube/Controls/Adverts/ChatAdvert.xaml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FoxTube/Controls/Adverts/ChatAdvert.xaml.cs b/FoxTube/Controls/Adverts/ChatAdvert.xaml.cs deleted file mode 100644 index 986722f..0000000 --- a/FoxTube/Controls/Adverts/ChatAdvert.xaml.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.Advertising.WinRT.UI; -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 FoxTube.Controls.Adverts -{ - public sealed partial class ChatAdvert : ListViewItem - { - readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); - NativeAdV2 advert; - - public ChatAdvert() - { - InitializeComponent(); - - manager.AdReady += AdReady; - manager.RequestAd(); - } - - void AdReady(object sender, NativeAdReadyEventArgs e) - { - advert = e.NativeAd; - Initialize(); - if (cta.Visibility == Visibility.Collapsed) - e.NativeAd.RegisterAdContainer(grid); - else - e.NativeAd.RegisterAdContainer(grid, new List { cta }); - } - - void Initialize() - { - name.Text = Methods.GuardFromNull(advert.SponsoredBy); - ToolTipService.SetToolTip(name, name.Text); - - icon.ProfilePicture = advert.AdIcon == null ? null : advert.AdIcon.Source; - title.Text = advert.Title; - - description.Text = Methods.GuardFromNull(advert.Description); - - cta.Text = Methods.GuardFromNull(advert.CallToActionText); - cta.Visibility = string.IsNullOrWhiteSpace(advert.CallToActionText) ? Visibility.Collapsed : Visibility.Visible; - - Visibility = Visibility.Visible; - } - } -} diff --git a/FoxTube/Controls/Adverts/CommentAdvert.xaml b/FoxTube/Controls/Adverts/CommentAdvert.xaml deleted file mode 100644 index b8bc141..0000000 --- a/FoxTube/Controls/Adverts/CommentAdvert.xaml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FoxTube/Controls/VideoPage/Comments.xaml.cs b/FoxTube/Controls/VideoPage/Comments.xaml.cs deleted file mode 100644 index 2893afb..0000000 --- a/FoxTube/Controls/VideoPage/Comments.xaml.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Google.Apis.YouTube.v3; -using Google.Apis.YouTube.v3.Data; -using FoxTube.Controls; -using Windows.UI.Popups; -using Windows.ApplicationModel.Resources; -using Microsoft.AppCenter.Analytics; -using System.Collections.Generic; - -namespace FoxTube.Controls.VideoPage -{ - /// - /// Comments placeholder - /// - public sealed partial class Comments : UserControl - { - ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage"); - - string threadId; - string token; - - CommentThreadsResource.ListRequest.OrderEnum order = CommentThreadsResource.ListRequest.OrderEnum.Relevance; - CommentThreadsResource.ListRequest request; - - public Comments() - { - InitializeComponent(); - } - - public async void Initialize(Video video) - { - threadId = video.Id; - Methods.CommentsPage = this; - - if (!SecretsVault.IsAuthorized) - grid.RowDefinitions[0].Height = new GridLength(0); - - counter.Text = $"{video.Statistics.CommentCount:0,0} {resources.GetString("/CommentsPage/comments")}"; - orderBtn.Content = resources.GetString("/CommentsPage/relevance/Text"); - - request = SecretsVault.Service.CommentThreads.List("snippet,replies"); - request.MaxResults = 25; - request.Order = order; - request.VideoId = video.Id; - request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText; - - var response = await request.ExecuteAsync(); - - token = response.NextPageToken; - if (string.IsNullOrWhiteSpace(token)) - more.Visibility = Visibility.Collapsed; - - foreach (CommentThread comment in response.Items) - { - if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled) - list.Children.Add(new Controls.Adverts.CommentAdvert()); - list.Children.Add(new CommentCard(comment)); - } - } - - public void RemoveComment(CommentCard commentCard, string topCommentId = null) - { - if (string.IsNullOrWhiteSpace(topCommentId)) - list.Children.Remove(commentCard); - else - (list.Children.Find(i => (i as CommentCard).thread.Id == topCommentId) as CommentCard)?.DeleteComment(commentCard); - } - - private async void toRelevance_Click(object sender, RoutedEventArgs e) - { - if (order == CommentThreadsResource.ListRequest.OrderEnum.Relevance) - return; - - more.Visibility = Visibility.Visible; - - order = CommentThreadsResource.ListRequest.OrderEnum.Relevance; - orderBtn.Content = resources.GetString("/CommentsPage/relevance/Text"); - - list.Children.Clear(); - - request.Order = order; - var response = await request.ExecuteAsync(); - - token = response.NextPageToken; - if (string.IsNullOrWhiteSpace(token)) - more.Visibility = Visibility.Collapsed; - - foreach (CommentThread comment in response.Items) - { - if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled) - list.Children.Add(new Controls.Adverts.CommentAdvert()); - list.Children.Add(new CommentCard(comment)); - } - - more.Complete(); - } - - private async void toDate_Click(object sender, RoutedEventArgs e) - { - if (order == CommentThreadsResource.ListRequest.OrderEnum.Time) - return; - - more.Visibility = Visibility.Visible; - - order = CommentThreadsResource.ListRequest.OrderEnum.Time; - orderBtn.Content = resources.GetString("/CommentsPage/publish"); - - list.Children.Clear(); - - request.Order = order; - var response = await request.ExecuteAsync(); - - token = response.NextPageToken; - if (string.IsNullOrWhiteSpace(token)) - more.Visibility = Visibility.Collapsed; - - foreach (CommentThread comment in response.Items) - { - if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled) - list.Children.Add(new Controls.Adverts.CommentAdvert()); - list.Children.Add(new CommentCard(comment)); - } - - more.Complete(); - } - - private async void send_Click(object sender, RoutedEventArgs args) - { - if (string.IsNullOrWhiteSpace(newComment.Text)) - return; - - newComment.IsEnabled = false; - send.IsEnabled = false; - sending.Visibility = Visibility.Visible; - - CommentThread thread = new CommentThread - { - Snippet = new CommentThreadSnippet - { - TopLevelComment = new Comment - { - Snippet = new CommentSnippet - { - TextOriginal = newComment.Text - } - }, - VideoId = threadId - } - }; - - try - { - CommentThread response = await SecretsVault.Service.CommentThreads.Insert(thread, "snippet").ExecuteAsync(); - list.Children.Insert(0, new CommentCard(response)); - newComment.Text = ""; - scroll.ChangeView(null, 0, null); - } - catch (Exception e) - { - await new MessageDialog("Failed to publish your comment. Please, try again later.").ShowAsync(); - Analytics.TrackEvent("Failed to post comment", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "Thread ID", threadId }, - { "StackTrace", e.StackTrace } - }); - } - - newComment.IsEnabled = true; - send.IsEnabled = true; - sending.Visibility = Visibility.Collapsed; - } - - private async void ShowMore_Clicked() - { - request.PageToken = token; - var response = await request.ExecuteAsync(); - - token = response.NextPageToken; - if (string.IsNullOrWhiteSpace(token)) - more.Visibility = Visibility.Collapsed; - - foreach (CommentThread comment in response.Items) - { - if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled) - list.Children.Add(new Controls.Adverts.CommentAdvert()); - list.Children.Add(new CommentCard(comment)); - } - - more.Complete(); - } - } -} diff --git a/FoxTube/Controls/VideoPage/RelatedVideos.xaml b/FoxTube/Controls/VideoPage/RelatedVideos.xaml deleted file mode 100644 index ed418a8..0000000 --- a/FoxTube/Controls/VideoPage/RelatedVideos.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs b/FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs deleted file mode 100644 index 7c94e88..0000000 --- a/FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Google.Apis.YouTube.v3; -using Google.Apis.YouTube.v3.Data; -using Windows.UI.Xaml.Controls; - -namespace FoxTube.Controls.VideoPage -{ - public sealed partial class RelatedVideos : UserControl - { - public RelatedVideos() => - InitializeComponent(); - - public async void Initialize(string id) - { - list.Clear(); - - SearchResource.ListRequest request = SecretsVault.Service.Search.List("id"); - request.RegionCode = SettingsStorage.Region; - request.RelevanceLanguage = SettingsStorage.RelevanceLanguage; - request.RelatedToVideoId = id; - request.SafeSearch = (SearchResource.ListRequest.SafeSearchEnum)SettingsStorage.SafeSearch; - request.MaxResults = 10; - request.Type = "video"; - - SearchListResponse response = await request.ExecuteAsync(); - - foreach (SearchResult video in response.Items) - list.Add(new VideoCard(video.Id.VideoId)); - - list.Children.Insert(1, new Adverts.CardAdvert()); - } - - public void OpenNext() => - (list.Children[0] as VideoCard).ItemClicked(); - } -} diff --git a/FoxTube/Controls/VideoPage/ReportVideo.xaml b/FoxTube/Controls/VideoPage/ReportVideo.xaml deleted file mode 100644 index e5f2dd2..0000000 --- a/FoxTube/Controls/VideoPage/ReportVideo.xaml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - diff --git a/FoxTube/Controls/VideoPage/ReportVideo.xaml.cs b/FoxTube/Controls/VideoPage/ReportVideo.xaml.cs deleted file mode 100644 index 7b1272f..0000000 --- a/FoxTube/Controls/VideoPage/ReportVideo.xaml.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Windows.UI.Xaml.Controls; -using Google.Apis.YouTube.v3.Data; -using Google.Apis.YouTube.v3; -using Windows.UI.Xaml; -using System; -using Windows.UI.Popups; -using Windows.ApplicationModel.Resources; - -namespace FoxTube.Controls -{ - public sealed partial class ReportVideo : ContentDialog - { - string videoId; - public ReportVideo(string id) - { - InitializeComponent(); - Initialize(); - videoId = id; - } - - async void Initialize() - { - VideoAbuseReportReasonsResource.ListRequest req = SecretsVault.Service.VideoAbuseReportReasons.List("id,snippet"); - req.Hl = SettingsStorage.RelevanceLanguage; - VideoAbuseReportReasonListResponse reasons = await req.ExecuteAsync(); - - foreach (VideoAbuseReportReason i in reasons.Items) - primaryReason.Items.Add(new ComboBoxItem - { - Tag = i, - Content = i.Snippet.Label - }); - } - - private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) - { - VideoAbuseReport report = new VideoAbuseReport - { - Comments = Methods.GuardFromNull(comment.Text), - VideoId = videoId, - ReasonId = ((primaryReason.SelectedItem as ComboBoxItem).Tag as VideoAbuseReportReason).Id, - SecondaryReasonId = secondaryReason.SelectedItem == null ? null : (secondaryReason.SelectedItem as ComboBoxItem).Tag as string - }; - - ResourceLoader resources = ResourceLoader.GetForCurrentView("Report"); - try { await SecretsVault.Service.Videos.ReportAbuse(report).ExecuteAsync(); } - catch - { - await new MessageDialog(resources.GetString("/Report/err")).ShowAsync(); - return; - } - await new MessageDialog(resources.GetString("/Report/submittedHeader"), resources.GetString("/Report/submittedBody")).ShowAsync(); - } - - private void PrimaryReason_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - IsPrimaryButtonEnabled = true; - secondaryReason.Items.Clear(); - foreach (VideoAbuseReportSecondaryReason i in ((primaryReason.SelectedItem as ComboBoxItem).Tag as VideoAbuseReportReason).Snippet.SecondaryReasons) - secondaryReason.Items.Add(new ComboBoxItem - { - Tag = i.Id, - Content = i.Label - }); - - secondaryReason.Visibility = secondaryReason.Items.Count == 0 ? Visibility.Collapsed : Visibility.Visible; - } - } -} diff --git a/FoxTube/Controls/VideoPage/VideoPlaylist.xaml b/FoxTube/Controls/VideoPage/VideoPlaylist.xaml deleted file mode 100644 index 7f656c1..0000000 --- a/FoxTube/Controls/VideoPage/VideoPlaylist.xaml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs b/FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs deleted file mode 100644 index 92effeb..0000000 --- a/FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs +++ /dev/null @@ -1,124 +0,0 @@ -using FoxTube.Classes; -using Google.Apis.YouTube.v3; -using Google.Apis.YouTube.v3.Data; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Windows.ApplicationModel.Resources; -using Windows.UI.Xaml.Controls; - -// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 - -namespace FoxTube.Controls.VideoPage -{ - public sealed partial class VideoPlaylist : UserControl - { - public class PlaylistItem - { - public int Number { get; } - public string Id { get; } - public string Title { get; } - public string Thumbnail { get; } - - public PlaylistItem(int number, Google.Apis.YouTube.v3.Data.PlaylistItem item) - { - Number = number; - Id = item.Snippet.ResourceId.VideoId; - Title = item.Snippet.Title; - Thumbnail = item.Snippet.Thumbnails.Medium.Url; - } - - public PlaylistItem(int number, Video item) - { - Number = number; - Id = item.Id; - Title = item.Snippet.Title; - Thumbnail = item.Snippet.Thumbnails.Medium.Url; - } - } - - ResourceLoader resources = ResourceLoader.GetForCurrentView("VideoPage"); - public event PlaylistItemChangedEventHandler ItemChanged; - - public VideoPlaylist() => - InitializeComponent(); - - public ItemCollection Items => list.Items; - public int SelectedIndex => list.SelectedIndex; - - public async Task Initialize(Video video, string playlist) - { - if (video.Id == "WL") - { - List