Archived
1
0

- Updated NuGet packs

- Added quality switcher to livestreams
- Fixed SettingsStorage cast
- Completed ManifestGenerator (but still doesn't work)
- Refactored and redesigned player
- Created test project
This commit is contained in:
Michael Gordeev
2019-05-14 19:34:04 +03:00
parent ade5c85eed
commit cebbf4fef5
29 changed files with 1244 additions and 798 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

+172
View File
@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{3D864717-2D87-4E54-BFC0-755FC2FCA2A7}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FoxTube.Tests</RootNamespace>
<AssemblyName>FoxTube.Tests</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.17134.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17134.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>FoxTube.Tests_TemporaryKey.pfx</PackageCertificateKeyFile>
<UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">$(VisualStudioVersion)</UnitTestPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
<OutputPath>bin\ARM64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<SDKReference Include="TestPlatform.Universal, Version=$(UnitTestPlatformVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UnitTestApp.xaml.cs">
<DependentUpon>UnitTestApp.xaml</DependentUpon>
</Compile>
<Compile Include="UnitTest.cs" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="UnitTestApp.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<None Include="FoxTube.Tests_TemporaryKey.pfx" />
</ItemGroup>
<ItemGroup>
<Content Include="Properties\UnitTestApp.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.8</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.4.0</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.4.0</Version>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
IgnorableNamespaces="uap mp">
<Identity Name="d50f8eba-0ea7-439d-850c-b1882d571731"
Publisher="CN=XFox"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="d50f8eba-0ea7-439d-850c-b1882d571731" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>FoxTube.Tests</DisplayName>
<PublisherDisplayName>XFox</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="vstest.executionengine.universal.App"
Executable="$targetnametoken$.exe"
EntryPoint="FoxTube.Tests.App">
<uap:VisualElements
DisplayName="FoxTube.Tests"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="FoxTube.Tests"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClientServer" />
<Capability Name="privateNetworkClientServer" />
</Capabilities>
</Package>
+18
View File
@@ -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)]
@@ -0,0 +1,29 @@
<!--
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
developers. However, you can modify these parameters to modify the behavior of the .NET Native
optimizer.
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
To fully enable reflection for App1.MyClass and all of its public/private members
<Type Name="App1.MyClass" Dynamic="Required All"/>
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
<Namespace Name="DataClasses.ViewModels" Serialize="All" />
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!--
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
</Application>
</Directives>
+15
View File
@@ -0,0 +1,15 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace FoxTube.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}
+7
View File
@@ -0,0 +1,7 @@
<Application
x:Class="FoxTube.Tests.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube.Tests">
</Application>
+102
View File
@@ -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
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// 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().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
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);
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}
+38 -2
View File
@@ -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
+1
View File
@@ -267,6 +267,7 @@ namespace FoxTube
SettingsStorage.SaveData();
DownloadAgent.QuitPrompt();
Controls.Player.ManifestGenerator.ClearRoaming();
deferral.Complete();
Analytics.TrackEvent("Session terminated");
}
+4
View File
@@ -19,6 +19,8 @@
- 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
</en-US>
<ru-RU>### Что нового:
- Исправлена проблема получения истории, "Посмотреть позже" и рекомендаций
@@ -37,6 +39,8 @@
- Теперь на кнопке отключения рекламы отображается текущая цена
- Исправлены вылеты при попытке открыть ссылку не содержащую http(s) префикс
- Исправлена обратная навигация при уменьшенном видео
- Редизайн плеера
- Добавлено меню выбора качества для прямых эфиров
</ru-RU>
</content>
</item>
+200 -18
View File
@@ -1,26 +1,42 @@
using System;
using System.Threading.Tasks;
using YoutubeExplode.Models.MediaStreams;
using Windows.Storage;
using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;
using Google.Apis.YouTube.v3.Data;
using System.Xml;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using Windows.Storage;
using YoutubeExplode.Models.MediaStreams;
namespace FoxTube.Controls.Player
{
public static class ManifestGenerator
{
static StorageFolder roaming = ApplicationData.Current.RoamingFolder;
static readonly StorageFolder roaming = ApplicationData.Current.RoamingFolder;
public static async Task<Uri> GetManifest(Video meta, VideoStreamInfo requestedQuality, MediaStreamInfoSet list)
{
StorageFile manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.ReplaceExisting);
StorageFile manifest;
try
{
manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.ReplaceExisting);
}
catch
{
manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.GenerateUniqueName);
}
XmlDocument doc = new XmlDocument();
XmlElement mpd = doc.CreateElement("MPD");
mpd.SetAttribute("mediaPresentationDuration", meta.ContentDetails.Duration);
mpd.SetAttribute("minBufferTime", "PT2S");
XmlElement period = doc.CreateElement("Period");
period.SetAttribute("duration", meta.ContentDetails.Duration);
@@ -31,7 +47,29 @@ namespace FoxTube.Controls.Player
videoMeta.SetAttribute("contentType", "video");
videoSet.AppendChild(videoMeta);
AppendVideoSet(doc, videoSet, list.Video);
StreamInfo streamInfo = await GetInfoAsync(meta, requestedQuality, list);
XmlElement representation = doc.CreateElement("Representation");
representation.SetAttribute("bandwidth", GetBandwidth(requestedQuality.VideoQuality));
representation.SetAttribute("height", requestedQuality.Resolution.Height.ToString());
representation.SetAttribute("width", requestedQuality.Resolution.Width.ToString());
representation.SetAttribute("id", "1");
representation.SetAttribute("codecs", requestedQuality.VideoEncoding.ToString());
representation.SetAttribute("mimeType", $"video/{requestedQuality.Container.GetFileExtension()}");
XmlElement baseUrl = doc.CreateElement("BaseURL");
baseUrl.InnerText = requestedQuality.Url;
representation.AppendChild(baseUrl);
XmlElement segmentBase = doc.CreateElement("SegmentBase");
segmentBase.SetAttribute("indexRange", streamInfo.Video.IndexRange);
representation.AppendChild(segmentBase);
XmlElement initialization = doc.CreateElement("Initialization");
initialization.SetAttribute("range", streamInfo.Video.InitRange);
segmentBase.AppendChild(initialization);
videoSet.AppendChild(representation);
XmlElement audioSet = doc.CreateElement("AdaptationSet");
XmlElement audioMeta = doc.CreateElement("ContentComponent");
@@ -39,15 +77,26 @@ namespace FoxTube.Controls.Player
audioSet.AppendChild(audioMeta);
XmlElement audio = doc.CreateElement("Representation");
audio.SetAttribute("bandwidth", "100000");
audio.SetAttribute("id", (list.Video.Count + 1).ToString());
audio.SetAttribute("mimeType", $"audio/{list.Audio.First().Container.GetFileExtension()}");
audio.SetAttribute("bandwidth", "200000");
audio.SetAttribute("id", "2");
audio.SetAttribute("sampleRate", streamInfo.Audio.SampleRate);
audio.SetAttribute("numChannels", streamInfo.Audio.ChannelsCount);
audio.SetAttribute("codecs", list.Audio.First(i => i.Container.GetFileExtension() == "webm").AudioEncoding.ToString());
audio.SetAttribute("mimeType", $"audio/{list.Audio.First(i => i.Container.GetFileExtension() == "webm").Container.GetFileExtension()}");
audioSet.AppendChild(audio);
XmlElement audioUrl = doc.CreateElement("BaseURL");
audioUrl.InnerText = list.Audio.First().Url;
audioUrl.InnerText = list.Audio.First(i => i.Container.GetFileExtension() == "webm").Url;
audio.AppendChild(audioUrl);
audioSet.AppendChild(audio);
XmlElement audioSegmentBase = doc.CreateElement("SegmentBase");
audioSegmentBase.SetAttribute("indexRange", streamInfo.Audio.IndexRange);
audioSegmentBase.SetAttribute("indexRangeExact", "true");
audio.AppendChild(audioSegmentBase);
XmlElement audioInit = doc.CreateElement("Initialization");
audioInit.SetAttribute("range", streamInfo.Audio.InitRange);
audioSegmentBase.AppendChild(audioInit);
doc.AppendChild(mpd);
mpd.AppendChild(period);
@@ -56,10 +105,69 @@ namespace FoxTube.Controls.Player
doc.Save(await manifest.OpenStreamForWriteAsync());
return "ms-appdata:///roaming/manifest.mpd".ToUri();
//TODO: Fix this shit. It doesn't work
return $"ms-appdata:///roaming/{manifest.Name}".ToUri();
}
private static void AppendVideoSet(XmlDocument doc, XmlElement root, IReadOnlyList<VideoStreamInfo> list)
private static async Task<StreamInfo> GetInfoAsync(Video info, VideoStreamInfo requestedQuality, MediaStreamInfoSet list)
{
HttpClient http = new HttpClient();
string response = HttpUtility.HtmlDecode(await http.GetStringAsync($"https://youtube.com/embed/{info.Id}?disable_polymer=true&hl=en"));
IHtmlDocument videoEmbedPageHtml = new HtmlParser().Parse(response);
string playerConfigRaw = Regex.Match(videoEmbedPageHtml.Source.Text,
@"yt\.setConfig\({'PLAYER_CONFIG': (?<Json>\{[^\{\}]*(((?<Open>\{)[^\{\}]*)+((?<Close-Open>\})[^\{\}]*)+)*(?(Open)(?!))\})")
.Groups["Json"].Value;
JToken playerConfigJson = JToken.Parse(playerConfigRaw);
string sts = playerConfigJson.SelectToken("sts").Value<string>();
string eurl = WebUtility.UrlEncode($"https://youtube.googleapis.com/v/{info.Id}");
string url = $"https://youtube.com/get_video_info?video_id={info.Id}&el=embedded&sts={sts}&eurl={eurl}&hl=en";
string raw = await http.GetStringAsync(url);
Dictionary<string, string> videoInfoDic = SplitQuery(raw);
StreamInfo si = new StreamInfo();
List<Dictionary<string, string>> adaptiveStreamInfosUrl = videoInfoDic.GetValueOrDefault("adaptive_fmts").Split(',').Select(SplitQuery).ToList();
Dictionary<string, string> video = adaptiveStreamInfosUrl.Find(i => i["quality_label"] == requestedQuality.VideoQualityLabel && i["type"].Contains(requestedQuality.Container.GetFileExtension()));
Dictionary<string, string> audio = adaptiveStreamInfosUrl.Find(i => i.ContainsKey("audio_sample_rate") && i["type"].Contains("webm"));
si.Video.IndexRange = video["index"];
si.Audio.ChannelsCount = audio["audio_channels"];
si.Audio.IndexRange = audio["index"];
si.Audio.SampleRate = audio["audio_sample_rate"];
return si;
}
public static Dictionary<string, string> SplitQuery(string query)
{
Dictionary<string, string> dic = new Dictionary<string, string>(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 void AppendVideoSet(XmlDocument doc, XmlElement root, List<VideoStreamInfo> list)
{
for (int k = 0; k < list.Count; k++)
{
@@ -87,7 +195,7 @@ namespace FoxTube.Controls.Player
private static string GetBandwidth(VideoQuality quality)
{
switch(quality)
switch (quality)
{
case VideoQuality.High4320:
return $"16763040";
@@ -114,5 +222,79 @@ namespace FoxTube.Controls.Player
return $"100000";
}
}
public static async Task<List<StreamQuality>> ResolveLiveSteream(string url)
{
List<StreamQuality> list = new List<StreamQuality>();
string playlistRaw = await new HttpClient().GetStringAsync(url);
List<string> streamsRaw = playlistRaw.Split("#EXT-X-STREAM-INF:").ToList();
streamsRaw.RemoveAt(0);
List<Dictionary<string, string>> streams = new List<Dictionary<string, string>>();
foreach (string i in streamsRaw)
{
Dictionary<string, string> item = new Dictionary<string, string>();
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 = "Auto",
Url = url.ToUri()
});
list.Reverse();
return list;
}
public static async void ClearRoaming()
{
IReadOnlyList<StorageFile> 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 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 VideoInfo Video { get; } = new VideoInfo();
public AudioInfo Audio { get; } = new AudioInfo();
}
public class StreamQuality
{
public Uri Url { get; set; }
public string Resolution { get; set; }
}
}
+2 -1
View File
@@ -59,7 +59,8 @@ namespace FoxTube
private static void Save()
{
ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] = JsonConvert.SerializeObject(Items);
try { ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] = JsonConvert.SerializeObject(Items); }
catch { }
}
public static void Load()
+1 -1
View File
@@ -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"];
+15 -13
View File
@@ -52,21 +52,23 @@ namespace FoxTube.Controls
else
rating.Text = comment.Snippet.TopLevelComment.Snippet.LikeCount.HasValue ? comment.Snippet.TopLevelComment.Snippet.LikeCount.ToString() : "";
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
if(item.Snippet.AuthorChannelId != null)
{
(specialAuthor.Child as TextBlock).Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
specialAuthor.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
editBtn.Visibility = Visibility.Visible;
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
{
(specialAuthor.Child as TextBlock).Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
specialAuthor.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
editBtn.Visibility = Visibility.Visible;
}
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == item.Snippet.ChannelId)
{
(specialAuthor.Child as TextBlock).Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
specialAuthor.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
}
}
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == item.Snippet.ChannelId)
{
(specialAuthor.Child as TextBlock).Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
specialAuthor.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
}
else
author.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
author.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
meta.Text = string.Format("{0} {1}", Methods.GetAgo(comment.Snippet.TopLevelComment.Snippet.PublishedAt.Value), comment.Snippet.TopLevelComment.Snippet.UpdatedAt != comment.Snippet.TopLevelComment.Snippet.PublishedAt ? resources.GetString("/CommentsPage/edited") : "");
Methods.FormatText(ref text, comment.Snippet.TopLevelComment.Snippet.TextDisplay);
+17 -3
View File
@@ -17,7 +17,9 @@ namespace FoxTube.Controls
set => text.FontSize = value;
}
public MediaPlayer Player { get; set; }
public bool IsActive => track != null;
public MediaElement Player { get; set; }
DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
ClosedCaption currentCaption = null;
@@ -32,7 +34,7 @@ namespace FoxTube.Controls
private void UpdateCaption(object sender, object e)
{
currentCaption = track.Captions.Find(i => i.Offset <= Player.PlaybackSession.Position && i.Offset + i.Duration > Player.PlaybackSession.Position);
currentCaption = track.Captions.Find(i => i.Offset <= Player.Position && i.Offset + i.Duration > Player.Position);
if (currentCaption != null)
text.Text = currentCaption.Text;
@@ -51,10 +53,22 @@ namespace FoxTube.Controls
public void Close()
{
timer.Stop();
track = null;
currentCaption = null;
Visibility = Visibility.Collapsed;
timer.Stop();
}
public void Minimize()
{
Margin = new Thickness(0, 0, 0, 20);
text.FontSize = 15;
}
public void Maximize()
{
Margin = new Thickness(0, 0, 0, 55);
text.FontSize = 24;
}
}
}
+407 -245
View File
@@ -1,38 +1,111 @@
using FoxTube.Controls;
using FoxTube.Controls.Adverts;
using FoxTube.Controls.Player;
using Google.Apis.YouTube.v3.Data;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Windows.ApplicationModel.Resources;
using Windows.Graphics.Display;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using YoutubeExplode;
using YoutubeExplode.Models.ClosedCaptions;
using YoutubeExplode.Models.MediaStreams;
namespace FoxTube
{
public delegate void QualityChangedEventHandler(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list);
public delegate void MinimodeChangedEventHandler(object sender, bool isOn);
public enum PlayerDisplayState { Normal, Minimized, Compact }
public class QualityComparer : IComparer<string>
{
public int Compare(string x, string y)
{
string[] xArr = x.Split('p');
string[] yArr = y.Split('p');
int qualityA = int.Parse(xArr[0]);
int qualityB = int.Parse(yArr[0]);
int framerateA = 30;
int framerateB = 30;
if (!string.IsNullOrWhiteSpace(xArr[1]))
framerateA = int.Parse(xArr[1]);
if (!string.IsNullOrWhiteSpace(yArr[1]))
framerateB = int.Parse(yArr[1]);
if (qualityA > qualityB)
return 1;
else if (qualityA < qualityB)
return -1;
else
return framerateA - framerateB > 0 ? 1 : -1;
}
}
/// <summary>
/// Custom controls for media player. MARKUP IS IN **Themes/Generic.xaml**!!!
/// </summary>
public sealed class PlayerControls : MediaTransportControls
{
public event RoutedEventHandler CloseRequested;
public event RoutedEventHandler LiveRequested;
public event MinimodeChangedEventHandler MiniModeChanged;
public event RoutedEventHandler NextRequested;
public event Event MuteClicked;
public event QualityChangedEventHandler QualityChanged;
#region Controls variables
Button minimize;
Button close;
Button miniview;
Button play;
Button next;
Button volumeMenu;
Button mute;
Button live;
Button fwd;
Button bwd;
Button captionsMenu;
Button settingsMenu;
Button fullscreen;
Button drag;
public MediaPlayer Player;
TextBlock title;
TextBlock channel;
TextBlock elapsed;
TextBlock remain;
Slider volume;
Slider seek;
ProgressBar seekIndicator;
ComboBox captions;
ComboBox quality;
ToggleSwitch captionsSwitch;
StackPanel rightFooter;
StackPanel leftFooter;
StackPanel rightHeader;
StackPanel centerStack;
Grid header;
Grid footer;
Grid center;
#endregion
PlayerDisplayState State { get; set; } = PlayerDisplayState.Normal;
public MediaElement Player { get; set; }
public PlayerAdvert Advert;
public LiveCaptions Caption;
TimeSpan timecodeBackup;
bool needUpdateTimecode = false;
public Video Meta { get; set; }
public IReadOnlyList<ClosedCaptionTrackInfo> ClosedCaptions { get; set; }
public MediaStreamInfoSet MediaStreams { get; set; }
@@ -47,31 +120,22 @@ namespace FoxTube
protected override void OnApplyTemplate()
{
AssignControls();
isReady = true;
Advert = GetTemplateChild("ad") as PlayerAdvert;
minimize.Click += Minimize_Click;
close.Click += Close_Click;
miniview.Click += Miniview_Click;
(GetTemplateChild("close") as Button).Click += Close_Click;
(GetTemplateChild("compactClose") as Button).Click += Close_Click;
next.Click += Next_Click;
volume.ValueChanged += Volume_ValueChanged;
live.Click += Live_Click;
(GetTemplateChild("minimize") as Button).Click += Minimize_Click;
(GetTemplateChild("maximize") as Button).Click += Minimize_Click;
(GetTemplateChild("CompactOverlayButton") as Button).Click += CompactOverlay_Click;
(GetTemplateChild("qualitySelector") as ComboBox).SelectionChanged += QualitySelector_SelectionChanged;
(GetTemplateChild("ccSwitch") as ToggleSwitch).Toggled += CcSwitch_Toggled;
(GetTemplateChild("ccSelector") as ComboBox).SelectionChanged += CcSelector_SelectionChanged;
(GetTemplateChild("next") as Button).Click += (s, e) => NextRequested.Invoke(s, e);
(GetTemplateChild("AudioMuteButton") as Button).Click += Mute_Click;
(GetTemplateChild("VolumeSlider") as Slider).ValueChanged += Volume_ValueChanged;
(GetTemplateChild("ProgressSlider") as Slider).ValueChanged += ProgressSlider_ValueChanged;
(GetTemplateChild("goLive") as Button).Click += (s, e) => LiveRequested.Invoke(s, e);
captionsSwitch.Toggled += CaptionsSwitch_Toggled;
captions.SelectionChanged += Captions_SelectionChanged;
quality.SelectionChanged += Quality_SelectionChanged;
seek.ValueChanged += Seek_ValueChanged;
if (queue.Count > 0)
foreach (Action i in queue)
@@ -80,96 +144,135 @@ namespace FoxTube
base.OnApplyTemplate();
}
private void Minimize_Click(object sender, RoutedEventArgs e)
void AssignControls()
{
if (sender == (GetTemplateChild("minimize") as Button))
minimize = GetTemplateChild("MinimizeButton") as Button;
close = GetTemplateChild("CloseButton") as Button;
miniview = GetTemplateChild("CompactOverlayButton") as Button;
play = GetTemplateChild("PlayPauseButton") as Button;
next = GetTemplateChild("NextButton") as Button;
volumeMenu = GetTemplateChild("VolumeMenuButton") as Button;
mute = GetTemplateChild("AudioMuteButton") as Button;
live = GetTemplateChild("PlayLiveButton") as Button;
fwd = GetTemplateChild("SkipForwardButton") as Button;
bwd = GetTemplateChild("SkipBackwardButton") as Button;
captionsMenu = GetTemplateChild("CaptionsMenuButton") as Button;
settingsMenu = GetTemplateChild("QualityMenuButton") as Button;
fullscreen = GetTemplateChild("FullWindowButton") as Button;
drag = GetTemplateChild("drag") as Button;
Advert = GetTemplateChild("AdvertControl") as PlayerAdvert;
Caption = GetTemplateChild("CaptionControl") as LiveCaptions;
title = GetTemplateChild("title") as TextBlock;
channel = GetTemplateChild("channel") as TextBlock;
elapsed = GetTemplateChild("TimeElapsedElement") as TextBlock;
remain = GetTemplateChild("TimeRemainingElement") as TextBlock;
volume = GetTemplateChild("VolumeSlider") as Slider;
seek = GetTemplateChild("ProgressSlider") as Slider;
seekIndicator = GetTemplateChild("SeekIndicator") as ProgressBar;
captions = GetTemplateChild("CaptionsSelector") as ComboBox;
quality = GetTemplateChild("QualitySelector") as ComboBox;
captionsSwitch = GetTemplateChild("CaptionsToggleSwitch") as ToggleSwitch;
rightFooter = GetTemplateChild("RightFooterControls") as StackPanel;
leftFooter = GetTemplateChild("LeftFooterControls") as StackPanel;
rightHeader = GetTemplateChild("RightHeaderControls") as StackPanel;
centerStack = GetTemplateChild("centerControls") as StackPanel;
header = GetTemplateChild("header") as Grid;
footer = GetTemplateChild("footer") as Grid;
center = GetTemplateChild("center") as Grid;
}
private void Seek_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
seekIndicator.Value = seek.Value;
}
private async void Quality_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (Meta.Snippet.LiveBroadcastContent == "live")
goto SetQuality;
if(!needUpdateTimecode)
timecodeBackup = Player.Position;
needUpdateTimecode = true;
Player.Pause();
Player.Source = null;
SetQuality:
object info = (quality.SelectedItem as ComboBoxItem).Tag;
if (info is MuxedStreamInfo)
Player.SetPlaybackSource(MediaSource.CreateFromUri((info as MuxedStreamInfo).Url.ToUri()));
else if (info is VideoStreamInfo)
Player.SetPlaybackSource(MediaSource.CreateFromUri(await ManifestGenerator.GetManifest(Meta, info as VideoStreamInfo, MediaStreams)));
else if (info is StreamQuality)
Player.SetPlaybackSource(MediaSource.CreateFromUri((info as StreamQuality).Url));
}
private void Captions_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(Caption.IsActive)
{
MiniModeChanged.Invoke(this, true);
SetMinimized();
}
else
{
MiniModeChanged.Invoke(this, false);
SetNormal();
Caption.Close();
Caption.Initialize((captions.SelectedItem as ComboBoxItem).Tag as ClosedCaptionTrackInfo);
}
}
public void Minimize()
private void CaptionsSwitch_Toggled(object sender, RoutedEventArgs e)
{
Minimize_Click(GetTemplateChild("minimize"), null);
}
private void ProgressSlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
(GetTemplateChild("compactSeek") as ProgressBar).Value = e.NewValue;
}
private void Mute_Click(object sender, RoutedEventArgs e)
{
if (((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph == "\xE74F")
Volume_ValueChanged(this, null);
if(captionsSwitch.IsOn)
Caption.Initialize((captions.SelectedItem as ComboBoxItem).Tag as ClosedCaptionTrackInfo);
else
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE74F";
Caption.Close();
}
MuteClicked?.Invoke();
private void Live_Click(object sender, RoutedEventArgs e)
{
Player.Position = Player.NaturalDuration.TimeSpan;
}
private void Next_Click(object sender, RoutedEventArgs e)
{
NextRequested.Invoke(sender, e);
}
private void Miniview_Click(object sender, RoutedEventArgs e)
{
if (State == PlayerDisplayState.Compact)
Maximize();
else
EnterMiniview();
}
public void UpdateVolumeIcon()
{
Volume_ValueChanged(this, null);
}
private void Volume_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
double v = (GetTemplateChild("VolumeSlider") as Slider).Value;
if (v == 0)
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE74F";
double v = volume.Value;
if (v == 0 || Player.IsMuted)
volumeMenu.Content = mute.Content = "\xE74F";
else if (v <= 25 && v > 0)
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE992";
volumeMenu.Content = mute.Content = "\xE992";
else if (v <= 50 && v > 25)
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE993";
volumeMenu.Content = mute.Content = "\xE993";
else if (v <= 75 && v > 50)
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE994";
volumeMenu.Content = mute.Content = "\xE994";
else if (v > 75)
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE995";
volumeMenu.Content = mute.Content = "\xE995";
}
private void CcSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
private void Player_MediaOpened(object sender, RoutedEventArgs args)
{
CcSwitch_Toggled((GetTemplateChild("ccSwitch") as ToggleSwitch), null);
}
if (!needUpdateTimecode)
return;
private void CcSwitch_Toggled(object sender, RoutedEventArgs e)
{
if((GetTemplateChild("captions") as LiveCaptions).Player == null)
(GetTemplateChild("captions") as LiveCaptions).Player = Player;
if ((sender as ToggleSwitch).IsOn)
(GetTemplateChild("captions") as LiveCaptions).Initialize(((GetTemplateChild("ccSelector") as ComboBox).SelectedItem as ComboBoxItem).Tag as ClosedCaptionTrackInfo);
else
(GetTemplateChild("captions") as LiveCaptions).Close();
}
private void QualitySelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SettingsStorage.RememberedQuality = e.AddedItems[0] as string;
MediaStreamInfo item = MediaStreams.Muxed.Find(i => i.VideoQualityLabel.Contains(e.AddedItems[0] as string));
if (item == null)
item = MediaStreams.Video.Find(i => i.VideoQualityLabel == e.AddedItems[0] as string);
QualityChanged?.Invoke(sender, item, MediaStreams);
}
private void CompactOverlay_Click(object sender, RoutedEventArgs e)
{
if((sender as Button).Margin.Top > 0)
{
Button btnCompact = sender as Button;
(GetTemplateChild("center") as Grid).Children.Remove(btnCompact);
(GetTemplateChild("headerToolbar") as StackPanel).Children.Add(btnCompact);
btnCompact.Margin = new Thickness(0);
(btnCompact.Content as FontIcon).Glyph = "\xE2B3";
SetNormal();
}
else
SetCompactView();
needUpdateTimecode = false;
Player.Position = timecodeBackup;
}
private void Close_Click(object sender, RoutedEventArgs e)
@@ -177,179 +280,238 @@ namespace FoxTube
CloseRequested?.Invoke(sender, e);
}
public void SetCompactView()
private void Minimize_Click(object sender, RoutedEventArgs e)
{
Button btn = GetTemplateChild("CompactOverlayButton") as Button;
(GetTemplateChild("headerToolbar") as StackPanel).Children.Remove(btn);
(GetTemplateChild("center") as Grid).Children.Add(btn);
btn.Margin = new Thickness(0, 32, 0, 0);
(btn.Content as FontIcon).Glyph = "\xE2B4";
Button btnPlay = GetTemplateChild("PlayPauseButton") as Button;
(GetTemplateChild("leftStack") as StackPanel).Children.Remove(btnPlay);
(GetTemplateChild("centralStack") as StackPanel).Children.Add(btnPlay);
Button btnFwd = GetTemplateChild("SkipForwardButton") as Button;
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnFwd);
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(1, btnFwd);
Button btnBwd = GetTemplateChild("SkipBackwardButton") as Button;
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnBwd);
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(0, btnBwd);
(GetTemplateChild("header") as Grid).Visibility = Visibility.Collapsed;
(GetTemplateChild("centerBgr") as Grid).Visibility = Visibility.Visible;
(GetTemplateChild("center") as Grid).Visibility = Visibility.Visible;
(GetTemplateChild("footer") as Grid).Visibility = Visibility.Collapsed;
(GetTemplateChild("maximize") as Button).Visibility = Visibility.Collapsed;
(GetTemplateChild("compactClose") as Button).Visibility = Visibility.Collapsed;
(GetTemplateChild("dragholder") as Button).Visibility = Visibility.Visible;
(GetTemplateChild("captions") as LiveCaptions).Size = 15;
(GetTemplateChild("root") as Grid).RowDefinitions[1].Height = new GridLength(0);
if (State == PlayerDisplayState.Normal)
Minimize();
else
Maximize();
}
public void SetMinimized()
public void Minimize()
{
Button btnPlay = GetTemplateChild("PlayPauseButton") as Button;
(GetTemplateChild("leftStack") as StackPanel).Children.Remove(btnPlay);
(GetTemplateChild("centralStack") as StackPanel).Children.Add(btnPlay);
Button btnFwd = GetTemplateChild("SkipForwardButton") as Button;
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnFwd);
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(1, btnFwd);
Button btnBwd = GetTemplateChild("SkipBackwardButton") as Button;
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnBwd);
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(0, btnBwd);
(GetTemplateChild("header") as Grid).Visibility = Visibility.Collapsed;
(GetTemplateChild("centerBgr") as Grid).Visibility = Visibility.Visible;
(GetTemplateChild("center") as Grid).Visibility = Visibility.Visible;
(GetTemplateChild("footer") as Grid).Visibility = Visibility.Collapsed;
(GetTemplateChild("compactClose") as Button).Visibility = Visibility.Visible;
(GetTemplateChild("maximize") as Button).Visibility = Visibility.Visible;
(GetTemplateChild("captions") as LiveCaptions).Size = 15;
(GetTemplateChild("root") as Grid).RowDefinitions[1].Height = new GridLength(0);
}
public void SetNormal()
{
Button btnPlay = GetTemplateChild("PlayPauseButton") as Button;
(GetTemplateChild("centralStack") as StackPanel).Children.Remove(btnPlay);
(GetTemplateChild("leftStack") as StackPanel).Children.Insert(0, btnPlay);
Button btnFwd = GetTemplateChild("SkipForwardButton") as Button;
(GetTemplateChild("centralStack") as StackPanel).Children.Remove(btnFwd);
(GetTemplateChild("rightStack") as StackPanel).Children.Insert(0, btnFwd);
Button btnBwd = GetTemplateChild("SkipBackwardButton") as Button;
(GetTemplateChild("centralStack") as StackPanel).Children.Remove(btnBwd);
(GetTemplateChild("rightStack") as StackPanel).Children.Insert(0, btnBwd);
(GetTemplateChild("header") as Grid).Visibility = Visibility.Visible;
(GetTemplateChild("centerBgr") as Grid).Visibility = Visibility.Collapsed;
(GetTemplateChild("center") as Grid).Visibility = Visibility.Collapsed;
(GetTemplateChild("footer") as Grid).Visibility = Visibility.Visible;
(GetTemplateChild("captions") as LiveCaptions).Size = 24;
(GetTemplateChild("dragholder") as Button).Visibility = Visibility.Collapsed;
(GetTemplateChild("root") as Grid).RowDefinitions[1].Height = new GridLength(1, GridUnitType.Auto);
}
public void SetMeta(string title, string channel)
{
if (!isReady)
{
queue.Enqueue(() => SetMeta(title, channel));
if (State == PlayerDisplayState.Minimized)
return;
}
(GetTemplateChild("title") as TextBlock).Text = title;
(GetTemplateChild("author") as TextBlock).Text = channel;
header.Children.Remove(minimize);
center.Children.Add(minimize);
rightHeader.Children.Remove(close);
center.Children.Add(close);
leftFooter.Children.Remove(play);
centerStack.Children.Add(play);
rightFooter.Children.Remove(fwd);
centerStack.Children.Add(fwd);
rightFooter.Children.Remove(bwd);
centerStack.Children.Insert(0, bwd);
header.Visibility = Visibility.Collapsed;
center.Visibility = Visibility.Visible;
footer.Visibility = Visibility.Collapsed;
minimize.Content = "\xE010";
MiniModeChanged.Invoke(this, true);
Caption.Minimize();
State = PlayerDisplayState.Minimized;
}
public void SetCaptions(IReadOnlyList<ClosedCaptionTrackInfo> list)
public void Maximize()
{
if (!isReady)
if (State == PlayerDisplayState.Normal)
return;
if(State == PlayerDisplayState.Compact)
{
queue.Enqueue(() => SetCaptions(list));
center.Children.Remove(miniview);
rightHeader.Children.Add(miniview);
centerStack.Children.Remove(play);
leftFooter.Children.Insert(0, play);
centerStack.Children.Remove(fwd);
rightFooter.Children.Insert(0, fwd);
centerStack.Children.Remove(bwd);
rightFooter.Children.Insert(0, bwd);
miniview.Margin = new Thickness();
miniview.Width = 50;
miniview.Height = 50;
}
else
{
center.Children.Remove(minimize);
header.Children.Insert(0, minimize);
center.Children.Remove(close);
rightHeader.Children.Insert(0, close);
centerStack.Children.Remove(play);
leftFooter.Children.Insert(0, play);
centerStack.Children.Remove(fwd);
rightFooter.Children.Insert(0, fwd);
centerStack.Children.Remove(bwd);
rightFooter.Children.Insert(0, bwd);
MiniModeChanged.Invoke(this, false);
}
drag.Visibility = Visibility.Collapsed;
header.Visibility = Visibility.Visible;
center.Visibility = Visibility.Collapsed;
footer.Visibility = Visibility.Visible;
miniview.Content = "\xE2B3";
minimize.Content = "\xE011";
Caption.Maximize();
State = PlayerDisplayState.Normal;
}
public void EnterMiniview()
{
if (State == PlayerDisplayState.Compact)
return;
rightHeader.Children.Remove(miniview);
center.Children.Add(miniview);
leftFooter.Children.Remove(play);
centerStack.Children.Add(play);
rightFooter.Children.Remove(fwd);
centerStack.Children.Add(fwd);
rightFooter.Children.Remove(bwd);
centerStack.Children.Insert(0, bwd);
drag.Visibility = Visibility.Visible;
header.Visibility = Visibility.Collapsed;
center.Visibility = Visibility.Visible;
footer.Visibility = Visibility.Collapsed;
miniview.Margin = new Thickness(0, 32, 0, 0);
miniview.Width = 47;
miniview.Height = 47;
miniview.Content = "\xE2B4";
Caption.Minimize();
State = PlayerDisplayState.Compact;
}
public async void Load(Video meta)
{
if(!isReady)
{
queue.Enqueue(() => Load(meta));
return;
}
ClosedCaptions = list;
Player.MediaOpened += Player_MediaOpened;
if (list.Count > 0)
Meta = meta;
title.Text = meta.Snippet.Title;
channel.Text = meta.Snippet.ChannelTitle;
MediaStreams = await new YoutubeClient().GetVideoMediaStreamInfosAsync(meta.Id);
if (meta.Snippet.LiveBroadcastContent == "none")
{
foreach(ClosedCaptionTrackInfo i in list)
(GetTemplateChild("ccSelector") as ComboBox).Items.Add(new ComboBoxItem()
ClosedCaptions = await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(meta.Id);
/*foreach (MuxedStreamInfo i in MediaStreams.Muxed)
quality.Items.Add(new ComboBoxItem
{
Content = $"{i.VideoQualityLabel} (muxed)",
Tag = i
});
foreach (VideoStreamInfo i in MediaStreams.Video)
quality.Items.Add(new ComboBoxItem
{
Content = $"{i.VideoQualityLabel} (video-only)",
Tag = i
});
foreach (AudioStreamInfo i in MediaStreams.Audio)
quality.Items.Add(new ComboBoxItem
{
Content = $"{i.Bitrate} (audio-only)",
Tag = i
});*/
uint screenHeight = DisplayInformation.GetForCurrentView().ScreenHeightInRawPixels;
List<string> qualityList = MediaStreams.GetAllVideoQualityLabels().ToList();
qualityList.Sort(new QualityComparer());
qualityList.Reverse();
foreach (string i in qualityList)
{
object tag;
if (MediaStreams.Muxed.Any(m => m.VideoQualityLabel == i && m.Resolution.Height <= screenHeight))
tag = MediaStreams.Muxed.Find(m => m.VideoQualityLabel == i);
else if (MediaStreams.Video.Any(m => m.VideoQualityLabel == i && m.Resolution.Height <= screenHeight && m.Container.GetFileExtension() == "webm"))
tag = MediaStreams.Video.Find(m => m.VideoQualityLabel == i && m.Container.GetFileExtension() == "webm");
else
continue;
quality.Items.Add(new ComboBoxItem
{
Content = i,
Tag = tag
});
}
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (quality.Items.Any(i => (i as ComboBoxItem).Content as string == s))
quality.SelectedItem = quality.Items.Find(i => (i as ComboBoxItem).Content as string == s);
else
quality.SelectedIndex = 0;
if (ClosedCaptions.Count == 0)
{
captionsMenu.Visibility = Visibility.Collapsed;
return;
}
foreach (ClosedCaptionTrackInfo i in ClosedCaptions)
captions.Items.Add(new ComboBoxItem
{
Content = string.Format("{0} {1}", CultureInfo.GetCultureInfo(i.Language.Code).DisplayName, i.IsAutoGenerated ? ResourceLoader.GetForCurrentView("VideoPage").GetString("/VideoPage/generatedCaption") : ""),
Tag = i
});
ClosedCaptionTrackInfo item = list.Find(i => SettingsStorage.RelevanceLanguage.Contains(i.Language.Code)) ?? list.Find(i => "en-US".Contains(i.Language.Code));
ClosedCaptionTrackInfo item = ClosedCaptions.Find(i => SettingsStorage.RelevanceLanguage.Contains(i.Language.Code)) ?? ClosedCaptions.Find(i => "en-US".Contains(i.Language.Code));
if (item == null)
item = list.First();
item = ClosedCaptions.First();
(GetTemplateChild("ccSelector") as ComboBox).SelectedItem = (GetTemplateChild("ccSelector") as ComboBox).Items.Find(i => (i as ComboBoxItem).Tag == item);
captions.SelectedItem = captions.Items.Find(i => (i as ComboBoxItem).Tag as ClosedCaptionTrackInfo == item);
Caption.Player = Player;
}
else
(GetTemplateChild("cc") as Button).Visibility = Visibility.Collapsed;
}
public void SetQualities(MediaStreamInfoSet list)
{
if (!isReady)
{
queue.Enqueue(() => SetQualities(list));
return;
captionsMenu.Visibility = Visibility.Collapsed;
seek.Visibility = Visibility.Collapsed;
live.Visibility = Visibility.Visible;
remain.Visibility = Visibility.Collapsed;
elapsed.FontSize = 24;
Grid.SetRow(elapsed, 0);
Grid.SetRowSpan(elapsed, 2);
elapsed.HorizontalAlignment = HorizontalAlignment.Right;
fwd.Visibility = Visibility.Collapsed;
bwd.Visibility = Visibility.Collapsed;
List<StreamQuality> list = await ManifestGenerator.ResolveLiveSteream(MediaStreams.HlsLiveStreamUrl);
foreach (StreamQuality i in list)
quality.Items.Add(new ComboBoxItem
{
Content = i.Resolution,
Tag = i
});
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (quality.Items.Any(i => (i as ComboBoxItem).Content as string == s))
quality.SelectedItem = quality.Items.Find(i => (i as ComboBoxItem).Content as string == s);
else
quality.SelectedIndex = 0;
}
MediaStreams = list;
List<VideoQuality> q = list.GetAllVideoQualities().ToList();
q.Sort();
q.Reverse();
List<string> labels = new List<string>();
q.ForEach(i => labels.Add(i.GetVideoQualityLabel()));
(GetTemplateChild("qualitySelector") as ComboBox).ItemsSource = labels;
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (labels.Contains(s))
(GetTemplateChild("qualitySelector") as ComboBox).SelectedItem = s;
else
(GetTemplateChild("qualitySelector") as ComboBox).SelectedIndex = 0;
}
public void SetStream(string url)
{
if (!isReady)
{
queue.Enqueue(() => SetStream(url));
return;
}
(GetTemplateChild("goLive") as Button).Visibility = Visibility.Visible;
(GetTemplateChild("sliderPan") as Grid).Children.Remove(GetTemplateChild("TimeElapsedElement") as TextBlock);
(GetTemplateChild("rightStack") as StackPanel).Children.Insert(0, GetTemplateChild("TimeElapsedElement") as TextBlock);
(GetTemplateChild("TimeElapsedElement") as TextBlock).VerticalAlignment = VerticalAlignment.Center;
(GetTemplateChild("TimeElapsedElement") as TextBlock).FontSize = 18;
(GetTemplateChild("TimeElapsedElement") as TextBlock).Margin = new Thickness(10, 0, 10, 0);
(GetTemplateChild("sliderPan") as Grid).Visibility = Visibility.Collapsed;
(GetTemplateChild("cc") as Button).Visibility = Visibility.Collapsed;
(GetTemplateChild("quality") as Button).Visibility = Visibility.Collapsed;
Player.Source = MediaSource.CreateFromUri(url.ToUri());
}
}
}
+6 -5
View File
@@ -8,16 +8,17 @@
mc:Ignorable="d"
d:DesignHeight="1080"
d:DesignWidth="1920"
RequestedTheme="Dark">
RequestedTheme="Dark"
Visibility="Collapsed">
<Grid Background="{StaticResource SystemChromeMediumColor}">
<MediaPlayerElement Name="videoSource" AreTransportControlsEnabled="True" PosterSource="ms-appx:///Assets/videoThumbSample.png">
<MediaPlayerElement.TransportControls>
<MediaElement Name="videoSource" AreTransportControlsEnabled="True" VolumeChanged="VideoSource_VolumeChanged" CurrentStateChanged="VideoSource_CurrentStateChanged" MarkerReached="VideoSource_MarkerReached" PosterSource="ms-appx:///Assets/videoThumbSample.png">
<MediaElement.TransportControls>
<foxtube:PlayerControls IsCompactOverlayButtonVisible="True" IsCompactOverlayEnabled="True"
IsFullWindowButtonVisible="True" IsFullWindowEnabled="True"
IsSkipBackwardButtonVisible="True" IsSkipBackwardEnabled="True"
IsSkipForwardButtonVisible="True" IsSkipForwardEnabled="True"/>
</MediaPlayerElement.TransportControls>
</MediaPlayerElement>
</MediaElement.TransportControls>
</MediaElement>
</Grid>
</UserControl>
+55 -114
View File
@@ -19,6 +19,8 @@ using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.Media.MediaProperties;
using FoxTube.Controls.Player;
using System.Diagnostics;
using Windows.UI.Xaml.Media;
namespace FoxTube
{
@@ -33,15 +35,7 @@ namespace FoxTube
public event ObjectEventHandler MiniMode;
public PlayerControls Controls => videoSource.TransportControls as PlayerControls;
public MediaPlayer Player { get; } = new MediaPlayer();
public TimeSpan Position
{
get { return videoSource.MediaPlayer.PlaybackSession.Position; }
set { videoSource.MediaPlayer.PlaybackSession.Position = value; }
}
//TimeSpan timecodeBackup;
//bool needUpdateTimecode = false;
public MediaElement Player => videoSource;
SystemMediaTransportControls systemControls;
@@ -50,7 +44,7 @@ namespace FoxTube
InitializeComponent();
}
public async void Initialize(Video meta, string channelAvatar, bool privateMode = false)
public void Initialize(Video meta, string channelAvatar, bool privateMode = false)
{
incognito = privateMode;
item = meta;
@@ -61,58 +55,41 @@ namespace FoxTube
};
videoSource.PosterSource = new BitmapImage((meta.Snippet.Thumbnails.Maxres ?? meta.Snippet.Thumbnails.Medium).Url.ToUri());
Controls.SetMeta(meta.Snippet.Localized.Title, meta.Snippet.ChannelTitle);
if (item.Snippet.LiveBroadcastContent == "none")
{
InitializeContols();
// TODO: make ads live again
/*if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5)
Player.PlaybackSession.Add(new Windows.UI.Xaml.Media.TimelineMarker { Time = Methods.GetDuration(item.ContentDetails.Duration) - TimeSpan.FromMinutes(1) });
Controls.Load(item);
if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5)
videoSource.Markers.Add(new TimelineMarker { Time = Methods.GetDuration(item.ContentDetails.Duration) - TimeSpan.FromMinutes(1) });
if(Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes >= 60)
for (int k = 1; k < Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes / 30; k++)
videoSource.Markers.Add(new Windows.UI.Xaml.Media.TimelineMarker { Time = TimeSpan.FromMinutes(k * 30) });*/
videoSource.Markers.Add(new TimelineMarker { Time = TimeSpan.FromMinutes(k * 30) });
Controls.SetQualities(await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id));
Controls.SetCaptions(await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(item.Id));
if (!privateMode)
HistorySet.Update(history);
}
else if (item.Snippet.LiveBroadcastContent == "live")
{
InitializeContols();
Controls.IsSkipBackwardButtonVisible = false;
Controls.IsSkipForwardButtonVisible = false;
Controls.LiveRequested += Controls_LiveRequested;
object i = await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id);
Controls.SetStream((await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id)).HlsLiveStreamUrl);
Controls.Load(item);
}
else
videoSource.AreTransportControlsEnabled = false;
if (!privateMode)
HistorySet.Update(history);
Visibility = Visibility.Visible;
}
private void Controls_LiveRequested(object sender, RoutedEventArgs e)
{
Position = Player.PlaybackSession.NaturalDuration;
}
public void InitializeContols()
{
videoSource.SetMediaPlayer(Player);
Player.Volume = SettingsStorage.Volume;
Player.CurrentStateChanged += VideoSource_CurrentStateChanged;
Player.MediaOpened += VideoSource_MediaOpened;
Player.VolumeChanged += VideoSource_VolumeChanged;
videoSource.Volume = SettingsStorage.Volume;
videoSource.AutoPlay = SettingsStorage.Autoplay;
Controls.CloseRequested += Controls_CloseRequested;
Controls.NextRequested += (s, e) => NextClicked?.Invoke();
Controls.QualityChanged += Controls_QualityChanged;
Controls.MiniModeChanged += Controls_MiniModeChanged;
Controls.Player = Player;
Controls.Player = videoSource;
#region System Media Transport Controls
systemControls = SystemMediaTransportControls.GetForCurrentView();
@@ -131,6 +108,25 @@ namespace FoxTube
#endregion
}
private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
videoSource.Play();
break;
case SystemMediaTransportControlsButton.Pause:
videoSource.Pause();
break;
case SystemMediaTransportControlsButton.Next:
NextClicked?.Invoke();
break;
}
});
}
public void Controls_MiniModeChanged(object sender, bool e)
{
videoSource.IsFullWindow = false;
@@ -143,101 +139,46 @@ namespace FoxTube
Controls.Minimize();
}
private async void Controls_QualityChanged(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list)
{
Player.Pause();
Player.Source = MediaSource.CreateFromUri(await ManifestGenerator.GetManifest(item, requestedQuality as VideoStreamInfo, list));
//await ManifestGenerator.GetManifest(item, requestedQuality as VideoStreamInfo, list);
/*FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".mpd");*/
//Player.Source = MediaSource.CreateFromUri("https://foxsharp.000webhostapp.com/dash_sample.mpd".ToUri());
/*timecodeBackup = videoSource.Position;
needUpdateTimecode = true;
if (requestedQuality is MuxedStreamInfo)
videoSource.Source = requestedQuality.Url.ToUri();
else
processor.Start(requestedQuality as VideoStreamInfo, list);*/
}
public void Controls_CloseRequested(object sender, RoutedEventArgs e)
{
if(systemControls != null)
systemControls.IsEnabled = false;
if (!incognito)
videoSource.Pause();
systemControls.IsEnabled = false;
videoSource.Source = null;
if (!incognito && item.Snippet.LiveBroadcastContent == "none")
{
history.LeftOn = Player.PlaybackSession.Position;
history.LeftOn = videoSource.Position;
HistorySet.Update(history);
}
Methods.MainPage.CloseVideo();
}
private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
Player.Play();
break;
case SystemMediaTransportControlsButton.Pause:
Player.Pause();
break;
case SystemMediaTransportControlsButton.Next:
NextClicked?.Invoke();
break;
}
});
}
public void Pause()
{
Player.Pause();
videoSource.Pause();
}
private void VideoSource_CurrentStateChanged(MediaPlayer sender, object e)
private void VideoSource_CurrentStateChanged(object sender, RoutedEventArgs e)
{
switch (Player.PlaybackSession.PlaybackState)
{
case MediaPlaybackState.Buffering:
case MediaPlaybackState.None:
case MediaPlaybackState.Opening:
case MediaPlaybackState.Paused:
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
if (!incognito)
{
history.LeftOn = Player.PlaybackSession.Position;
HistorySet.Update(history);
}
break;
case MediaPlaybackState.Playing:
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
break;
}
systemControls.PlaybackStatus = videoSource.CurrentState == MediaElementState.Playing ? MediaPlaybackStatus.Playing : MediaPlaybackStatus.Paused;
if(videoSource.CurrentState == MediaElementState.Paused)
if (!incognito && item.Snippet.LiveBroadcastContent == "none")
{
history.LeftOn = videoSource.Position;
HistorySet.Update(history);
}
}
private void VideoSource_MediaOpened(MediaPlayer sender, object e)
private void VideoSource_VolumeChanged(object sender, RoutedEventArgs e)
{
/*if (!needUpdateTimecode)
return;
videoSource.Position = timecodeBackup;
needUpdateTimecode = false;*/
if(videoSource.Volume != 0)
SettingsStorage.Volume = videoSource.Volume;
Controls.UpdateVolumeIcon();
}
private void VideoSource_VolumeChanged(MediaPlayer sender, object e)
{
SettingsStorage.Volume = Player.Volume;
}
private void VideoSource_MarkerReached(object sender, Windows.UI.Xaml.Media.TimelineMarkerRoutedEventArgs e)
private void VideoSource_MarkerReached(object sender, TimelineMarkerRoutedEventArgs e)
{
Controls.Advert.PushAdvert();
}
+2
View File
@@ -35,6 +35,7 @@ namespace FoxTube.Controls
Initialize(id, playlist);
}
public VideoCard(Video meta, string playlist = null)
{
InitializeComponent();
@@ -108,6 +109,7 @@ namespace FoxTube.Controls
watched.Visibility = Visibility.Visible;
if (HistorySet.Items.Exists(i => i.Id == item.Id))
{
history = HistorySet.Items.Find(i => i.Id == item.Id);
watched.Visibility = Visibility.Visible;
leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds;
}
+2 -2
View File
@@ -430,7 +430,7 @@
<Version>10.1811.22001</Version>
</PackageReference>
<PackageReference Include="Microsoft.AppCenter.Analytics">
<Version>1.14.0</Version>
<Version>2.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.8</Version>
@@ -451,7 +451,7 @@
<Version>4.3.2</Version>
</PackageReference>
<PackageReference Include="YoutubeExplode">
<Version>4.7.0-beta</Version>
<Version>4.7.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
+3 -3
View File
@@ -444,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());
}
@@ -778,7 +778,7 @@ namespace FoxTube.Pages
private void Left_Click(object sender, RoutedEventArgs e)
{
Player.Position = history.LeftOn;
Player.Player.Position = history.LeftOn;
}
}
}
+102 -391
View File
@@ -6,9 +6,7 @@
xmlns:adverts="using:FoxTube.Controls.Adverts">
<Style TargetType="local:PlayerControls">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="FlowDirection" Value="LeftToRight" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="IsTextScaleFactorEnabled" Value="False" />
<Setter Property="Template">
@@ -20,132 +18,51 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style x:Key="AppBarButtonStyle" TargetType="AppBarButton">
<Setter Property="Width" Value="{ThemeResource MTCMediaButtonWidth}" />
<Setter Property="Height" Value="{ThemeResource MTCMediaButtonHeight}" />
<Setter Property="AllowFocusOnInteraction" Value="True" />
</Style>
<Style x:Key="CommandBarStyle" TargetType="CommandBar">
<Setter Property="Height" Value="{ThemeResource MTCMediaButtonHeight}" />
<Setter Property="Background" Value="Transparent" />
</Style>
<Style x:Key="MediaTextBlockStyle" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="FontSize" Value="{ThemeResource MTCMediaFontSize}" />
<Setter Property="FontFamily" Value="{ThemeResource MTCMediaFontFamily}" />
<Setter Property="Style" Value="{ThemeResource CaptionTextBlockStyle }" />
<Setter Property="IsTextScaleFactorEnabled" Value="False" />
</Style>
<Style x:Key="PlayerSeek" TargetType="Slider">
<Setter Property="Background" Value="{ThemeResource SliderTrackFill}" />
<Setter Property="BorderThickness" Value="{ThemeResource SliderBorderThemeThickness}" />
<Setter Property="Foreground" Value="{ThemeResource SliderTrackValueFill}" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="ManipulationMode" Value="None" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="IsFocusEngagementEnabled" Value="True" />
<Setter Property="Margin" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Grid Margin="{TemplateBinding Padding}">
<Grid>
<Grid.Resources>
<Style TargetType="Thumb" x:Key="SliderThumbStyle">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="{ThemeResource SliderThumbBackground}" />
<Setter Property="Foreground" Value="{ThemeResource SystemControlBackgroundChromeMediumBrush}" />
<Style TargetType="Thumb">
<Setter Property="Background" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Ellipse x:Name="ellipse"
Stroke="{TemplateBinding Background}"
StrokeThickness="2"
Fill="{TemplateBinding Foreground}" />
<Ellipse Fill="{TemplateBinding Background}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ProgressBar" x:Key="MediaSliderProgressBarStyle">
<Setter Property="Height" Value="{ThemeResource SliderTrackThemeHeight}" />
<Setter Property="Minimum" Value="0" />
<Setter Property="Maximum" Value="100" />
<Setter Property="Foreground" Value="{ThemeResource SystemControlHighlightChromeAltLowBrush}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Normal"/>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderHeaderForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TopTickBar" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BottomTickBar" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="LeftTickBar" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RightTickBar" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
@@ -156,29 +73,17 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusEngagementStates">
<VisualState x:Name="FocusDisengaged" />
<VisualState x:Name="FocusDisengaged"/>
<VisualState x:Name="FocusEngagedHorizontal">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
@@ -189,156 +94,43 @@
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="FocusEngagedVertical">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
<DiscreteObjectKeyFrame KeyTime="0" Value="True" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="HeaderContentPresenter"
x:DeferLoadStrategy="Lazy"
Visibility="Collapsed"
Foreground="{ThemeResource SliderHeaderForeground}"
Margin="{ThemeResource SliderHeaderThemeMargin}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="{ThemeResource SliderHeaderThemeFontWeight}"
TextWrapping="Wrap" />
<Grid x:Name="SliderContainer"
Background="{ThemeResource SliderContainerBackground}"
Grid.Row="1"
Control.IsTemplateFocusTarget="True">
<Grid x:Name="HorizontalTemplate" MinHeight="44">
<Grid x:Name="SliderContainer" Control.IsTemplateFocusTarget="True" Background="{ThemeResource SliderContainerBackground}">
<Grid x:Name="HorizontalTemplate">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="15" />
<RowDefinition Height="Auto" />
<RowDefinition Height="15" />
</Grid.RowDefinitions>
<Rectangle x:Name="HorizontalTrackRect"
Fill="{TemplateBinding Background}"
Height="{ThemeResource SliderTrackThemeHeight}"
Grid.Row="1"
Grid.ColumnSpan="3" />
<ProgressBar x:Name="DownloadProgressIndicator"
Style="{StaticResource MediaSliderProgressBarStyle}"
Grid.Row="1"
Grid.ColumnSpan="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
<Rectangle x:Name="HorizontalDecreaseRect" Fill="{TemplateBinding Foreground}" Grid.Row="1" />
<TickBar x:Name="TopTickBar"
Visibility="Collapsed"
Fill="{ThemeResource SliderTickBarFill}"
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
VerticalAlignment="Bottom"
Margin="0,0,0,4"
Grid.ColumnSpan="3" />
<TickBar x:Name="HorizontalInlineTickBar"
Visibility="Collapsed"
Fill="{ThemeResource SliderInlineTickBarFill}"
Height="{ThemeResource SliderTrackThemeHeight}"
Grid.Row="1"
Grid.ColumnSpan="3" />
<TickBar x:Name="BottomTickBar"
Visibility="Collapsed"
Fill="{ThemeResource SliderTickBarFill}"
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
VerticalAlignment="Top"
Margin="0,4,0,0"
Grid.Row="2"
Grid.ColumnSpan="3" />
<Rectangle x:Name="HorizontalTrackRect" Height="{StaticResource SliderTrackThemeHeight}" Fill="{TemplateBinding Background}" Grid.ColumnSpan="3"/>
<ProgressBar x:Name="DownloadProgressIndicator" Grid.ColumnSpan="3"
Height="{ThemeResource SliderTrackThemeHeight}"
Foreground="{ThemeResource SystemControlHighlightChromeAltLowBrush}"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="1"/>
<Rectangle Height="4" x:Name="HorizontalDecreaseRect" Fill="{StaticResource SystemAccentColor}"/>
<Thumb x:Name="HorizontalThumb"
Style="{StaticResource SliderThumbStyle}"
Height="15"
Width="15"
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="1"
AutomationProperties.AccessibilityView="Raw">
<ToolTipService.ToolTip>
<ToolTip x:Name="ThumbnailTooltip">
<ContentPresenter Content="{Binding}" />
</ToolTip>
</ToolTipService.ToolTip>
Height="15" Width="15" Grid.Column="1"
AutomationProperties.AccessibilityView="Raw">
<Thumb.DataContext>
<Grid Height="112" Width="192">
<Image x:Name="ThumbnailImage"/>
<Border Background="{ThemeResource SystemControlBackgroundBaseMediumBrush}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left">
<TextBlock x:Name="TimeElapsedPreview"
Margin="6,1,6,3"
Style="{StaticResource BodyTextBlockStyle}"
IsTextScaleFactorEnabled="False"
Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}" />
</Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="112"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image x:Name="ThumbnailImage" Width="192"/>
<TextBlock Grid.Row="1" x:Name="TimeElapsedPreview"/>
</Grid>
</Thumb.DataContext>
</Thumb>
</Grid>
<Grid x:Name="VerticalTemplate" MinWidth="44" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="18" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="VerticalTrackRect"
Fill="{TemplateBinding Background}"
Width="{ThemeResource SliderTrackThemeHeight}"
Grid.Column="1"
Grid.RowSpan="3" />
<Rectangle x:Name="VerticalDecreaseRect"
Fill="{TemplateBinding Foreground}"
Grid.Column="1"
Grid.Row="2" />
<TickBar x:Name="LeftTickBar"
Visibility="Collapsed"
Fill="{ThemeResource SliderTickBarFill}"
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
HorizontalAlignment="Right"
Margin="0,0,4,0"
Grid.RowSpan="3" />
<TickBar x:Name="VerticalInlineTickBar"
Visibility="Collapsed"
Fill="{ThemeResource SliderInlineTickBarFill}"
Width="{ThemeResource SliderTrackThemeHeight}"
Grid.Column="1"
Grid.RowSpan="3" />
<TickBar x:Name="RightTickBar"
Visibility="Collapsed"
Fill="{ThemeResource SliderTickBarFill}"
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
HorizontalAlignment="Left"
Margin="4,0,0,0"
Grid.Column="2"
Grid.RowSpan="3" />
<Thumb x:Name="VerticalThumb"
Style="{StaticResource SliderThumbStyle}"
DataContext="{TemplateBinding Value}"
Width="24"
Height="8"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
FocusVisualMargin="-6,-14,-6,-14"
AutomationProperties.AccessibilityView="Raw" />
</Grid>
</Grid>
</Grid>
</ControlTemplate>
@@ -348,7 +140,6 @@
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<!-- ControlPanel Visibility states -->
<VisualStateGroup x:Name="ControlPanelVisibilityStates">
<VisualState x:Name="ControlPanelFadeIn">
<Storyboard>
@@ -356,66 +147,34 @@
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="center">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="centerBgr">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalBottom" From="50" To ="0" Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalTop" From="-50" To ="0" Duration="0:0:0.3"/>
</Storyboard>
</VisualState>
<VisualState x:Name="ControlPanelFadeOut">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Border">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="center">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="centerBgr">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="Border">
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="center">
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalBottom" From="0" To ="50" Duration="0:0:0.7"/>
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalTop" From="0" To ="-50" Duration="0:0:0.7"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<!-- ControlPanel Visibility states -->
<!-- Media state visual states -->
<VisualStateGroup x:Name="MediaStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Buffering">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="BufferingProgressBar">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loading">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="BufferingProgressBar">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="ProgressSlider"
Storyboard.TargetProperty="Opacity"
@@ -435,28 +194,10 @@
</Storyboard>
</VisualState>-->
<VisualState x:Name="Disabled">
<Storyboard />
<Storyboard/>
</VisualState>
</VisualStateGroup>
<!-- Focus states -->
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
<VisualState x:Name="PointerFocused" />
</VisualStateGroup>
<VisualStateGroup x:Name="PlayPauseStates">
<VisualState x:Name="PlayState" />
<VisualState x:Name="PauseState">
@@ -467,7 +208,6 @@
</Storyboard>
</VisualState>
</VisualStateGroup>
<!-- FullWindow states -->
<VisualStateGroup x:Name="FullWindowStates">
<VisualState x:Name="NonFullWindowState" />
<VisualState x:Name="FullWindowState">
@@ -481,161 +221,132 @@
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border">
<Border.Resources>
<Style TargetType="Button" BasedOn="{StaticResource ButtonRevealStyle}">
<Setter Property="Width" Value="50"/>
<Setter Property="Height" Value="50"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="Segoe UI, Segoe MDL2 Assets"/>
</Style>
</Border.Resources>
<Grid x:Name="ControlPanelGrid">
<Grid.Resources>
<Style TargetType="Button" BasedOn="{StaticResource ButtonRevealStyle}">
<Setter Property="Width" Value="50"/>
<Setter Property="Height" Value="50"/>
<Setter Property="Background" Value="Transparent"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="header">
<Grid.RenderTransform>
<TranslateTransform x:Name="TranslateVerticalTop"/>
</Grid.RenderTransform>
<Grid.Background>
<AcrylicBrush TintColor="#CC000000" TintOpacity=".6"/>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="#00000000" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button x:Name="minimize">
<FontIcon Glyph="&#xE011;"/>
</Button>
<Button x:Name="MinimizeButton" HorizontalAlignment="Left" VerticalAlignment="Top" Content="&#xE011;"/>
<StackPanel Margin="10,0" Grid.Column="1" VerticalAlignment="Center">
<TextBlock x:Name="title" TextTrimming="CharacterEllipsis" FontSize="20" MaxLines="1"/>
<TextBlock Foreground="LightGray" FontStyle="Italic" x:Name="author"/>
<StackPanel VerticalAlignment="Top" Grid.Column="1" Margin="10,0">
<TextBlock x:Name="title" Text="Name" TextTrimming="CharacterEllipsis" MaxLines="1" Style="{StaticResource TitleTextBlockStyle}"/>
<TextBlock x:Name="channel" Text="Channel" TextTrimming="CharacterEllipsis" FontStyle="Italic"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="2" x:Name="headerToolbar">
<Button x:Name="close">
<FontIcon Glyph="&#xE106;"/>
</Button>
<Button x:Name="CastButton">
<FontIcon Glyph="&#xEC15;"/>
</Button>
<Button x:Name="CompactOverlayButton" VerticalAlignment="Top" HorizontalAlignment="Right">
<FontIcon Glyph="&#xE2B3;"/>
</Button>
<StackPanel x:Name="RightHeaderControls" Orientation="Horizontal" VerticalAlignment="Top" Grid.Column="2">
<Button x:Name="CloseButton" HorizontalAlignment="Right" VerticalAlignment="Top" Content="&#xE106;"/>
<Button x:Name="CastButton" Content="&#xEC15;"/>
<Button x:Name="CompactOverlayButton" HorizontalAlignment="Right" VerticalAlignment="Top" Content="&#xE2B3;"/>
</StackPanel>
</Grid>
<Grid Grid.Row="1" x:Name="centerBgr" Visibility="Collapsed" IsHitTestVisible="False" Background="#66000000"/>
<Grid Grid.Row="1" x:Name="center" Visibility="Collapsed">
<Button x:Name="maximize" VerticalAlignment="Top" HorizontalAlignment="Left" IsHitTestVisible="True">
<FontIcon Glyph="&#xE010;"/>
</Button>
<Button x:Name="compactClose" VerticalAlignment="Top" HorizontalAlignment="Right">
<FontIcon Glyph="&#xE106;"/>
</Button>
<Button Height="32" Width="50" Margin="0,0,48,0" VerticalAlignment="Top" HorizontalAlignment="Right" Visibility="Collapsed" FontFamily="Segoe MDL2 Assets" Content="&#xE700;" IsHitTestVisible="False" x:Name="dragholder"/>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="centralStack"/>
<ProgressBar VerticalAlignment="Bottom" x:Name="compactSeek" Background="Transparent"/>
<Grid x:Name="center" Grid.Row="1" Visibility="Collapsed" Background="#7F000000">
<Button x:Name="drag" IsHitTestVisible="False" Height="32" Width="47" Margin="0,0,47,0" Content="&#xE700;" Visibility="Collapsed" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<StackPanel x:Name="centerControls" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<ProgressBar x:Name="SeekIndicator" Background="Transparent" VerticalAlignment="Bottom"/>
</Grid>
<Grid Grid.Row="2" x:Name="footer">
<Grid.RenderTransform>
<TranslateTransform x:Name="TranslateVerticalBottom"/>
</Grid.RenderTransform>
<Grid x:Name="footer" Grid.Row="2">
<Grid.Background>
<AcrylicBrush TintColor="#CC000000" TintOpacity=".6"/>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="#00000000" Offset="0"/>
</LinearGradientBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" x:Name="leftStack">
<ProgressBar x:Name="BufferingProgressBar" VerticalAlignment="Bottom" Grid.ColumnSpan="3" Background="Transparent" IsIndeterminate="True" Visibility="Collapsed"/>
<StackPanel x:Name="LeftFooterControls" Orientation="Horizontal" VerticalAlignment="Bottom">
<Button x:Name="PlayPauseButton">
<SymbolIcon x:Name="PlayPauseSymbol" Symbol="Play"/>
</Button>
<Button x:Name="next">
<SymbolIcon Symbol="Next"/>
</Button>
<Button x:Name="volume">
<FontIcon Glyph="&#xE15D;"/>
<Button x:Name="NextButton" Content="&#xE101;"/>
<Button x:Name="VolumeMenuButton" Content="&#xE15D;">
<Button.Flyout>
<Flyout>
<StackPanel Orientation="Horizontal" Margin="-10">
<Button x:Name="AudioMuteButton" Width="50" Height="50" Background="Transparent" FontFamily="Segoe MDL2 Assets" FontSize="25">
<FontIcon Glyph="&#xE15D;"/>
</Button>
<Slider Foreground="Red" Orientation="Horizontal" Width="150" Margin="10,5,10,0" VerticalAlignment="Center" x:Name="VolumeSlider"/>
<Button x:Name="AudioMuteButton" Content="&#xE15D;" FontFamily="Segoe MDL2 Assets" Height="50" Width="50" Background="Transparent" FontSize="20"/>
<Slider x:Name="VolumeSlider" Width="150" Margin="10,5,10,0" VerticalAlignment="Center" TickPlacement="Outside" TickFrequency="10"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button x:Name="goLive" Width="NaN" Visibility="Collapsed">
<Button x:Name="PlayLiveButton" Width="NaN" Visibility="Collapsed">
<TextBlock x:Uid="/VideoPage/live" Text="🔴 LIVE"/>
</Button>
</StackPanel>
<Grid Grid.Column="1" Margin="10,5" x:Name="sliderPan">
<TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Left" x:Name="TimeElapsedElement" Text="00:00"/>
<TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Right" x:Name="TimeRemainingElement" Text="00:00"/>
<Grid VerticalAlignment="Top" Height="4" Margin="0,15,0,0">
<ProgressBar Background="#66FFFFFF" Foreground="#66FFFFFF" x:Name="BufferingProgressBar"/>
</Grid>
<Slider x:Name="ProgressSlider" Style="{StaticResource PlayerSeek}" IsThumbToolTipEnabled="False" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<Grid x:Name="CenterFooterControls" Height="50" Grid.Column="1" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" Margin="5,0" VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="TimeElapsedElement" Text="00:00"/>
<TextBlock Grid.Row="1" Margin="5,0" VerticalAlignment="Center" HorizontalAlignment="Right" x:Name="TimeRemainingElement" Text="00:00"/>
<Slider x:Name="ProgressSlider" Style="{StaticResource PlayerSeek}" IsThumbToolTipEnabled="False" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,0"/>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Column="2" x:Name="rightStack">
<Button x:Name="SkipBackwardButton">
<FontIcon Glyph="&#xED3C;"/>
</Button>
<Button x:Name="SkipForwardButton">
<FontIcon Glyph="&#xED3D;"/>
</Button>
<StackPanel x:Name="RightFooterControls" Grid.Column="2" VerticalAlignment="Bottom" Orientation="Horizontal">
<Button x:Name="SkipBackwardButton" Content="&#xED3C;"/>
<Button x:Name="SkipForwardButton" Content="&#xED3D;"/>
<Line Stroke="White" StrokeThickness="2" Y1="5" Y2="45"/>
<Button x:Name="cc">
<SymbolIcon Symbol="ClosedCaption"/>
<Button x:Name="CaptionsMenuButton" Content="&#xE7F0;">
<Button.Flyout>
<Flyout>
<StackPanel Width="225">
<ToggleSwitch x:Name="ccSwitch" OnContent="Subtitles" OffContent="Subtitles" x:Uid="/VideoPage/subsSwitch"/>
<ComboBox x:Name="ccSelector" Header="Language" x:Uid="/VideoPage/subsSelector" PlaceholderText="No subtitles are available" HorizontalAlignment="Stretch"/>
<ToggleSwitch x:Name="CaptionsToggleSwitch" OnContent="Subtitles" OffContent="Subtitles" x:Uid="/VideoPage/subsSwitch"/>
<ComboBox x:Name="CaptionsSelector" Header="Language" x:Uid="/VideoPage/subsSelector" PlaceholderText="No captions are available" HorizontalAlignment="Stretch"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button x:Name="quality">
<SymbolIcon Symbol="Setting"/>
<Button x:Name="QualityMenuButton" Content="&#xE115;">
<Button.Flyout>
<Flyout>
<ComboBox Width="225" x:Uid="/VideoPage/qualitySelector" Header="Quality" x:Name="qualitySelector"/>
<ComboBox Width="225" x:Name="QualitySelector" Header="Language" x:Uid="/VideoPage/qualitySelector"/>
</Flyout>
</Button.Flyout>
</Button>
<Button x:Name="FullWindowButton">
<SymbolIcon x:Name="FullWindowSymbol" Symbol="FullScreen"/>
</Button>
</StackPanel>
</Grid>
</Grid>
</Border>
<controls:LiveCaptions Visibility="Collapsed" x:Name="captions"/>
<adverts:PlayerAdvert Grid.Row="1" x:Name="ad" VerticalAlignment="Bottom"/>
<controls:LiveCaptions Visibility="Collapsed" x:Name="CaptionControl"/>
<adverts:PlayerAdvert Grid.Row="1" x:Name="AdvertControl" VerticalAlignment="Bottom"/>
</Grid>
</ControlTemplate>
</Setter.Value>