From 3ded1f4f186f3c46668958d41a13ee5d607d492b Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Fri, 19 Jul 2019 17:19:43 +0300 Subject: [PATCH] New arch (not working) --- FoxTube/App.xaml.cs | 259 +------ FoxTube/Classes/AdaptiveCommandBar.cs | 4 +- FoxTube/Classes/DownloadAgent.cs | 12 +- FoxTube/Classes/Extensions.cs | 34 + FoxTube/Classes/InboxItem.cs | 45 +- FoxTube/Classes/ManifestGenerator.cs | 49 +- FoxTube/Classes/Methods.cs | 194 +++--- FoxTube/Classes/Navigation.cs | 36 + FoxTube/Classes/Processes.cs | 299 +++++++++ FoxTube/Classes/QualityComparer.cs | 30 + FoxTube/Classes/SecretsVault.cs | 134 ++-- FoxTube/Classes/SettingsStorage.cs | 2 +- FoxTube/Classes/StreamInfo.cs | 42 ++ FoxTube/Controls/Adverts/CardAdvert.xaml | 74 +- FoxTube/Controls/Adverts/CardAdvert.xaml.cs | 19 +- FoxTube/Controls/Adverts/ChatAdvert.xaml | 48 ++ FoxTube/Controls/Adverts/ChatAdvert.xaml.cs | 60 ++ .../Controls/Adverts/CommentAdvert.xaml.cs | 14 +- FoxTube/Controls/Adverts/PlayerAdvert.xaml.cs | 19 +- FoxTube/Controls/ChannelCard.xaml | 94 ++- FoxTube/Controls/ChannelCard.xaml.cs | 62 +- FoxTube/Controls/CommentCard.xaml.cs | 24 +- 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.cs | 21 +- FoxTube/Controls/DownloadItem.xaml | 1 - FoxTube/Controls/DownloadItem.xaml.cs | 41 +- .../Controls/{ => Player}/LiveCaptions.xaml | 0 .../{ => Player}/LiveCaptions.xaml.cs | 0 FoxTube/Controls/Player/PlayerControls.cs | 262 +++----- FoxTube/Controls/Player/PlayerMain.cs | 208 ++++++ FoxTube/Controls/Player/VideoPlayer.xaml | 2 +- FoxTube/Controls/Player/VideoPlayer.xaml.cs | 19 +- FoxTube/Controls/PlaylistCard.xaml | 74 +- FoxTube/Controls/PlaylistCard.xaml.cs | 54 +- FoxTube/Controls/ShowMore.xaml.cs | 15 +- FoxTube/Controls/VideoCard.xaml | 91 ++- FoxTube/Controls/VideoCard.xaml.cs | 470 +++---------- FoxTube/Controls/{ => VideoPage}/Chat.xaml | 4 +- FoxTube/Controls/{ => VideoPage}/Chat.xaml.cs | 143 +--- .../VideoPage/Comments.xaml} | 6 +- .../VideoPage/Comments.xaml.cs} | 6 +- FoxTube/Controls/VideoPage/RelatedVideos.xaml | 15 + .../Controls/VideoPage/RelatedVideos.xaml.cs | 35 + .../Controls/{ => VideoPage}/ReportVideo.xaml | 0 .../{ => VideoPage}/ReportVideo.xaml.cs | 0 FoxTube/Controls/VideoPage/VideoPlaylist.xaml | 35 + .../Controls/VideoPage/VideoPlaylist.xaml.cs | 124 ++++ FoxTube/FoxTube.csproj | 69 +- FoxTube/Pages/Browser.xaml | 25 - FoxTube/Pages/Browser.xaml.cs | 49 -- FoxTube/Pages/ChannelPage.xaml.cs | 11 +- FoxTube/Pages/Downloads.xaml.cs | 9 +- FoxTube/Pages/History.xaml | 40 +- FoxTube/Pages/History.xaml.cs | 101 ++- FoxTube/Pages/Home.xaml.cs | 15 +- FoxTube/Pages/LoadingPage.xaml.cs | 67 +- FoxTube/Pages/MainFrame.xaml | 15 + FoxTube/Pages/MainFrame.xaml.cs | 153 +++++ FoxTube/Pages/MainPage.xaml | 54 +- FoxTube/Pages/MainPage.xaml.cs | 598 ++++------------- FoxTube/Pages/PlaylistPage.xaml | 11 +- FoxTube/Pages/PlaylistPage.xaml.cs | 182 ++--- FoxTube/Pages/Search.xaml | 1 - FoxTube/Pages/Search.xaml.cs | 98 ++- FoxTube/Pages/Settings.xaml | 49 +- FoxTube/Pages/Settings.xaml.cs | 9 +- FoxTube/Pages/SettingsPages/About.xaml.cs | 4 +- FoxTube/Pages/SettingsPages/General.xaml.cs | 48 +- FoxTube/Pages/SettingsPages/Inbox.xaml.cs | 14 +- FoxTube/Pages/SettingsPages/Translate.xaml.cs | 13 +- FoxTube/Pages/Subscriptions.xaml | 40 +- FoxTube/Pages/Subscriptions.xaml.cs | 12 +- FoxTube/Pages/VideoGrid.xaml | 2 +- FoxTube/Pages/VideoGrid.xaml.cs | 41 +- FoxTube/Pages/VideoPage.xaml | 72 +- FoxTube/Pages/VideoPage.xaml.cs | 630 ++++-------------- FoxTube/Strings/en-US/VideoPage.resw | 2 +- FoxTube/Strings/ru-RU/VideoPage.resw | 2 +- FoxTube/Themes/Generic.xaml | 3 +- 84 files changed, 2816 insertions(+), 3159 deletions(-) create mode 100644 FoxTube/Classes/Extensions.cs create mode 100644 FoxTube/Classes/Navigation.cs create mode 100644 FoxTube/Classes/Processes.cs create mode 100644 FoxTube/Classes/QualityComparer.cs create mode 100644 FoxTube/Classes/StreamInfo.cs create mode 100644 FoxTube/Controls/Adverts/ChatAdvert.xaml create mode 100644 FoxTube/Controls/Adverts/ChatAdvert.xaml.cs create mode 100644 FoxTube/Controls/Common/AccountManager.xaml create mode 100644 FoxTube/Controls/Common/AccountManager.xaml.cs create mode 100644 FoxTube/Controls/Common/AddToPlaylist.cs create mode 100644 FoxTube/Controls/Common/CreateAndAddPlaylist.xaml create mode 100644 FoxTube/Controls/Common/CreateAndAddPlaylist.xaml.cs create mode 100644 FoxTube/Controls/Common/DownloadSelector.cs rename FoxTube/Controls/{ => Player}/LiveCaptions.xaml (100%) rename FoxTube/Controls/{ => Player}/LiveCaptions.xaml.cs (100%) create mode 100644 FoxTube/Controls/Player/PlayerMain.cs rename FoxTube/Controls/{ => VideoPage}/Chat.xaml (98%) rename FoxTube/Controls/{ => VideoPage}/Chat.xaml.cs (51%) rename FoxTube/{Pages/CommentsPage.xaml => Controls/VideoPage/Comments.xaml} (97%) rename FoxTube/{Pages/CommentsPage.xaml.cs => Controls/VideoPage/Comments.xaml.cs} (98%) create mode 100644 FoxTube/Controls/VideoPage/RelatedVideos.xaml create mode 100644 FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs rename FoxTube/Controls/{ => VideoPage}/ReportVideo.xaml (100%) rename FoxTube/Controls/{ => VideoPage}/ReportVideo.xaml.cs (100%) create mode 100644 FoxTube/Controls/VideoPage/VideoPlaylist.xaml create mode 100644 FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs delete mode 100644 FoxTube/Pages/Browser.xaml delete mode 100644 FoxTube/Pages/Browser.xaml.cs create mode 100644 FoxTube/Pages/MainFrame.xaml create mode 100644 FoxTube/Pages/MainFrame.xaml.cs diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index 09122f7..0d49758 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -1,34 +1,20 @@ -using Google.Apis.YouTube.v3.Data; -using Microsoft.AppCenter; +using FoxTube.Classes; using Microsoft.AppCenter.Analytics; -using System; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Xml; -using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; -using Windows.ApplicationModel.Background; using Windows.Globalization; -using Windows.Storage; -using Windows.System.Power; using Windows.UI.Notifications; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Navigation; namespace FoxTube { sealed partial class App : Application { - public static string[] AvailableLanguages => new[] { "en-US", "ru-RU" }; - - Stopwatch sw = new Stopwatch(); public App() { SettingsStorage.LoadData(); - + switch (SettingsStorage.Theme) { case 0: @@ -40,113 +26,30 @@ namespace FoxTube } ApplicationLanguages.PrimaryLanguageOverride = SettingsStorage.Language; - CheckVersion(); - InitializeComponent(); - Suspending += OnSuspending; - UnhandledException += UnhandledError; - - AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics)); - AppCenter.SetCountryCode(SettingsStorage.Region); - - sw.Start(); - } - - /// - /// Comparing current version with last recorded version. If doesn't match, poping up changelog notification - /// - public async void CheckVersion() - { - PackageVersion ver = Package.Current.Id.Version; - if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}.{ver.Build}") + Suspending += (s, e) => Processes.SuspendApp(); + UnhandledException += (s, e) => Analytics.TrackEvent("The app crashed", new Dictionary() { - try - { - XmlDocument changelog = new XmlDocument(); - StorageFile file = await (await Package.Current.InstalledLocation.GetFolderAsync(@"Assets\Data")).GetFileAsync("Patchnotes.xml"); - changelog.Load(await file.OpenStreamForReadAsync()); - XmlElement e = changelog["items"].ChildNodes[0] as XmlElement; + { "Exception", e.Exception.GetType().ToString() }, + { "Details", e.Message }, + { "StackTrace", e.Exception.StackTrace } + }); - ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(e.GetAttribute("version"))); - - 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 } - }); - } - } + Processes.InitializeApp(); } protected override void OnLaunched(LaunchActivatedEventArgs e) { - if (!(Window.Current.Content is Frame rootFrame)) - { - rootFrame = new Frame(); - rootFrame.NavigationFailed += OnNavigationFailed; - Window.Current.Content = rootFrame; - } + if (!(Window.Current.Content is Frame)) + Window.Current.Content = new Frame(); if (e.PrelaunchActivated == false) { - if (rootFrame.Content == null) - rootFrame.Navigate(typeof(MainPage), e.Arguments); + if ((Window.Current.Content as Frame).Content == null) + (Window.Current.Content as Frame).Navigate(typeof(MainPage), e.Arguments); Window.Current.Activate(); } - - ActivateToastBackgoundTask(); - ActivateBackgoundTask(); - } - - /// - /// Initializes background task for processing toast notifications' clicks - /// - public async void ActivateToastBackgoundTask() - { - 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 - /// - public 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(); } protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) @@ -154,50 +57,10 @@ namespace FoxTube var deferral = args.TaskInstance.GetDeferral(); base.OnBackgroundActivated(args); - if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" && args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details) - { - string[] arguments = details.Argument.Split('|'); + if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" || !(args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details)) + return; - switch (arguments[0]) - { - case "later": - try - { - SecretsVault.AuthorizationStateChanged += async (s, e) => - { - if ((bool)e[0]) - { - PlaylistItem item = new PlaylistItem() - { - Snippet = new PlaylistItemSnippet() - { - ResourceId = new ResourceId() - { - Kind = "youtube#video", - VideoId = arguments[1] - }, - PlaylistId = "WL" - } - }; - - await SecretsVault.Service.PlaylistItems.Insert(item, "snippet").ExecuteAsync(); - } - }; - SecretsVault.CheckAuthorization(false); - } - catch (Exception e) - { - Analytics.TrackEvent("Failed to add video to WL from toast", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "Video ID", arguments[1] }, - { "StackTrace", e.StackTrace } - }); - } - break; - } - } + Processes.ProcessToast(details.Argument); deferral.Complete(); } @@ -206,7 +69,8 @@ namespace FoxTube { base.OnActivated(e); - if (!(Window.Current.Content is Frame rootFrame)) + //TODO: Check this shit + /*if (!(Window.Current.Content is Frame rootFrame)) { rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; @@ -217,88 +81,15 @@ namespace FoxTube if (rootFrame.Content == null) rootFrame.Navigate(typeof(MainPage)); - Window.Current.Activate(); + Window.Current.Activate();*/ - switch (e.Kind) - { - case ActivationKind.ToastNotification: - if (SecretsVault.IsAuthorized) - ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); - else - SecretsVault.AuthorizationStateChanged += (s, arg) => ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); - break; - } - } + if (e.Kind != ActivationKind.ToastNotification) + return; - private void ProcessToast(string arg) - { - string[] args = arg.Split('|'); - switch (args[0]) - { - case "changelog": - case "inbox": - Methods.MainPage.GoToDeveloper(args[1]); - break; - - case "video": - Methods.MainPage.GoToVideo(args[1]); - break; - - case "channel": - Methods.MainPage.GoToChannel(args[1]); - break; - - case "download": - Methods.MainPage.GoToDownloads(); - break; - case "dcancel": - DownloadAgent.Cancel(args[1]); - break; - case "clipboard": - switch (args[1]) - { - case "video": - Methods.MainPage.GoToVideo(args[2]); - break; - case "channel": - Methods.MainPage.GoToChannel(args[2]); - break; - case "playlist": - Methods.MainPage.GoToPlaylist(args[2]); - break; - } - break; - } - } - - void OnNavigationFailed(object sender, NavigationFailedEventArgs e) - { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); - } - - private void OnSuspending(object sender, SuspendingEventArgs e) - { - var deferral = e.SuspendingOperation.GetDeferral(); - - sw.Stop(); - SettingsStorage.Uptime += sw.Elapsed; - - HistorySet.Save(); - SettingsStorage.SaveData(); - DownloadAgent.QuitPrompt(); - Controls.Player.ManifestGenerator.ClearRoaming(); - deferral.Complete(); - Analytics.TrackEvent("Session terminated"); - } - - private void UnhandledError(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) - { - Analytics.TrackEvent("The app crashed", new Dictionary() - { - { "Exception", e.Exception.GetType().ToString() }, - { "Details", e.Message }, - { "StackTrace", e.Exception.StackTrace } - }); + if (SecretsVault.IsAuthorized) + Processes.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); + else + SecretsVault.AuthorizationStateChanged += (arg) => Processes.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); } } } diff --git a/FoxTube/Classes/AdaptiveCommandBar.cs b/FoxTube/Classes/AdaptiveCommandBar.cs index 3b88891..3af6a2d 100644 --- a/FoxTube/Classes/AdaptiveCommandBar.cs +++ b/FoxTube/Classes/AdaptiveCommandBar.cs @@ -4,9 +4,7 @@ namespace FoxTube.Classes { class AdaptiveCommandBar : CommandBar { - public AdaptiveCommandBar() - { + public AdaptiveCommandBar() => ClosedDisplayMode = SettingsStorage.AppBarClosedMode; - } } } diff --git a/FoxTube/Classes/DownloadAgent.cs b/FoxTube/Classes/DownloadAgent.cs index 8c7fd4d..1cfffdf 100644 --- a/FoxTube/Classes/DownloadAgent.cs +++ b/FoxTube/Classes/DownloadAgent.cs @@ -3,26 +3,26 @@ using System.Collections.Generic; using Windows.Storage; using Newtonsoft.Json; using YoutubeExplode.Models.MediaStreams; -using Google.Apis.YouTube.v3.Data; 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(); - private static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; + static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; public static Downloads Page { get; set; } public static StorageFolder Downloads { get; set; } public static async void Initialize() { - Downloads = await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists); 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))); } @@ -38,10 +38,8 @@ namespace FoxTube } } - public static void Add(MediaStreamInfo info, Video meta, string qualty) - { - Items.Insert(0, new DownloadItem(info, meta, qualty)); - } + 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) { diff --git a/FoxTube/Classes/Extensions.cs b/FoxTube/Classes/Extensions.cs new file mode 100644 index 0000000..7efc0f9 --- /dev/null +++ b/FoxTube/Classes/Extensions.cs @@ -0,0 +1,34 @@ +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/InboxItem.cs b/FoxTube/Classes/InboxItem.cs index 6ea8f09..f2def2f 100644 --- a/FoxTube/Classes/InboxItem.cs +++ b/FoxTube/Classes/InboxItem.cs @@ -9,55 +9,18 @@ namespace FoxTube.Classes { public InboxItemType Type { get; set; } = InboxItemType.Default; public DateTime TimeStamp { get; set; } - public string TimeStampString - { - get - { - if (Type == InboxItemType.PatchNote) - return TimeStamp.ToShortDateString(); - else - return TimeStamp.ToString(); - } - } + 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 - { - get - { - if (Type == InboxItemType.PatchNote) - return "\xE728"; - else - return "\xE119"; - } - } - public string Subtitle - { - get - { - if (Type == InboxItemType.PatchNote) - return resources.GetString("changelog"); - else - return resources.GetString("dev"); - } - } - public string Title - { - get - { - if (Type == InboxItemType.PatchNote) - return $"{resources.GetString("whatsnew")}{Id}"; - else - return Subject; - } - } + 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) { diff --git a/FoxTube/Classes/ManifestGenerator.cs b/FoxTube/Classes/ManifestGenerator.cs index 6cea736..c187b16 100644 --- a/FoxTube/Classes/ManifestGenerator.cs +++ b/FoxTube/Classes/ManifestGenerator.cs @@ -16,7 +16,7 @@ using Windows.ApplicationModel.Resources; using Windows.Storage; using YoutubeExplode.Models.MediaStreams; -namespace FoxTube.Controls.Player +namespace FoxTube.Classes { public static class ManifestGenerator { @@ -82,7 +82,7 @@ namespace FoxTube.Controls.Player } } - private static XmlElement GetVideoPresentation(XmlDocument doc, StreamInfo.VideoInfo info) + static XmlElement GetVideoPresentation(XmlDocument doc, StreamInfo.VideoInfo info) { XmlElement representation = doc.CreateElement("Representation"); representation.SetAttribute("bandwidth", GetBandwidth(info.Label)); @@ -108,7 +108,7 @@ namespace FoxTube.Controls.Player return representation; } - private static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info) + static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info) { XmlElement audio = doc.CreateElement("Representation"); audio.SetAttribute("bandwidth", "200000"); @@ -133,7 +133,7 @@ namespace FoxTube.Controls.Player return audio; } - private static async Task GetInfoAsync(Video info, VideoStreamInfo requestedQuality) + static async Task GetInfoAsync(Video info, VideoStreamInfo requestedQuality) { try { @@ -195,7 +195,7 @@ namespace FoxTube.Controls.Player } } - public static Dictionary SplitQuery(string query) + static Dictionary SplitQuery(string query) { Dictionary dic = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] paramsEncoded = query.TrimStart('?').Split("&"); @@ -221,7 +221,7 @@ namespace FoxTube.Controls.Player return dic; } - private static string GetBandwidth(string quality) + static string GetBandwidth(string quality) { string parsed = quality.Split('p')[0]; switch (quality) @@ -301,41 +301,4 @@ namespace FoxTube.Controls.Player await f.DeleteAsync(StorageDeleteOption.PermanentDelete); } } - - 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/Classes/Methods.cs b/FoxTube/Classes/Methods.cs index c744546..ca0190e 100644 --- a/FoxTube/Classes/Methods.cs +++ b/FoxTube/Classes/Methods.cs @@ -1,6 +1,7 @@ -using FoxTube.Pages; +using FoxTube.Classes; +using FoxTube.Controls.VideoPage; +using Google.Apis.YouTube.v3.Data; using Microsoft.AppCenter.Analytics; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; @@ -9,7 +10,6 @@ using System.Net.Mail; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml; -using Windows.ApplicationModel.Core; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.Resources; using Windows.Storage.Streams; @@ -26,21 +26,47 @@ namespace FoxTube public delegate void ObjectEventHandler(object sender = null, params object[] args); - public interface INavigationPage - { - object Parameter { get; set; } - } - public static class Methods { private static readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods"); - public static CommentsPage CommentsPage { get; set; } + public static Comments CommentsPage { get; set; } public static MainPage MainPage => (Window.Current.Content as Frame).Content as MainPage; - public static void CloseApp() + public static async Task AddItemToWL(string id) { - CoreApplication.Exit(); + 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) @@ -49,10 +75,8 @@ namespace FoxTube catch { return null; } } - public static string GuardFromNull(string str) - { - return str ?? string.Empty; - } + public static string GuardFromNull(string str) => + str ?? string.Empty; public static void SendMail(string content) { @@ -68,22 +92,6 @@ namespace FoxTube client.Send(msg); } - [Obsolete] - public static string GetChars(this string str, int count) - { - try - { - string s = ""; - for (int i = 0; i < count; i++) - s += str[i]; - return s; - } - catch - { - return ""; - } - } - public static List ToReversedList(this Array array) { List list = new List(); @@ -93,22 +101,15 @@ namespace FoxTube return list; } - public static void ForEach(this IEnumerable array, Action action) - { + public static void ForEach(this IEnumerable array, Action action) => array.ToList().ForEach(action); - } - public static T Find(this IEnumerable array, Predicate match) - { - return array.ToList().Find(match); - } + public static T Find(this IEnumerable array, Predicate match) => + array.ToList().Find(match); - public static List FindAll(this IEnumerable array, Predicate match) - { - return array.ToList().FindAll(match); - } + public static List FindAll(this IEnumerable array, Predicate match) => + array.ToList().FindAll(match); - [Obsolete] public static string ReplaceInvalidChars(this string str, char newValue) { foreach (char i in Path.GetInvalidFileNameChars()) @@ -116,12 +117,7 @@ namespace FoxTube return str; } - [Obsolete] - public static string Last(this string[] arr) - { - return arr[arr.Length - 1]; - } - + [Obsolete("Use *YoutubeExplode.Models.Video instead*")] public static TimeSpan GetDuration(this string str) { try @@ -244,48 +240,52 @@ namespace FoxTube public async static void ProcessLink(string url) { - string type; + 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; - } + 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; + await Launcher.LaunchUriAsync(new Uri(url)); + return; LinkFound: - switch (type) - { - case "channel": - MainPage.GoToChannel(output); - break; - case "video": - MainPage.GoToVideo(output); - break; - case "playlist": - MainPage.GoToPlaylist(output); - break; - case "user": - MainPage.GoToChannel(await new YoutubeClient().GetChannelIdAsync(output)); - break; + 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) @@ -302,32 +302,18 @@ namespace FoxTube request.Data.SetBitmap(RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri())); } - public static async Task> GetHistory() + public static async Task GetHistory() { SecretsVault.RefreshToken(); - List list = new List(); - - string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=HL&hl={SettingsStorage.RelevanceLanguage}"); - dynamic raw = JsonConvert.DeserializeObject(output); - foreach (dynamic i in raw.video) - list.Add(i.encrypted_id.ToString()); - - return list; + return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("HL"); } - public static async Task> GetLater() + public static async Task GetLater() { SecretsVault.RefreshToken(); - List list = new List(); - - string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=WL&hl={SettingsStorage.RelevanceLanguage}"); - dynamic raw = JsonConvert.DeserializeObject(output); - foreach (dynamic i in raw.video) - list.Add(i.encrypted_id.ToString()); - - return list; + return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("WL"); } } } diff --git a/FoxTube/Classes/Navigation.cs b/FoxTube/Classes/Navigation.cs new file mode 100644 index 0000000..0c3c2b6 --- /dev/null +++ b/FoxTube/Classes/Navigation.cs @@ -0,0 +1,36 @@ +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 new file mode 100644 index 0000000..4b9475b --- /dev/null +++ b/FoxTube/Classes/Processes.cs @@ -0,0 +1,299 @@ +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 new file mode 100644 index 0000000..0a3bb90 --- /dev/null +++ b/FoxTube/Classes/QualityComparer.cs @@ -0,0 +1,30 @@ +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/SecretsVault.cs b/FoxTube/Classes/SecretsVault.cs index dd3fedf..5bf49a1 100644 --- a/FoxTube/Classes/SecretsVault.cs +++ b/FoxTube/Classes/SecretsVault.cs @@ -6,8 +6,6 @@ using Google.Apis.Auth.OAuth2; using Google.Apis.Services; using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; -using Newtonsoft.Json; -using Windows.Storage; using Windows.Services.Store; using System.Net.Http; using Google.Apis.Oauth2.v2.Data; @@ -15,6 +13,8 @@ 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 { @@ -22,12 +22,12 @@ namespace FoxTube { #region Properties //Events - public static event ObjectEventHandler AuthorizationStateChanged; - public static event ObjectEventHandler SubscriptionsChanged; - public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version + 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("mikhailagord@gmail.com", "JkY39w$.7?bT57O,8k3a"); + 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", @@ -38,7 +38,7 @@ namespace FoxTube ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", ApplicationName = "FoxTube" }); - public static BaseClientService.Initializer Initializer => new BaseClientService.Initializer + private static BaseClientService.Initializer Initializer => new BaseClientService.Initializer { HttpClientInitializer = Credential, ApplicationName = "FoxTube" @@ -46,7 +46,7 @@ namespace FoxTube public static YouTubeService Service => IsAuthorized ? new YouTubeService(Initializer) : NoAuthService; public static HttpClient HttpClient { get; } = new HttpClient(); - private static bool TestAds => false; //TODO: Change this bool + 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; @@ -60,15 +60,13 @@ namespace FoxTube public static Userinfoplus UserInfo { get; private set; } public static List Subscriptions { get; } = new List(); - public static List History { get; set; } = new List(); - public static List WatchLater { get; set; } = 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() - { + public static void RefreshToken() => Credential?.RefreshTokenAsync(CancellationToken.None); - } /// /// Subscribes or unsibscribes authorized user from the channel @@ -77,26 +75,23 @@ namespace FoxTube /// Returns 'true' if channel is in subscriptions now; 'false' if it's not public static async Task ChangeSubscriptionState(string id) { - if(Subscriptions.Exists(x => x.Snippet.ResourceId.ChannelId == id)) + if(Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == id) is Subscription subscription) { - Subscription s = Subscriptions.Find(x => x.Snippet.ResourceId.ChannelId == id); - - try { await Service.Subscriptions.Delete(s.Id).ExecuteAsync(); } + try { await Service.Subscriptions.Delete(subscription.Id).ExecuteAsync(); } catch { return true; } - SubscriptionsChanged?.Invoke(null, "remove", s); - Subscriptions.Remove(s); + SubscriptionsChanged?.Invoke("remove", subscription); + Subscriptions.Remove(subscription); - SaveSubscriptions(); return false; } else { - var request = Service.Subscriptions.Insert(new Subscription() + var request = Service.Subscriptions.Insert(new Subscription { - Snippet = new SubscriptionSnippet() + Snippet = new SubscriptionSnippet { - ResourceId = new ResourceId() + ResourceId = new ResourceId { ChannelId = id, Kind = "youtube#channel" @@ -104,13 +99,12 @@ namespace FoxTube } }, "snippet"); - Subscription s = await request.ExecuteAsync(); - if (s == null) + if (!(await request.ExecuteAsync() is Subscription sub)) return false; - Subscriptions.Add(s); - SubscriptionsChanged?.Invoke(null, "add", s); - SaveSubscriptions(); + Subscriptions.Add(sub); + SubscriptionsChanged?.Invoke("add", sub); + return true; } } @@ -140,30 +134,23 @@ namespace FoxTube Oauth2Service.Scope.UserinfoProfile, Oauth2Service.Scope.UserinfoEmail, YouTubeService.Scope.YoutubeForceSsl, - YouTubeService.Scope.Youtube, - YouTubeService.Scope.YoutubeUpload, - YouTubeService.Scope.YoutubeReadonly, - YouTubeService.Scope.Youtubepartner + YouTubeService.Scope.YoutubeUpload }, "user", CancellationToken.None); await Credential.RefreshTokenAsync(CancellationToken.None); } - catch (AuthenticateException e) + catch (AuthenticateException e) when (e.Message.Contains("UserCancel")) { } + catch(Exception e) { - if (e.Message.Contains("UserCancel")) - return; - else - { - AuthorizationStateChanged?.Invoke(args: new bool?[] { null }); - Analytics.TrackEvent("Failed to authorize", new Dictionary + AuthorizationStateChanged?.Invoke(null); + Analytics.TrackEvent("Failed to authorize", new Dictionary { { "Exception", e.GetType().ToString() }, { "Message", e.Message }, { "StackTrace", e.StackTrace } }); - } } if (Credential == null || !retrieveSubs) @@ -188,7 +175,6 @@ namespace FoxTube subRequest.Order = SubscriptionsResource.ListRequest.OrderEnum.Relevance; SubscriptionListResponse subResponse; string nextToken = null; - Subscriptions.Clear(); do { @@ -200,22 +186,17 @@ namespace FoxTube } while (!string.IsNullOrWhiteSpace(nextToken)); - var request = Service.Channels.List("snippet,contentDetails"); + ChannelsResource.ListRequest request = Service.Channels.List("snippet,contentDetails"); request.Mine = true; - UserChannel = (await request.ExecuteAsync()).Items[0]; + UserChannel = (await request.ExecuteAsync()).Items.FirstOrDefault(); #endregion - //Saving user's subscriptions for background task - SaveSubscriptions(); - - AuthorizationStateChanged?.Invoke(args: true); + AuthorizationStateChanged?.Invoke(true); } catch (Exception e) { - AuthorizationStateChanged?.Invoke(args: new bool?[] { null }); - Methods.SendMail($@"Exception: {e.GetType()} -Message: {e.Message} -Stack trace: {e.StackTrace}"); + AuthorizationStateChanged?.Invoke(null); + Methods.SendMail(e.ToString()); Analytics.TrackEvent("Failed to retrieve user's info", new Dictionary { { "Exception", e.GetType().ToString() }, @@ -225,43 +206,6 @@ Stack trace: {e.StackTrace}"); } } - /// - /// Saves user's subscriptions keypairs (channel ID: avatar URL) into "background.json" file for concurrent background processing - /// - public static void SaveSubscriptions() - { - try - { - Dictionary subs = new Dictionary(); - foreach (Subscription i in Subscriptions) - try - { - subs.Add(i.Snippet.ResourceId.ChannelId, i.Snippet.Thumbnails.Default__.Url); - } - catch (Exception e) - { - Analytics.TrackEvent("Failed to save user's subscription", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "Channel ID", i.Snippet.ResourceId.ChannelId }, - { "StackTrace", e.StackTrace } - }); - continue; - } - ApplicationData.Current.RoamingSettings.Values["subscriptions"] = JsonConvert.SerializeObject(subs); - } - catch (Exception e) - { - Analytics.TrackEvent("Failed to write user's subscriptions", new Dictionary - { - { "Exception", e.GetType().ToString() }, - { "Message", e.Message }, - { "StackTrace", e.StackTrace } - }); - } - } - /// /// Deauthenticates current user /// @@ -273,12 +217,13 @@ Stack trace: {e.StackTrace}"); Credential = null; UserChannel = null; UserInfo = null; - History.Clear(); - WatchLater.Clear(); + History = null; + WatchLater = null; Subscriptions.Clear(); - ApplicationData.Current.RoamingSettings.Values["subscriptions"] = ""; - AuthorizationStateChanged?.Invoke(args: false); + HttpClient.DefaultRequestHeaders.Authorization = null; + + AuthorizationStateChanged?.Invoke(false); SettingsStorage.HasAccount = false; } @@ -291,10 +236,7 @@ Stack trace: {e.StackTrace}"); if (SettingsStorage.HasAccount) Authorize(retrieveSubs); else - { - AuthorizationStateChanged.Invoke(args: false); - ApplicationData.Current.RoamingSettings.Values["subscriptions"] = ""; - } + AuthorizationStateChanged.Invoke(false); } /// diff --git a/FoxTube/Classes/SettingsStorage.cs b/FoxTube/Classes/SettingsStorage.cs index 1c68b7a..52f545c 100644 --- a/FoxTube/Classes/SettingsStorage.cs +++ b/FoxTube/Classes/SettingsStorage.cs @@ -41,7 +41,7 @@ namespace FoxTube private static string GetLanguage() { - if (App.AvailableLanguages.Contains(CultureInfo.InstalledUICulture.Name)) + 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"; diff --git a/FoxTube/Classes/StreamInfo.cs b/FoxTube/Classes/StreamInfo.cs new file mode 100644 index 0000000..b906d03 --- /dev/null +++ b/FoxTube/Classes/StreamInfo.cs @@ -0,0 +1,42 @@ +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 index 62e4913..038beec 100644 --- a/FoxTube/Controls/Adverts/CardAdvert.xaml +++ b/FoxTube/Controls/Adverts/CardAdvert.xaml @@ -6,10 +6,9 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" HorizontalAlignment="Stretch" - VerticalAlignment="Top" + VerticalAlignment="Stretch" d:DesignHeight="290" d:DesignWidth="384" - Visibility="Collapsed" Opacity="0" Name="card"> @@ -17,49 +16,38 @@ - - - - - - - - - - + + + diff --git a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs b/FoxTube/Controls/Adverts/CardAdvert.xaml.cs index 6c1789a..55274af 100644 --- a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs +++ b/FoxTube/Controls/Adverts/CardAdvert.xaml.cs @@ -12,8 +12,8 @@ namespace FoxTube.Controls.Adverts /// public sealed partial class CardAdvert : UserControl { - NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); - public NativeAdV2 advert; + readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); + NativeAdV2 advert; public CardAdvert() { InitializeComponent(); @@ -22,22 +22,21 @@ namespace FoxTube.Controls.Adverts manager.RequestAd(); } - private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) + void ErrorOccurred(object sender, NativeAdErrorEventArgs e) { (Parent as AdaptiveGridView)?.Items.Remove(this); System.Diagnostics.Debug.WriteLine("Error has occured while loading ad"); } - private void AdReady(object sender, NativeAdReadyEventArgs e) + void AdReady(object sender, NativeAdReadyEventArgs e) { advert = e.NativeAd; Initialize(); e.NativeAd.RegisterAdContainer(grid); } - public void Initialize() + void Initialize() { - Visibility = Visibility.Visible; title.Text = advert.Title; image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri()); @@ -57,13 +56,9 @@ namespace FoxTube.Controls.Adverts if (!string.IsNullOrWhiteSpace(advert.Rating)) desc.Text += " " + advert.Rating; + } + void Image_ImageOpened(object sender, RoutedEventArgs e) => show.Begin(); - } - - private void Image_ImageOpened(object sender, RoutedEventArgs e) - { - showThumb.Begin(); - } } } diff --git a/FoxTube/Controls/Adverts/ChatAdvert.xaml b/FoxTube/Controls/Adverts/ChatAdvert.xaml new file mode 100644 index 0000000..01b673d --- /dev/null +++ b/FoxTube/Controls/Adverts/ChatAdvert.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FoxTube/Controls/Adverts/ChatAdvert.xaml.cs b/FoxTube/Controls/Adverts/ChatAdvert.xaml.cs new file mode 100644 index 0000000..986722f --- /dev/null +++ b/FoxTube/Controls/Adverts/ChatAdvert.xaml.cs @@ -0,0 +1,60 @@ +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.cs b/FoxTube/Controls/Adverts/CommentAdvert.xaml.cs index 20db638..f4f621c 100644 --- a/FoxTube/Controls/Adverts/CommentAdvert.xaml.cs +++ b/FoxTube/Controls/Adverts/CommentAdvert.xaml.cs @@ -10,23 +10,17 @@ namespace FoxTube.Controls.Adverts /// public sealed partial class CommentAdvert : UserControl { - NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); - public NativeAdV2 advert; + readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); + NativeAdV2 advert; public CommentAdvert() { InitializeComponent(); manager.AdReady += AdReady; - manager.ErrorOccurred += ErrorOccurred; manager.RequestAd(); } - private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) - { - System.Diagnostics.Debug.WriteLine("Error has occured while loading ad"); - } - - private void AdReady(object sender, NativeAdReadyEventArgs e) + void AdReady(object sender, NativeAdReadyEventArgs e) { advert = e.NativeAd; Initialize(); @@ -36,7 +30,7 @@ namespace FoxTube.Controls.Adverts e.NativeAd.RegisterAdContainer(grid, new List { cta }); } - private void Initialize() + void Initialize() { title.Text = advert.Title; description.Text = Methods.GuardFromNull(advert.Description); diff --git a/FoxTube/Controls/Adverts/PlayerAdvert.xaml.cs b/FoxTube/Controls/Adverts/PlayerAdvert.xaml.cs index 78d18dc..1e3eaae 100644 --- a/FoxTube/Controls/Adverts/PlayerAdvert.xaml.cs +++ b/FoxTube/Controls/Adverts/PlayerAdvert.xaml.cs @@ -8,10 +8,9 @@ namespace FoxTube.Controls.Adverts { public sealed partial class PlayerAdvert : UserControl { - NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); + readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); NativeAdV2 advert; - - DispatcherTimer timer = new DispatcherTimer() + readonly DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10000) }; @@ -20,7 +19,6 @@ namespace FoxTube.Controls.Adverts InitializeComponent(); manager.AdReady += AdReady; - manager.ErrorOccurred += ErrorOccurred; timer.Tick += (s, e) => { @@ -29,12 +27,7 @@ namespace FoxTube.Controls.Adverts }; } - private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) - { - System.Diagnostics.Debug.WriteLine("Error has occured while loading ad"); - } - - private void AdReady(object sender, NativeAdReadyEventArgs arg) + void AdReady(object sender, NativeAdReadyEventArgs arg) { advert = arg.NativeAd; @@ -68,12 +61,10 @@ namespace FoxTube.Controls.Adverts show.Begin(); } - public void PushAdvert() - { + public void PushAdvert() => manager.RequestAd(); - } - private void Button_Click(object sender, RoutedEventArgs e) + void Button_Click(object sender, RoutedEventArgs e) { timer.Stop(); hide.Begin(); diff --git a/FoxTube/Controls/ChannelCard.xaml b/FoxTube/Controls/ChannelCard.xaml index a68f90b..7718e84 100644 --- a/FoxTube/Controls/ChannelCard.xaml +++ b/FoxTube/Controls/ChannelCard.xaml @@ -6,69 +6,65 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" HorizontalAlignment="Stretch" - VerticalAlignment="Top" + VerticalAlignment="Stretch" d:DesignHeight="290" d:DesignWidth="384" MaxWidth="700" + MaxHeight="600" Opacity="0" - Name="card" - SizeChanged="Card_SizeChanged"> + Name="card"> - - - - + + + + + Log in to manage your subscriptions + + + + + + + + + + + + + + + + + + + + + + diff --git a/FoxTube/Controls/PlaylistCard.xaml.cs b/FoxTube/Controls/PlaylistCard.xaml.cs index cce19da..24cb287 100644 --- a/FoxTube/Controls/PlaylistCard.xaml.cs +++ b/FoxTube/Controls/PlaylistCard.xaml.cs @@ -1,9 +1,11 @@ -using Google.Apis.YouTube.v3; +using FoxTube.Classes; +using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; using Microsoft.AppCenter.Analytics; using Microsoft.Toolkit.Uwp.UI.Controls; using System; using System.Collections.Generic; +using System.Threading.Tasks; using Windows.ApplicationModel.DataTransfer; using Windows.System; using Windows.UI.Xaml; @@ -16,24 +18,21 @@ namespace FoxTube.Controls /// /// Playlist card control /// - public sealed partial class PlaylistCard : UserControl + public sealed partial class PlaylistCard : UserControl, ICard { Playlist item; - public string playlistId; - - public bool NeedInitialize { get; set; } = true; + readonly string playlistId; public PlaylistCard(string id) { InitializeComponent(); - Initialize(id); + playlistId = id; } - public async void Initialize(string id) + public async Task Initialize() { try { - playlistId = id; PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails"); request.Id = playlistId; request.Hl = SettingsStorage.RelevanceLanguage; @@ -44,17 +43,12 @@ namespace FoxTube.Controls counter.Text = item.ContentDetails.ItemCount.ToString(); date.Text = Methods.GetAgo(item.Snippet.PublishedAt.Value); - ChannelsResource.ListRequest r = SecretsVault.Service.Channels.List("snippet"); - r.Id = item.Snippet.ChannelId; - try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); - avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 }; + avatar.ProfilePicture = new BitmapImage((await new YoutubeClient(SecretsVault.HttpClient).GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 46, DecodePixelWidth = 46 }; } catch { } - - show.Begin(); } catch (Exception e) { @@ -70,36 +64,26 @@ namespace FoxTube.Controls } } - public void Button_Click(object sender, RoutedEventArgs e) - { - Methods.MainPage.GoToPlaylist(item.Id); - } + public void ItemClicked() => + Navigation.GoToPlaylist(item.Id); - private void OpenChannel_Click(object sender, RoutedEventArgs e) - { - Methods.MainPage.GoToChannel(item.Snippet.ChannelId); - } + public void Button_Click(object sender, RoutedEventArgs e) => + ItemClicked(); - private void GetLink_Click(object sender, RoutedEventArgs e) + void OpenChannel_Click(object sender, RoutedEventArgs e) => + Navigation.GoToChannel(item.Snippet.ChannelId); + + void GetLink_Click(object sender, RoutedEventArgs e) { DataPackage data = new DataPackage(); data.SetText($"https://www.youtube.com/playlist?list={playlistId}"); Clipboard.SetContent(data); } - private async void InBrowser_Click(object sender, RoutedEventArgs e) - { + async void InBrowser_Click(object sender, RoutedEventArgs e) => await Launcher.LaunchUriAsync($"https://www.youtube.com/playlist?list={playlistId}".ToUri()); - } - private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) - { - showThumb.Begin(); - } - - private void Card_SizeChanged(object sender, SizeChangedEventArgs e) - { - Height = e.NewSize.Width * 0.75; - } + void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) => + show.Begin(); } } diff --git a/FoxTube/Controls/ShowMore.xaml.cs b/FoxTube/Controls/ShowMore.xaml.cs index eccffb7..6d7c190 100644 --- a/FoxTube/Controls/ShowMore.xaml.cs +++ b/FoxTube/Controls/ShowMore.xaml.cs @@ -6,10 +6,8 @@ namespace FoxTube.Controls public sealed partial class ShowMore : UserControl { public event Event Clicked; - public ShowMore() - { + public ShowMore() => InitializeComponent(); - } private void btn_Click(object sender, RoutedEventArgs e) { @@ -18,17 +16,6 @@ namespace FoxTube.Controls Clicked.Invoke(); } - public void Invoke() - { - btn_Click(this, null); - } - - public void Show() - { - btn.Visibility = Visibility.Collapsed; - bar.Visibility = Visibility.Visible; - } - public void Complete() { bar.Visibility = Visibility.Collapsed; diff --git a/FoxTube/Controls/VideoCard.xaml b/FoxTube/Controls/VideoCard.xaml index eec9910..8be212f 100644 --- a/FoxTube/Controls/VideoCard.xaml +++ b/FoxTube/Controls/VideoCard.xaml @@ -6,65 +6,60 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" HorizontalAlignment="Stretch" - VerticalAlignment="Top" + VerticalAlignment="Stretch" d:DesignHeight="290" d:DesignWidth="384" MaxWidth="700" + MaxHeight="600" Opacity="0" - Name="card" - SizeChanged="Card_SizeChanged"> + Name="card"> - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -80,11 +75,7 @@ - - - - - + diff --git a/FoxTube/Controls/VideoCard.xaml.cs b/FoxTube/Controls/VideoCard.xaml.cs index a47ef55..fb2ea65 100644 --- a/FoxTube/Controls/VideoCard.xaml.cs +++ b/FoxTube/Controls/VideoCard.xaml.cs @@ -2,7 +2,6 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Google.Apis.YouTube.v3; -using Google.Apis.YouTube.v3.Data; using Windows.UI.Xaml.Media.Imaging; using Windows.System; using Windows.ApplicationModel.DataTransfer; @@ -10,112 +9,108 @@ using Windows.ApplicationModel.Resources; using Microsoft.AppCenter.Analytics; using System.Collections.Generic; using YoutubeExplode; +using YoutubeExplode.Models; using Windows.UI.Popups; -using YoutubeExplode.Models.MediaStreams; using Windows.Foundation; using FoxTube.Pages; using Windows.Networking.Connectivity; using Microsoft.Toolkit.Uwp.UI.Controls; +using FoxTube.Classes; +using FoxTube.Controls.Common; +using System.Threading.Tasks; +using System.Linq; namespace FoxTube.Controls { /// /// Video item card /// - public sealed partial class VideoCard : UserControl + public sealed partial class VideoCard : UserControl, ICard { ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards"); - public string playlistId; + readonly string playlistId; + readonly string id; Video item; HistoryItem history; + Google.Apis.YouTube.v3.Data.Video liveItem; + public VideoCard(string id, string playlist = null) { InitializeComponent(); - Initialize(id, playlist); - } - - - public VideoCard(Video meta, string playlist = null) - { - InitializeComponent(); - item = meta; + this.id = id; playlistId = playlist; - LoadMeta(); } - public async void Initialize(string id, string playlist = null) + public async Task Initialize() { try { - playlistId = playlist; - delete.Visibility = string.IsNullOrWhiteSpace(playlistId) ? Visibility.Collapsed : Visibility.Visible; + item = await new YoutubeClient(SecretsVault.HttpClient).GetVideoAsync(id); - VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails"); + title.Text = item.Title; + channelName.Text = item.Author; + + ValidatePlaylist(); + + VideosResource.ListRequest request = SecretsVault.Service.Videos.List("liveStreamingDetails,contentDetails"); request.Id = id; - request.Hl = SettingsStorage.RelevanceLanguage; - item = (await request.ExecuteAsync()).Items[0]; + liveItem = (await request.ExecuteAsync()).Items[0]; - title.Text = item.Snippet.Localized.Title; - channelName.Text = item.Snippet.ChannelTitle; - if (item.Snippet.Title == "Deleted video") + if(liveItem.LiveStreamingDetails.ConcurrentViewers != null) { - ContextFlyout = null; - show.Begin(); - return; - } - - if (item.Snippet.LiveBroadcastContent == "live") - { - views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}"; - if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue) - info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}"; + if (liveItem.LiveStreamingDetails.ActualStartTime.HasValue) + { + views.Text = $"{liveItem.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}"; + if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue && liveItem.LiveStreamingDetails.ScheduledEndTime.HasValue) + info.Text = $"{liveItem.LiveStreamingDetails.ScheduledEndTime - liveItem.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(liveItem.LiveStreamingDetails.ActualStartTime.Value)}"; + else + info.Text = Methods.GetAgo(liveItem.LiveStreamingDetails.ActualStartTime.Value); + } else - info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value); - liveTag.Visibility = Visibility.Visible; - download.Visibility = Visibility.Collapsed; - } - else if (item.Snippet.LiveBroadcastContent == "upcoming") - { - views.Text = ""; - if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue) - info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}"; - else - info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; - liveTag.Visibility = Visibility.Visible; + { + views.Text = ""; + if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue && liveItem.LiveStreamingDetails.ScheduledEndTime.HasValue) + info.Text = $"{liveItem.LiveStreamingDetails.ScheduledEndTime - liveItem.LiveStreamingDetails.ScheduledStartTime} | {liveItem.LiveStreamingDetails.ScheduledStartTime}"; + else + info.Text = Methods.GetAgo(item.UploadDate.DateTime); - if (item.LiveStreamingDetails.ScheduledStartTime.HasValue) - liveContent.Text = resources.GetString("/Cards/goesLive") + (item.LiveStreamingDetails.ScheduledStartTime.Value > DateTime.Now ? " " : " -") + (item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss"); - else liveContent.Text = resources.GetString("/Cards/upcoming"); + if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue) + liveContent.Text = resources.GetString("/Cards/goesLive") + (liveItem.LiveStreamingDetails.ScheduledStartTime.Value > DateTime.Now ? " " : " -") + (liveItem.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss"); + else liveContent.Text = resources.GetString("/Cards/upcoming"); + } + liveTag.Visibility = Visibility.Visible; download.Visibility = Visibility.Collapsed; } else { views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}"; - info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; - LoadDownloads(); + info.Text = $"{item.Duration} | {item.UploadDate}"; + + try { new DownloadSelector(download.Items).Initialize(item); } + catch { download.Visibility = Visibility.Collapsed; } } - LoadAddTo(); + + try { new AddToPlaylist(addTo.Items).Initialize(id); } + catch { addTo.Visibility = Visibility.Collapsed; } try { - thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); - avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 }; + thumbnail.Source = new BitmapImage(item.Thumbnails.MediumResUrl.ToUri()); + avatar.ProfilePicture = new BitmapImage((await new YoutubeClient(SecretsVault.HttpClient).GetVideoAuthorChannelAsync(id)).LogoUrl.ToUri()) { DecodePixelHeight = 46, DecodePixelWidth = 46 }; } catch { } - if (SecretsVault.History.Contains(item.Id)) + if (SecretsVault.History.Videos.Any(i => i.Id == id)) watched.Visibility = Visibility.Visible; if (HistorySet.Items.Exists(i => i.Id == item.Id)) { history = HistorySet.Items.Find(i => i.Id == item.Id); watched.Visibility = Visibility.Visible; - leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds; + leftOn.Value = 100 * history.LeftOn.TotalSeconds / item.Duration.TotalSeconds; } - - show.Begin(); } catch (Exception e) { @@ -133,95 +128,30 @@ namespace FoxTube.Controls } } - async void LoadDownloads() + async void ValidatePlaylist() { - try - { - MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id); - foreach (MuxedStreamInfo i in infoSet.Muxed) - { - MenuFlyoutItem menuItem = new MenuFlyoutItem() - { - Text = i.VideoQualityLabel, - Tag = new object[] { i, i.VideoQualityLabel } - }; - menuItem.Click += downloadItemSelected; - download.Items.Add(menuItem); - } - - MenuFlyoutItem audioItem = new MenuFlyoutItem() - { - Text = resources.GetString("/VideoPage/audio"), - Tag = new object[] { infoSet.Audio[0], resources.GetString("/Cards/audioOnly") } - }; - audioItem.Click += downloadItemSelected; - download.Items.Add(audioItem); - } - catch - { - download.Visibility = Visibility.Collapsed; - } - } - - private void downloadItemSelected(object sender, RoutedEventArgs e) - { - DownloadAgent.Add(((sender as MenuFlyoutItem).Tag as object[])[0] as MediaStreamInfo, item, ((sender as MenuFlyoutItem).Tag as object[])[1] as string); - } - - public async void LoadMeta() - { - title.Text = item.Snippet.Title; - channelName.Text = item.Snippet.ChannelTitle; - - if (item.Snippet.Title == "Deleted video") - { - ContextFlyout = null; - show.Begin(); + if (string.IsNullOrWhiteSpace(playlistId) || !SecretsVault.IsAuthorized) return; - } - if (item.Snippet.LiveBroadcastContent == "live") - { - views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}"; - if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue) - info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}"; - else - info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value); - liveTag.Visibility = Visibility.Visible; - } - else if (item.Snippet.LiveBroadcastContent == "upcoming") - { - views.Text = ""; - if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue) - info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}"; - else - info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; - liveTag.Visibility = Visibility.Visible; - - if (item.LiveStreamingDetails.ScheduledStartTime.HasValue) - liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {item.LiveStreamingDetails.ScheduledStartTime - DateTime.Now}"; - else liveContent.Text = resources.GetString("/Cards/upcoming"); - } + if (playlistId == "WL" || playlistId == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes) + delete.Visibility = Visibility.Visible; else { - views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}"; - info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; + PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet"); + playlistRequest.Id = playlistId; + var response = await playlistRequest.ExecuteAsync(); + + if (response.Items[0].Snippet.ChannelId == SecretsVault.AccountId) + delete.Visibility = Visibility.Visible; } - - thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); - avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 }; - - if (SecretsVault.History.Contains(item.Id)) - watched.Visibility = Visibility.Visible; - if (HistorySet.Items.Exists(i => i.Id == item.Id)) - leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds; - - show.Begin(); } - public async void Button_Click(object sender, RoutedEventArgs e) + public void ItemClicked() => + ItemClicked(false); + + async void ItemClicked(bool incognito = false) { - if (item.ContentDetails.ContentRating != null) + if (liveItem.ContentDetails.ContentRating != null) { if (SecretsVault.IsAuthorized) { @@ -268,11 +198,11 @@ namespace FoxTube.Controls { try { - PlaylistItem playlistItem = new PlaylistItem() + Google.Apis.YouTube.v3.Data.PlaylistItem playlistItem = new Google.Apis.YouTube.v3.Data.PlaylistItem { - Snippet = new PlaylistItemSnippet() + Snippet = new Google.Apis.YouTube.v3.Data.PlaylistItemSnippet { - ResourceId = new ResourceId() + ResourceId = new Google.Apis.YouTube.v3.Data.ResourceId { Kind = "youtube#video", VideoId = item.Id @@ -290,251 +220,37 @@ namespace FoxTube.Controls return; } - Methods.MainPage.GoToVideo(item.Id, playlistId == "HL" ? null : playlistId, ((FrameworkElement)sender).Name == "incognito" ? true : false); + Navigation.GoToVideo(id, playlistId == "HL" ? null : playlistId, incognito); } - private void Share(DataTransferManager sender, DataRequestedEventArgs args) - { + void Share(DataTransferManager sender, DataRequestedEventArgs args) => Methods.Share(args, - item.Snippet.Thumbnails.Medium.Url, - item.Snippet.Title, - $"https://www.youtube.com/watch?v={item.Id}", + item.Thumbnails.MediumResUrl, + item.Title, + item.GetShortUrl(), resources.GetString("/Cards/videoShare")); - } - private void share_Click(object sender, RoutedEventArgs e) + void share_Click(object sender, RoutedEventArgs e) { DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler(Share); DataTransferManager.ShowShareUI(); } - private void ViewChannel_Click(object sender, RoutedEventArgs e) - { - Methods.MainPage.GoToChannel(item.Snippet.ChannelId); - } + async void ViewChannel_Click(object sender, RoutedEventArgs e) => + Navigation.GoToChannel((await new YoutubeClient(SecretsVault.HttpClient).GetVideoAuthorChannelAsync(id)).Id); - private void GetLink_Click(object sender, RoutedEventArgs e) + void GetLink_Click(object sender, RoutedEventArgs e) { DataPackage data = new DataPackage(); - data.SetText($"https://www.youtube.com/watch?v={item.Id}"); + data.SetText(item.GetShortUrl()); Clipboard.SetContent(data); } - private async void InBrowser_Click(object sender, RoutedEventArgs e) - { - await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={item.Id}".ToUri()); - } + async void InBrowser_Click(object sender, RoutedEventArgs e) => + await Launcher.LaunchUriAsync(item.GetShortUrl().ToUri()); - private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) - { - showThumb.Begin(); - } - - private async void NewPlaylist_Click(object sender, RoutedEventArgs e) - { - StackPanel stack = new StackPanel(); - stack.Children.Add(new TextBox - { - PlaceholderText = resources.GetString("/VideoPage/newPlaylistName/PlaceholderText") - }); - - ComboBox comboBox = new ComboBox - { - Header = resources.GetString("/VideoPage/privacy/Header"), - SelectedIndex = 0, - HorizontalAlignment = HorizontalAlignment.Stretch - }; - comboBox.Items.Add(new ComboBoxItem { Content = resources.GetString("/VideoPage/public/Content") }); - comboBox.Items.Add(new ComboBoxItem { Content = resources.GetString("/VideoPage/private/Content") }); - comboBox.Items.Add(new ComboBoxItem { Content = resources.GetString("/VideoPage/direct/Content") }); - - stack.Children.Add(comboBox); - - ContentDialog playlistDialog = new ContentDialog - { - PrimaryButtonText = resources.GetString("/VideoPage/dialog/PrimaryButtonText"), - CloseButtonText = resources.GetString("/VideoPage/dialog/CloseButtonText"), - DefaultButton = ContentDialogButton.Primary, - Title = resources.GetString("/VideoPage/dialog/Title"), - Content = stack - }; - playlistDialog.PrimaryButtonClick += PlaylistDialog_PrimaryButtonClick; - await playlistDialog.ShowAsync(); - } - - private async void PlaylistDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) - { - string privacy = "private"; - switch (((sender.Content as StackPanel).Children[1] as ComboBox).SelectedIndex) - { - case 0: - privacy = "public"; - break; - case 1: - privacy = "private"; - break; - case 2: - privacy = "unlisted"; - break; - } - - Playlist newItem = new Playlist - { - Snippet = new PlaylistSnippet - { - Title = ((sender.Content as StackPanel).Children[0] as TextBox).Text - }, - Status = new PlaylistStatus - { - PrivacyStatus = privacy, - } - }; - - Playlist i; - - try { i = await SecretsVault.Service.Playlists.Insert(newItem, "snippet,status").ExecuteAsync(); } - catch - { - return; - } - - ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem - { - Text = i.Snippet.Title, - IsChecked = true, - Tag = i, - Icon = new FontIcon - { - Glyph = "\xE728" - } - }; - menuItem.Click += Item_Click; - addTo.Items.Add(menuItem); - - Item_Click(menuItem, null); - } - - private async void Wl_Click(object sender, RoutedEventArgs e) - { - if (wl.IsChecked) - { - try - { - PlaylistItem playlist = new PlaylistItem - { - Snippet = new PlaylistItemSnippet - { - PlaylistId = "WL", - ResourceId = new ResourceId - { - VideoId = item.Id, - Kind = "youtube#video" - } - } - }; - PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet"); - - await request.ExecuteAsync(); - } - catch - { - wl.IsChecked = false; - } - } - else - wl.IsChecked = true; - } - - async void LoadAddTo() - { - try - { - if (SecretsVault.UserChannel == null) - { - addTo.Visibility = Visibility.Collapsed; - return; - } - - if (SecretsVault.WatchLater.Contains(item.Id)) - (addTo.Items[1] as ToggleMenuFlyoutItem).IsChecked = true; - - PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet"); - request.Mine = true; - request.MaxResults = 50; - - PlaylistListResponse response = await request.ExecuteAsync(); - - PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet"); - itemRequest.VideoId = item.Id; - - foreach (Playlist i in response.Items) - { - itemRequest.PlaylistId = i.Id; - ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem - { - Text = i.Snippet.Title, - IsChecked = (await itemRequest.ExecuteAsync()).Items.Count > 0, - Tag = i, - Icon = new FontIcon - { - Glyph = "\xE728" - } - }; - menuItem.Click += Item_Click; - addTo.Items.Add(menuItem); - } - } - catch - { - addTo.Visibility = Visibility.Collapsed; - } - } - - private async void Item_Click(object sender, RoutedEventArgs e) - { - if (((ToggleMenuFlyoutItem)sender).IsChecked) - { - try - { - PlaylistItem playlist = new PlaylistItem - { - Snippet = new PlaylistItemSnippet - { - PlaylistId = (((ToggleMenuFlyoutItem)sender).Tag as Playlist).Id, - ResourceId = new ResourceId - { - VideoId = item.Id, - Kind = "youtube#video" - } - } - }; - PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet"); - - await request.ExecuteAsync(); - } - catch - { - ((ToggleMenuFlyoutItem)sender).IsChecked = false; - } - } - else - { - try - { - PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet"); - itemRequest.VideoId = item.Id; - itemRequest.PlaylistId = ((Playlist)((ToggleMenuFlyoutItem)sender).Tag).Id; - - PlaylistItemsResource.DeleteRequest request = SecretsVault.Service.PlaylistItems.Delete((await itemRequest.ExecuteAsync()).Items[0].Id); - - await request.ExecuteAsync(); - } - catch - { - ((ToggleMenuFlyoutItem)sender).IsChecked = true; - } - } - } + void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) => + show.Begin(); private async void Delete_Click(object sender, RoutedEventArgs e) { @@ -550,7 +266,7 @@ namespace FoxTube.Controls else { HistorySet.Delete(history); - (Methods.MainPage.PageContent.Frame.Content as History).Delete(this); + (Navigation.Frame.Frame.Content as History).Delete(this); } return; } @@ -559,23 +275,15 @@ namespace FoxTube.Controls PlaylistItemsResource.ListRequest request = SecretsVault.Service.PlaylistItems.List("snippet"); request.PlaylistId = playlistId; request.VideoId = item.Id; - PlaylistItemListResponse response = await request.ExecuteAsync(); - PlaylistItem playlistItem = response.Items.Find(i => i.Snippet.PlaylistId == playlistId); - - await SecretsVault.Service.PlaylistItems.Delete(playlistItem.Id).ExecuteAsync(); - - (Methods.MainPage.PageContent.Frame.Content as PlaylistPage).DeleteItem(this); - } - catch - { + await SecretsVault.Service.PlaylistItems.Delete((await request.ExecuteAsync()).Items[0].Id).ExecuteAsync(); + (Navigation.Frame.Frame.Content as PlaylistPage).DeleteItem(this); } + catch { } } - private void Card_SizeChanged(object sender, SizeChangedEventArgs e) - { - Height = e.NewSize.Width * 0.75; - } + private void Button_Click(object sender, RoutedEventArgs e) => + ItemClicked(((FrameworkElement)sender).Name == "incognito" ? true : false); } } diff --git a/FoxTube/Controls/Chat.xaml b/FoxTube/Controls/VideoPage/Chat.xaml similarity index 98% rename from FoxTube/Controls/Chat.xaml rename to FoxTube/Controls/VideoPage/Chat.xaml index 8537b9c..2331490 100644 --- a/FoxTube/Controls/Chat.xaml +++ b/FoxTube/Controls/VideoPage/Chat.xaml @@ -1,10 +1,10 @@  diff --git a/FoxTube/Controls/Chat.xaml.cs b/FoxTube/Controls/VideoPage/Chat.xaml.cs similarity index 51% rename from FoxTube/Controls/Chat.xaml.cs rename to FoxTube/Controls/VideoPage/Chat.xaml.cs index 649a940..31be4cb 100644 --- a/FoxTube/Controls/Chat.xaml.cs +++ b/FoxTube/Controls/VideoPage/Chat.xaml.cs @@ -10,10 +10,11 @@ using System.Collections.Generic; using Windows.UI.Popups; using Windows.ApplicationModel.Resources; using Microsoft.Advertising.WinRT.UI; -using Windows.UI.Xaml.Media.Imaging; using System.Linq; +using FoxTube.Classes; +using FoxTube.Controls.Adverts; -namespace FoxTube.Controls +namespace FoxTube.Controls.VideoPage { public class ChatMessage { @@ -63,16 +64,12 @@ namespace FoxTube.Controls private LiveChatMessage message; - public ChatMessage(LiveChatMessage item) - { + public ChatMessage(LiveChatMessage item) => message = item; - } } public sealed partial class Chat : UserControl { - NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); - public NativeAdV2 advert; string chatId; DateTime lastInsert; @@ -81,7 +78,7 @@ namespace FoxTube.Controls DispatcherTimer timer = new DispatcherTimer { - Interval = TimeSpan.FromSeconds(1) + Interval = TimeSpan.FromSeconds(3) }; DispatcherTimer adTimer = new DispatcherTimer @@ -104,130 +101,8 @@ namespace FoxTube.Controls if (SecretsVault.AdsDisabled) return; - adTimer.Tick += (s, e) => manager.RequestAd(); + adTimer.Tick += (s, e) => list.Items.Add(new ChatAdvert()); adTimer.Start(); - - manager.AdReady += AdReady; - manager.ErrorOccurred += ErrorOccurred; - } - - private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) - { - System.Diagnostics.Debug.WriteLine("Error has occured while loading ad"); - } - - private void AdReady(object sender, NativeAdReadyEventArgs e) - { - advert = e.NativeAd; - - Grid grid = new Grid - { - Margin = new Thickness(0, 5, 5, 5), - }; - - grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) }); - grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) }); - grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); - - StackPanel iconStack = new StackPanel - { - Orientation = Orientation.Horizontal, - VerticalAlignment = VerticalAlignment.Top, - Margin = new Thickness(5, 0, 5, 0) - }; - - iconStack.Children.Add(new PersonPicture - { - Height = 20, - ProfilePicture = advert.AdIcon == null ? null : advert.AdIcon.Source - }); - FontIcon sponsor = new FontIcon - { - Glyph = "\xE735", - Margin = new Thickness(2, 0, 2, 0) - }; - ToolTipService.SetToolTip(sponsor, ResourceLoader.GetForCurrentView("Chat").GetString("/Chat/sponsor/Text")); - iconStack.Children.Add(sponsor); - - StackPanel nameStack = new StackPanel - { - Orientation = Orientation.Horizontal, - VerticalAlignment = VerticalAlignment.Top, - Margin = new Thickness(0, 0, 2, 0) - }; - Grid.SetColumn(nameStack, 1); - HyperlinkButton sponsorName = new HyperlinkButton - { - Margin = new Thickness(0, -6, 0, 0), - FontWeight = Windows.UI.Text.FontWeights.Bold, - Content = new TextBlock - { - TextTrimming = TextTrimming.CharacterEllipsis, - MaxWidth = 150, - Text = Methods.GuardFromNull(advert.SponsoredBy) - } - }; - ToolTipService.SetToolTip(sponsorName, Methods.GuardFromNull(advert.SponsoredBy)); - nameStack.Children.Add(sponsorName); - nameStack.Children.Add(new TextBlock { Text = ":" }); - - StackPanel contentStack = new StackPanel - { - Padding = new Thickness(2, 0, 0, 0) - }; - Grid.SetColumn(contentStack, 2); - contentStack.Children.Add(new TextBlock - { - VerticalAlignment = VerticalAlignment.Top, - TextWrapping = TextWrapping.WrapWholeWords, - FontWeight = Windows.UI.Text.FontWeights.Bold, - Text = advert.Title - }); - if(!string.IsNullOrWhiteSpace(advert.Description)) - contentStack.Children.Add(new TextBlock - { - VerticalAlignment = VerticalAlignment.Top, - TextWrapping = TextWrapping.WrapWholeWords, - Text = advert.Description - }); - if (!string.IsNullOrWhiteSpace(advert.CallToActionText)) - contentStack.Children.Add(new HyperlinkButton - { - VerticalAlignment = VerticalAlignment.Top, - Content = new TextBlock - { - TextWrapping = TextWrapping.WrapWholeWords, - Text = advert.CallToActionText - } - }); - - grid.Children.Add(iconStack); - grid.Children.Add(nameStack); - grid.Children.Add(contentStack); - - Grid mainGrid = new Grid(); - mainGrid.Children.Add(new Border - { - BorderBrush = new SolidColorBrush(Colors.Red), - BorderThickness = new Thickness(2), - CornerRadius = new CornerRadius(5), - HorizontalAlignment = HorizontalAlignment.Stretch, - Margin = new Thickness(0, 27, 0, 2), - Background = new SolidColorBrush(Colors.Red) { Opacity = .2 }, - Child = grid - }); - - ListViewItem item = new ListViewItem - { - Content = mainGrid - }; - - list.Items.Insert(0, item); - - if (contentStack.Children.Last() is HyperlinkButton) - advert.RegisterAdContainer(mainGrid, new List { contentStack.Children.Last() as HyperlinkButton }); - else - advert.RegisterAdContainer(mainGrid); } public async void Update(object sender, object e) @@ -241,10 +116,8 @@ namespace FoxTube.Controls timer.Start(); } - private void HyperlinkButton_Click(object sender, RoutedEventArgs e) - { - Methods.MainPage.GoToChannel(((HyperlinkButton)sender).Tag as string); - } + private void HyperlinkButton_Click(object sender, RoutedEventArgs e) => + Navigation.GoToChannel(((HyperlinkButton)sender).Tag as string); private async void send_Click(object sender, RoutedEventArgs args) { diff --git a/FoxTube/Pages/CommentsPage.xaml b/FoxTube/Controls/VideoPage/Comments.xaml similarity index 97% rename from FoxTube/Pages/CommentsPage.xaml rename to FoxTube/Controls/VideoPage/Comments.xaml index 434e98c..7100fb0 100644 --- a/FoxTube/Pages/CommentsPage.xaml +++ b/FoxTube/Controls/VideoPage/Comments.xaml @@ -1,5 +1,5 @@ - - + diff --git a/FoxTube/Pages/CommentsPage.xaml.cs b/FoxTube/Controls/VideoPage/Comments.xaml.cs similarity index 98% rename from FoxTube/Pages/CommentsPage.xaml.cs rename to FoxTube/Controls/VideoPage/Comments.xaml.cs index 994e3ef..2893afb 100644 --- a/FoxTube/Pages/CommentsPage.xaml.cs +++ b/FoxTube/Controls/VideoPage/Comments.xaml.cs @@ -9,12 +9,12 @@ using Windows.ApplicationModel.Resources; using Microsoft.AppCenter.Analytics; using System.Collections.Generic; -namespace FoxTube.Pages +namespace FoxTube.Controls.VideoPage { /// /// Comments placeholder /// - public sealed partial class CommentsPage : Page + public sealed partial class Comments : UserControl { ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage"); @@ -24,7 +24,7 @@ namespace FoxTube.Pages CommentThreadsResource.ListRequest.OrderEnum order = CommentThreadsResource.ListRequest.OrderEnum.Relevance; CommentThreadsResource.ListRequest request; - public CommentsPage() + public Comments() { InitializeComponent(); } diff --git a/FoxTube/Controls/VideoPage/RelatedVideos.xaml b/FoxTube/Controls/VideoPage/RelatedVideos.xaml new file mode 100644 index 0000000..ed418a8 --- /dev/null +++ b/FoxTube/Controls/VideoPage/RelatedVideos.xaml @@ -0,0 +1,15 @@ + + + + + + diff --git a/FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs b/FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs new file mode 100644 index 0000000..7c94e88 --- /dev/null +++ b/FoxTube/Controls/VideoPage/RelatedVideos.xaml.cs @@ -0,0 +1,35 @@ +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/ReportVideo.xaml b/FoxTube/Controls/VideoPage/ReportVideo.xaml similarity index 100% rename from FoxTube/Controls/ReportVideo.xaml rename to FoxTube/Controls/VideoPage/ReportVideo.xaml diff --git a/FoxTube/Controls/ReportVideo.xaml.cs b/FoxTube/Controls/VideoPage/ReportVideo.xaml.cs similarity index 100% rename from FoxTube/Controls/ReportVideo.xaml.cs rename to FoxTube/Controls/VideoPage/ReportVideo.xaml.cs diff --git a/FoxTube/Controls/VideoPage/VideoPlaylist.xaml b/FoxTube/Controls/VideoPage/VideoPlaylist.xaml new file mode 100644 index 0000000..7f656c1 --- /dev/null +++ b/FoxTube/Controls/VideoPage/VideoPlaylist.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs b/FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs new file mode 100644 index 0000000..92effeb --- /dev/null +++ b/FoxTube/Controls/VideoPage/VideoPlaylist.xaml.cs @@ -0,0 +1,124 @@ +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