diff --git a/FoxTube.Background/BackgroundProcessor.cs b/FoxTube.Background/BackgroundProcessor.cs index 3819038..83ff2c7 100644 --- a/FoxTube.Background/BackgroundProcessor.cs +++ b/FoxTube.Background/BackgroundProcessor.cs @@ -1,4 +1,6 @@ -using Google.Apis.Services; +using Google.Apis.Auth.OAuth2; +using Google.Apis.Oauth2.v2; +using Google.Apis.Services; using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; using Newtonsoft.Json; @@ -6,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using System.Xml; using Windows.ApplicationModel.Background; @@ -19,11 +22,6 @@ namespace FoxTube.Background private DateTime lastCheck; private readonly ApplicationDataContainer settings = ApplicationData.Current.RoamingSettings; dynamic prefs; - private readonly YouTubeService Service = new YouTubeService(new BaseClientService.Initializer() - { - ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", - ApplicationName = "FoxTube" - }); BackgroundTaskDeferral def; public async void Run(IBackgroundTaskInstance taskInstance) @@ -44,7 +42,7 @@ namespace FoxTube.Background prefs = JsonConvert.DeserializeObject(settings.Values["settings"] as string); if ((bool)prefs.devNotifications) CheckAnnouncements(); - if ((bool)prefs.videoNotifications) + if ((bool)prefs.videoNotifications && (bool)prefs.hasAccount) await CheckAccount(); } finally @@ -58,15 +56,59 @@ namespace FoxTube.Background { try { - Dictionary subscriptions = JsonConvert.DeserializeObject>(settings.Values["subscriptions"] as string); + UserCredential Credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( + new ClientSecrets + { + ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", + ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_" + }, + new[] + { + Oauth2Service.Scope.UserinfoProfile, + Oauth2Service.Scope.UserinfoEmail, + YouTubeService.Scope.YoutubeForceSsl, + YouTubeService.Scope.Youtube, + YouTubeService.Scope.YoutubeUpload, + YouTubeService.Scope.YoutubeReadonly, + YouTubeService.Scope.Youtubepartner + }, + "user", + CancellationToken.None); + if (Credential == null) + return; + + YouTubeService service = new YouTubeService(new BaseClientService.Initializer + { + HttpClientInitializer = Credential, + ApplicationName = "FoxTube" + }); + + List subscriptions = new List(); List results = new List(); - foreach (var s in subscriptions) + SubscriptionsResource.ListRequest subRequest = service.Subscriptions.List("snippet"); + subRequest.Mine = true; + subRequest.MaxResults = 50; + subRequest.Order = SubscriptionsResource.ListRequest.OrderEnum.Relevance; + SubscriptionListResponse subResponse; + string nextToken = null; + + do { - SearchResource.ListRequest request = Service.Search.List("snippet"); + subRequest.PageToken = nextToken; + subResponse = await subRequest.ExecuteAsync(); + foreach (Subscription s in subResponse.Items) + subscriptions.Add(s); + nextToken = subResponse.NextPageToken; + + } while (!string.IsNullOrWhiteSpace(nextToken)); + + foreach (Subscription item in subscriptions) + { + SearchResource.ListRequest request = service.Search.List("snippet"); request.PublishedAfter = lastCheck; - request.ChannelId = s.Key; + request.ChannelId = item.Snippet.ResourceId.ChannelId; request.Type = "video"; request.MaxResults = 5; SearchListResponse response = await request.ExecuteAsync(); @@ -75,15 +117,15 @@ namespace FoxTube.Background { results.Add(i); - if(i.Snippet.LiveBroadcastContent == "live") + if (i.Snippet.LiveBroadcastContent == "live") ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetStreamToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, s.Value)); - else if(i.Snippet.LiveBroadcastContent == "upcoming") + Notification.GetStreamToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, item.Snippet.Thumbnails.Default__.Url)); + else if (i.Snippet.LiveBroadcastContent == "upcoming") ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetUpcomingToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, s.Value)); + Notification.GetUpcomingToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, item.Snippet.Thumbnails.Default__.Url)); else ToastNotificationManager.CreateToastNotifier().Show( - Notification.GetVideoToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, s.Value)); + Notification.GetVideoToast(i.Id.VideoId, i.Snippet.ChannelId, i.Snippet.Title.ConvertEscapeSymbols(), i.Snippet.ChannelTitle, i.Snippet.Thumbnails.Medium.Url, i.Snippet.PublishedAt.Value, item.Snippet.Thumbnails.Default__.Url)); } } @@ -93,7 +135,7 @@ namespace FoxTube.Background updater.EnableNotificationQueue(true); updater.Clear(); for (int i = 0; i < 5 && i < results.Count; i++) - updater.Update(Tiles.GetTileLayout(System.Security.SecurityElement.Escape(results[i].Snippet.Title.ConvertEscapeSymbols()), System.Security.SecurityElement.Escape(results[i].Snippet.ChannelTitle), results[i].Snippet.Thumbnails.Medium.Url.Replace("&", "%26"), subscriptions[results[i].Snippet.ChannelId])); + updater.Update(Tiles.GetTileLayout(System.Security.SecurityElement.Escape(results[i].Snippet.Title.ConvertEscapeSymbols()), System.Security.SecurityElement.Escape(results[i].Snippet.ChannelTitle), results[i].Snippet.Thumbnails.Medium.Url.Replace("&", "%26"), subscriptions.Find(x => x.Snippet.ResourceId.ChannelId == results[i].Snippet.ChannelId).Snippet.Thumbnails.Medium.Url)); } catch { } } diff --git a/FoxTube.Background/FoxTube.Background.csproj b/FoxTube.Background/FoxTube.Background.csproj index e8b0cea..4dbe1ff 100644 --- a/FoxTube.Background/FoxTube.Background.csproj +++ b/FoxTube.Background/FoxTube.Background.csproj @@ -128,7 +128,7 @@ 1.29.2.1006 - 1.13.2 + 2.1.1 6.2.8 @@ -137,7 +137,7 @@ 5.1.1 - 4.6.7 + 4.7.6 diff --git a/FoxTube.Background/ResourceCreators.cs b/FoxTube.Background/ResourceCreators.cs index 254f1ff..b5fd2a0 100644 --- a/FoxTube.Background/ResourceCreators.cs +++ b/FoxTube.Background/ResourceCreators.cs @@ -16,7 +16,8 @@ namespace FoxTube.Background private static Dictionary LoadPack() { dynamic saved = JsonConvert.DeserializeObject(ApplicationData.Current.RoamingSettings.Values["settings"] as string); - if (saved.language as string == "ru-RU") + string hl = saved.language; + if (hl == "ru-RU") return new Dictionary() { { "addLater", "Посмотреть позже" }, diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index f101394..09122f7 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -22,6 +22,8 @@ namespace FoxTube { sealed partial class App : Application { + public static string[] AvailableLanguages => new[] { "en-US", "ru-RU" }; + Stopwatch sw = new Stopwatch(); public App() { @@ -252,6 +254,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 0000000..862d989 Binary files /dev/null and b/FoxTube/Assets/Data/Package.zip differ diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml index d300d6f..09510f7 100644 --- a/FoxTube/Assets/Data/Patchnotes.xml +++ b/FoxTube/Assets/Data/Patchnotes.xml @@ -1,5 +1,69 @@  + + + ### What's new: +- Fixed video playback problems +- 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 +- Fixed history page refresh button +- You can now report comments as spam +- You can now report videos +- Enchanced background subscriptions check sequence +- Corrected misspells in English localization + + ### Что нового: +- Исправлены проблемы с воспроизведением видео +- Исправлены некоторые случаи при которых карточки плейлистов не отображались +- Исправлены некоторые случай при которых приложение вылетало +- Исправлены вылеты приложения при попытке перейти на несуществующий канал/плейлист/видео +- Исправлена кнопка обновления на странице истории просмотров +- Теперь вы можете помечать комментарии как спам +- Теперь вы можете отправлять жалобы на видео +- Улучшена проверка новых видео подписок в фоне +- Исправлены ошибки в английской локализации + + + + + + ### 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/Assets/Data/RevEn.xml b/FoxTube/Assets/Data/RevEn.xml deleted file mode 100644 index 5d5cf2f..0000000 --- a/FoxTube/Assets/Data/RevEn.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - ### What's new: - -- Small fixes -- First public pre-release version -- Some content was cut out due to its incompleteness - - ### Что нового: - -- Мелкие исправления багов -- Эта версия является первой пред-релизной публичной версией -- Некотроые функции были вырезаны из-за их незавершенности - - - - - - ### What's new: - -- 'Live' button fixed in the player -- Long channel names on crads fixed -- Fixed video description disappearing on window resizing -- Player seek is fixed -- Added video buffering progress indicatior -- Small fixes - -### Known issues: - -- Recommended and subscriptions pages aren't implemented -- History isn't implemented -- Playlists management isn't implemented -- Ads aren't implemented - - ### Что нового: - -- Кнопка перехода к прямому эфиру на стримах теперь работает -- Исправлен баг с длинными именами каналов на карточках -- Исправлено исчезание описания видео при изменении размеров окна -- Исправлен ползунок перемотки видео -- Добавлен индикатор буферизации видео -- Мелкие исправления - -### Что по-прежнему не работает: - -- Страница рекомендованных видео и страница видео с подписок -- История -- Работа с плейлистами -- Нет карточек рекламы - - - - 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/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/Classes/ManifestGenerator.cs b/FoxTube/Classes/ManifestGenerator.cs index 3be4856..6cea736 100644 --- a/FoxTube/Classes/ManifestGenerator.cs +++ b/FoxTube/Classes/ManifestGenerator.cs @@ -1,5 +1,5 @@ -using AngleSharp.Dom.Html; -using AngleSharp.Parser.Html; +using AngleSharp.Html.Dom; +using AngleSharp.Html.Parser; using Google.Apis.YouTube.v3.Data; using Microsoft.AppCenter.Analytics; using Newtonsoft.Json.Linq; @@ -141,7 +141,7 @@ namespace FoxTube.Controls.Player HttpClient http = new HttpClient(); string response = await http.GetStringAsync($"https://youtube.com/watch?v={info.Id}&disable_polymer=true&bpctr=9999999999&hl=en"); - IHtmlDocument videoEmbedPageHtml = new HtmlParser().Parse(response); + IHtmlDocument videoEmbedPageHtml = new HtmlParser().ParseDocument(response); string playerConfigRaw = Regex.Match(videoEmbedPageHtml.Source.Text, @"ytplayer\.config = (?\{[^\{\}]*(((?\{)[^\{\}]*)+((?\})[^\{\}]*)+)*(?(Open)(?!))\})") 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/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/Classes/SecretsVault.cs b/FoxTube/Classes/SecretsVault.cs index ad71473..dd3fedf 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" @@ -46,7 +48,7 @@ namespace FoxTube public static HttpClient HttpClient { get; } = new HttpClient(); private static bool TestAds => false; //TODO: Change this bool public static string AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh"; - public static string AdUnitId => TestAds ? "test" : "1100044398"; + public static string AdUnitId => TestAds ? "test" : "1100037769"; public static bool AdsDisabled { get; private set; } = true; //User info @@ -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..1c68b7a 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 { @@ -23,9 +24,9 @@ namespace FoxTube public bool autoplay = true; public double volume = 100; - public string language = (new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName) ? "ru-RU" : "en-US"; - public string relevanceLanguage = (new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName) ? "ru" : "en"; - public string region = (new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName) ? "ru" : "us"; + public string language = GetLanguage(); + public string relevanceLanguage = CultureInfo.InstalledUICulture.TwoLetterISOLanguageName; + public string region = CultureInfo.InstalledUICulture.Name.Split('-')[1]; public int safeSearch = 0; public bool hasAccount = false; @@ -34,6 +35,19 @@ namespace FoxTube public TimeSpan uptime = TimeSpan.FromSeconds(0); public bool promptReview = true; public bool promptFeedback = true; + + public bool processClipboard = true; + public bool minimizeCommandbar = false; + + private static string GetLanguage() + { + if (App.AvailableLanguages.Contains(CultureInfo.InstalledUICulture.Name)) + return CultureInfo.InstalledUICulture.Name; + else if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName)) + return "ru-RU"; + else + return "en-US"; + } } public static class SettingsStorage @@ -221,6 +235,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/Advert.xaml b/FoxTube/Controls/Advert.xaml deleted file mode 100644 index 5c2bd8e..0000000 --- a/FoxTube/Controls/Advert.xaml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/FoxTube/Controls/Advert.xaml.cs b/FoxTube/Controls/Advert.xaml.cs deleted file mode 100644 index 36d3b90..0000000 --- a/FoxTube/Controls/Advert.xaml.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace FoxTube.Controls -{ - public sealed partial class Advert : UserControl - { - public string AdUnitId { get; set; } = "test"; - public string AppId => "3f83fe91-d6be-434d-a0ae-7351c5a997f1"; - public bool OverrideSize { get; set; } = false; - - public new double Height - { - get { return ad.Height; } - set - { - ad.Height = value; - OverrideSize = true; - } - } - - public new double Width - { - get { return ad.Width; } - set - { - ad.Width = value; - OverrideSize = true; - } - } - - public Advert() - { - InitializeComponent(); - if (!SecretsVault.AdsDisabled) - Visibility = Visibility.Visible; - else - Visibility = Visibility.Collapsed; - SecretsVault.Purchased += (s, e) => Visibility = (bool)e[0] ? Visibility.Collapsed : Visibility.Visible; - } - - private void Grid_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (OverrideSize) - return; - - if(grid.ActualWidth >= 728) - { - ad.Width = 728; - ad.Height = 90; - } - else if (grid.ActualWidth >= 640) - { - ad.Width = 640; - ad.Height = 100; - } - else if (grid.ActualWidth >= 320) - { - ad.Width = 320; - ad.Height = 50; - } - else - { - ad.Width = 300; - ad.Height = 50; - } - } - } -} 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/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/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 b/FoxTube/Controls/PlaylistCard.xaml index d094c72..58685d5 100644 --- a/FoxTube/Controls/PlaylistCard.xaml +++ b/FoxTube/Controls/PlaylistCard.xaml @@ -1,4 +1,4 @@ - - + diff --git a/FoxTube/Controls/PlaylistCard.xaml.cs b/FoxTube/Controls/PlaylistCard.xaml.cs index 8ed514f..cce19da 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; @@ -8,13 +9,14 @@ using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Imaging; +using YoutubeExplode; namespace FoxTube.Controls { /// /// Playlist card control /// - public sealed partial class PlaylistCard : Page + public sealed partial class PlaylistCard : UserControl { Playlist item; public string playlistId; @@ -45,13 +47,18 @@ 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(); } catch (Exception e) { + (Parent as AdaptiveGridView)?.Items.Remove(this); Visibility = Visibility.Collapsed; Analytics.TrackEvent("PlaylistCard loading failed", new Dictionary() { 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/Controls/VideoCard.xaml.cs b/FoxTube/Controls/VideoCard.xaml.cs index 1c22402..a47ef55 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,7 +119,10 @@ namespace FoxTube.Controls } catch (Exception e) { + (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/FoxTube.csproj b/FoxTube/FoxTube.csproj index cadd022..fbb71a4 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 @@ -103,15 +103,13 @@ App.xaml + - + - - Advert.xaml - CardAdvert.xaml @@ -144,6 +142,9 @@ PlaylistCard.xaml + + ReportVideo.xaml + ShowMore.xaml @@ -183,6 +184,9 @@ PlaylistPage.xaml + + Translate.xaml + Subscriptions.xaml @@ -220,7 +224,6 @@ - @@ -270,6 +273,7 @@ + @@ -279,16 +283,16 @@ + + + + MSBuild:Compile Designer - - Designer - MSBuild:Compile - Designer MSBuild:Compile @@ -329,6 +333,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -389,6 +397,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -419,6 +431,9 @@ + + 0.12.1 + 1.30.0-beta02 @@ -438,7 +453,7 @@ 10.1811.22001 - 2.0.0 + 2.1.1 6.2.8 @@ -458,8 +473,11 @@ 4.3.2 + + 4.3.0 + - 4.7.0 + 4.7.6 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 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/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/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/History.xaml.cs b/FoxTube/Pages/History.xaml.cs index a939ab7..d8dd439 100644 --- a/FoxTube/Pages/History.xaml.cs +++ b/FoxTube/Pages/History.xaml.cs @@ -74,7 +74,7 @@ namespace FoxTube.Pages private void Refresh_Click(object sender, RoutedEventArgs e) { - Methods.MainPage.VideoContent.Refresh(); + Methods.MainPage.PageContent.Refresh(); } private async void ShowMore_Clicked() 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/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/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/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/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..d4a12e6 100644 --- a/FoxTube/Pages/SettingsPages/Inbox.xaml.cs +++ b/FoxTube/Pages/SettingsPages/Inbox.xaml.cs @@ -8,6 +8,7 @@ using FoxTube.Classes; using Windows.Storage; using System.Xml; using Microsoft.AppCenter.Analytics; +using System.Globalization; namespace FoxTube.Pages.SettingsPages { @@ -23,6 +24,14 @@ namespace FoxTube.Pages.SettingsPages InitializeComponent(); } + string GetLanguage() + { + if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName)) + return "ru-RU"; + else + return "en-US"; + } + public async void LoadItems(string id = null) { try @@ -34,15 +43,15 @@ namespace FoxTube.Pages.SettingsPages foreach (XmlElement e in doc["items"].ChildNodes) items.Add(new InboxItem( e.GetAttribute("version"), - e["content"][SettingsStorage.Language].InnerText, - DateTime.Parse(e.GetAttribute("time")))); + e["content"][GetLanguage()].InnerText, + DateTime.Parse(e.GetAttribute("time"), 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")), + e["header"][GetLanguage()].InnerText, + e["content"][GetLanguage()].InnerText, + DateTime.Parse(e.GetAttribute("time"), 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..5f2681b 100644 --- a/FoxTube/Pages/VideoPage.xaml.cs +++ b/FoxTube/Pages/VideoPage.xaml.cs @@ -14,6 +14,7 @@ using Windows.System; using Windows.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Documents; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; using Windows.UI.Xaml.Navigation; @@ -95,6 +96,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 @@ -130,6 +137,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() { @@ -246,7 +259,32 @@ namespace FoxTube.Pages { //Setting meta title.Text = item.Snippet.Localized.Title; - date.Text = $"{resources.GetString("/VideoPage/publishedAt")}: {item.Snippet.PublishedAt} ({Methods.GetAgo(item.Snippet.PublishedAt.Value)})"; + meta.Text = ""; + meta.Inlines.Add(new Run + { + Text = $"{resources.GetString("/VideoPage/publishedAt")}: {item.Snippet.PublishedAt} ({Methods.GetAgo(item.Snippet.PublishedAt.Value)})" + }); + if(!string.IsNullOrWhiteSpace(item.Snippet.CategoryId)) + { + VideoCategoriesResource.ListRequest request = SecretsVault.Service.VideoCategories.List("snippet,id"); + request.Id = item.Snippet.CategoryId; + request.Hl = SettingsStorage.RelevanceLanguage; + VideoCategoryListResponse response = await request.ExecuteAsync(); + if(response.Items.Count != 0) + { + meta.Inlines.Add(new Run + { + Text = $" {resources.GetString("/VideoPage/inCat")} " + }); + Hyperlink hl = new Hyperlink(); + hl.Inlines.Add(new Run + { + Text = response.Items[0].Snippet.Title + }); + hl.Click += (s, e) => Methods.MainPage.GoToSearch(new SearchParameters(response.Items[0])); + meta.Inlines.Add(hl); + } + } description.FormatText(item.Snippet.Localized.Description); //Setting channel button @@ -391,6 +429,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) @@ -772,5 +812,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..3c3da4c 100644 --- a/FoxTube/Strings/en-US/CommentsPage.resw +++ b/FoxTube/Strings/en-US/CommentsPage.resw @@ -145,10 +145,10 @@ Submit - Failed to delete your commentary. Please, try again later. + Failed to delete your comment. Please, try again later. - Failed to edit your commentary. Please, try again later. + Failed to edit your comment. Please, try again later. Failed to send your reply. Please, try again later. @@ -174,6 +174,9 @@ Sort by: + + Report as spam + Add a public comment diff --git a/FoxTube/Strings/en-US/Downloads.resw b/FoxTube/Strings/en-US/Downloads.resw index 930816e..9e6a9b1 100644 --- a/FoxTube/Strings/en-US/Downloads.resw +++ b/FoxTube/Strings/en-US/Downloads.resw @@ -142,7 +142,7 @@ Bug report has been sent - Error occured while dowloading a video + Error occurred while downloading a video Go to original 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/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/Search.resw b/FoxTube/Strings/en-US/Search.resw index 36c2fa0..d4910a3 100644 --- a/FoxTube/Strings/en-US/Search.resw +++ b/FoxTube/Strings/en-US/Search.resw @@ -129,6 +129,9 @@ Apply + + Collection from + Creative Commons 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..a0ae3f0 100644 --- a/FoxTube/Strings/en-US/VideoPage.resw +++ b/FoxTube/Strings/en-US/VideoPage.resw @@ -175,7 +175,7 @@ Go to live broadcast - This content may be not apropriate for children under 18 + This content may be not appropriate for children under 18 Maximize @@ -303,4 +303,13 @@ Auto + + Playback speed + + + Report this video + + + in + \ 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/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/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/Search.resw b/FoxTube/Strings/ru-RU/Search.resw index b2d2794..a5a0fb9 100644 --- a/FoxTube/Strings/ru-RU/Search.resw +++ b/FoxTube/Strings/ru-RU/Search.resw @@ -129,6 +129,9 @@ Применить + + Коллекция категории + Лицензия Creative Commons 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..e46bdef 100644 --- a/FoxTube/Strings/ru-RU/VideoPage.resw +++ b/FoxTube/Strings/ru-RU/VideoPage.resw @@ -303,4 +303,13 @@ Авто + + Скорость видео + + + Пожаловаться на видео + + + в категории + \ 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 @@ -