diff --git a/FoxTube.Background/BackgroundProcessor.cs b/FoxTube.Background/BackgroundProcessor.cs
index 6e06785..3819038 100644
--- a/FoxTube.Background/BackgroundProcessor.cs
+++ b/FoxTube.Background/BackgroundProcessor.cs
@@ -123,7 +123,7 @@ namespace FoxTube.Background
{
public static string ConvertEscapeSymbols(this string str)
{
- return str.Replace(""", "\"").Replace("'", "'").Replace("<", "<").Replace(">", ">").Replace("&", "&");
+ return str.Replace(""", "\"").Replace("'", "'").Replace("<", "<").Replace(">", ">").Replace("&", "&").Replace(""", "\"").Replace("'", "'").Replace("<", "<").Replace(">", ">").Replace("&", "&");
}
}
}
diff --git a/FoxTube.Tests/Assets/LockScreenLogo.scale-200.png b/FoxTube.Tests/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000..735f57a
Binary files /dev/null and b/FoxTube.Tests/Assets/LockScreenLogo.scale-200.png differ
diff --git a/FoxTube.Tests/Assets/SplashScreen.scale-200.png b/FoxTube.Tests/Assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000..023e7f1
Binary files /dev/null and b/FoxTube.Tests/Assets/SplashScreen.scale-200.png differ
diff --git a/FoxTube.Tests/Assets/Square150x150Logo.scale-200.png b/FoxTube.Tests/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..af49fec
Binary files /dev/null and b/FoxTube.Tests/Assets/Square150x150Logo.scale-200.png differ
diff --git a/FoxTube.Tests/Assets/Square44x44Logo.scale-200.png b/FoxTube.Tests/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..ce342a2
Binary files /dev/null and b/FoxTube.Tests/Assets/Square44x44Logo.scale-200.png differ
diff --git a/FoxTube.Tests/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/FoxTube.Tests/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000..f6c02ce
Binary files /dev/null and b/FoxTube.Tests/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/FoxTube.Tests/Assets/StoreLogo.png b/FoxTube.Tests/Assets/StoreLogo.png
new file mode 100644
index 0000000..7385b56
Binary files /dev/null and b/FoxTube.Tests/Assets/StoreLogo.png differ
diff --git a/FoxTube.Tests/Assets/Wide310x150Logo.scale-200.png b/FoxTube.Tests/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..288995b
Binary files /dev/null and b/FoxTube.Tests/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/FoxTube.Tests/FoxTube.Tests.csproj b/FoxTube.Tests/FoxTube.Tests.csproj
new file mode 100644
index 0000000..9136def
--- /dev/null
+++ b/FoxTube.Tests/FoxTube.Tests.csproj
@@ -0,0 +1,172 @@
+
+
+
+
+ Debug
+ x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}
+ AppContainerExe
+ Properties
+ FoxTube.Tests
+ FoxTube.Tests
+ en-US
+ UAP
+ 10.0.17134.0
+ 10.0.17134.0
+ 14
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ FoxTube.Tests_TemporaryKey.pfx
+ $(VisualStudioVersion)
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x86
+ false
+ prompt
+ true
+
+
+ bin\x86\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ bin\ARM\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ bin\ARM64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ bin\x64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+ true
+
+
+ PackageReference
+
+
+
+
+
+
+
+ UnitTestApp.xaml
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 6.2.8
+
+
+ 1.4.0
+
+
+ 1.4.0
+
+
+
+ 14.0
+
+
+
+
\ No newline at end of file
diff --git a/FoxTube.Tests/Package.appxmanifest b/FoxTube.Tests/Package.appxmanifest
new file mode 100644
index 0000000..c1d5753
--- /dev/null
+++ b/FoxTube.Tests/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ FoxTube.Tests
+ XFox
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FoxTube.Tests/Properties/AssemblyInfo.cs b/FoxTube.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..c55a525
--- /dev/null
+++ b/FoxTube.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,18 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("FoxTube.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("FoxTube.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: AssemblyMetadata("TargetPlatform","UAP")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/FoxTube.Tests/Properties/UnitTestApp.rd.xml b/FoxTube.Tests/Properties/UnitTestApp.rd.xml
new file mode 100644
index 0000000..996a839
--- /dev/null
+++ b/FoxTube.Tests/Properties/UnitTestApp.rd.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FoxTube.Tests/UnitTest.cs b/FoxTube.Tests/UnitTest.cs
new file mode 100644
index 0000000..16e64c1
--- /dev/null
+++ b/FoxTube.Tests/UnitTest.cs
@@ -0,0 +1,15 @@
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace FoxTube.Tests
+{
+ [TestClass]
+ public class UnitTest1
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+ }
+ }
+}
diff --git a/FoxTube.Tests/UnitTestApp.xaml b/FoxTube.Tests/UnitTestApp.xaml
new file mode 100644
index 0000000..32e7cb5
--- /dev/null
+++ b/FoxTube.Tests/UnitTestApp.xaml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/FoxTube.Tests/UnitTestApp.xaml.cs b/FoxTube.Tests/UnitTestApp.xaml.cs
new file mode 100644
index 0000000..df5c576
--- /dev/null
+++ b/FoxTube.Tests/UnitTestApp.xaml.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Activation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+namespace FoxTube.Tests
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ sealed partial class App : Application
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ this.Suspending += OnSuspending;
+ }
+
+ ///
+ /// Invoked when the application is launched normally by the end user. Other entry points
+ /// will be used such as when the application is launched to open a specific file.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnLaunched(LaunchActivatedEventArgs e)
+ {
+
+#if DEBUG
+ if (System.Diagnostics.Debugger.IsAttached)
+ {
+ this.DebugSettings.EnableFrameRateCounter = true;
+ }
+#endif
+
+ Frame rootFrame = Window.Current.Content as Frame;
+
+ // Do not repeat app initialization when the Window already has content,
+ // just ensure that the window is active
+ if (rootFrame == null)
+ {
+ // Create a Frame to act as the navigation context and navigate to the first page
+ rootFrame = new Frame();
+
+ rootFrame.NavigationFailed += OnNavigationFailed;
+
+ if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
+ {
+ //TODO: Load state from previously suspended application
+ }
+
+ // Place the frame in the current Window
+ Window.Current.Content = rootFrame;
+ }
+
+ Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI();
+
+ // Ensure the current window is active
+ Window.Current.Activate();
+
+ Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments);
+ }
+
+ ///
+ /// Invoked when Navigation to a certain page fails
+ ///
+ /// The Frame which failed navigation
+ /// Details about the navigation failure
+ void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
+ {
+ throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
+ }
+
+ ///
+ /// Invoked when application execution is being suspended. Application state is saved
+ /// without knowing whether the application will be terminated or resumed with the contents
+ /// of memory still intact.
+ ///
+ /// The source of the suspend request.
+ /// Details about the suspend request.
+ private void OnSuspending(object sender, SuspendingEventArgs e)
+ {
+ var deferral = e.SuspendingOperation.GetDeferral();
+ //TODO: Save application state and stop any background activity
+ deferral.Complete();
+ }
+ }
+}
diff --git a/FoxTube.sln b/FoxTube.sln
index 7357c1c..e8ba8c1 100644
--- a/FoxTube.sln
+++ b/FoxTube.sln
@@ -1,20 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27428.1
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28803.352
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FoxTube", "FoxTube\FoxTube.csproj", "{2597B816-7316-4D20-BA6C-D78001E89C1A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FoxTube.Background", "FoxTube.Background\FoxTube.Background.csproj", "{FC9128D7-E3AA-48ED-8641-629794B88B28}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FoxTube.Tests", "FoxTube.Tests\FoxTube.Tests.csproj", "{3D864717-2D87-4E54-BFC0-755FC2FCA2A7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
@@ -23,6 +27,7 @@ Global
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Debug|ARM.ActiveCfg = Debug|ARM
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Debug|ARM.Build.0 = Debug|ARM
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Debug|ARM.Deploy.0 = Debug|ARM
+ {2597B816-7316-4D20-BA6C-D78001E89C1A}.Debug|ARM64.ActiveCfg = Debug|x86
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Debug|x64.ActiveCfg = Debug|x64
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Debug|x64.Build.0 = Debug|x64
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Debug|x64.Deploy.0 = Debug|x64
@@ -33,6 +38,7 @@ Global
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Release|ARM.ActiveCfg = Release|ARM
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Release|ARM.Build.0 = Release|ARM
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Release|ARM.Deploy.0 = Release|ARM
+ {2597B816-7316-4D20-BA6C-D78001E89C1A}.Release|ARM64.ActiveCfg = Release|x86
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Release|x64.ActiveCfg = Release|x64
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Release|x64.Build.0 = Release|x64
{2597B816-7316-4D20-BA6C-D78001E89C1A}.Release|x64.Deploy.0 = Release|x64
@@ -43,6 +49,8 @@ Global
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|ARM.ActiveCfg = Debug|ARM
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|ARM.Build.0 = Debug|ARM
+ {FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|ARM64.Build.0 = Debug|Any CPU
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|x64.ActiveCfg = Debug|x64
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|x64.Build.0 = Debug|x64
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Debug|x86.ActiveCfg = Debug|x86
@@ -51,10 +59,38 @@ Global
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|Any CPU.Build.0 = Release|Any CPU
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|ARM.ActiveCfg = Release|ARM
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|ARM.Build.0 = Release|ARM
+ {FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|ARM64.Build.0 = Release|Any CPU
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|x64.ActiveCfg = Release|x64
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|x64.Build.0 = Release|x64
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|x86.ActiveCfg = Release|x86
{FC9128D7-E3AA-48ED-8641-629794B88B28}.Release|x86.Build.0 = Release|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|ARM.ActiveCfg = Debug|ARM
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|ARM.Build.0 = Debug|ARM
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|ARM.Deploy.0 = Debug|ARM
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|ARM64.Build.0 = Debug|ARM64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|x64.ActiveCfg = Debug|x64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|x64.Build.0 = Debug|x64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|x64.Deploy.0 = Debug|x64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|x86.ActiveCfg = Debug|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|x86.Build.0 = Debug|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Debug|x86.Deploy.0 = Debug|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|Any CPU.ActiveCfg = Release|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|ARM.ActiveCfg = Release|ARM
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|ARM.Build.0 = Release|ARM
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|ARM.Deploy.0 = Release|ARM
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|ARM64.ActiveCfg = Release|ARM64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|ARM64.Build.0 = Release|ARM64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|ARM64.Deploy.0 = Release|ARM64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|x64.ActiveCfg = Release|x64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|x64.Build.0 = Release|x64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|x64.Deploy.0 = Release|x64
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|x86.ActiveCfg = Release|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|x86.Build.0 = Release|x86
+ {3D864717-2D87-4E54-BFC0-755FC2FCA2A7}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs
index 771f189..4acf069 100644
--- a/FoxTube/App.xaml.cs
+++ b/FoxTube/App.xaml.cs
@@ -267,6 +267,7 @@ namespace FoxTube
SettingsStorage.SaveData();
DownloadAgent.QuitPrompt();
+ Controls.Player.ManifestGenerator.ClearRoaming();
deferral.Complete();
Analytics.TrackEvent("Session terminated");
}
diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml
index 5bb3172..3052bc7 100644
--- a/FoxTube/Assets/Data/Patchnotes.xml
+++ b/FoxTube/Assets/Data/Patchnotes.xml
@@ -1,5 +1,61 @@
+ -
+
+ ##[Final pre-release version]
+### What's new:
+- Fixed fails when trying to retrieve history, WL or recommended
+- Fixed ads appearance
+- Fixed ads watermarks on video when it was opened through notification
+- Optimized and enchanced video playback
+- Fixed special characters appearing in toast notifications
+- History page re-design
+- Added app history management (doesn't affect web site's history)
+- Extended history information for videos (watching progress)
+- Continue where you left off feature
+- Watch later playlist now acts like regular playlist
+- If video is longer than 1 hour ads will be shown every 30 minutes
+- Added incognito mode (available in video card context menu)
+- Search suggestions now run smoother
+- FoxTube pro price is now displayed in menu
+- Fixed crashes on opening links which don't contain http(s) prefix
+- Fixed backward navigation with minimized video
+- Player re-design
+- Added quality selector to live streams playback
+- Added "Auto" quality option for videos
+- Updated design of user's avatar in the top-right corner
+
+####[NB]
+This is the final pre-release minor version. That means that until 1.0 release there will be no new features implemented. All subsequent updates will contain only bugfixes
+
+ ##[Последняя предварительная версия]
+### Что нового:
+- Исправлена проблема получения истории, "Посмотреть позже" и рекомендаций
+- Исправлен внешний вид рекламы
+- Исправлено появление водяных занков рекламы на видео при открытии через уведомления
+- Оптимизирован и улучшен просмотр видео
+- Исправлено появление особых символов в уведомлениях
+- Редизайн страницы истории
+- Добавлено управление историей просмотра приложения (не влияет на историю просмотров на сайте)
+- Расширенная информация о просмотренном видео (прогресс просмотра)
+- Функция продолжения просмотра
+- Плейлист "Посмотреть позже" теперь ведет себя как обычный плейлист
+- Если видео длится более 1 часа, рекламный баннер будет появляться каждые 30 минут
+- Добавлен режим инкогнито (доступен в контекстном меню видео карточки)
+- Подсказки при поиске работают плавнее
+- Теперь на кнопке отключения рекламы отображается текущая цена
+- Исправлены вылеты при попытке открыть ссылку не содержащую http(s) префикс
+- Исправлена обратная навигация при уменьшенном видео
+- Редизайн плеера
+- Добавлено меню выбора качества для прямых эфиров
+- Добавлено опция "Авто" в меню выбора качеста видео
+- Обновлен дизайн аватара пользователя в верхнем правом углу
+
+####[NB]
+Версия 0.6 станет последней пред релизной версией. Это значит, что новые функции не будут добовляться до полного релиза приложения. Все последующие обновления будут содержать лишь исправления
+
+
+
-
### What's new:
diff --git a/FoxTube/Classes/ManifestGenerator.cs b/FoxTube/Classes/ManifestGenerator.cs
new file mode 100644
index 0000000..0ce6ea5
--- /dev/null
+++ b/FoxTube/Classes/ManifestGenerator.cs
@@ -0,0 +1,338 @@
+using AngleSharp.Dom.Html;
+using AngleSharp.Parser.Html;
+using Google.Apis.YouTube.v3.Data;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Xml;
+using Windows.ApplicationModel.Resources;
+using Windows.Storage;
+using YoutubeExplode.Models.MediaStreams;
+
+namespace FoxTube.Controls.Player
+{
+ public static class ManifestGenerator
+ {
+ static readonly StorageFolder roaming = ApplicationData.Current.RoamingFolder;
+ public static async Task GetManifest(Video meta, VideoStreamInfo requestedQuality, MediaStreamInfoSet list)
+ {
+ StorageFile manifest;
+ try
+ {
+ manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.ReplaceExisting);
+ }
+ catch
+ {
+ manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.GenerateUniqueName);
+ }
+ try
+ {
+
+ XmlDocument doc = new XmlDocument();
+
+ XmlElement mpd = doc.CreateElement("MPD");
+ mpd.SetAttribute("mediaPresentationDuration", meta.ContentDetails.Duration);
+ mpd.SetAttribute("minBufferTime", "PT2S");
+ mpd.SetAttribute("profiles", "urn:mpeg:dash:profile:isoff-on-demand:2011");
+ mpd.SetAttribute("type", "static");
+
+ XmlElement period = doc.CreateElement("Period");
+
+ XmlElement videoSet = doc.CreateElement("AdaptationSet");
+ XmlElement videoMeta = doc.CreateElement("ContentComponent");
+ videoMeta.SetAttribute("contentType", "video");
+ videoMeta.SetAttribute("id", "1");
+ videoSet.AppendChild(videoMeta);
+
+ StreamInfo streamInfo = await GetInfoAsync(meta, requestedQuality);
+
+ foreach (var i in streamInfo.Video)
+ videoSet.AppendChild(GetVideoPresentation(doc, i));
+
+ XmlElement audioSet = doc.CreateElement("AdaptationSet");
+ XmlElement audioMeta = doc.CreateElement("ContentComponent");
+ audioMeta.SetAttribute("contentType", "audio");
+ audioMeta.SetAttribute("id", "2");
+ audioSet.AppendChild(audioMeta);
+
+ foreach (var i in streamInfo.Audio)
+ audioSet.AppendChild(GetAudioPresentation(doc, i));
+
+ doc.AppendChild(mpd);
+ mpd.AppendChild(period);
+ period.AppendChild(videoSet);
+ period.AppendChild(audioSet);
+
+ doc.Save(await manifest.OpenStreamForWriteAsync());
+
+ return $"ms-appdata:///roaming/{manifest.Name}".ToUri();
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ private static XmlElement GetVideoPresentation(XmlDocument doc, StreamInfo.VideoInfo info)
+ {
+ XmlElement representation = doc.CreateElement("Representation");
+ representation.SetAttribute("bandwidth", GetBandwidth(info.Label));
+ representation.SetAttribute("id", info.Itag);
+ representation.SetAttribute("mimeType", info.MimeType);
+ representation.SetAttribute("codecs", info.Codecs);
+ representation.SetAttribute("fps", info.Fps);
+ representation.SetAttribute("height", info.Height);
+ representation.SetAttribute("width", info.Width);
+
+ XmlElement baseUrl = doc.CreateElement("BaseURL");
+ baseUrl.InnerText = info.Url;
+ representation.AppendChild(baseUrl);
+
+ XmlElement segmentBase = doc.CreateElement("SegmentBase");
+ segmentBase.SetAttribute("indexRange", info.IndexRange);
+ representation.AppendChild(segmentBase);
+
+ XmlElement initialization = doc.CreateElement("Initialization");
+ initialization.SetAttribute("range", info.InitRange);
+ segmentBase.AppendChild(initialization);
+
+ return representation;
+ }
+
+ private static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info)
+ {
+ XmlElement audio = doc.CreateElement("Representation");
+ audio.SetAttribute("bandwidth", "200000");
+ audio.SetAttribute("id", info.Itag);
+ audio.SetAttribute("sampleRate", info.SampleRate);
+ audio.SetAttribute("numChannels", info.ChannelsCount);
+ audio.SetAttribute("codecs", info.Codecs);
+ audio.SetAttribute("mimeType", info.MimeType);
+
+ XmlElement audioUrl = doc.CreateElement("BaseURL");
+ audioUrl.InnerText = info.Url;
+ audio.AppendChild(audioUrl);
+
+ XmlElement audioSegmentBase = doc.CreateElement("SegmentBase");
+ audioSegmentBase.SetAttribute("indexRange", info.IndexRange);
+ audio.AppendChild(audioSegmentBase);
+
+ XmlElement audioInit = doc.CreateElement("Initialization");
+ audioInit.SetAttribute("range", info.InitRange);
+ audioSegmentBase.AppendChild(audioInit);
+
+ return audio;
+ }
+
+ private static async Task GetInfoAsync(Video info, VideoStreamInfo requestedQuality)
+ {
+ try
+ {
+ StreamInfo si = new StreamInfo();
+ 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);
+
+ string playerConfigRaw = Regex.Match(videoEmbedPageHtml.Source.Text,
+ @"ytplayer\.config = (?\{[^\{\}]*(((?\{)[^\{\}]*)+((?\})[^\{\}]*)+)*(?(Open)(?!))\})")
+ .Groups["Json"].Value;
+ JToken playerConfigJson = JToken.Parse(playerConfigRaw);
+
+ var playerResponseRaw = playerConfigJson.SelectToken("args.player_response").Value();
+ JToken playerResponseJson = JToken.Parse(playerResponseRaw);
+ string errorReason = playerResponseJson.SelectToken("playabilityStatus.reason")?.Value();
+ if (!string.IsNullOrWhiteSpace(errorReason))
+ throw new InvalidDataException($"Video [{info.Id}] is unplayable. Reason: {errorReason}");
+
+ List> adaptiveStreamInfosUrl = playerConfigJson.SelectToken("args.adaptive_fmts")?.Value().Split(',').Select(SplitQuery).ToList();
+ List> video =
+ requestedQuality == null ?
+ adaptiveStreamInfosUrl.FindAll(i => i.ContainsKey("quality_label")) :
+ adaptiveStreamInfosUrl.FindAll(i => i.ContainsValue(requestedQuality.VideoQualityLabel));
+ List> audio = adaptiveStreamInfosUrl.FindAll(i => i.ContainsKey("audio_sample_rate"));
+
+ foreach (var i in video)
+ si.Video.Add(new StreamInfo.VideoInfo
+ {
+ IndexRange = i["index"],
+ Url = i["url"],
+ Itag = i["itag"],
+ Fps = i["fps"],
+ Height = i["size"].Split('x')[1],
+ Width = i["size"].Split('x')[0],
+ Codecs = i["type"].Split('"')[1],
+ MimeType = i["type"].Split(';')[0],
+ Label = i["quality_label"]
+ });
+
+ foreach (var i in audio)
+ si.Audio.Add(new StreamInfo.AudioInfo
+ {
+ ChannelsCount = i["audio_channels"],
+ IndexRange = i["index"],
+ SampleRate = i["audio_sample_rate"],
+ Codecs = i["type"].Split('"')[1],
+ MimeType = i["type"].Split(';')[0],
+ Url = i["url"],
+ Itag = i["itag"]
+ });
+
+ return si;
+ }
+ catch
+ {
+ return new StreamInfo();
+ }
+ }
+
+ public static Dictionary SplitQuery(string query)
+ {
+ Dictionary dic = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ string[] paramsEncoded = query.TrimStart('?').Split("&");
+ foreach (string paramEncoded in paramsEncoded)
+ {
+ string param = WebUtility.UrlDecode(paramEncoded);
+
+ // Look for the equals sign
+ int equalsPos = param.IndexOf('=');
+ if (equalsPos <= 0)
+ continue;
+
+ // Get the key and value
+ string key = param.Substring(0, equalsPos);
+ string value = equalsPos < param.Length
+ ? param.Substring(equalsPos + 1)
+ : string.Empty;
+
+ // Add to dictionary
+ dic[key] = value;
+ }
+
+ return dic;
+ }
+
+ private static string GetBandwidth(string quality)
+ {
+ string parsed = quality.Split('p')[0];
+ switch (quality)
+ {
+ case "4320":
+ return $"16763040";
+ case "3072":
+ return $"11920384";
+ case "2880":
+ return $"11175360";
+ case "2160":
+ return $"8381520";
+ case "1440":
+ return $"5587680";
+ case "1080":
+ return $"4190760";
+ case "720":
+ return $"2073921";
+ case "480":
+ return $"869460";
+ case "360":
+ return $"686521";
+ case "240":
+ return $"264835";
+ case "144":
+ default:
+ return $"100000";
+ }
+ }
+
+ public static async Task
> ResolveLiveSteream(string url)
+ {
+ List list = new List();
+ string playlistRaw = await new HttpClient().GetStringAsync(url);
+
+ List streamsRaw = playlistRaw.Split("#EXT-X-STREAM-INF:").ToList();
+ streamsRaw.RemoveAt(0);
+ List> streams = new List>();
+ foreach (string i in streamsRaw)
+ {
+ Dictionary item = new Dictionary();
+ string[] par = i.Split('\n');
+ item.Add("URL", par[1]);
+ par = par[0].Split(',');
+ foreach (string k in par)
+ {
+ string[] pair = k.Split('=');
+ if (pair.Length < 2)
+ continue;
+ item[pair[0]] = pair[1];
+ }
+ streams.Add(item);
+ }
+
+ foreach (var i in streams)
+ {
+ StreamQuality item = new StreamQuality();
+ item.Resolution = $"{i["RESOLUTION"].Split('x')[1]}p";
+ item.Url = i["URL"].ToUri();
+ list.Add(item);
+ }
+
+ list.Add(new StreamQuality
+ {
+ Resolution = ResourceLoader.GetForCurrentView("VideoPage").GetString("/VideoPage/auto"),
+ Url = url.ToUri()
+ });
+ list.Reverse();
+
+ return list;
+ }
+
+ public static async void ClearRoaming()
+ {
+ IReadOnlyList items = await roaming.GetFilesAsync();
+ foreach (StorageFile f in items)
+ await f.DeleteAsync(StorageDeleteOption.PermanentDelete);
+ }
+ }
+
+ public class StreamInfo
+ {
+ public class VideoInfo
+ {
+ public string IndexRange { get; set; }
+ public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}";
+ public string Itag { get; set; }
+ public string Fps { get; set; }
+ public string Url { get; set; }
+ public string Codecs { get; set; }
+ public string MimeType { get; set; }
+ public string Height { get; set; }
+ public string Width { get; set; }
+ public string Label { get; set; }
+ }
+ public class AudioInfo
+ {
+ public string IndexRange { get; set; }
+ public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}";
+ public string SampleRate { get; set; }
+ public string ChannelsCount { get; set; }
+ public string Codecs { get; set; }
+ public string MimeType { get; set; }
+ public string Url { get; set; }
+ public string Itag { get; set; }
+ }
+
+ public List Video { get; } = new List();
+ public List Audio { get; } = new List();
+ }
+
+ public class StreamQuality
+ {
+ public Uri Url { get; set; }
+ public string Resolution { get; set; }
+ }
+}
diff --git a/FoxTube/Classes/Methods.cs b/FoxTube/Classes/Methods.cs
index f6ebd82..13d41d0 100644
--- a/FoxTube/Classes/Methods.cs
+++ b/FoxTube/Classes/Methods.cs
@@ -25,6 +25,51 @@ namespace FoxTube
{
object Parameter { get; set; }
}
+
+ public class HistoryItem
+ {
+ public string Id { get; set; }
+ public TimeSpan LeftOn { get; set; } = TimeSpan.FromSeconds(0);
+ }
+
+ public static class HistorySet
+ {
+ public static List Items { get; set; } = new List();
+
+ public static void Update(HistoryItem item)
+ {
+ if(Items.Exists(i => i.Id == item.Id))
+ Items.RemoveAll(i => i.Id == item.Id);
+
+ Items.Insert(0, item);
+ Save();
+ }
+
+ public static void Delete(HistoryItem item)
+ {
+ Items.Remove(item);
+ Save();
+ }
+
+ public static void Clear()
+ {
+ Items.Clear();
+ Save();
+ }
+
+ private static void Save()
+ {
+ try { ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] = JsonConvert.SerializeObject(Items); }
+ catch { }
+ }
+
+ public static void Load()
+ {
+ if (ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] != null)
+ Items = JsonConvert.DeserializeObject>(ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] as string);
+ }
+ }
+
public static class Methods
{
private static ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods");
@@ -170,8 +215,12 @@ namespace FoxTube
{
if (link.IsMatch(item))
{
+ string str = item;
+ if (!str.Contains("http"))
+ str = str.Insert(0, "http://");
+
Hyperlink hl = new Hyperlink();
- hl.Click += (s, arg) => ProcessLink(item);
+ hl.Click += (s, arg) => ProcessLink(str);
hl.Inlines.Add(new Run { Text = item });
block.Inlines.Add(hl);
}
diff --git a/FoxTube/Classes/SecretsVault.cs b/FoxTube/Classes/SecretsVault.cs
index cc0c68a..8bd72a4 100644
--- a/FoxTube/Classes/SecretsVault.cs
+++ b/FoxTube/Classes/SecretsVault.cs
@@ -137,6 +137,8 @@ namespace FoxTube
},
"user",
CancellationToken.None);
+
+ await Credential.RefreshTokenAsync(CancellationToken.None);
}
catch (AuthenticateException e)
{
@@ -255,7 +257,7 @@ namespace FoxTube
if (!requset.Products["9NP1QK556625"].IsInUserCollection)
{
AdsDisabled = false;
- Purchased?.Invoke(args:false);
+ Purchased?.Invoke(null, false, requset.Products["9NP1QK556625"].Price.FormattedPrice);
}
}
catch { }
diff --git a/FoxTube/Classes/SettingsStorage.cs b/FoxTube/Classes/SettingsStorage.cs
index d564172..09197ca 100644
--- a/FoxTube/Classes/SettingsStorage.cs
+++ b/FoxTube/Classes/SettingsStorage.cs
@@ -180,7 +180,7 @@ namespace FoxTube
{
if (storage.Values["mature"] == null)
{
- storage.Values["mature"] = MatureState.Blocked;
+ storage.Values["mature"] = (int)MatureState.Blocked;
return MatureState.Blocked;
}
else return (MatureState)storage.Values["mature"];
diff --git a/FoxTube/Controls/Adverts/CardAdvert.xaml b/FoxTube/Controls/Adverts/CardAdvert.xaml
index c947886..62e4913 100644
--- a/FoxTube/Controls/Adverts/CardAdvert.xaml
+++ b/FoxTube/Controls/Adverts/CardAdvert.xaml
@@ -29,7 +29,7 @@
@@ -120,47 +124,12 @@
-
-
+
-
-
diff --git a/FoxTube/Pages/MainPage.xaml.cs b/FoxTube/Pages/MainPage.xaml.cs
index 14ede21..b524f0c 100644
--- a/FoxTube/Pages/MainPage.xaml.cs
+++ b/FoxTube/Pages/MainPage.xaml.cs
@@ -17,6 +17,8 @@ using Windows.UI.Popups;
using Windows.Networking.Connectivity;
using Windows.ApplicationModel.Resources;
using Microsoft.Services.Store.Engagement;
+using Windows.UI.Xaml.Shapes;
+using Windows.UI.Xaml.Media;
namespace FoxTube
{
@@ -28,7 +30,7 @@ namespace FoxTube
bool wasInvoked = false;
public static event ObjectEventHandler VideoPageSizeChanged;
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
- Dictionary headers;
+ Dictionary headers;
public Page PageContent => content.Content as Page;
@@ -45,6 +47,7 @@ namespace FoxTube
SecretsVault.Purchased += async (sender, e) =>
{
removeAds.Visibility = (e[0] as bool?).Value ? Visibility.Collapsed : Visibility.Visible;
+ removeAds.Content = $"{resources.GetString("/Main/adsFree/Content")} ({e[1]})";
if (!(bool)e[0])
return;
@@ -57,22 +60,16 @@ namespace FoxTube
};
SecretsVault.Initialize();
- headers = new Dictionary()
+ headers = new Dictionary()
{
- { typeof(Settings), () => Title.Text = resources.GetString("/Main/settings/Content") },
- { typeof(ChannelPage), () => Title.Text = resources.GetString("/Main/channel") },
- { typeof(PlaylistPage), () => Title.Text = resources.GetString("/Main/playlist") },
- { typeof(Search), () => Title.Text = resources.GetString("/Main/searchPlaceholder/PlaceholderText") },
- { typeof(Subscriptions), () => Title.Text = resources.GetString("/Main/subscriptions/Content") },
- { typeof(History), () =>
- {
- if((content.Content as History).id == "HL")
- Title.Text = resources.GetString("/Main/history/Content");
- else
- Title.Text = resources.GetString("/Main/later/Content");
- } },
- { typeof(Home), () => Title.Text = resources.GetString("/Main/home/Content") },
- { typeof(Downloads), () => Title.Text = resources.GetString("/Main/downloads/Content") }
+ { typeof(Settings), resources.GetString("/Main/settings/Content") },
+ { typeof(ChannelPage), resources.GetString("/Main/channel") },
+ { typeof(PlaylistPage), resources.GetString("/Main/playlist") },
+ { typeof(Search), resources.GetString("/Main/searchPlaceholder/PlaceholderText") },
+ { typeof(Subscriptions), resources.GetString("/Main/subscriptions/Content") },
+ { typeof(History), resources.GetString("/Main/history/Content") },
+ { typeof(Home), resources.GetString("/Main/home/Content") },
+ { typeof(Downloads), resources.GetString("/Main/downloads/Content") }
};
if(StoreServicesFeedbackLauncher.IsSupported())
@@ -182,7 +179,7 @@ namespace FoxTube
myNameFlyout.Text = SecretsVault.UserInfo.Name;
myEmail.Text = SecretsVault.UserInfo.Email;
avatarFlyout.ProfilePicture = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 65, DecodePixelWidth = 65 };
- (avatar.Content as PersonPicture).ProfilePicture = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 25, DecodePixelWidth = 25 };
+ ((avatar.Content as Ellipse).Fill as ImageBrush).ImageSource = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 25, DecodePixelWidth = 25 };
avatar.Visibility = Visibility.Visible;
@@ -200,6 +197,7 @@ namespace FoxTube
for (int k = 0; k < SecretsVault.Subscriptions.Count && k < 10; k++)
nav.MenuItems.Add(SecretsVault.Subscriptions[k]);
}
+ HistorySet.Load();
break;
case false:
@@ -283,7 +281,7 @@ namespace FoxTube
content.Navigate(typeof(Home));
}
- public async void GoToVideo(string id, string playlistId = null)
+ public async void GoToVideo(string id, string playlistId = null, bool incognito = false)
{
if (SettingsStorage.CheckConnection)
try
@@ -342,7 +340,7 @@ namespace FoxTube
nav.IsPaneOpen = false;
VideoPageSizeChanged?.Invoke(this, true);
- videoPlaceholder.Navigate(typeof(VideoPage), new string[2] { id, playlistId });
+ videoPlaceholder.Navigate(typeof(VideoPage), new object[3] { id, playlistId, incognito });
Title.Text = resources.GetString("/Main/video");
}
@@ -357,6 +355,11 @@ namespace FoxTube
content.Navigate(typeof(PlaylistPage), id);
}
+ public void GoToHistory()
+ {
+ content.Navigate(typeof(History));
+ }
+
public void GoToDownloads()
{
content.Navigate(typeof(Downloads));
@@ -372,8 +375,7 @@ namespace FoxTube
else
(videoPlaceholder.Content as VideoPage).Player.Minimize();
- try { headers[content.SourcePageType](); }
- catch { }
+ Title.Text = headers[content.SourcePageType];
}
public void MinimizeVideo()
@@ -393,8 +395,7 @@ namespace FoxTube
SetNavigationMenu();
- try { headers[content.SourcePageType](); }
- catch { }
+ Title.Text = headers[content.SourcePageType];
}
void SetNavigationMenu()
@@ -444,9 +445,8 @@ namespace FoxTube
VideoPageSizeChanged?.Invoke(this, false);
SetNavigationMenu();
-
- try { headers[content.SourcePageType](); }
- catch { }
+
+ Title.Text = headers[content.SourcePageType];
}
private void Search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
@@ -455,25 +455,27 @@ namespace FoxTube
GoToSearch(new SearchParameters(search.Text));
}
- private void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
+ private async void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
- //TODO: Make it run async
- if (search.Text.Length > 2 && args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
+ await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
- try
+ if (search.Text.Length > 2 && args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
- XmlDocument doc = new XmlDocument();
- doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}");
+ try
+ {
+ XmlDocument doc = new XmlDocument();
+ doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}");
- List suggestions = new List();
+ List suggestions = new List();
- for (int i = 0; i < doc["toplevel"].ChildNodes.Count && i < 5; i++)
- suggestions.Add(doc["toplevel"].ChildNodes[i]["suggestion"].GetAttribute("data"));
+ for (int i = 0; i < doc["toplevel"].ChildNodes.Count && i < 5; i++)
+ suggestions.Add(doc["toplevel"].ChildNodes[i]["suggestion"].GetAttribute("data"));
- search.ItemsSource = suggestions;
+ search.ItemsSource = suggestions;
+ }
+ catch { search.ItemsSource = new List(); }
}
- catch { search.ItemsSource = new List(); }
- }
+ });
}
void SetNavigationItem(object item)
@@ -490,8 +492,7 @@ namespace FoxTube
public void Content_Navigated(object sender, NavigationEventArgs e)
{
- try { headers[e.SourcePageType](); }
- catch { }
+ Title.Text = headers[content.SourcePageType];
if (!wasInvoked)
{
@@ -517,29 +518,18 @@ namespace FoxTube
SetNavigationItem(null);
}
else if(e.SourcePageType == typeof(History))
- {
- if (e.Parameter.ToString() == "HL")
- SetNavigationItem(toHistory);
- else
- SetNavigationItem(toLater);
- }
+ SetNavigationItem(toHistory);
else if(e.SourcePageType == typeof(PlaylistPage))
{
if (e.Parameter.ToString() == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
SetNavigationItem(toLiked);
+ else if (e.Parameter.Equals("WL"))
+ SetNavigationItem(toLater);
}
}
else
wasInvoked = false;
- if(e.SourcePageType == typeof(History))
- {
- if(e.Parameter.ToString() == "HL")
- Title.Text = resources.GetString("/Main/history/Content");
- else if(e.Parameter.ToString() == "WL")
- Title.Text = resources.GetString("/Main/later/Content");
- }
-
if (content.CanGoBack)
nav.IsBackEnabled = true;
else
@@ -580,11 +570,11 @@ namespace FoxTube
if (args.SelectedItem == toHome)
content.Navigate(typeof(Home));
else if (args.SelectedItem == toHistory)
- content.Navigate(typeof(History), "HL");
+ content.Navigate(typeof(History));
else if (args.SelectedItem == toLiked)
content.Navigate(typeof(PlaylistPage), SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes);
else if (args.SelectedItem == toLater)
- content.Navigate(typeof(History), "WL");
+ content.Navigate(typeof(PlaylistPage), "WL");
else if (args.SelectedItem == toSubscriptions)
content.Navigate(typeof(Subscriptions));
else if (args.SelectedItem == toDownloads)
@@ -603,7 +593,7 @@ namespace FoxTube
private void Nav_BackRequested(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewBackRequestedEventArgs args)
{
- if (videoPlaceholder.Content != null)
+ if (videoPlaceholder.Content != null && videoPlaceholder.Width == double.NaN)
{
if ((videoPlaceholder.Content as VideoPage).LoadingPage.State != LoadingState.Loaded)
CloseVideo();
diff --git a/FoxTube/Pages/PlaylistPage.xaml b/FoxTube/Pages/PlaylistPage.xaml
index b4ac536..fb2db57 100644
--- a/FoxTube/Pages/PlaylistPage.xaml
+++ b/FoxTube/Pages/PlaylistPage.xaml
@@ -58,6 +58,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FoxTube/Pages/PlaylistPage.xaml.cs b/FoxTube/Pages/PlaylistPage.xaml.cs
index 2e8e04d..d404e62 100644
--- a/FoxTube/Pages/PlaylistPage.xaml.cs
+++ b/FoxTube/Pages/PlaylistPage.xaml.cs
@@ -4,6 +4,7 @@ using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics;
using System;
using System.Collections.Generic;
+using System.Linq;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
@@ -26,6 +27,7 @@ namespace FoxTube.Pages
PlaylistItemsResource.ListRequest request;
string token;
+ int page = 1;
public PlaylistPage()
{
@@ -40,6 +42,8 @@ namespace FoxTube.Pages
Parameter = e.Parameter;
if (e.Parameter == null)
loading.Error("NullReferenceException", "Unable to initialize page. Playlist ID is not stated.");
+ else if (e.Parameter as string == "WL")
+ LoadWL();
else
Initialize(e.Parameter as string);
}
@@ -103,6 +107,48 @@ namespace FoxTube.Pages
}
}
+ public async void LoadWL()
+ {
+ loading.Refresh();
+
+ try
+ {
+ playlistId = "WL";
+ share.Visibility = Visibility.Collapsed;
+ wlAlert.Visibility = Visibility.Visible;
+
+ SecretsVault.WatchLater = await Methods.GetLater();
+
+ title.Text = ResourceLoader.GetForCurrentView("Main").GetString("/Main/later/Content");
+ info.Text = $"{SecretsVault.WatchLater.Count} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}";
+ description.Text = "";
+
+ channelName.Text = SecretsVault.UserChannel.Snippet.Title;
+
+ try { avatar.ProfilePicture = new BitmapImage(SecretsVault.UserChannel.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; }
+ catch { }
+ try { thumbnail.Source = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetVideoAsync(SecretsVault.WatchLater.First())).Thumbnails.HighResUrl.ToUri()); }
+ catch { }
+
+ for (int k = 0; k < 25 && k < SecretsVault.WatchLater.Count; k++)
+ list.Add(new VideoCard(SecretsVault.WatchLater[k], "WL"));
+
+ if (list.Count >= SecretsVault.WatchLater.Count)
+ more.Visibility = Visibility.Collapsed;
+
+ loading.Close();
+ }
+ catch (Exception e)
+ {
+ loading.Error(e.GetType().ToString(), e.Message);
+ Analytics.TrackEvent("WL playlist loading error", new Dictionary()
+ {
+ { "Exception", e.GetType().ToString() },
+ { "Message", e.Message }
+ });
+ }
+ }
+
private void toChannel_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
@@ -134,6 +180,12 @@ namespace FoxTube.Pages
private async void ShowMore_Clicked()
{
+ if(playlistId == "WL")
+ {
+ MoreWL();
+ return;
+ }
+
request.PageToken = token;
PlaylistItemListResponse response = await request.ExecuteAsync();
@@ -149,6 +201,17 @@ namespace FoxTube.Pages
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
}
+ private void MoreWL()
+ {
+ for (int k = 25 * page++; k < 25 * page && k < SecretsVault.WatchLater.Count; k++)
+ list.Add(new VideoCard(SecretsVault.WatchLater[k], "WL"));
+
+ if (list.Count >= SecretsVault.WatchLater.Count)
+ more.Visibility = Visibility.Collapsed;
+ else
+ more.Complete();
+ }
+
public void DeleteItem(FrameworkElement card)
{
list.DeleteItem(card);
diff --git a/FoxTube/Pages/SettingsPages/General.xaml b/FoxTube/Pages/SettingsPages/General.xaml
index 1bd03f3..0123479 100644
--- a/FoxTube/Pages/SettingsPages/General.xaml
+++ b/FoxTube/Pages/SettingsPages/General.xaml
@@ -29,6 +29,7 @@
+
diff --git a/FoxTube/Pages/VideoPage.xaml b/FoxTube/Pages/VideoPage.xaml
index ed989d0..c303df7 100644
--- a/FoxTube/Pages/VideoPage.xaml
+++ b/FoxTube/Pages/VideoPage.xaml
@@ -58,6 +58,7 @@
+
diff --git a/FoxTube/Pages/VideoPage.xaml.cs b/FoxTube/Pages/VideoPage.xaml.cs
index 0bbcdef..bfc9be7 100644
--- a/FoxTube/Pages/VideoPage.xaml.cs
+++ b/FoxTube/Pages/VideoPage.xaml.cs
@@ -49,6 +49,8 @@ namespace FoxTube.Pages
public string videoId;
public string playlistId = null;
public Video item;
+ public HistoryItem history;
+ public bool incognito = false;
bool isExtended = false;
@@ -92,27 +94,28 @@ namespace FoxTube.Pages
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
- if (e.Parameter == null || string.IsNullOrWhiteSpace((e.Parameter as string[])[0]))
+ if (e.Parameter == null)
loading.Error("NullReferenceException", "Unable to initialize page. Video ID is not stated.");
else
- Initialize(e.Parameter as string[]);
+ Initialize(e.Parameter as object[]);
}
- public async void Initialize(string[] ids)
+ public async void Initialize(object[] ids)
{
loading.Refresh();
try
{
- videoId = ids[0];
+ videoId = ids[0] as string;
+ incognito = (bool)ids[2];
if (ids[1] != null)
- LoadPlaylist(ids[1]);
+ LoadPlaylist(ids[1] as string);
else
pivot.Items.Remove(playlist);
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,statistics,status,contentDetails,liveStreamingDetails");
- request.Id = ids[0];
+ request.Id = ids[0] as string;
request.Hl = SettingsStorage.RelevanceLanguage;
item = (await request.ExecuteAsync()).Items[0];
@@ -181,37 +184,63 @@ namespace FoxTube.Pages
List items = new List();
VideoPlaylistItem selection = null;
- //Retrieving data
- PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
- playlistRequest.Id = id;
- playlistRequest.Hl = SettingsStorage.RelevanceLanguage;
- Playlist playlistItem = (await playlistRequest.ExecuteAsync()).Items[0];
-
- PlaylistItemsResource.ListRequest listRequest = SecretsVault.Service.PlaylistItems.List("snippet");
- listRequest.MaxResults = 50;
- listRequest.PlaylistId = id;
-
- PlaylistItemListResponse listResponse;
- do
+ if (id == "WL")
{
- listResponse = await listRequest.ExecuteAsync();
- listRequest.PageToken = listResponse.NextPageToken;
+ SecretsVault.WatchLater = await Methods.GetLater();
- foreach(PlaylistItem i in listResponse.Items)
- items.Add(new VideoPlaylistItem(i.Snippet.Thumbnails.Medium.Url, i.Snippet.Title, i.Snippet.ResourceId.VideoId));
+ foreach (string i in SecretsVault.WatchLater)
+ {
+ VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet");
+ request.Id = i;
+ Video v = (await request.ExecuteAsync()).Items[0];
+ items.Add(new VideoPlaylistItem(v.Snippet.Thumbnails.Medium.Url, v.Snippet.Title, i));
+ }
+
+ selection = items.Find(i => i.Id == item.Id);
+
+ for (int k = 0; k < items.Count; k++)
+ items[k].Number = k + 1;
+
+ playlistName.Text = resources.GetString("/Main/later/Content");
+ playlistChannel.Text = SecretsVault.UserChannel.Snippet.Title;
+
+ playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{SecretsVault.WatchLater.Count}";
}
- while (!string.IsNullOrWhiteSpace(listRequest.PageToken));
+ else
+ {
- selection = items.Find(i => i.Id == item.Id);
+ //Retrieving data
+ PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
+ playlistRequest.Id = id;
+ playlistRequest.Hl = SettingsStorage.RelevanceLanguage;
+ Playlist playlistItem = (await playlistRequest.ExecuteAsync()).Items[0];
- for (int k = 0; k < items.Count; k++)
- items[k].Number = k + 1;
+ PlaylistItemsResource.ListRequest listRequest = SecretsVault.Service.PlaylistItems.List("snippet");
+ listRequest.MaxResults = 50;
+ listRequest.PlaylistId = id;
- //Setting data
- playlistName.Text = playlistItem.Snippet.Localized.Title;
- playlistChannel.Text = playlistItem.Snippet.ChannelTitle;
+ PlaylistItemListResponse listResponse;
+ do
+ {
+ listResponse = await listRequest.ExecuteAsync();
+ listRequest.PageToken = listResponse.NextPageToken;
- playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{playlistItem.ContentDetails.ItemCount}";
+ foreach (PlaylistItem i in listResponse.Items)
+ items.Add(new VideoPlaylistItem(i.Snippet.Thumbnails.Medium.Url, i.Snippet.Title, i.Snippet.ResourceId.VideoId));
+ }
+ while (!string.IsNullOrWhiteSpace(listRequest.PageToken));
+
+ selection = items.Find(i => i.Id == item.Id);
+
+ for (int k = 0; k < items.Count; k++)
+ items[k].Number = k + 1;
+
+ //Setting data
+ playlistName.Text = playlistItem.Snippet.Localized.Title;
+ playlistChannel.Text = playlistItem.Snippet.ChannelTitle;
+
+ playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{playlistItem.ContentDetails.ItemCount}";
+ }
playlistList.ItemsSource = items;
playlistList.SelectedItem = selection;
@@ -279,8 +308,15 @@ namespace FoxTube.Pages
subscribe.Visibility = Visibility.Collapsed;
}
+ if(HistorySet.Items.Exists(i => i.Id == videoId) && HistorySet.Items.Find(i => i.Id == videoId).LeftOn.TotalSeconds >= 30 && Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds - HistorySet.Items.Find(i => i.Id == videoId).LeftOn.TotalSeconds >= 30)
+ {
+ history = HistorySet.Items.Find(i => i.Id == videoId);
+ left.Visibility = Visibility.Visible;
+ left.Content = $"\xE122 {resources.GetString("/VideoPage/continue")}: {history.LeftOn.ToString(@"hh\:mm\:ss")}";
+ }
+
//Initializing player
- player.Initialize(item, item1.Snippet.Thumbnails.Medium.Url);
+ player.Initialize(item, item1.Snippet.Thumbnails.Medium.Url, incognito);
LoadRelatedVideos();
}
@@ -408,8 +444,8 @@ namespace FoxTube.Pages
private async void openBrowser_Click(object sender, RoutedEventArgs e)
{
player.Pause();
- string timecode = player.Position.TotalSeconds > 10 ?
- "&t=" + (int)player.Position.TotalSeconds + "s" : string.Empty;
+ string timecode = player.Player.Position.TotalSeconds > 10 ?
+ "&t=" + (int)player.Player.Position.TotalSeconds + "s" : string.Empty;
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}{timecode}".ToUri());
}
@@ -657,9 +693,35 @@ namespace FoxTube.Pages
await playlistDialog.ShowAsync();
}
- private void Wl_Click(object sender, RoutedEventArgs e)
+ private async void Wl_Click(object sender, RoutedEventArgs e)
{
- // TODO: Add video to WL playlist
+ if (wl.IsChecked)
+ {
+ try
+ {
+ PlaylistItem playlist = new PlaylistItem
+ {
+ Snippet = new PlaylistItemSnippet
+ {
+ PlaylistId = "WL",
+ ResourceId = new ResourceId
+ {
+ VideoId = item.Id,
+ Kind = "youtube#video"
+ }
+ }
+ };
+ PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet");
+
+ await request.ExecuteAsync();
+ }
+ catch
+ {
+ wl.IsChecked = false;
+ }
+ }
+ else
+ wl.IsChecked = true;
}
private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
@@ -713,5 +775,10 @@ namespace FoxTube.Pages
Item_Click(menuItem, null);
}
+
+ private void Left_Click(object sender, RoutedEventArgs e)
+ {
+ Player.Player.Position = history.LeftOn;
+ }
}
}
diff --git a/FoxTube/Strings/en-US/Cards.resw b/FoxTube/Strings/en-US/Cards.resw
index c435418..e695626 100644
--- a/FoxTube/Strings/en-US/Cards.resw
+++ b/FoxTube/Strings/en-US/Cards.resw
@@ -195,4 +195,7 @@
Downloads
+
+ Sponsored content
+
\ No newline at end of file
diff --git a/FoxTube/Strings/en-US/CommentsPage.resw b/FoxTube/Strings/en-US/CommentsPage.resw
index 8c91f2a..c60c8cd 100644
--- a/FoxTube/Strings/en-US/CommentsPage.resw
+++ b/FoxTube/Strings/en-US/CommentsPage.resw
@@ -141,7 +141,7 @@
Delete comment
-
+
Submit
diff --git a/FoxTube/Strings/en-US/General.resw b/FoxTube/Strings/en-US/General.resw
index e5d2758..bc61e55 100644
--- a/FoxTube/Strings/en-US/General.resw
+++ b/FoxTube/Strings/en-US/General.resw
@@ -207,4 +207,7 @@
Search relevance language
+
+ Auto
+
\ No newline at end of file
diff --git a/FoxTube/Strings/en-US/Playlist.resw b/FoxTube/Strings/en-US/Playlist.resw
index 9b515a3..117b5d6 100644
--- a/FoxTube/Strings/en-US/Playlist.resw
+++ b/FoxTube/Strings/en-US/Playlist.resw
@@ -120,6 +120,27 @@
Add to
+
+ Application
+
+
+ Clear history
+
+
+ Are you sure? This action cannot be undone
+
+
+ Delete app history
+
+
+ Unfortunately, for now we are unable to manage your youtube history. If you want to see your web history go to Website tab. If you want to delete some items, please go to website
+
+
+ Missing some stuff?
+
+
+ No
+
Open in browser
@@ -132,4 +153,16 @@
videos
+
+ Website
+
+
+ Unfortunately, for now we are unable to delete videos from 'Watch later' playlist. If you want to do that you have to visit YouTube web page
+
+
+ Don't know how to delete video from 'Watch later' playlist?
+
+
+ Yes
+
\ No newline at end of file
diff --git a/FoxTube/Strings/en-US/VideoPage.resw b/FoxTube/Strings/en-US/VideoPage.resw
index 01a8fc6..732f809 100644
--- a/FoxTube/Strings/en-US/VideoPage.resw
+++ b/FoxTube/Strings/en-US/VideoPage.resw
@@ -297,4 +297,10 @@
🔴 LIVE
+
+ Continue from
+
+
+ Auto
+
\ No newline at end of file
diff --git a/FoxTube/Strings/ru-RU/Cards.resw b/FoxTube/Strings/ru-RU/Cards.resw
index 2e61389..f43fadc 100644
--- a/FoxTube/Strings/ru-RU/Cards.resw
+++ b/FoxTube/Strings/ru-RU/Cards.resw
@@ -195,4 +195,7 @@
Загрузки
+
+ Реклама
+
\ No newline at end of file
diff --git a/FoxTube/Strings/ru-RU/CommentsPage.resw b/FoxTube/Strings/ru-RU/CommentsPage.resw
index 1e4b8f2..4befdf2 100644
--- a/FoxTube/Strings/ru-RU/CommentsPage.resw
+++ b/FoxTube/Strings/ru-RU/CommentsPage.resw
@@ -141,7 +141,7 @@
Удалить комментарий
-
+
Отправить
diff --git a/FoxTube/Strings/ru-RU/General.resw b/FoxTube/Strings/ru-RU/General.resw
index 667f827..b7a646e 100644
--- a/FoxTube/Strings/ru-RU/General.resw
+++ b/FoxTube/Strings/ru-RU/General.resw
@@ -207,4 +207,7 @@
Предпочитаемый язык поиска
+
+ Авто
+
\ No newline at end of file
diff --git a/FoxTube/Strings/ru-RU/Playlist.resw b/FoxTube/Strings/ru-RU/Playlist.resw
index 054ca36..ce18e88 100644
--- a/FoxTube/Strings/ru-RU/Playlist.resw
+++ b/FoxTube/Strings/ru-RU/Playlist.resw
@@ -120,6 +120,27 @@
Добавить в
+
+ Приложение
+
+
+ Очистить историю
+
+
+ Вы уверены? Это действие невозможно отменить
+
+
+ Очистить историю просмотра?
+
+
+ К сожалению, пока мы не можем управлять историей просмотров. Если вам нужна история просмотров сайта, перейдите на вкладку "Сайт". Если вы хотите что-то удалить из истории, пожалуйста перейдите на веб-версию сайта
+
+
+ Не можете что-то найти?
+
+
+ Нет
+
Открыть в браузере
@@ -132,4 +153,16 @@
видео
+
+ Сайт
+
+
+ К сожалению, пока мы не можем удалять видео из этого плейлиста. Если вы хотите сделать это, вам придется перейти на веб-версию сайта
+
+
+ Не знаете как удалить видео из "Посмотреть позже"?
+
+
+ Да
+
\ No newline at end of file
diff --git a/FoxTube/Strings/ru-RU/VideoPage.resw b/FoxTube/Strings/ru-RU/VideoPage.resw
index 9b1d1e8..1392183 100644
--- a/FoxTube/Strings/ru-RU/VideoPage.resw
+++ b/FoxTube/Strings/ru-RU/VideoPage.resw
@@ -297,4 +297,10 @@
🔴 ПРЯМОЙ ЭФИР
+
+ Продолжить с
+
+
+ Авто
+
\ No newline at end of file
diff --git a/FoxTube/Themes/Generic.xaml b/FoxTube/Themes/Generic.xaml
index 32bdbfc..ce5a0e6 100644
--- a/FoxTube/Themes/Generic.xaml
+++ b/FoxTube/Themes/Generic.xaml
@@ -6,9 +6,7 @@
xmlns:adverts="using:FoxTube.Controls.Adverts">
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -156,29 +73,17 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -189,156 +94,43 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+ Height="15" Width="15" Grid.Column="1"
+ AutomationProperties.AccessibilityView="Raw">
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -348,7 +140,6 @@
-
@@ -356,66 +147,34 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
- Visible
-
-
+
-
-
- Visible
-
-
+
-->
-
+
-
-
-
-
-
-
-
-
-
-
-
-
@@ -467,7 +208,6 @@
-
@@ -481,161 +221,132 @@
+
+
+
-
-
-
-
+
-
+
-
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
-
+
+
+
+
-
+
-
+
-
+
+
+
-
-
-
-
-
+
+
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
-
-
+
+
-
-
-
+
-
+
-
-
-
-
+
+
+
diff --git a/Src/FoxTube screenshots/Eng/Dark/Channel_dark_eng.png b/Src/FoxTube screenshots/Eng/Dark/Channel_dark_eng.png
index 2ec6082..80c382a 100644
Binary files a/Src/FoxTube screenshots/Eng/Dark/Channel_dark_eng.png and b/Src/FoxTube screenshots/Eng/Dark/Channel_dark_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Dark/Main_dark_eng.png b/Src/FoxTube screenshots/Eng/Dark/Main_dark_eng.png
index 7b5d6b5..67c3563 100644
Binary files a/Src/FoxTube screenshots/Eng/Dark/Main_dark_eng.png and b/Src/FoxTube screenshots/Eng/Dark/Main_dark_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Dark/Playlist_dark_eng.png b/Src/FoxTube screenshots/Eng/Dark/Playlist_dark_eng.png
index 61f73ee..813d69a 100644
Binary files a/Src/FoxTube screenshots/Eng/Dark/Playlist_dark_eng.png and b/Src/FoxTube screenshots/Eng/Dark/Playlist_dark_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Dark/Stream_dark_eng.png b/Src/FoxTube screenshots/Eng/Dark/Stream_dark_eng.png
index 8264569..dac5eba 100644
Binary files a/Src/FoxTube screenshots/Eng/Dark/Stream_dark_eng.png and b/Src/FoxTube screenshots/Eng/Dark/Stream_dark_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Dark/Video_dark_eng.png b/Src/FoxTube screenshots/Eng/Dark/Video_dark_eng.png
index ddcc2f0..f0be7b3 100644
Binary files a/Src/FoxTube screenshots/Eng/Dark/Video_dark_eng.png and b/Src/FoxTube screenshots/Eng/Dark/Video_dark_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Light/Channel_light_eng.png b/Src/FoxTube screenshots/Eng/Light/Channel_light_eng.png
index 3f08c2e..9671340 100644
Binary files a/Src/FoxTube screenshots/Eng/Light/Channel_light_eng.png and b/Src/FoxTube screenshots/Eng/Light/Channel_light_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Light/Home_light_eng.png b/Src/FoxTube screenshots/Eng/Light/Home_light_eng.png
index 7fb22ed..a271117 100644
Binary files a/Src/FoxTube screenshots/Eng/Light/Home_light_eng.png and b/Src/FoxTube screenshots/Eng/Light/Home_light_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Light/Playlist_light_eng.png b/Src/FoxTube screenshots/Eng/Light/Playlist_light_eng.png
index 18090f0..afd01cd 100644
Binary files a/Src/FoxTube screenshots/Eng/Light/Playlist_light_eng.png and b/Src/FoxTube screenshots/Eng/Light/Playlist_light_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Light/Stream_light_eng.png b/Src/FoxTube screenshots/Eng/Light/Stream_light_eng.png
index 8e29581..74306c1 100644
Binary files a/Src/FoxTube screenshots/Eng/Light/Stream_light_eng.png and b/Src/FoxTube screenshots/Eng/Light/Stream_light_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Light/Video_light_eng.png b/Src/FoxTube screenshots/Eng/Light/Video_light_eng.png
index c2347df..26c3af4 100644
Binary files a/Src/FoxTube screenshots/Eng/Light/Video_light_eng.png and b/Src/FoxTube screenshots/Eng/Light/Video_light_eng.png differ
diff --git a/Src/FoxTube screenshots/Eng/Pip_eng.png b/Src/FoxTube screenshots/Eng/Pip_eng.png
index c0fabd4..6b3f4a6 100644
Binary files a/Src/FoxTube screenshots/Eng/Pip_eng.png and b/Src/FoxTube screenshots/Eng/Pip_eng.png differ
diff --git a/Src/FoxTube screenshots/Rus/Dark/Channel_dark_rus.png b/Src/FoxTube screenshots/Rus/Dark/Channel_dark_rus.png
index 0fb15b4..29028c4 100644
Binary files a/Src/FoxTube screenshots/Rus/Dark/Channel_dark_rus.png and b/Src/FoxTube screenshots/Rus/Dark/Channel_dark_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Dark/Home_dark_rus.png b/Src/FoxTube screenshots/Rus/Dark/Home_dark_rus.png
index 2c13e2c..761359c 100644
Binary files a/Src/FoxTube screenshots/Rus/Dark/Home_dark_rus.png and b/Src/FoxTube screenshots/Rus/Dark/Home_dark_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Dark/Playlist_dark_rus.png b/Src/FoxTube screenshots/Rus/Dark/Playlist_dark_rus.png
index 1f2d332..4abc12c 100644
Binary files a/Src/FoxTube screenshots/Rus/Dark/Playlist_dark_rus.png and b/Src/FoxTube screenshots/Rus/Dark/Playlist_dark_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Dark/Stream_dark_rus.png b/Src/FoxTube screenshots/Rus/Dark/Stream_dark_rus.png
index 346b370..2eaa262 100644
Binary files a/Src/FoxTube screenshots/Rus/Dark/Stream_dark_rus.png and b/Src/FoxTube screenshots/Rus/Dark/Stream_dark_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Dark/Video_dark_rus.png b/Src/FoxTube screenshots/Rus/Dark/Video_dark_rus.png
index eab7011..36ab600 100644
Binary files a/Src/FoxTube screenshots/Rus/Dark/Video_dark_rus.png and b/Src/FoxTube screenshots/Rus/Dark/Video_dark_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Light/Channel_light_rus.png b/Src/FoxTube screenshots/Rus/Light/Channel_light_rus.png
index 6293762..75b03fe 100644
Binary files a/Src/FoxTube screenshots/Rus/Light/Channel_light_rus.png and b/Src/FoxTube screenshots/Rus/Light/Channel_light_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Light/Home_light_rus.png b/Src/FoxTube screenshots/Rus/Light/Home_light_rus.png
index eb97fb5..1b0c61c 100644
Binary files a/Src/FoxTube screenshots/Rus/Light/Home_light_rus.png and b/Src/FoxTube screenshots/Rus/Light/Home_light_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Light/Playlist_light_rus.png b/Src/FoxTube screenshots/Rus/Light/Playlist_light_rus.png
index 96d3de3..98d97db 100644
Binary files a/Src/FoxTube screenshots/Rus/Light/Playlist_light_rus.png and b/Src/FoxTube screenshots/Rus/Light/Playlist_light_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Light/Stream_light_rus.png b/Src/FoxTube screenshots/Rus/Light/Stream_light_rus.png
index 6f63caf..86cf3c4 100644
Binary files a/Src/FoxTube screenshots/Rus/Light/Stream_light_rus.png and b/Src/FoxTube screenshots/Rus/Light/Stream_light_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Light/Video_light_rus.png b/Src/FoxTube screenshots/Rus/Light/Video_light_rus.png
index f0ce71e..dfda59d 100644
Binary files a/Src/FoxTube screenshots/Rus/Light/Video_light_rus.png and b/Src/FoxTube screenshots/Rus/Light/Video_light_rus.png differ
diff --git a/Src/FoxTube screenshots/Rus/Pip_rus.png b/Src/FoxTube screenshots/Rus/Pip_rus.png
index efd2c4a..e74e2cc 100644
Binary files a/Src/FoxTube screenshots/Rus/Pip_rus.png and b/Src/FoxTube screenshots/Rus/Pip_rus.png differ