diff --git a/FoxTube.Background/BackgroundProcessor.cs b/FoxTube.Background/BackgroundProcessor.cs index a05d2e3..774aeee 100644 --- a/FoxTube.Background/BackgroundProcessor.cs +++ b/FoxTube.Background/BackgroundProcessor.cs @@ -1,11 +1,11 @@ using Google.Apis.Services; using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; -using Microsoft.AppCenter.Analytics; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading.Tasks; using System.Xml; using Windows.ApplicationModel.Background; @@ -17,8 +17,9 @@ namespace FoxTube.Background public sealed class BackgroundProcessor : IBackgroundTask { private DateTime lastCheck = DateTime.Now; - private readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; - private YouTubeService Service => new YouTubeService(new BaseClientService.Initializer() + private readonly ApplicationDataContainer settings = ApplicationData.Current.RoamingSettings; + dynamic prefs; + private readonly YouTubeService Service = new YouTubeService(new BaseClientService.Initializer() { ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", ApplicationName = "FoxTube" @@ -30,21 +31,20 @@ namespace FoxTube.Background try { def = taskInstance.GetDeferral(); - taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled); if (settings.Values["lastCheck"] == null) { - settings.Values.Add("lastCheck", DateTime.Now.ToString()); + settings.Values["lastCheck"] = DateTime.Now.ToString(); def.Complete(); return; } else lastCheck = DateTime.Parse(settings.Values["lastCheck"] as string); - bool[] notificationsSettings = JsonConvert.DeserializeObject(await FileIO.ReadTextAsync(await ApplicationData.Current.RoamingFolder.GetFileAsync("notifications.json"))); - if (notificationsSettings[0]) + prefs = JsonConvert.DeserializeObject(settings.Values["settings"] as string); + if ((bool)prefs.devNotifications) CheckAnnouncements(); - if (notificationsSettings[1]) + if ((bool)prefs.videoNotifications) await CheckAccount(); } finally @@ -54,20 +54,11 @@ namespace FoxTube.Background } } - private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) - { - Analytics.TrackEvent("Background task caneled", new Dictionary() - { - { "Reason", reason.ToString() } - }); - settings.Values["lastCheck"] = DateTime.Now.ToString(); - } - async Task CheckAccount() { try { - Dictionary subscriptions = JsonConvert.DeserializeObject>(await FileIO.ReadTextAsync(await ApplicationData.Current.RoamingFolder.GetFileAsync("background.json"))); + Dictionary subscriptions = JsonConvert.DeserializeObject>(settings.Values["subscriptions"] as string); List results = new List(); @@ -94,25 +85,28 @@ namespace FoxTube.Background TileUpdater updater = TileUpdateManager.CreateTileUpdaterForApplication(); updater.EnableNotificationQueue(true); updater.Clear(); - for (int i = 0; i < 5; i++) - updater.Update(Tiles.GetTileLayout(results[i].Snippet.Title, results[i].Snippet.ChannelTitle, results[i].Snippet.Thumbnails.Medium.Url, subscriptions[results[i].Snippet.ChannelId])); + for (int i = 0; i < 5 && i < results.Count; i++) + updater.Update(Tiles.GetTileLayout(System.Security.SecurityElement.Escape(results[i].Snippet.Title), System.Security.SecurityElement.Escape(results[i].Snippet.ChannelTitle), results[i].Snippet.Thumbnails.Medium.Url.Replace("&", "%26"), subscriptions[results[i].Snippet.ChannelId])); } catch { } } - void CheckAnnouncements() + async void CheckAnnouncements() { try { XmlDocument doc = new XmlDocument(); - doc.Load(XmlReader.Create("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml")); - if ((DateTime.Parse((doc["posts"].FirstChild as XmlElement).GetAttribute("time")) - lastCheck).TotalSeconds > 0) + doc.LoadXml(await new HttpClient().GetStringAsync("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml")); + XmlElement item = doc["posts"].FirstChild as XmlElement; + + DateTime date = DateTime.Parse(item.GetAttribute("time")); + if (date > lastCheck && date < DateTime.Now) ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetInternalToast(doc["posts"].FirstChild["id"].InnerText, - doc["posts"].FirstChild["header"].InnerText, - doc["posts"].FirstChild["content"].InnerText, - doc["posts"].FirstChild["thumbnail"].InnerText, - doc["posts"].FirstChild["avatar"].InnerText)); + Notification.GetInternalToast(item["id"].InnerText, + item["header"][(string)prefs.language].InnerText, + item["content"][(string)prefs.language].InnerText, + item["thumbnail"].InnerText, + item["avatar"].InnerText)); } catch { } } diff --git a/FoxTube.Background/ResourceCreators.cs b/FoxTube.Background/ResourceCreators.cs index 3df0305..539283b 100644 --- a/FoxTube.Background/ResourceCreators.cs +++ b/FoxTube.Background/ResourceCreators.cs @@ -8,12 +8,12 @@ namespace FoxTube.Background { public static class Notification { - private static Dictionary languagePack = LoadPack(); + private static readonly Dictionary languagePack = LoadPack(); private static Dictionary LoadPack() { - object[] saved = JsonConvert.DeserializeObject(ApplicationData.Current.RoamingSettings.Values["settings"] as string); - if (saved[7] as string == "ru-RU") + dynamic saved = JsonConvert.DeserializeObject(ApplicationData.Current.RoamingSettings.Values["settings"] as string); + if (saved.language as string == "ru-RU") return new Dictionary() { { "addLater", "Посмотреть позже" }, @@ -60,8 +60,8 @@ namespace FoxTube.Background - {title} - {channel} {languagePack["videoContent"]} + {System.Security.SecurityElement.Escape(title)} + {System.Security.SecurityElement.Escape(channel)} {languagePack["videoContent"]} diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index f71b751..3056e94 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -12,7 +12,6 @@ using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.Background; using Windows.Globalization; using Windows.Storage; -using Windows.System; using Windows.System.Power; using Windows.UI.Notifications; using Windows.UI.Xaml; @@ -63,7 +62,7 @@ namespace FoxTube changelog.Load(await file.OpenStreamForReadAsync()); XmlElement e = changelog["items"].ChildNodes[0] as XmlElement; - ToastNotificationManager.CreateToastNotifier().Show(FoxTube.Background.Notification.GetChangelogToast(e.GetAttribute("version"))); + ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(e.GetAttribute("version"))); SettingsStorage.Version = $"{ver.Major}.{ver.Minor}"; } @@ -189,9 +188,6 @@ namespace FoxTube Debug.WriteLine(e.Message); } break; - case "download": - await Launcher.LaunchFileAsync(await StorageFile.GetFileFromPathAsync(arguments[1])); - break; } } @@ -217,32 +213,41 @@ namespace FoxTube Window.Current.Activate(); - if (e is ToastNotificationActivatedEventArgs) + switch (e.Kind) { - string[] args = (e as ToastNotificationActivatedEventArgs).Argument.Split('|'); - switch (args[0]) - { - case "changelog": - case "inbox": - Methods.MainPage.GoToDeveloper(args[1]); - break; + case ActivationKind.Protocol: + break; + case ActivationKind.ToastNotification: + string[] args = (e as ToastNotificationActivatedEventArgs).Argument.Split('|'); + switch (args[0]) + { + case "changelog": + case "inbox": + Methods.MainPage.GoToDeveloper(args[1]); + break; - case "video": - Methods.MainPage.GoToVideo(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.Remove(args[1]); - break; - } + case "channel": + Methods.MainPage.GoToChannel(args[1]); + break; + case "download": + Methods.MainPage.GoToDownloads(); + break; + case "dcancel": + DownloadAgent.Cancel(args[1]); + break; + } + break; } } + + void Launch(string e = null) + { + + } void OnNavigationFailed(object sender, NavigationFailedEventArgs e) { @@ -255,6 +260,7 @@ namespace FoxTube SettingsStorage.SaveData(); DownloadAgent.QuitPrompt(); deferral.Complete(); + Analytics.TrackEvent("Session terminated"); } private void UnhandledError(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml index 5d5cf2f..5e117f4 100644 --- a/FoxTube/Assets/Data/Patchnotes.xml +++ b/FoxTube/Assets/Data/Patchnotes.xml @@ -1,5 +1,33 @@  + + + ### What's new: +- Improved stability and speed of the app +- Fixed a lot of bugs +- Fixed player +- Added animations and acrylic + +### Following features awaits their implementation: +- 'History' and 'Watch later' playlists (Progress has been made but it still incomplete) +- The same problem with 'Recommended' and 'Subscriptions' tabs on home page +- Playlists management will be introduced in the next version but it may miss some playlists +- Even though I've done ads delivering system it's not introduced because of some problems with ads' lookup. I just want to make your ads experience the best one. So it won't be implemented until I make sure that they are ready + + ### Что нового: +- Улучшена стабильность и скорость приложения +- Исправлена куча багов +- Исправлен плеер +- Добавлены анимации и акрил + +### Следующие функции ждут своего внедрения: +- История и плейлист 'Посмотреть позже' (прогресс есть, но нужно еще работать) +- Та же фигня и со вкладками 'Рекомендованные' и 'Подписки' на домашней странице +- Управление плейлистами будет добавлено в следующей версии, но некоторые плейлисты могут отсутствовать +- Хотя я сделал систему доставки рекламы, она не введена из-за некоторых проблем с видом банеров. Я хочу сделать ваш опыт взаимодействия с рекламой в приложении лучше, так что она не будет введена до тех пор, пока я не буду в этом уверен + + + ### What's new: diff --git a/FoxTube/Assets/videoPlaceholder.png b/FoxTube/Assets/videoPlaceholder.png new file mode 100644 index 0000000..54f5e6a Binary files /dev/null and b/FoxTube/Assets/videoPlaceholder.png differ diff --git a/FoxTube/Classes/DownloadAgent.cs b/FoxTube/Classes/DownloadAgent.cs index f83d9ef..c46fdb6 100644 --- a/FoxTube/Classes/DownloadAgent.cs +++ b/FoxTube/Classes/DownloadAgent.cs @@ -1,19 +1,19 @@ using System; using System.Collections.Generic; using Windows.Storage; -using FoxTube.Classes; using Newtonsoft.Json; using YoutubeExplode.Models.MediaStreams; using Google.Apis.YouTube.v3.Data; using FoxTube.Controls; +using FoxTube.Pages; namespace FoxTube { - // TODO: Refactor DownloadAgent public static class DownloadAgent { public static List items = new List(); private static ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; + public static Downloads Page { get; set; } public static StorageFolder Downloads { get; set; } public static async void Initialize() @@ -32,45 +32,34 @@ namespace FoxTube items.Insert(0, new DownloadItem(info, meta, qualty)); } - public static void CancelItem(string id) + public static void Remove(DownloadItem item) { - DownloadItem item = items.Find(x => x.Container.Id == id); - if (item == null || !item.InProgress) - return; - - item.CancelPrompt(); + try { Page.Remove(item); } + catch { } + items.Remove(item); } - public static void Remove(string id) + public static void Cancel(string id) { - DownloadItem item = items.Find(x => x.Container.Id == id); - if (item == null) - return; - - if (item.InProgress) + DownloadItem item = items.Find(i => i.Container.Id == id); + if (item != null) item.Cancel(); - else - items.Remove(item); } public static void QuitPrompt() { - foreach (DownloadItem i in items.FindAll(i => i.InProgress)) + foreach (DownloadItem i in items.FindAll(i => !i.Container.IsDownloaded)) + { i.Cancel(); + items.Remove(i); + } List containers = new List(); items.ForEach(i => containers.Add(i.Container)); string data = JsonConvert.SerializeObject(containers); - try - { - settings.Values["downloads"] = data; - } - catch - { - settings.Values.Add("downloads", data); - } + settings.Values["downloads"] = data; } } } diff --git a/FoxTube/Classes/DownloadItemContainer.cs b/FoxTube/Classes/DownloadItemContainer.cs deleted file mode 100644 index 0a99e6d..0000000 --- a/FoxTube/Classes/DownloadItemContainer.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace FoxTube.Classes -{ - public class DownloadItemContainer - { - public string Title { get; set; } - public string Channel { get; set; } - public string Id { get; set; } - public string Name { get; set; } - public string Extension { get; set; } - public Uri Thumbnail { get; set; } - public string Quality { get; set; } - public TimeSpan Duration { get; set; } - } -} diff --git a/FoxTube/Classes/InboxItem.cs b/FoxTube/Classes/InboxItem.cs index 5342d2c..21ff0cc 100644 --- a/FoxTube/Classes/InboxItem.cs +++ b/FoxTube/Classes/InboxItem.cs @@ -9,6 +9,16 @@ 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 Subject { get; set; } public string Content { get; set; } diff --git a/FoxTube/Classes/Methods.cs b/FoxTube/Classes/Methods.cs index 81ad788..fb37d62 100644 --- a/FoxTube/Classes/Methods.cs +++ b/FoxTube/Classes/Methods.cs @@ -21,6 +21,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Documents; using Windows.UI.Xaml.Media; +using YoutubeExplode; using YoutubeExplode.Models.MediaStreams; namespace FoxTube @@ -45,15 +46,6 @@ namespace FoxTube return new Uri(url); } - public static void Try(Action action) - { - try - { - action(); - } - catch { } - } - public static string GetChars(this string str, int count) { try @@ -160,41 +152,25 @@ namespace FoxTube public static void FormatText(ref TextBlock block, string text) { block.Inlines.Clear(); + Regex filter = new Regex(@"\b((?:https?://|www\.)\S+)|(\S+@\S+)\b", RegexOptions.IgnoreCase); + Regex link = new Regex(@"\b(?:https?://|www\.)\S+\b", RegexOptions.IgnoreCase); + Regex mail = new Regex(@"\b\S+@\S+\b", RegexOptions.IgnoreCase); - Regex regx = new Regex(@"(http(s)?://[\S]+|www.[\S]+|[\S]+@[\S]+)", RegexOptions.IgnoreCase); - Regex isWWW = new Regex(@"(http[s]?://[\S]+|www.[\S]+)"); - Regex isEmail = new Regex(@"[\S]+@[\S]+"); - foreach (string item in regx.Split(text)) + foreach (string item in filter.Split(text)) { - if (isWWW.IsMatch(item)) + if (link.IsMatch(item)) { - try - { - Hyperlink link = new Hyperlink(); - link.Click += (s, arg) => { ProcessLink(item); }; - link.Inlines.Add(new Run { Text = item }); - block.Inlines.Add(link); - } - catch - { - block.Inlines.Add(new Run { Text = item }); - } + Hyperlink hl = new Hyperlink(); + hl.Click += (s, arg) => ProcessLink(item); + hl.Inlines.Add(new Run { Text = item }); + block.Inlines.Add(hl); } - else if (isEmail.IsMatch(item)) + else if (mail.IsMatch(item)) { - try - { - Hyperlink link = new Hyperlink { NavigateUri = new Uri($"mailto:{item}"), Foreground = new SolidColorBrush(Colors.Red) }; - link.Inlines.Add(new Run { Text = item }); - block.Inlines.Add(link); - } - catch - { - block.Inlines.Add(new Run { Text = item }); - } + Hyperlink hl = new Hyperlink { NavigateUri = $"mailto:{item}".ToUri() }; + hl.Inlines.Add(new Run { Text = item }); + block.Inlines.Add(hl); } - else if (item == "s") - continue; else block.Inlines.Add(new Run { Text = item }); } @@ -230,68 +206,51 @@ namespace FoxTube return "Unknown"; } } - + public async static void ProcessLink(string url) { - try - { - Debug.WriteLine($"Processing link: {url}"); - if (url.Contains("youtube.com/") || url.Contains("youtu.be/")) - { - Debug.WriteLine("This is an internal youtube link"); - url = url.Replace("https://", "").Replace("http://", "").Replace("wwww.", "").Replace("//", ""); - Debug.WriteLine($"Prepared link: {url}"); + string output; + string type; - if (url.Contains("/playlist")) - { - Debug.WriteLine($"This is a playlist link. ID: {HttpUtility.ParseQueryString(url).Get("list")}"); - MainPage.GoToPlaylist(HttpUtility.ParseQueryString(url).Get("list")); - } - else if (url.Contains("youtu.be/")) - { - Debug.WriteLine($"This is obfuscated video link. Video ID: {url.Split('/')[1]}"); - MainPage.GoToVideo(url.Split('/')[1]); - } - else if (url.Contains("/watch")) - { - Debug.WriteLine($"This is regular video link. Video ID: {HttpUtility.ParseQueryString(url).Get("v")}"); - MainPage.GoToVideo(HttpUtility.ParseQueryString(url).Get(0), HttpUtility.ParseQueryString(url).Get("list")); - } - else if (url.Contains("/v/")) - { - Debug.WriteLine($"This is video link. ID: {url.Split('/')[2].Split('?')[0]}"); - MainPage.GoToVideo(url.Split('/')[2].Split('?')[0]); - } - else if (url.Contains("/channel/")) - { - Debug.WriteLine($"This is channel link. ID: {url.Split('/')[2]}"); - MainPage.GoToChannel(url.Split('/')[2]); - } - else if (url.Contains("/user/")) - { - Debug.WriteLine($"This is channel link with username. Username: {url.Split('/')[2]}"); - ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("id"); - Debug.WriteLine(request.ForUsername = url.Split('/')[2]); - request.MaxResults = 1; - MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id); - } - else if (url.Contains("/c/")) - { - Debug.WriteLine($"This is channel link with custom url. Custom name: {url.Split('/')[2]}"); - SearchResource.ListRequest request = SecretsVault.Service.Search.List("id"); - Debug.WriteLine(request.Q = url.Split('/')[2]); - request.MaxResults = 1; - MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id.ChannelId); - } - else - throw new Exception(); - } - else - throw new Exception(); - } - catch + if (YoutubeClient.TryParseChannelId(url, out output)) { - await Launcher.LaunchUriAsync(new Uri(url)); + type = "channel"; + goto LinkFound; + } + else if (YoutubeClient.TryParsePlaylistId(url, out output)) + { + type = "playlist"; + goto LinkFound; + } + else if (YoutubeClient.TryParseUsername(url, out output)) + { + type = "user"; + goto LinkFound; + } + else if (YoutubeClient.TryParseVideoId(url, out output)) + { + type = "video"; + goto LinkFound; + } + + await Launcher.LaunchUriAsync(new Uri(url)); + return; + + LinkFound: + switch (type) + { + case "channel": + 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; } } diff --git a/FoxTube/Classes/SecretsVault.cs b/FoxTube/Classes/SecretsVault.cs index 9469c84..98e3d5f 100644 --- a/FoxTube/Classes/SecretsVault.cs +++ b/FoxTube/Classes/SecretsVault.cs @@ -174,7 +174,7 @@ namespace FoxTube //Saving user's subscriptions for background task SaveSubscriptions(); - InvokeEvent: + InvokeEvent: AuthorizationStateChanged?.Invoke(args: IsAuthorized); } catch @@ -186,13 +186,11 @@ namespace FoxTube /// /// Saves user's subscriptions keypairs (channel ID: avatar URL) into "background.json" file for concurrent background processing /// - public static async void SaveSubscriptions() + public static void SaveSubscriptions() { Dictionary subs = new Dictionary(); Subscriptions.ForEach(x => subs.Add(x.Snippet.ResourceId.ChannelId, x.Snippet.Thumbnails.Medium.Url)); - await FileIO.WriteTextAsync( - await ApplicationData.Current.RoamingFolder.CreateFileAsync("background.json", CreationCollisionOption.ReplaceExisting), - JsonConvert.SerializeObject(subs)); + ApplicationData.Current.RoamingSettings.Values["subscriptions"] = JsonConvert.SerializeObject(subs); } /// @@ -206,10 +204,7 @@ namespace FoxTube AuthorizationStateChanged?.Invoke(args: false); SettingsStorage.HasAccount = false; - await FileIO.WriteTextAsync( - await ApplicationData.Current.RoamingFolder.CreateFileAsync( - "background.json", CreationCollisionOption.ReplaceExisting) - , ""); + ApplicationData.Current.RoamingSettings.Values["subscriptions"] = ""; } } @@ -217,17 +212,14 @@ namespace FoxTube /// Checks if any user has already been logged in. If has, calls *Authorize()* to retrieve his info /// /// Loads user's subscriptions if true - public static async void CheckAuthorization(bool retrieveSubs = true) + public static void CheckAuthorization(bool retrieveSubs = true) { if (SettingsStorage.HasAccount) Authorize(retrieveSubs); else { AuthorizationStateChanged.Invoke(args: false); - await FileIO.WriteTextAsync( - await ApplicationData.Current.RoamingFolder.CreateFileAsync( - "background.json", CreationCollisionOption.ReplaceExisting), - ""); + ApplicationData.Current.RoamingSettings.Values["subscriptions"] = ""; } } diff --git a/FoxTube/Classes/SettingsStorage.cs b/FoxTube/Classes/SettingsStorage.cs index 917d46d..30f9368 100644 --- a/FoxTube/Classes/SettingsStorage.cs +++ b/FoxTube/Classes/SettingsStorage.cs @@ -7,7 +7,7 @@ using Windows.Storage; namespace FoxTube { - public enum MatureState { Blocked, Allowed, AllowedOnce } + public enum MatureState { Blocked, Allowed } public class SettingsContainer { @@ -207,19 +207,6 @@ namespace FoxTube public static void SaveData() { storage.Values["settings"] = JsonConvert.SerializeObject(Container); - ExportSettings(); - } - - public static async void ExportSettings() - { - try - { - bool[] notificationsSettings = new[] { VideoNotifications, DevNotifications }; - await FileIO.WriteTextAsync( - await ApplicationData.Current.RoamingFolder.CreateFileAsync("notifications.json", CreationCollisionOption.ReplaceExisting), - JsonConvert.SerializeObject(notificationsSettings)); - } - catch { } } } } diff --git a/FoxTube/Controls/Adverts/CardAdvert.xaml b/FoxTube/Controls/Adverts/CardAdvert.xaml index 8582208..4f5a4f6 100644 --- a/FoxTube/Controls/Adverts/CardAdvert.xaml +++ b/FoxTube/Controls/Adverts/CardAdvert.xaml @@ -7,38 +7,38 @@ mc:Ignorable="d" HorizontalAlignment="Stretch" VerticalAlignment="Top" - SizeChanged="UserControl_SizeChanged" d:DesignHeight="290" - d:DesignWidth="384"> + d:DesignWidth="384" + Visibility="Collapsed"> - diff --git a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs b/FoxTube/Controls/Adverts/CardAdvert.xaml.cs index 64fd8e8..cb567f1 100644 --- a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs +++ b/FoxTube/Controls/Adverts/CardAdvert.xaml.cs @@ -31,10 +31,8 @@ namespace FoxTube.Controls.Adverts { title.Text = advert.Title; image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri()); - if (advert.AdIcon == null) - contentGrid.ColumnDefinitions[0].Width = new GridLength(0); - else - icon.ProfilePicture = advert.AdIcon.Source; + + icon.ProfilePicture = advert.AdIcon.Source; if (string.IsNullOrWhiteSpace(advert.SponsoredBy)) sponsor.Visibility = Visibility.Collapsed; @@ -53,10 +51,5 @@ namespace FoxTube.Controls.Adverts Visibility = Visibility.Visible; } - - private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) - { - Height = e.NewSize.Width * 0.75; - } } } diff --git a/FoxTube/Controls/ChannelCard.xaml b/FoxTube/Controls/ChannelCard.xaml index 79ccc3d..357b1b7 100644 --- a/FoxTube/Controls/ChannelCard.xaml +++ b/FoxTube/Controls/ChannelCard.xaml @@ -4,51 +4,65 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)" mc:Ignorable="d" HorizontalAlignment="Stretch" VerticalAlignment="Top" - SizeChanged="UserControl_SizeChanged" d:DesignHeight="290" - d:DesignWidth="384"> + d:DesignWidth="384" + MaxWidth="500"> - diff --git a/FoxTube/Controls/ChannelCard.xaml.cs b/FoxTube/Controls/ChannelCard.xaml.cs index cf2e66b..3236fc7 100644 --- a/FoxTube/Controls/ChannelCard.xaml.cs +++ b/FoxTube/Controls/ChannelCard.xaml.cs @@ -15,24 +15,19 @@ namespace FoxTube.Controls /// /// Channel item card /// - public sealed partial class ChannelCard : UserControl, IItemCard + public sealed partial class ChannelCard : UserControl { ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards"); string channelId; Channel item; - public ChannelCard(string id, string live = "null") + public ChannelCard(string id, string live = null) { InitializeComponent(); Initialize(id, live); } - private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) - { - Height = e.NewSize.Width * 0.75; - } - public async void Initialize(string id, string live) { ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings"); @@ -43,6 +38,7 @@ namespace FoxTube.Controls channelId = id; title.Text = item.Snippet.Title; + description.Text = item.Snippet.Description; subs.Text = $"{item.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}"; uploads.Text = $"{item.Statistics.VideoCount:0,0} {resources.GetString("/Cards/videos")}"; @@ -52,14 +48,11 @@ namespace FoxTube.Controls if(SecretsVault.IsAuthorized) { - foreach(Subscription s in SecretsVault.Subscriptions) + if(SecretsVault.Subscriptions.Exists(i => i.Snippet.ResourceId.ChannelId == id)) { - if(s.Snippet.ResourceId.ChannelId == id) - { - subscribe.Background = new SolidColorBrush(Colors.Transparent); - subscribe.Foreground = new SolidColorBrush(Colors.Gray); - subscribe.Content = resources.GetString("/Cards/unsubscribe"); - } + subscribe.Background = new SolidColorBrush(Colors.Transparent); + subscribe.Foreground = new SolidColorBrush(Colors.Gray); + subscribe.Content = resources.GetString("/Cards/unsubscribe"); } subscriptionPane.Visibility = Visibility.Visible; } @@ -68,14 +61,15 @@ namespace FoxTube.Controls catch { } try { - if (item.BrandingSettings.Image.BannerImageUrl.Contains("default")) - throw new Exception("Default channel cover detected"); - cover.Source = new BitmapImage((item.BrandingSettings.Image.BannerTvHighImageUrl ?? item.BrandingSettings.Image.BannerTvImageUrl).ToUri()); + if (!item.BrandingSettings.Image.BannerImageUrl.Contains("default")) + cover.Source = new BitmapImage(item.BrandingSettings.Image.BannerImageUrl.ToUri()); } catch { } + + Opacity = 1; } - private void Button_Click(object sender, RoutedEventArgs e) + public void Button_Click(object sender, RoutedEventArgs e) { Methods.MainPage.GoToChannel(channelId); } @@ -104,13 +98,18 @@ namespace FoxTube.Controls private void GetLink_Click(object sender, RoutedEventArgs e) { DataPackage data = new DataPackage(); - data.SetText(string.IsNullOrWhiteSpace(item.Snippet.CustomUrl) ? $"https://www.youtube.com/channel/{item.Id}" : $"https://www.youtube.com/user/{item.Snippet.CustomUrl}"); + data.SetText($"https://www.youtube.com/channel/{item.Id}"); Clipboard.SetContent(data); } private async void InBrowser_Click(object sender, RoutedEventArgs e) { - await Launcher.LaunchUriAsync((string.IsNullOrWhiteSpace(item.Snippet.CustomUrl) ? $"https://www.youtube.com/channel/{item.Id}" : $"https://www.youtube.com/user/{item.Snippet.CustomUrl}").ToUri()); + await Launcher.LaunchUriAsync($"https://www.youtube.com/channel/{item.Id}".ToUri()); + } + + private void Cover_ImageOpened(object sender, RoutedEventArgs e) + { + cover.Opacity = 1; } } } diff --git a/FoxTube/Controls/Chat.xaml b/FoxTube/Controls/Chat.xaml index 0968ad0..71077a7 100644 --- a/FoxTube/Controls/Chat.xaml +++ b/FoxTube/Controls/Chat.xaml @@ -24,53 +24,51 @@ Content="" FontSize="30"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FoxTube/Controls/Chat.xaml.cs b/FoxTube/Controls/Chat.xaml.cs index 4235fd9..1f95fda 100644 --- a/FoxTube/Controls/Chat.xaml.cs +++ b/FoxTube/Controls/Chat.xaml.cs @@ -5,6 +5,10 @@ using Windows.UI.Xaml.Media; using Google.Apis.YouTube.v3.Data; using Google.Apis.YouTube.v3; using Windows.UI; +using Microsoft.AppCenter.Analytics; +using System.Collections.Generic; +using Windows.UI.Popups; +using Windows.ApplicationModel.Resources; namespace FoxTube.Controls { @@ -66,6 +70,10 @@ namespace FoxTube.Controls { string chatId; DateTime lastInsert; + + LiveChatMessagesResource.ListRequest request; + LiveChatMessageListResponse response; + DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) @@ -76,19 +84,20 @@ namespace FoxTube.Controls InitializeComponent(); if (!SecretsVault.IsAuthorized) inputField.Visibility = Visibility.Collapsed; - chatId = id; + + request = SecretsVault.Service.LiveChatMessages.List(chatId, "snippet,authorDetails"); + timer.Tick += Update; timer.Start(); } public async void Update(object sender, object e) { - LiveChatMessagesResource.ListRequest request = SecretsVault.Service.LiveChatMessages.List(chatId, "snippet,authorDetails"); - LiveChatMessageListResponse response = await request.ExecuteAsync(); - foreach (LiveChatMessage i in response.Items) - if(i.Snippet.PublishedAt >= lastInsert) - list.Items.Insert(0, new ChatMessage(i)); + response = await request.ExecuteAsync(); + foreach (LiveChatMessage i in response.Items.FindAll(i => i.Snippet.PublishedAt >= lastInsert)) + list.Items.Insert(0, new ChatMessage(i)); + lastInsert = DateTime.Now; timer.Interval = TimeSpan.FromMilliseconds(response.PollingIntervalMillis.Value); timer.Start(); @@ -99,16 +108,16 @@ namespace FoxTube.Controls Methods.MainPage.GoToChannel(((HyperlinkButton)sender).Tag as string); } - private async void send_Click(object sender, RoutedEventArgs e) + private async void send_Click(object sender, RoutedEventArgs args) { try { - newMessage.IsEnabled = false; - send.IsEnabled = false; - if (string.IsNullOrWhiteSpace(newMessage.Text)) return; + newMessage.IsEnabled = false; + send.IsEnabled = false; + LiveChatMessage message = new LiveChatMessage() { Snippet = new LiveChatMessageSnippet() @@ -131,11 +140,18 @@ namespace FoxTube.Controls list.Items.Add(new ChatMessage(response)); } } - finally + catch(Exception e) { - newMessage.IsEnabled = true; - send.IsEnabled = true; + await new MessageDialog(ResourceLoader.GetForCurrentView("Chat").GetString("/Chat/failed")).ShowAsync(); + Analytics.TrackEvent("Failed to send a chat message", new Dictionary() + { + { "Exception", e.GetType().ToString() }, + { "Message", e.Message } + }); } + + newMessage.IsEnabled = true; + send.IsEnabled = true; } } } diff --git a/FoxTube/Controls/CommentCard.xaml b/FoxTube/Controls/CommentCard.xaml index 93a22c0..13e4747 100644 --- a/FoxTube/Controls/CommentCard.xaml +++ b/FoxTube/Controls/CommentCard.xaml @@ -6,96 +6,84 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="400"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - + + + + + + - + - - - - - diff --git a/FoxTube/Controls/PlaylistCard.xaml.cs b/FoxTube/Controls/PlaylistCard.xaml.cs index 1d81103..7019744 100644 --- a/FoxTube/Controls/PlaylistCard.xaml.cs +++ b/FoxTube/Controls/PlaylistCard.xaml.cs @@ -1,6 +1,8 @@ using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; +using Microsoft.AppCenter.Analytics; using System; +using System.Collections.Generic; using Windows.ApplicationModel.DataTransfer; using Windows.System; using Windows.UI.Xaml; @@ -12,11 +14,13 @@ namespace FoxTube.Controls /// /// Playlist card control /// - public sealed partial class PlaylistCard : Page, IItemCard + public sealed partial class PlaylistCard : Page { Playlist item; public string playlistId; + public bool NeedInitialize { get; set; } = true; + public PlaylistCard(string id) { InitializeComponent(); @@ -25,34 +29,41 @@ namespace FoxTube.Controls public async void Initialize(string id) { - PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails"); - request.Id = id; - PlaylistListResponse response = await request.ExecuteAsync(); - - item = response.Items[0]; - playlistId = id; - - title.Text = item.Snippet.Title; - channelName.Text = item.Snippet.ChannelTitle; - 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.Maxres ?? item.Snippet.Thumbnails.Medium).Url.ToUri()); - avatar.ProfilePicture = new BitmapImage(new Uri((await r.ExecuteAsync()).Items[0].Snippet.Thumbnails.Medium.Url)); - } catch { } + playlistId = id; + PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails"); + request.Id = playlistId; + item = (await request.ExecuteAsync()).Items[0]; + + title.Text = item.Snippet.Title; + channelName.Text = item.Snippet.ChannelTitle; + 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()); } + catch { } + try { avatar.ProfilePicture = new BitmapImage(new Uri((await r.ExecuteAsync()).Items[0].Snippet.Thumbnails.Medium.Url)); } + catch { } + + Opacity = 1; + } + catch (Exception e) + { + Visibility = Visibility.Collapsed; + Analytics.TrackEvent("PlaylistCard loading failed", new Dictionary() + { + { "Exception", e.GetType().ToString() }, + { "Message", e.Message }, + { "Playlist ID", playlistId } + }); + } } - private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) - { - Height = e.NewSize.Width * 0.75; - } - - private void Button_Click(object sender, RoutedEventArgs e) + public void Button_Click(object sender, RoutedEventArgs e) { Methods.MainPage.GoToPlaylist(item.Id); } @@ -73,5 +84,10 @@ namespace FoxTube.Controls { await Launcher.LaunchUriAsync($"https://www.youtube.com/playlist?list={playlistId}".ToUri()); } + + private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) + { + thumbnail.Opacity = 1; + } } } diff --git a/FoxTube/Controls/ShowMore.xaml b/FoxTube/Controls/ShowMore.xaml index ea507f5..ba69b19 100644 --- a/FoxTube/Controls/ShowMore.xaml +++ b/FoxTube/Controls/ShowMore.xaml @@ -2,7 +2,6 @@ x:Class="FoxTube.Controls.ShowMore" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:FoxTube.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> diff --git a/FoxTube/Controls/ShowMore.xaml.cs b/FoxTube/Controls/ShowMore.xaml.cs index 2fad8d0..eccffb7 100644 --- a/FoxTube/Controls/ShowMore.xaml.cs +++ b/FoxTube/Controls/ShowMore.xaml.cs @@ -1,19 +1,5 @@ -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; 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 { @@ -22,7 +8,7 @@ namespace FoxTube.Controls public event Event Clicked; public ShowMore() { - this.InitializeComponent(); + InitializeComponent(); } private void btn_Click(object sender, RoutedEventArgs e) @@ -32,11 +18,21 @@ namespace FoxTube.Controls Clicked.Invoke(); } - public void Complete(bool close = false) + 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; - if (!close) - btn.Visibility = Visibility.Visible; + btn.Visibility = Visibility.Visible; } } } diff --git a/FoxTube/Controls/VideoCard.xaml b/FoxTube/Controls/VideoCard.xaml index a7bfd17..493bd1f 100644 --- a/FoxTube/Controls/VideoCard.xaml +++ b/FoxTube/Controls/VideoCard.xaml @@ -4,51 +4,63 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)" mc:Ignorable="d" HorizontalAlignment="Stretch" VerticalAlignment="Top" - SizeChanged="UserControl_SizeChanged" d:DesignHeight="290" - d:DesignWidth="384"> + d:DesignWidth="384" + MaxWidth="500" + Opacity="0"> - diff --git a/FoxTube/Controls/VideoCard.xaml.cs b/FoxTube/Controls/VideoCard.xaml.cs index 774657f..520e2ff 100644 --- a/FoxTube/Controls/VideoCard.xaml.cs +++ b/FoxTube/Controls/VideoCard.xaml.cs @@ -7,51 +7,59 @@ using Windows.UI.Xaml.Media.Imaging; using Windows.System; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.Resources; +using Microsoft.AppCenter.Analytics; +using System.Collections.Generic; +using YoutubeExplode; +using Windows.UI.Popups; namespace FoxTube.Controls { /// /// Video item card /// - public sealed partial class VideoCard : UserControl, IItemCard + public sealed partial class VideoCard : UserControl { ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards"); public string playlistId; public string videoId; Video item; - + public VideoCard(string id, string playlist = null) { InitializeComponent(); Initialize(id, playlist); } - private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) + public VideoCard(Video meta, string playlist = null) { - Height = e.NewSize.Width * 0.75; + InitializeComponent(); + item = meta; + playlistId = playlist; + LoadMeta(); } public async void Initialize(string id, string playlist = null) { - try { - VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails"); - request.Id = id; - VideoListResponse response = await request.ExecuteAsync(); - - item = response.Items[0]; + try + { videoId = id; playlistId = playlist; + VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails"); + request.Id = id; + item = (await request.ExecuteAsync()).Items[0]; + title.Text = item.Snippet.Title; channelName.Text = item.Snippet.ChannelTitle; + 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.ScheduledStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}"; + info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}"; else - info.Text = item.LiveStreamingDetails.ActualStartTime.Value.ToString(); + info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value); liveTag.Visibility = Visibility.Visible; } else if (item.Snippet.LiveBroadcastContent == "upcoming") @@ -63,8 +71,8 @@ namespace FoxTube.Controls info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; liveTag.Visibility = Visibility.Visible; - if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && (item.LiveStreamingDetails.ScheduledStartTime - DateTime.Now).Value.TotalMilliseconds > 0) - liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {item.LiveStreamingDetails.ScheduledStartTime}"; + if (item.LiveStreamingDetails.ScheduledStartTime.HasValue) + liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {(item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss")}"; else liveContent.Text = resources.GetString("/Cards/upcoming"); } else @@ -73,31 +81,107 @@ namespace FoxTube.Controls info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; } - var request1 = SecretsVault.Service.Channels.List("snippet"); - request1.Id = item.Snippet.ChannelId; - ChannelListResponse response1 = await request1.ExecuteAsync(); - - try - { - avatar.ProfilePicture = new BitmapImage(new Uri(response1.Items[0].Snippet.Thumbnails.Medium.Url)); - thumbnail.Source = new BitmapImage(new Uri((item.Snippet.Thumbnails.Maxres ?? item.Snippet.Thumbnails.Medium).Url)); - } + try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); } + catch { } + try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()); } catch { } - /*if(SecretsVault.UserHistory.Exists(x => x.Id == videoId)) - { + if(SecretsVault.History.Contains(videoId)) watched.Visibility = Visibility.Visible; - leftOn.Value = SecretsVault.UserHistory.Find(x => x.Id == videoId).LeftOn; - }*/ + + Opacity = 1; } - catch + catch (Exception e) { Visibility = Visibility.Collapsed; + Analytics.TrackEvent("VideoCard loading failed", new Dictionary() + { + { "Exception", e.GetType().ToString() }, + { "Message", e.Message }, + { "Video ID", videoId } + }); } } - public void Button_Click(object sender, RoutedEventArgs e) + public async void LoadMeta() { + videoId = item.Id; + title.Text = item.Snippet.Title; + channelName.Text = item.Snippet.ChannelTitle; + + 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"); + } + else + { + views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}"; + info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; + } + + try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); } + catch { } + try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 }; } + catch { } + + if (SecretsVault.History.Contains(videoId)) + watched.Visibility = Visibility.Visible; + + Opacity = 1; + } + + public async void Button_Click(object sender, RoutedEventArgs e) + { + if (item.ContentDetails.ContentRating != null) + { + if (SecretsVault.IsAuthorized) + { + if (SettingsStorage.Mature == MatureState.Blocked) + { + MessageDialog dialog = new MessageDialog(resources.GetString("/VideoPage/matureText"), resources.GetString("/VideoPage/wantContinue")); + dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/yes"), null, true)); + dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/always"), (command) => SettingsStorage.Mature = MatureState.Allowed, true)); + dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/no"), null, false)); + + dialog.CancelCommandIndex = 2; + dialog.DefaultCommandIndex = 0; + + if (!(bool)(await dialog.ShowAsync()).Id) + return; + } + } + else + { + MessageDialog dialog = new MessageDialog(resources.GetString("/VideoPage/matureText"), resources.GetString("/VideoPage/signRequired")); + dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/signin"), (command) => SecretsVault.Authorize())); + dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/cancel"))); + + dialog.CancelCommandIndex = 1; + dialog.DefaultCommandIndex = 0; + + await dialog.ShowAsync(); + return; + } + } + Methods.MainPage.GoToVideo(videoId, playlistId); } @@ -117,5 +201,10 @@ namespace FoxTube.Controls { await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}".ToUri()); } + + private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) + { + thumbnail.Opacity = 1; + } } } diff --git a/FoxTube/Controls/VideoPlayer.xaml b/FoxTube/Controls/VideoPlayer.xaml deleted file mode 100644 index c2a808e..0000000 --- a/FoxTube/Controls/VideoPlayer.xaml +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -74,6 +77,6 @@ - + diff --git a/FoxTube/Pages/PlaylistPage.xaml.cs b/FoxTube/Pages/PlaylistPage.xaml.cs index 70ee05e..3bd717c 100644 --- a/FoxTube/Pages/PlaylistPage.xaml.cs +++ b/FoxTube/Pages/PlaylistPage.xaml.cs @@ -23,15 +23,13 @@ namespace FoxTube.Pages public string playlistId; Playlist item; - LoadingPage loading; - VideoGrid list; + PlaylistItemsResource.ListRequest request; + string token; public PlaylistPage() { InitializeComponent(); - loading = grid.Children[2] as LoadingPage; - list = ((grid.Children[0] as ScrollViewer).Content as Grid).Children[1] as VideoGrid; - loading.RefreshPage += refresh_Click; + DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler(Share); } @@ -52,42 +50,38 @@ namespace FoxTube.Pages { playlistId = id; - PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails"); - request.Id = id; + PlaylistsResource.ListRequest infoRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails"); + infoRequest.Id = id; - item = (await request.ExecuteAsync()).Items[0]; + item = (await infoRequest.ExecuteAsync()).Items[0]; title.Text = item.Snippet.Title; info.Text = $"{item.ContentDetails.ItemCount} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}"; description.Text = item.Snippet.Description; channelName.Text = item.Snippet.ChannelTitle; - - thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); + ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet"); channelRequest.Id = item.Snippet.ChannelId; Channel channel = (await channelRequest.ExecuteAsync()).Items[0]; - avatar.ProfilePicture = new BitmapImage(channel.Snippet.Thumbnails.Medium.Url.ToUri()); - PlaylistItemsResource.ListRequest listRequest = SecretsVault.Service.PlaylistItems.List("contentDetails"); - listRequest.PlaylistId = id; - listRequest.MaxResults = 50; + try { avatar.ProfilePicture = new BitmapImage(channel.Snippet.Thumbnails.Medium.Url.ToUri()); } + catch { } + try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); } + catch { } - PlaylistItemListResponse response = await listRequest.ExecuteAsync(); + request = SecretsVault.Service.PlaylistItems.List("contentDetails"); + request.PlaylistId = id; + request.MaxResults = 25; - list.Clear(); + PlaylistItemListResponse response = await request.ExecuteAsync(); + token = response.NextPageToken; + if (string.IsNullOrWhiteSpace(token)) + more.Visibility = Visibility.Collapsed; + foreach (PlaylistItem i in response.Items) list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId)); - while (response.NextPageToken != null) - { - listRequest.PageToken = response.NextPageToken; - response = await listRequest.ExecuteAsync(); - - foreach (PlaylistItem i in response.Items) - list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId)); - } - loading.Close(); } catch (System.Net.Http.HttpRequestException) @@ -134,5 +128,22 @@ namespace FoxTube.Pages $"https://www.youtube.com/playlist?list={item.Id}", ResourceLoader.GetForCurrentView("Cards").GetString("/Cards/playlistShare")); } + + private async void ShowMore_Clicked() + { + request.PageToken = token; + PlaylistItemListResponse response = await request.ExecuteAsync(); + + if (string.IsNullOrWhiteSpace(request.PageToken)) + more.Visibility = Visibility.Collapsed; + else + { + token = response.NextPageToken; + more.Complete(); + } + + foreach (PlaylistItem i in response.Items) + list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId)); + } } } diff --git a/FoxTube/Pages/Search.xaml b/FoxTube/Pages/Search.xaml index 98cd9c4..ff7ff98 100644 --- a/FoxTube/Pages/Search.xaml +++ b/FoxTube/Pages/Search.xaml @@ -67,8 +67,8 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +