From d8306c81820913b12219e4e19cfef0f992ef4264 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Thu, 20 Jun 2019 16:47:55 +0300 Subject: [PATCH] - 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 @@ -