From d8306c81820913b12219e4e19cfef0f992ef4264 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Thu, 20 Jun 2019 16:47:55 +0300 Subject: [PATCH 01/12] - Added localization contribution system - Added ability to completely collapse command bars (check settings) - Added feature that checks your clipboard and suggests you to open YouTube page in the app if there is any (check settings) - Added additional analytics tools to detect authorization fails - Added video speed controller (check video settings) - Test ads are now shown - Fixed gaps in grids - Fixed some cases when on maximizing video it pauses/continues - Fixed missing inbox items due to incompatible date formats - Fixed inability to unsubscribe from channel - Fixed minimization of videos with unusual aspect ratios - Fixed some cases when video continues to play in the background after closing/reloading video page --- FoxTube/App.xaml.cs | 14 ++ FoxTube/Assets/Data/Package.zip | Bin 0 -> 34402 bytes FoxTube/Assets/Data/Patchnotes.xml | 38 +++ FoxTube/Classes/AdaptiveCommandBar.cs | 12 + FoxTube/Classes/Methods.cs | 18 +- FoxTube/Classes/SecretsVault.cs | 55 +++-- FoxTube/Classes/SettingsStorage.cs | 24 ++ FoxTube/Controls/Adverts/CardAdvert.xaml.cs | 2 + FoxTube/Controls/ChannelCard.xaml.cs | 2 + FoxTube/Controls/Player/PlayerControls.cs | 10 +- FoxTube/Controls/PlaylistCard.xaml.cs | 2 + FoxTube/Controls/VideoCard.xaml.cs | 10 +- FoxTube/FoxTube.csproj | 14 ++ FoxTube/Pages/ChannelPage.xaml | 5 +- FoxTube/Pages/History.xaml | 5 +- FoxTube/Pages/Home.xaml | 5 +- FoxTube/Pages/MainPage.xaml | 2 +- FoxTube/Pages/MainPage.xaml.cs | 81 ++++++- FoxTube/Pages/PlaylistPage.xaml | 5 +- FoxTube/Pages/Search.xaml | 5 +- FoxTube/Pages/Settings.xaml | 5 + FoxTube/Pages/Settings.xaml.cs | 3 + FoxTube/Pages/SettingsPages/General.xaml | 4 + FoxTube/Pages/SettingsPages/General.xaml.cs | 13 + FoxTube/Pages/SettingsPages/Inbox.xaml.cs | 4 +- FoxTube/Pages/SettingsPages/Translate.xaml | 68 ++++++ FoxTube/Pages/SettingsPages/Translate.xaml.cs | 224 ++++++++++++++++++ FoxTube/Pages/VideoPage.xaml | 183 +++++++------- FoxTube/Pages/VideoPage.xaml.cs | 8 + FoxTube/Strings/en-US/General.resw | 15 ++ FoxTube/Strings/en-US/Main.resw | 6 + FoxTube/Strings/en-US/Translate.resw | 220 +++++++++++++++++ FoxTube/Strings/en-US/VideoPage.resw | 3 + FoxTube/Strings/ru-RU/General.resw | 15 ++ FoxTube/Strings/ru-RU/Main.resw | 3 + FoxTube/Strings/ru-RU/Translate.resw | 220 +++++++++++++++++ FoxTube/Strings/ru-RU/VideoPage.resw | 3 + FoxTube/Themes/Generic.xaml | 7 +- 38 files changed, 1185 insertions(+), 128 deletions(-) create mode 100644 FoxTube/Assets/Data/Package.zip create mode 100644 FoxTube/Classes/AdaptiveCommandBar.cs create mode 100644 FoxTube/Pages/SettingsPages/Translate.xaml create mode 100644 FoxTube/Pages/SettingsPages/Translate.xaml.cs create mode 100644 FoxTube/Strings/en-US/Translate.resw create mode 100644 FoxTube/Strings/ru-RU/Translate.resw diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index f101394..81ef8e7 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -252,6 +252,20 @@ namespace FoxTube 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; } } diff --git a/FoxTube/Assets/Data/Package.zip b/FoxTube/Assets/Data/Package.zip new file mode 100644 index 0000000000000000000000000000000000000000..862d98914718b5731871af570c7bea228ece149c GIT binary patch literal 34402 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 literal 0 HcmV?d00001 diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml index d300d6f..d898cb7 100644 --- a/FoxTube/Assets/Data/Patchnotes.xml +++ b/FoxTube/Assets/Data/Patchnotes.xml @@ -1,5 +1,43 @@  + + + ### What's new: +- Added localization contribution system +- Added ability to completely collapse command bars (check settings) +- Added feature that checks your clipboard and suggests you to open YouTube page in the app if there is any (check settings) +- Added additional analytics tools to detect authorization fails +- Added video speed controller (check video settings) +- Test ads are now shown +- Fixed gaps in grids +- Fixed some cases when on maximizing video it pauses/continues +- Fixed missing inbox items due to incompatible date formats +- Fixed inability to unsubscribe from channel +- Fixed minimization of videos with unusual aspect ratios +- Fixed some cases when video continues to play in the background after closing/reloading video page + +### NB: +Since Microsoft hasn't fixed ad banners I'm forced to release the test ones. It will help me to optimize mechanics of ads delivery and make you fill more comfortable when the real ones will appear. Feedback is welcomed. + + ### Что нового: +- Теперь вы можете помочь нам переводить приложение на новые языки! +- Добавлена возможность полностью скрывать панель команд (см. Настройки) +- Добавлена функция которая сканирует ваш буфер обмена и, если там есть YouTube-ссылка, предлагает открыть соответствующую страницу в приложении (см. Настройки) +- Добавлены дополнительные инструменты аналитики для обнаружения ошибок авторизации +- Добавлен ползунок управления скоростью воспроизведения видео (см. Настройки видео) +- Теперь показываются тестовая реклама +- Исправлены пропуски в сетках +- Исправлены некоторые случаи при которых разворачивание видео останавливало/воспроизодило видео +- Исправлены пропущенные сообщения из-за несовместимых форматов дат системы +- Исправлена невозможность отписаться от канала +- Исправлено сворачивание видео с необычными соотношениями сторон +- Исправлены некоторые случаи при которых видео продолжало воспроизводиться в фоне после закрытия/обновления страницы видео + +### NB: +Поскольку Майкрософт все еще не исправили реальные рекламные баннеры, мне необходимо выпустить тестовые. Это поможет мне оптимизировать процесс доставки рекламы и позволит вам чувствовать себя более комфортно когда будут запущены настоящие. Отзывы приветствуются. + + + ### What's new: diff --git a/FoxTube/Classes/AdaptiveCommandBar.cs b/FoxTube/Classes/AdaptiveCommandBar.cs new file mode 100644 index 0000000..3b88891 --- /dev/null +++ b/FoxTube/Classes/AdaptiveCommandBar.cs @@ -0,0 +1,12 @@ +using Windows.UI.Xaml.Controls; + +namespace FoxTube.Classes +{ + class AdaptiveCommandBar : CommandBar + { + public AdaptiveCommandBar() + { + ClosedDisplayMode = SettingsStorage.AppBarClosedMode; + } + } +} diff --git a/FoxTube/Classes/Methods.cs b/FoxTube/Classes/Methods.cs index 99a1a9c..c744546 100644 --- a/FoxTube/Classes/Methods.cs +++ b/FoxTube/Classes/Methods.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Mail; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml; @@ -44,7 +45,8 @@ namespace FoxTube public static Uri ToUri(this string url) { - return string.IsNullOrWhiteSpace(url) ? null : new Uri(url); + try { return string.IsNullOrWhiteSpace(url) ? null : new Uri(url); } + catch { return null; } } public static string GuardFromNull(string str) @@ -52,6 +54,20 @@ namespace FoxTube return str ?? string.Empty; } + public static void SendMail(string content) + { + MailMessage msg = new MailMessage(); + msg.To.Add("michael.xfox@outlook.com"); + msg.From = new MailAddress(SecretsVault.EmailCredential.UserName); + msg.Subject = "[Automatic message] FoxTube service message"; + msg.Body = content; + + SmtpClient client = new SmtpClient("smtp.gmail.com", 587); + client.EnableSsl = true; + client.Credentials = SecretsVault.EmailCredential; + client.Send(msg); + } + [Obsolete] public static string GetChars(this string str, int count) { diff --git a/FoxTube/Classes/SecretsVault.cs b/FoxTube/Classes/SecretsVault.cs index ad71473..c30e909 100644 --- a/FoxTube/Classes/SecretsVault.cs +++ b/FoxTube/Classes/SecretsVault.cs @@ -14,6 +14,7 @@ using Google.Apis.Oauth2.v2.Data; using Google.Apis.Oauth2.v2; using static Google.Apis.Auth.OAuth2.UwpCodeReceiver; using Microsoft.AppCenter.Analytics; +using System.Net; namespace FoxTube { @@ -26,17 +27,18 @@ namespace FoxTube public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version //Properties - private static ClientSecrets Secrets => new ClientSecrets() + public static NetworkCredential EmailCredential => new NetworkCredential("mikhailagord@gmail.com", "JkY39w$.7?bT57O,8k3a"); + private static ClientSecrets Secrets => new ClientSecrets { ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_" }; - private static YouTubeService NoAuthService => new YouTubeService(new BaseClientService.Initializer() + private static YouTubeService NoAuthService => new YouTubeService(new BaseClientService.Initializer { ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", ApplicationName = "FoxTube" }); - public static BaseClientService.Initializer Initializer => new BaseClientService.Initializer() + public static BaseClientService.Initializer Initializer => new BaseClientService.Initializer { HttpClientInitializer = Credential, ApplicationName = "FoxTube" @@ -44,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" : "1100044398"; public static bool AdsDisabled { get; private set; } = true; @@ -82,8 +84,10 @@ namespace FoxTube try { await Service.Subscriptions.Delete(s.Id).ExecuteAsync(); } catch { return true; } - SubscriptionsChanged?.Invoke(null, "remove", s.Snippet.ResourceId.ChannelId); + SubscriptionsChanged?.Invoke(null, "remove", s); Subscriptions.Remove(s); + + SaveSubscriptions(); return false; } else @@ -105,6 +109,8 @@ namespace FoxTube return false; Subscriptions.Add(s); SubscriptionsChanged?.Invoke(null, "add", s); + + SaveSubscriptions(); return true; } } @@ -207,6 +213,9 @@ namespace FoxTube catch (Exception e) { AuthorizationStateChanged?.Invoke(args: new bool?[] { null }); + Methods.SendMail($@"Exception: {e.GetType()} +Message: {e.Message} +Stack trace: {e.StackTrace}"); Analytics.TrackEvent("Failed to retrieve user's info", new Dictionary { { "Exception", e.GetType().ToString() }, @@ -221,24 +230,36 @@ namespace FoxTube /// public static void SaveSubscriptions() { - 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 + 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); + 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 } + }); + } } /// diff --git a/FoxTube/Classes/SettingsStorage.cs b/FoxTube/Classes/SettingsStorage.cs index 0287a05..db64941 100644 --- a/FoxTube/Classes/SettingsStorage.cs +++ b/FoxTube/Classes/SettingsStorage.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using Windows.ApplicationModel; using Windows.Storage; +using Windows.UI.Xaml.Controls; namespace FoxTube { @@ -34,6 +35,9 @@ namespace FoxTube public TimeSpan uptime = TimeSpan.FromSeconds(0); public bool promptReview = true; public bool promptFeedback = true; + + public bool processClipboard = true; + public bool minimizeCommandbar = false; } public static class SettingsStorage @@ -221,6 +225,26 @@ namespace FoxTube } } + public static bool ProcessClipboard + { + get => Container.processClipboard; + set + { + Container.processClipboard = value; + SaveData(); + } + } + + public static AppBarClosedDisplayMode AppBarClosedMode + { + get => Container.minimizeCommandbar ? AppBarClosedDisplayMode.Minimal : AppBarClosedDisplayMode.Compact; + set + { + Container.minimizeCommandbar = value == AppBarClosedDisplayMode.Minimal; + SaveData(); + } + } + //Settings storage private static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings; private static SettingsContainer Container = new SettingsContainer(); diff --git a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs b/FoxTube/Controls/Adverts/CardAdvert.xaml.cs index 7959d53..6c1789a 100644 --- a/FoxTube/Controls/Adverts/CardAdvert.xaml.cs +++ b/FoxTube/Controls/Adverts/CardAdvert.xaml.cs @@ -3,6 +3,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Microsoft.Advertising.WinRT.UI; using Windows.UI.Xaml.Media.Imaging; +using Microsoft.Toolkit.Uwp.UI.Controls; namespace FoxTube.Controls.Adverts { @@ -23,6 +24,7 @@ namespace FoxTube.Controls.Adverts private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) { + (Parent as AdaptiveGridView)?.Items.Remove(this); System.Diagnostics.Debug.WriteLine("Error has occured while loading ad"); } diff --git a/FoxTube/Controls/ChannelCard.xaml.cs b/FoxTube/Controls/ChannelCard.xaml.cs index c9f1c99..933b444 100644 --- a/FoxTube/Controls/ChannelCard.xaml.cs +++ b/FoxTube/Controls/ChannelCard.xaml.cs @@ -1,5 +1,6 @@ using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; +using Microsoft.Toolkit.Uwp.UI.Controls; using System; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.Resources; @@ -73,6 +74,7 @@ namespace FoxTube.Controls } catch (Exception e) { + (Parent as AdaptiveGridView)?.Items.Remove(this); Visibility = Visibility.Collapsed; Microsoft.AppCenter.Analytics.Analytics.TrackEvent("VideoCard loading failed", new System.Collections.Generic.Dictionary() { diff --git a/FoxTube/Controls/Player/PlayerControls.cs b/FoxTube/Controls/Player/PlayerControls.cs index ae6e295..c7d8894 100644 --- a/FoxTube/Controls/Player/PlayerControls.cs +++ b/FoxTube/Controls/Player/PlayerControls.cs @@ -79,6 +79,7 @@ namespace FoxTube Slider volume; Slider seek; + Slider playbackSpeed; ProgressBar seekIndicator; ComboBox captions; @@ -130,6 +131,7 @@ namespace FoxTube next.Click += Next_Click; volume.ValueChanged += Volume_ValueChanged; + playbackSpeed.ValueChanged += PlaybackSpeed_ValueChanged; live.Click += Live_Click; captionsSwitch.Toggled += CaptionsSwitch_Toggled; @@ -142,7 +144,7 @@ namespace FoxTube Rect view = new Rect(0, 0, centerTrigger.ActualWidth, centerTrigger.ActualHeight); Point p = e.GetPosition(centerTrigger); - if (!view.Contains(p) || e.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Mouse) + if (!view.Contains(p) || e.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Mouse || State != PlayerDisplayState.Normal) return; if (Player.CurrentState == Windows.UI.Xaml.Media.MediaElementState.Playing) @@ -158,6 +160,11 @@ namespace FoxTube base.OnApplyTemplate(); } + private void PlaybackSpeed_ValueChanged(object sender, RangeBaseValueChangedEventArgs e) + { + Player.PlaybackRate = playbackSpeed.Value; + } + void AssignControls() { minimize = GetTemplateChild("MinimizeButton") as Button; @@ -183,6 +190,7 @@ namespace FoxTube volume = GetTemplateChild("VolumeSlider") as Slider; seek = GetTemplateChild("ProgressSlider") as Slider; + playbackSpeed = GetTemplateChild("PlaybackSpeedSlider") as Slider; seekIndicator = GetTemplateChild("SeekIndicator") as ProgressBar; captions = GetTemplateChild("CaptionsSelector") as ComboBox; diff --git a/FoxTube/Controls/PlaylistCard.xaml.cs b/FoxTube/Controls/PlaylistCard.xaml.cs index 8ed514f..bdc5ecd 100644 --- a/FoxTube/Controls/PlaylistCard.xaml.cs +++ b/FoxTube/Controls/PlaylistCard.xaml.cs @@ -1,6 +1,7 @@ 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 Windows.ApplicationModel.DataTransfer; @@ -52,6 +53,7 @@ namespace FoxTube.Controls } catch (Exception e) { + (Parent as AdaptiveGridView)?.Items.Remove(this); Visibility = Visibility.Collapsed; Analytics.TrackEvent("PlaylistCard loading failed", new Dictionary() { diff --git a/FoxTube/Controls/VideoCard.xaml.cs b/FoxTube/Controls/VideoCard.xaml.cs index 1c22402..5373e0b 100644 --- a/FoxTube/Controls/VideoCard.xaml.cs +++ b/FoxTube/Controls/VideoCard.xaml.cs @@ -15,6 +15,7 @@ using YoutubeExplode.Models.MediaStreams; using Windows.Foundation; using FoxTube.Pages; using Windows.Networking.Connectivity; +using Microsoft.Toolkit.Uwp.UI.Controls; namespace FoxTube.Controls { @@ -98,8 +99,12 @@ namespace FoxTube.Controls } LoadAddTo(); - 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 }; + 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 }; + } + catch { } if (SecretsVault.History.Contains(item.Id)) watched.Visibility = Visibility.Visible; @@ -114,6 +119,7 @@ namespace FoxTube.Controls } catch (Exception e) { + (Parent as AdaptiveGridView)?.Items.Remove(this); Visibility = Visibility.Collapsed; Analytics.TrackEvent("VideoCard loading failed", new Dictionary() { diff --git a/FoxTube/FoxTube.csproj b/FoxTube/FoxTube.csproj index cadd022..bfc9bbe 100644 --- a/FoxTube/FoxTube.csproj +++ b/FoxTube/FoxTube.csproj @@ -103,6 +103,7 @@ App.xaml + @@ -183,6 +184,9 @@ PlaylistPage.xaml + + Translate.xaml + Subscriptions.xaml @@ -270,6 +274,7 @@ + @@ -279,6 +284,8 @@ + + @@ -389,6 +396,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -458,6 +469,9 @@ 4.3.2 + + 4.3.0 + 4.7.0 diff --git a/FoxTube/Pages/ChannelPage.xaml b/FoxTube/Pages/ChannelPage.xaml index 8a42d62..7e8879b 100644 --- a/FoxTube/Pages/ChannelPage.xaml +++ b/FoxTube/Pages/ChannelPage.xaml @@ -8,6 +8,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:pages="using:FoxTube.Pages" xmlns:controls="using:FoxTube.Controls" + xmlns:classes="using:FoxTube.Classes" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> @@ -95,10 +96,10 @@ - + - + diff --git a/FoxTube/Pages/History.xaml b/FoxTube/Pages/History.xaml index 2ac64d3..07fc324 100644 --- a/FoxTube/Pages/History.xaml +++ b/FoxTube/Pages/History.xaml @@ -7,6 +7,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:foxtube="using:FoxTube" xmlns:controls="using:FoxTube.Controls" + xmlns:classes="using:FoxTube.Classes" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> @@ -56,9 +57,9 @@ - + - + diff --git a/FoxTube/Pages/Home.xaml b/FoxTube/Pages/Home.xaml index 4a14c14..a9ed7f8 100644 --- a/FoxTube/Pages/Home.xaml +++ b/FoxTube/Pages/Home.xaml @@ -7,6 +7,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:pages="using:FoxTube.Pages" xmlns:controls="using:FoxTube.Controls" + xmlns:classes="using:FoxTube.Classes" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> @@ -54,8 +55,8 @@ - + - + diff --git a/FoxTube/Pages/MainPage.xaml b/FoxTube/Pages/MainPage.xaml index fff3ece..02448ca 100644 --- a/FoxTube/Pages/MainPage.xaml +++ b/FoxTube/Pages/MainPage.xaml @@ -8,7 +8,7 @@ xmlns:ui="using:Microsoft.UI.Xaml.Controls" xmlns:controls="using:FoxTube.Controls"> - + diff --git a/FoxTube/Pages/MainPage.xaml.cs b/FoxTube/Pages/MainPage.xaml.cs index 9f73150..3d28cef 100644 --- a/FoxTube/Pages/MainPage.xaml.cs +++ b/FoxTube/Pages/MainPage.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections.Generic; using Windows.UI; using Windows.UI.ViewManagement; @@ -7,7 +8,6 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Media.Imaging; -using System.Xml; using Google.Apis.YouTube.v3.Data; using Windows.ApplicationModel.Core; using Windows.System; @@ -18,6 +18,10 @@ using Microsoft.Services.Store.Engagement; using Windows.UI.Xaml.Shapes; using Windows.UI.Xaml.Media; using FoxTube.Controls; +using Microsoft.AppCenter.Analytics; +using Windows.ApplicationModel.DataTransfer; +using Windows.UI.Notifications; +using System.Xml; namespace FoxTube { @@ -77,9 +81,77 @@ namespace FoxTube if(StoreServicesFeedbackLauncher.IsSupported()) feedback.Visibility = Visibility.Visible; + if(SettingsStorage.ProcessClipboard) + { + Clipboard.ContentChanged += ParseClipboard; + ParseClipboard(this); + } + PromptFeedback(); } + async void ParseClipboard(object sender = null, object e = null) + { + if (sender == null && Window.Current.CoreWindow.ActivationMode != Windows.UI.Core.CoreWindowActivationMode.Deactivated) + return; + try + { + string link = await Clipboard.GetContent().GetTextAsync(); + + if (!link.Contains("youtube") && !link.Contains("youtu.be")) + return; + + string id; + string type; + string name; + + if (YoutubeExplode.YoutubeClient.TryParseChannelId(link, out id)) + { + type = "channel"; + name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title; + goto Complete; + } + else if (YoutubeExplode.YoutubeClient.TryParsePlaylistId(link, out id)) + { + type = "playlist"; + name = (await new YoutubeExplode.YoutubeClient().GetPlaylistAsync(id)).Title; + goto Complete; + } + 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; + goto Complete; + } + else if (YoutubeExplode.YoutubeClient.TryParseVideoId(link, out id)) + { + type = "video"; + name = (await new YoutubeExplode.YoutubeClient().GetVideoAsync(id)).Title; + goto Complete; + } + return; + + Complete: + 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)); + return; + } + catch { } + } + async void PromptFeedback() { if (SettingsStorage.Uptime.TotalHours >= 12 && SettingsStorage.PromptFeedback) @@ -157,9 +229,9 @@ namespace FoxTube break; case "remove": - if (nav.MenuItems.Find(i => ((i as Microsoft.UI.Xaml.Controls.NavigationViewItem).Content as StackPanel).Tag.ToString() == (string)args[1]) is Microsoft.UI.Xaml.Controls.NavigationViewItem item) + if(nav.MenuItems.Contains((Subscription)args[1])) { - nav.MenuItems.Remove(item); + nav.MenuItems.Remove(args[1]); if (SecretsVault.Subscriptions.Count >= 10) nav.MenuItems.Add(SecretsVault.Subscriptions[9]); } @@ -266,6 +338,7 @@ namespace FoxTube private void SignIn_Click(object sender, RoutedEventArgs e) { SecretsVault.Authorize(); + Analytics.TrackEvent("Initialized authorization sequence"); } private void Logout_Click(object sender, RoutedEventArgs e) @@ -338,7 +411,7 @@ namespace FoxTube public void MinimizeVideo() { videoPlaceholder.Width = 432; - videoPlaceholder.Height = 243; + videoPlaceholder.Height = 432 * (videoPlaceholder.Frame.Content as VideoPage).Player.ActualHeight / (videoPlaceholder.Frame.Content as VideoPage).Player.ActualWidth; videoPlaceholder.VerticalAlignment = VerticalAlignment.Bottom; videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Right; videoPlaceholder.Margin = new Thickness(0, 0, 25, 50); diff --git a/FoxTube/Pages/PlaylistPage.xaml b/FoxTube/Pages/PlaylistPage.xaml index 7de6904..158b93f 100644 --- a/FoxTube/Pages/PlaylistPage.xaml +++ b/FoxTube/Pages/PlaylistPage.xaml @@ -6,6 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:FoxTube.Controls" + xmlns:classes="using:FoxTube.Classes" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> @@ -76,7 +77,7 @@ - + @@ -87,6 +88,6 @@ - + diff --git a/FoxTube/Pages/Search.xaml b/FoxTube/Pages/Search.xaml index 76ad1e6..110774f 100644 --- a/FoxTube/Pages/Search.xaml +++ b/FoxTube/Pages/Search.xaml @@ -7,6 +7,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:FoxTube.Controls" + xmlns:classes="using:FoxTube.Classes" mc:Ignorable="d"> @@ -72,9 +73,9 @@ - + - + diff --git a/FoxTube/Pages/Settings.xaml b/FoxTube/Pages/Settings.xaml index 92b0b35..7aa33f4 100644 --- a/FoxTube/Pages/Settings.xaml +++ b/FoxTube/Pages/Settings.xaml @@ -19,6 +19,11 @@ + + + + + diff --git a/FoxTube/Pages/Settings.xaml.cs b/FoxTube/Pages/Settings.xaml.cs index 8a4d4c3..2c3c295 100644 --- a/FoxTube/Pages/Settings.xaml.cs +++ b/FoxTube/Pages/Settings.xaml.cs @@ -29,6 +29,9 @@ namespace FoxTube case "about": pivot.SelectedItem = aboutTab; break; + case "translate": + pivot.SelectedItem = translateTab; + break; default: inboxId = (string)e.Parameter; pivot.SelectedItem = inboxTab; diff --git a/FoxTube/Pages/SettingsPages/General.xaml b/FoxTube/Pages/SettingsPages/General.xaml index 0123479..87188e6 100644 --- a/FoxTube/Pages/SettingsPages/General.xaml +++ b/FoxTube/Pages/SettingsPages/General.xaml @@ -26,6 +26,10 @@ + + + + diff --git a/FoxTube/Pages/SettingsPages/General.xaml.cs b/FoxTube/Pages/SettingsPages/General.xaml.cs index a4452e3..50db48f 100644 --- a/FoxTube/Pages/SettingsPages/General.xaml.cs +++ b/FoxTube/Pages/SettingsPages/General.xaml.cs @@ -27,6 +27,9 @@ namespace FoxTube.Pages.SettingsPages quality.Items.Add(new ComboBoxItem() { Tag = i.GetVideoQualityLabel(), Content = i.GetVideoQualityLabel() }); quality.SelectedItem = quality.Items.ToList().Find(i => ((ComboBoxItem)i).Tag.ToString() == SettingsStorage.VideoQuality); + clipboardProcessing.IsOn = SettingsStorage.ProcessClipboard; + minimizedCB.IsOn = SettingsStorage.AppBarClosedMode == AppBarClosedDisplayMode.Minimal; + mobileWarning.IsOn = SettingsStorage.CheckConnection; autoplay.IsOn = SettingsStorage.Autoplay; @@ -152,5 +155,15 @@ namespace FoxTube.Pages.SettingsPages { CoreApplication.Exit(); } + + private void MinimizedCB_Toggled(object sender, RoutedEventArgs e) + { + SettingsStorage.AppBarClosedMode = minimizedCB.IsOn ? AppBarClosedDisplayMode.Minimal : AppBarClosedDisplayMode.Compact; + } + + private void ClipboardProcessing_Toggled(object sender, RoutedEventArgs e) + { + SettingsStorage.ProcessClipboard = clipboardProcessing.IsOn; + } } } diff --git a/FoxTube/Pages/SettingsPages/Inbox.xaml.cs b/FoxTube/Pages/SettingsPages/Inbox.xaml.cs index 73955a1..de60f58 100644 --- a/FoxTube/Pages/SettingsPages/Inbox.xaml.cs +++ b/FoxTube/Pages/SettingsPages/Inbox.xaml.cs @@ -35,14 +35,14 @@ namespace FoxTube.Pages.SettingsPages items.Add(new InboxItem( e.GetAttribute("version"), e["content"][SettingsStorage.Language].InnerText, - DateTime.Parse(e.GetAttribute("time")))); + DateTime.Parse(e.GetAttribute("time"), System.Globalization.CultureInfo.GetCultureInfo("en-US").DateTimeFormat))); doc.Load("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml"); foreach (XmlElement e in doc["posts"].ChildNodes) items.Add(new InboxItem( e["header"][SettingsStorage.Language].InnerText, e["content"][SettingsStorage.Language].InnerText, - DateTime.Parse(e.GetAttribute("time")), + DateTime.Parse(e.GetAttribute("time"), System.Globalization.CultureInfo.GetCultureInfo("en-US").DateTimeFormat), e["id"].InnerText, e["contentHeader"].InnerText)); } diff --git a/FoxTube/Pages/SettingsPages/Translate.xaml b/FoxTube/Pages/SettingsPages/Translate.xaml new file mode 100644 index 0000000..da5eb7a --- /dev/null +++ b/FoxTube/Pages/SettingsPages/Translate.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FoxTube/Pages/VideoPage.xaml.cs b/FoxTube/Pages/VideoPage.xaml.cs index fed868b..26765dd 100644 --- a/FoxTube/Pages/VideoPage.xaml.cs +++ b/FoxTube/Pages/VideoPage.xaml.cs @@ -95,6 +95,12 @@ namespace FoxTube.Pages Initialize(e.Parameter as object[]); } + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + base.OnNavigatedFrom(e); + Player.Player.Stop(); + } + public async void Initialize(object[] ids) { try @@ -391,6 +397,8 @@ namespace FoxTube.Pages foreach (SearchResult video in response.Items) relatedVideos.Add(new VideoCard(video.Id.VideoId)); + + relatedVideos.Children.Insert(1, new Controls.Adverts.CardAdvert()); } private void Player_Minimize(object sender, params object[] e) diff --git a/FoxTube/Strings/en-US/General.resw b/FoxTube/Strings/en-US/General.resw index 5e381f6..e064f97 100644 --- a/FoxTube/Strings/en-US/General.resw +++ b/FoxTube/Strings/en-US/General.resw @@ -210,4 +210,19 @@ Auto + + Interface + + + Use compact command bar + + + Use compact command bar + + + Process YouTube links from your clipboard + + + Process YouTube links from your clipboard + \ No newline at end of file diff --git a/FoxTube/Strings/en-US/Main.resw b/FoxTube/Strings/en-US/Main.resw index 61f9bae..269ff76 100644 --- a/FoxTube/Strings/en-US/Main.resw +++ b/FoxTube/Strings/en-US/Main.resw @@ -126,6 +126,12 @@ Channel + + We've found this on your clipboard + + + Open in FoxTube + Close the app diff --git a/FoxTube/Strings/en-US/Translate.resw b/FoxTube/Strings/en-US/Translate.resw new file mode 100644 index 0000000..4fa42af --- /dev/null +++ b/FoxTube/Strings/en-US/Translate.resw @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Thanks for your contribution into my application. It's very important to me. It will take some time to process your package, correct mistakes and prepare it for integration. I will send you an e-mail when it's done. +Cheers, +XFox + +This is an automatic message. Please, don't respond it. Nah, ok, only if you want to :) + + + Package certification result + + + Choose another file + + + You can help us make this app even better by contributing to its development by translating this app + + + Export to PC (.zip) + + + Export + + + Failed + + + Failed to read file. File's structure is corrupted + + + We were unable to send your submission due to connection problems or internal server error. Please, try again later. + + + Failed to send your package + + + File + + + It's quite simple: + + + 1. Choose language you you want to translate on + + + Choose language... + + + 2. Save language pack file to your PC + + + 3. Open archive's files with any text editor you want (Notepad, Wordpad, Notepad++, VS Code, etc.) + + + 4. Edit file by translating nececcary words and sentences + + + 5. Upload final package to our servers + + + Help us translate this app + + + It takes about 2-3 weeks to process new language pack and include it to the next update +Thank you for your help 😉 + +Cheers, + + + In progress... + + + Language pack scheme + + + View log + + + not found + + + Passed + + + FoxTube language pack contribution + + + Upload + + + Your language pack has been sent! + + + Thank you! It's very imortant for us. You help us making the app better + + + Choose file to upload + + + FoxTube auto reply + + \ No newline at end of file diff --git a/FoxTube/Strings/en-US/VideoPage.resw b/FoxTube/Strings/en-US/VideoPage.resw index 732f809..1b3e246 100644 --- a/FoxTube/Strings/en-US/VideoPage.resw +++ b/FoxTube/Strings/en-US/VideoPage.resw @@ -303,4 +303,7 @@ Auto + + Playback speed + \ No newline at end of file diff --git a/FoxTube/Strings/ru-RU/General.resw b/FoxTube/Strings/ru-RU/General.resw index 4f0b6e0..b17047d 100644 --- a/FoxTube/Strings/ru-RU/General.resw +++ b/FoxTube/Strings/ru-RU/General.resw @@ -210,4 +210,19 @@ Авто + + Интерфейс + + + Использовать компактную панель команд + + + Использовать компактную панель команд + + + Обрабатывать ссылки YouTube из буфера обмена + + + Обрабатывать ссылки YouTube из буфера обмена + \ No newline at end of file diff --git a/FoxTube/Strings/ru-RU/Main.resw b/FoxTube/Strings/ru-RU/Main.resw index 56a9cd4..587c34a 100644 --- a/FoxTube/Strings/ru-RU/Main.resw +++ b/FoxTube/Strings/ru-RU/Main.resw @@ -126,6 +126,9 @@ Канал + + Открыть в FoxTube + Закрыть приложение diff --git a/FoxTube/Strings/ru-RU/Translate.resw b/FoxTube/Strings/ru-RU/Translate.resw new file mode 100644 index 0000000..1cae7e2 --- /dev/null +++ b/FoxTube/Strings/ru-RU/Translate.resw @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Спасибо за ваш вклад в развитие моего приложения. Это очень важно для меня. Обработка пакета, исправление технических ошибок и подготовка пакета к интеграции займет некоторое время. Я пришлю вам письмо, когда локализация будет готова. +Всего лучшего, +XFox + +Это автоматический ответ. Пожалуйста, не отвечайте на это письмо. Ладно, только если очень хочется) + + + Результат сертификации пакета + + + Выбрать другой файл + + + Вы можете помочь нам сделать приложение еще лучше, помогая перевести его на новые языки + + + Экспортировать на ПК (.zip) + + + Сохранить + + + Провал + + + Ошибка чтения файла. Структура данных повреждена + + + Не удалось отправить ваш пакет из-за проблем с соединением или внутренней ошибкой сервера. Пожалуйста, попробуйте позже + + + Ошибка при отправке пакета + + + Файл + + + Это достаточно просто: + + + 1. Выберите язык на который вы хотите перевести + + + Выберите язык... + + + 2. Сохраните языковой пакет на свой ПК + + + 3. Откройте файлы в архиве любым текстовым редактором на ваш выбор (Блокнот, Wordpad, Notepad++, VS Code, и т.д.) + + + 4. Переведите необходимые слова и предложения + + + 5. Загрузите финальный пакет на наши сервера + + + Помогите нам перевести это приложение + + + Обработка пакета займет 2-3 недели. Новая локализация будет доступна в ближайших обновлениях +Спасибо за вашу помощь 😉 + +С наилучшими пожеланиями, + + + В процессе... + + + Схема языкового пакета + + + Посмотреть лог + + + не найден + + + Пройдена + + + Помощь в локализации приложения FoxTube + + + Загрузить + + + Ваш языковой пакт отправлен! + + + Спасибо! Это очень важно для нас. Вы помогаете сделать приложение лучше! + + + Выберите файл для отправки + + + Автоответчик FoxTube + + \ No newline at end of file diff --git a/FoxTube/Strings/ru-RU/VideoPage.resw b/FoxTube/Strings/ru-RU/VideoPage.resw index 1392183..76c703f 100644 --- a/FoxTube/Strings/ru-RU/VideoPage.resw +++ b/FoxTube/Strings/ru-RU/VideoPage.resw @@ -303,4 +303,7 @@ Авто + + Скорость видео + \ No newline at end of file diff --git a/FoxTube/Themes/Generic.xaml b/FoxTube/Themes/Generic.xaml index 8435d59..413aabc 100644 --- a/FoxTube/Themes/Generic.xaml +++ b/FoxTube/Themes/Generic.xaml @@ -342,10 +342,13 @@ - From 8f863edfad56b063fe8dc224afa627213f69807a Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Thu, 20 Jun 2019 17:33:54 +0300 Subject: [PATCH 02/12] Updated version number --- FoxTube/FoxTube.csproj | 4 ++-- FoxTube/Package.appxmanifest | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/FoxTube/FoxTube.csproj b/FoxTube/FoxTube.csproj index bfc9bbe..f13388e 100644 --- a/FoxTube/FoxTube.csproj +++ b/FoxTube/FoxTube.csproj @@ -20,8 +20,8 @@ FoxTube_StoreKey.pfx 50B93E6A246058D555BA65CD203D7A02064A7409 False - False - E:\XFox\Documents\FoxTube builds\1.0\ + True + E:\XFox\Documents\FoxTube builds\1.1\ Always x86|x64|arm 1 diff --git a/FoxTube/Package.appxmanifest b/FoxTube/Package.appxmanifest index 65f608e..885d213 100644 --- a/FoxTube/Package.appxmanifest +++ b/FoxTube/Package.appxmanifest @@ -1,6 +1,6 @@  - + FoxTube From 2140ec0d5f76cd80a9d7f2c4b0db300602724f59 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Sun, 23 Jun 2019 12:26:03 +0300 Subject: [PATCH 03/12] - Fixed some cases when playlist cards aren't displayed - Fixed some cases when the app crashes - Fixed app crashes on trying to navigate to not existing channel/playlist/video --- FoxTube/Assets/Data/Patchnotes.xml | 14 ++++++++++++++ FoxTube/Classes/DownloadAgent.cs | 1 + FoxTube/Controls/PlaylistCard.xaml.cs | 9 +++++++-- FoxTube/Controls/VideoCard.xaml.cs | 2 ++ FoxTube/Pages/ChannelPage.xaml.cs | 6 ++++++ FoxTube/Pages/PlaylistPage.xaml.cs | 15 ++++++++++++--- FoxTube/Pages/VideoPage.xaml.cs | 6 ++++++ 7 files changed, 48 insertions(+), 5 deletions(-) diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml index d898cb7..e74565d 100644 --- a/FoxTube/Assets/Data/Patchnotes.xml +++ b/FoxTube/Assets/Data/Patchnotes.xml @@ -1,5 +1,19 @@  + + + ### What's new: +- Fixed some cases when playlist cards aren't displayed +- Fixed some cases when the app crashes +- Fixed app crashes on trying to navigate to not existing channel/playlist/video + + ### Что нового: +- Исправлены некоторые случаи при которых карточки плейлистов не отображались +- Исправлены некоторые случай при которых приложение вылетало +- Исправлены вылеты приложения при попытке перейти на несуществующий канал/плейлист/видео + + + ### What's new: diff --git a/FoxTube/Classes/DownloadAgent.cs b/FoxTube/Classes/DownloadAgent.cs index 8bf1d48..8c7fd4d 100644 --- a/FoxTube/Classes/DownloadAgent.cs +++ b/FoxTube/Classes/DownloadAgent.cs @@ -28,6 +28,7 @@ namespace FoxTube } catch (Exception e) { + settings.Values["downloads"] = JsonConvert.SerializeObject(new List()); Analytics.TrackEvent("Failed to load downloads history", new Dictionary { { "Exception", e.GetType().ToString() }, diff --git a/FoxTube/Controls/PlaylistCard.xaml.cs b/FoxTube/Controls/PlaylistCard.xaml.cs index bdc5ecd..ce97d9b 100644 --- a/FoxTube/Controls/PlaylistCard.xaml.cs +++ b/FoxTube/Controls/PlaylistCard.xaml.cs @@ -9,6 +9,7 @@ using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Imaging; +using YoutubeExplode; namespace FoxTube.Controls { @@ -46,8 +47,12 @@ namespace FoxTube.Controls ChannelsResource.ListRequest r = SecretsVault.Service.Channels.List("snippet"); r.Id = item.Snippet.ChannelId; - thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); - avatar.ProfilePicture = new BitmapImage(new Uri((await r.ExecuteAsync()).Items[0].Snippet.Thumbnails.Medium.Url)) { DecodePixelWidth = 46, DecodePixelHeight = 46 }; + 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 }; + } + catch { } show.Begin(); } diff --git a/FoxTube/Controls/VideoCard.xaml.cs b/FoxTube/Controls/VideoCard.xaml.cs index 5373e0b..a47ef55 100644 --- a/FoxTube/Controls/VideoCard.xaml.cs +++ b/FoxTube/Controls/VideoCard.xaml.cs @@ -121,6 +121,8 @@ namespace FoxTube.Controls { (Parent as AdaptiveGridView)?.Items.Remove(this); Visibility = Visibility.Collapsed; + if (item == null) + return; Analytics.TrackEvent("VideoCard loading failed", new Dictionary() { { "Exception", e.GetType().ToString() }, diff --git a/FoxTube/Pages/ChannelPage.xaml.cs b/FoxTube/Pages/ChannelPage.xaml.cs index aa69916..00bebb4 100644 --- a/FoxTube/Pages/ChannelPage.xaml.cs +++ b/FoxTube/Pages/ChannelPage.xaml.cs @@ -113,6 +113,12 @@ namespace FoxTube.Pages } catch (Exception e) { + if(item == null) + { + Methods.MainPage.PageContent.LoadingPage.Error("ChannelNotFound", "Such channel doesn't exist"); + return; + } + Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message); Analytics.TrackEvent("Channel loading error", new Dictionary() { diff --git a/FoxTube/Pages/PlaylistPage.xaml.cs b/FoxTube/Pages/PlaylistPage.xaml.cs index bfb5083..fb15b41 100644 --- a/FoxTube/Pages/PlaylistPage.xaml.cs +++ b/FoxTube/Pages/PlaylistPage.xaml.cs @@ -63,8 +63,12 @@ namespace FoxTube.Pages channelName.Text = Methods.GuardFromNull(item.Snippet.ChannelTitle); - avatar.ProfilePicture = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; - thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); + try + { + thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); + avatar.ProfilePicture = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; + } + catch { } request = SecretsVault.Service.PlaylistItems.List("contentDetails"); request.PlaylistId = id; @@ -126,6 +130,11 @@ namespace FoxTube.Pages } catch (Exception e) { + if (item == null) + { + Methods.MainPage.PageContent.LoadingPage.Error("PlaylistNotFound", "Such playlist doesn't exist"); + return; + } Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message); Analytics.TrackEvent("WL playlist loading error", new Dictionary() { @@ -148,7 +157,7 @@ namespace FoxTube.Pages private void refresh_Click(object sender, RoutedEventArgs e) { - Methods.MainPage.VideoContent.Refresh(); + Methods.MainPage.PageContent.Refresh(); } private void share_Click(object sender, RoutedEventArgs e) diff --git a/FoxTube/Pages/VideoPage.xaml.cs b/FoxTube/Pages/VideoPage.xaml.cs index 26765dd..c0bb70c 100644 --- a/FoxTube/Pages/VideoPage.xaml.cs +++ b/FoxTube/Pages/VideoPage.xaml.cs @@ -136,6 +136,12 @@ namespace FoxTube.Pages } catch (Exception e) { + if (item == null) + { + Methods.MainPage.PageContent.LoadingPage.Error("VideoNotFound", "Such video doesn't exist"); + return; + } + Methods.MainPage.VideoContent.LoadingPage.Error(e.GetType().ToString(), e.Message); Analytics.TrackEvent("Video loading error", new Dictionary() { From bb45734e2dc053784420ac2f0e4993a7ef77b7d2 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Sun, 23 Jun 2019 14:31:24 +0300 Subject: [PATCH 04/12] Added ability to report videos and comments --- FoxTube/Assets/Data/Patchnotes.xml | 4 + FoxTube/Controls/CommentCard.xaml | 14 ++- FoxTube/Controls/CommentCard.xaml.cs | 14 +++ FoxTube/Controls/ReportVideo.xaml | 22 ++++ FoxTube/Controls/ReportVideo.xaml.cs | 69 +++++++++++ FoxTube/FoxTube.csproj | 9 ++ FoxTube/Pages/VideoPage.xaml | 4 + FoxTube/Pages/VideoPage.xaml.cs | 6 + FoxTube/Strings/en-US/CommentsPage.resw | 3 + FoxTube/Strings/en-US/Report.resw | 150 ++++++++++++++++++++++++ FoxTube/Strings/en-US/VideoPage.resw | 3 + FoxTube/Strings/ru-RU/CommentsPage.resw | 3 + FoxTube/Strings/ru-RU/Report.resw | 150 ++++++++++++++++++++++++ FoxTube/Strings/ru-RU/VideoPage.resw | 3 + 14 files changed, 451 insertions(+), 3 deletions(-) create mode 100644 FoxTube/Controls/ReportVideo.xaml create mode 100644 FoxTube/Controls/ReportVideo.xaml.cs create mode 100644 FoxTube/Strings/en-US/Report.resw create mode 100644 FoxTube/Strings/ru-RU/Report.resw diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml index e74565d..9b9a1e3 100644 --- a/FoxTube/Assets/Data/Patchnotes.xml +++ b/FoxTube/Assets/Data/Patchnotes.xml @@ -6,11 +6,15 @@ - Fixed some cases when playlist cards aren't displayed - Fixed some cases when the app crashes - Fixed app crashes on trying to navigate to not existing channel/playlist/video +- You can now report comments as spam +- You can now report videos ### Что нового: - Исправлены некоторые случаи при которых карточки плейлистов не отображались - Исправлены некоторые случай при которых приложение вылетало - Исправлены вылеты приложения при попытке перейти на несуществующий канал/плейлист/видео +- Теперь вы можете помечать комментарии как спам +- Теперь вы можете отправлять жалобы на видео diff --git a/FoxTube/Controls/CommentCard.xaml b/FoxTube/Controls/CommentCard.xaml index 13e4747..9a99864 100644 --- a/FoxTube/Controls/CommentCard.xaml +++ b/FoxTube/Controls/CommentCard.xaml @@ -36,7 +36,7 @@ FontFamily="Segoe MDL2 Assets" Text="" FontSize="20"/> - - - + + diff --git a/FoxTube/Controls/CommentCard.xaml.cs b/FoxTube/Controls/CommentCard.xaml.cs index 8d98e5a..7ffa2b0 100644 --- a/FoxTube/Controls/CommentCard.xaml.cs +++ b/FoxTube/Controls/CommentCard.xaml.cs @@ -39,6 +39,7 @@ namespace FoxTube.Controls thread = comment; replyBtn.Visibility = comment.Snippet.CanReply.Value && SecretsVault.IsAuthorized ? Visibility.Visible : Visibility.Collapsed; + spam.Visibility = SecretsVault.IsAuthorized ? Visibility.Visible : Visibility.Collapsed; if (!comment.Snippet.TotalReplyCount.HasValue || comment.Snippet.TotalReplyCount.Value == 0) showReplies.Visibility = Visibility.Collapsed; else @@ -101,6 +102,7 @@ namespace FoxTube.Controls replyBtn.Visibility = Visibility.Collapsed; showReplies.Visibility = Visibility.Collapsed; + spam.Visibility = SecretsVault.IsAuthorized ? Visibility.Visible : Visibility.Collapsed; if (comment.Snippet.CanRate == false) { @@ -327,5 +329,17 @@ namespace FoxTube.Controls await dialog.ShowAsync(); } + + private async void MarkAsSpam_Click(object sender, RoutedEventArgs e) + { + ResourceLoader resources = ResourceLoader.GetForCurrentView("Report"); + try { await SecretsVault.Service.Comments.MarkAsSpam(item.Id).ExecuteAsync(); } + catch + { + await new MessageDialog(resources.GetString("/Report/err")).ShowAsync(); + return; + } + await new MessageDialog(resources.GetString("/Report/submittedHeader"), resources.GetString("/Report/submittedBody")).ShowAsync(); + } } } diff --git a/FoxTube/Controls/ReportVideo.xaml b/FoxTube/Controls/ReportVideo.xaml new file mode 100644 index 0000000..e5f2dd2 --- /dev/null +++ b/FoxTube/Controls/ReportVideo.xaml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/FoxTube/Controls/ReportVideo.xaml.cs b/FoxTube/Controls/ReportVideo.xaml.cs new file mode 100644 index 0000000..7b1272f --- /dev/null +++ b/FoxTube/Controls/ReportVideo.xaml.cs @@ -0,0 +1,69 @@ +using Windows.UI.Xaml.Controls; +using Google.Apis.YouTube.v3.Data; +using Google.Apis.YouTube.v3; +using Windows.UI.Xaml; +using System; +using Windows.UI.Popups; +using Windows.ApplicationModel.Resources; + +namespace FoxTube.Controls +{ + public sealed partial class ReportVideo : ContentDialog + { + string videoId; + public ReportVideo(string id) + { + InitializeComponent(); + Initialize(); + videoId = id; + } + + async void Initialize() + { + VideoAbuseReportReasonsResource.ListRequest req = SecretsVault.Service.VideoAbuseReportReasons.List("id,snippet"); + req.Hl = SettingsStorage.RelevanceLanguage; + VideoAbuseReportReasonListResponse reasons = await req.ExecuteAsync(); + + foreach (VideoAbuseReportReason i in reasons.Items) + primaryReason.Items.Add(new ComboBoxItem + { + Tag = i, + Content = i.Snippet.Label + }); + } + + private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VideoAbuseReport report = new VideoAbuseReport + { + Comments = Methods.GuardFromNull(comment.Text), + VideoId = videoId, + ReasonId = ((primaryReason.SelectedItem as ComboBoxItem).Tag as VideoAbuseReportReason).Id, + SecondaryReasonId = secondaryReason.SelectedItem == null ? null : (secondaryReason.SelectedItem as ComboBoxItem).Tag as string + }; + + ResourceLoader resources = ResourceLoader.GetForCurrentView("Report"); + try { await SecretsVault.Service.Videos.ReportAbuse(report).ExecuteAsync(); } + catch + { + await new MessageDialog(resources.GetString("/Report/err")).ShowAsync(); + return; + } + await new MessageDialog(resources.GetString("/Report/submittedHeader"), resources.GetString("/Report/submittedBody")).ShowAsync(); + } + + private void PrimaryReason_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + IsPrimaryButtonEnabled = true; + secondaryReason.Items.Clear(); + foreach (VideoAbuseReportSecondaryReason i in ((primaryReason.SelectedItem as ComboBoxItem).Tag as VideoAbuseReportReason).Snippet.SecondaryReasons) + secondaryReason.Items.Add(new ComboBoxItem + { + Tag = i.Id, + Content = i.Label + }); + + secondaryReason.Visibility = secondaryReason.Items.Count == 0 ? Visibility.Collapsed : Visibility.Visible; + } + } +} diff --git a/FoxTube/FoxTube.csproj b/FoxTube/FoxTube.csproj index f13388e..07e15a2 100644 --- a/FoxTube/FoxTube.csproj +++ b/FoxTube/FoxTube.csproj @@ -145,6 +145,9 @@ PlaylistCard.xaml + + ReportVideo.xaml + ShowMore.xaml @@ -284,6 +287,8 @@ + + @@ -336,6 +341,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/FoxTube/Pages/VideoPage.xaml b/FoxTube/Pages/VideoPage.xaml index 4bc704c..c889205 100644 --- a/FoxTube/Pages/VideoPage.xaml +++ b/FoxTube/Pages/VideoPage.xaml @@ -128,6 +128,10 @@ + + + + diff --git a/FoxTube/Pages/VideoPage.xaml.cs b/FoxTube/Pages/VideoPage.xaml.cs index c0bb70c..b8f1e8e 100644 --- a/FoxTube/Pages/VideoPage.xaml.cs +++ b/FoxTube/Pages/VideoPage.xaml.cs @@ -786,5 +786,11 @@ namespace FoxTube.Pages { Player.Player.Position = history.LeftOn; } + + private async void Report_Click(object sender, RoutedEventArgs e) + { + await new ReportVideo(item.Id).ShowAsync(); + + } } } diff --git a/FoxTube/Strings/en-US/CommentsPage.resw b/FoxTube/Strings/en-US/CommentsPage.resw index c60c8cd..be9ffda 100644 --- a/FoxTube/Strings/en-US/CommentsPage.resw +++ b/FoxTube/Strings/en-US/CommentsPage.resw @@ -174,6 +174,9 @@ Sort by: + + Report as spam + Add a public comment diff --git a/FoxTube/Strings/en-US/Report.resw b/FoxTube/Strings/en-US/Report.resw new file mode 100644 index 0000000..4775246 --- /dev/null +++ b/FoxTube/Strings/en-US/Report.resw @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Additional information (optional) + + + We are unable to send your report right now. Please, try again later + + + Reason + + + Select a reason... + + + Cancel + + + Report + + + Report a video + + + Select a category... + + + Thanks for making YouTube community better + + + Your report has been sent + + \ No newline at end of file diff --git a/FoxTube/Strings/en-US/VideoPage.resw b/FoxTube/Strings/en-US/VideoPage.resw index 1b3e246..6860e65 100644 --- a/FoxTube/Strings/en-US/VideoPage.resw +++ b/FoxTube/Strings/en-US/VideoPage.resw @@ -306,4 +306,7 @@ Playback speed + + Report this video + \ No newline at end of file diff --git a/FoxTube/Strings/ru-RU/CommentsPage.resw b/FoxTube/Strings/ru-RU/CommentsPage.resw index 4befdf2..bedef53 100644 --- a/FoxTube/Strings/ru-RU/CommentsPage.resw +++ b/FoxTube/Strings/ru-RU/CommentsPage.resw @@ -174,6 +174,9 @@ Сортировать по: + + Отметить как спам + Оставить комментарий diff --git a/FoxTube/Strings/ru-RU/Report.resw b/FoxTube/Strings/ru-RU/Report.resw new file mode 100644 index 0000000..938d7c9 --- /dev/null +++ b/FoxTube/Strings/ru-RU/Report.resw @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Дополнительная информация (необязательно) + + + Нам не удалось отправить жалобу прямо сейчас. Пожалуйста, попробуйте позже + + + Причина + + + Выберите причину... + + + Отмена + + + Пожаловаться + + + Пожаловаться на видео + + + Выберите категорию... + + + Спасибо что помогаете делать сообщество YouTube лучше + + + Ваша жалоба отправлена + + \ No newline at end of file diff --git a/FoxTube/Strings/ru-RU/VideoPage.resw b/FoxTube/Strings/ru-RU/VideoPage.resw index 76c703f..525c73a 100644 --- a/FoxTube/Strings/ru-RU/VideoPage.resw +++ b/FoxTube/Strings/ru-RU/VideoPage.resw @@ -306,4 +306,7 @@ Скорость видео + + Пожаловаться на видео + \ No newline at end of file From 7a1f918df204d071a9217b29f469d449140c9508 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Sun, 23 Jun 2019 18:03:04 +0300 Subject: [PATCH 05/12] Added video categories link to video page and improved search to display video categories --- ...SearchPaameters.cs => SearchParameters.cs} | 21 ++++++++++---- FoxTube/FoxTube.csproj | 2 +- FoxTube/Pages/Search.xaml.cs | 11 ++++++-- FoxTube/Pages/VideoPage.xaml | 2 +- FoxTube/Pages/VideoPage.xaml.cs | 28 ++++++++++++++++++- FoxTube/Strings/en-US/Search.resw | 3 ++ FoxTube/Strings/en-US/VideoPage.resw | 3 ++ FoxTube/Strings/ru-RU/Search.resw | 3 ++ FoxTube/Strings/ru-RU/VideoPage.resw | 3 ++ 9 files changed, 66 insertions(+), 10 deletions(-) rename FoxTube/Classes/{SearchPaameters.cs => SearchParameters.cs} (90%) diff --git a/FoxTube/Classes/SearchPaameters.cs b/FoxTube/Classes/SearchParameters.cs similarity index 90% rename from FoxTube/Classes/SearchPaameters.cs rename to FoxTube/Classes/SearchParameters.cs index 17299bd..bd31364 100644 --- a/FoxTube/Classes/SearchPaameters.cs +++ b/FoxTube/Classes/SearchParameters.cs @@ -1,4 +1,5 @@ -using System; +using Google.Apis.YouTube.v3.Data; +using System; using System.Collections.Generic; using static Google.Apis.YouTube.v3.SearchResource.ListRequest; @@ -34,11 +35,11 @@ namespace FoxTube case Enumerations.ConversionType.Captions: if (Captions) return VideoCaptionEnum.ClosedCaption; - else return null; + else return VideoCaptionEnum.Any; case Enumerations.ConversionType.CreativeCommons: if (CreativeCommons) return VideoLicenseEnum.CreativeCommon; - else return null; + else return VideoLicenseEnum.Any; case Enumerations.ConversionType.Date: switch(Date) { @@ -61,7 +62,7 @@ namespace FoxTube case Enumerations.ConversionType.HD: if (HD) return VideoDefinitionEnum.High; - else return null; + else return VideoDefinitionEnum.Any; case Enumerations.ConversionType.LiveEvent: if (LiveEvent) return EventTypeEnum.Live; @@ -79,7 +80,7 @@ namespace FoxTube case Enumerations.ConversionType.ThreeD: if (Is3D) return VideoDimensionEnum.Value3d; - else return null; + else return VideoDimensionEnum.Any; case Enumerations.ConversionType.Type: switch(Type) { @@ -100,6 +101,7 @@ namespace FoxTube public string Term { get; private set; } public string Channel { get; private set; } + public VideoCategory Category { get; set; } public Filters Filter { get; private set; } = new Filters(); public SearchParameters(string term) @@ -107,6 +109,13 @@ namespace FoxTube Term = term; } + public SearchParameters(VideoCategory category) + { + Category = category; + Filter = new Filters(); + Filter.Type = Filters.Enumerations.Type.Video; + } + public SearchParameters(string term, Filters filters) { Term = term; @@ -119,6 +128,7 @@ namespace FoxTube Channel = channelId; } + public SearchParameters(string term, string channelId, Filters filters) { Term = term; @@ -130,6 +140,7 @@ namespace FoxTube { return $@"Term: {Term} Channel id: {Channel} +Category id: {Category} Filters: Order: {Filter.Order} Type: {Filter.Type} diff --git a/FoxTube/FoxTube.csproj b/FoxTube/FoxTube.csproj index 07e15a2..2207327 100644 --- a/FoxTube/FoxTube.csproj +++ b/FoxTube/FoxTube.csproj @@ -107,7 +107,7 @@ - + diff --git a/FoxTube/Pages/Search.xaml.cs b/FoxTube/Pages/Search.xaml.cs index 6f4dc51..2d91044 100644 --- a/FoxTube/Pages/Search.xaml.cs +++ b/FoxTube/Pages/Search.xaml.cs @@ -77,12 +77,20 @@ namespace FoxTube { Parameters = arg; request = SecretsVault.Service.Search.List("id,snippet"); + + searchTerm.Text = $"{resources.GetString("/Search/header")} '{Parameters.Term}'"; if (!string.IsNullOrWhiteSpace(arg.Channel)) { request.ChannelId = arg.Channel; (type.Items[2] as ComboBoxItem).Visibility = Visibility.Collapsed; (grid.Children[1] as CommandBar).Visibility = Visibility.Collapsed; } + else if (arg.Category != null) + { + (filters.Children[0] as GridView).Items.Remove(type); + searchTerm.Text = $"{resources.GetString("/Search/category")} '{arg.Category.Snippet.Title}'"; + request.VideoCategoryId = arg.Category.Id; + } request.Q = arg.Term; request.SafeSearch = (SearchResource.ListRequest.SafeSearchEnum)SettingsStorage.SafeSearch; @@ -101,7 +109,7 @@ namespace FoxTube request.VideoDefinition = (SearchResource.ListRequest.VideoDefinitionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.HD); request.VideoDimension = (SearchResource.ListRequest.VideoDimensionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.ThreeD); request.VideoCaption = (SearchResource.ListRequest.VideoCaptionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Captions); - request.EventType = (SearchResource.ListRequest.EventTypeEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.LiveEvent); + request.EventType = (SearchResource.ListRequest.EventTypeEnum?)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.LiveEvent); request.VideoLicense = (SearchResource.ListRequest.VideoLicenseEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.CreativeCommons); } @@ -120,7 +128,6 @@ namespace FoxTube features.SelectedItems.Add(features.Items[4]); SearchListResponse response = await request.ExecuteAsync(); - searchTerm.Text = $"{resources.GetString("/Search/header")} '{Parameters.Term}'"; resultsCount.Text = $"{resources.GetString("/Search/found")}: {SetResults(response.PageInfo.TotalResults)} {resources.GetString("/Search/items")}"; if (!string.IsNullOrWhiteSpace(response.NextPageToken)) diff --git a/FoxTube/Pages/VideoPage.xaml b/FoxTube/Pages/VideoPage.xaml index c889205..7a4bafc 100644 --- a/FoxTube/Pages/VideoPage.xaml +++ b/FoxTube/Pages/VideoPage.xaml @@ -66,7 +66,7 @@