- 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
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
@@ -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)' < '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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace FoxTube.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class UnitTest1
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestMethod1()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -267,6 +267,7 @@ namespace FoxTube
|
||||
|
||||
SettingsStorage.SaveData();
|
||||
DownloadAgent.QuitPrompt();
|
||||
Controls.Player.ManifestGenerator.ClearRoaming();
|
||||
deferral.Complete();
|
||||
Analytics.TrackEvent("Session terminated");
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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=""/>
|
||||
</Button>
|
||||
<Button x:Name="MinimizeButton" HorizontalAlignment="Left" VerticalAlignment="Top" Content=""/>
|
||||
|
||||
<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=""/>
|
||||
</Button>
|
||||
<Button x:Name="CastButton">
|
||||
<FontIcon Glyph=""/>
|
||||
</Button>
|
||||
<Button x:Name="CompactOverlayButton" VerticalAlignment="Top" HorizontalAlignment="Right">
|
||||
<FontIcon Glyph=""/>
|
||||
</Button>
|
||||
<StackPanel x:Name="RightHeaderControls" Orientation="Horizontal" VerticalAlignment="Top" Grid.Column="2">
|
||||
<Button x:Name="CloseButton" HorizontalAlignment="Right" VerticalAlignment="Top" Content=""/>
|
||||
<Button x:Name="CastButton" Content=""/>
|
||||
<Button x:Name="CompactOverlayButton" HorizontalAlignment="Right" VerticalAlignment="Top" Content=""/>
|
||||
</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=""/>
|
||||
</Button>
|
||||
<Button x:Name="compactClose" VerticalAlignment="Top" HorizontalAlignment="Right">
|
||||
<FontIcon Glyph=""/>
|
||||
</Button>
|
||||
<Button Height="32" Width="50" Margin="0,0,48,0" VerticalAlignment="Top" HorizontalAlignment="Right" Visibility="Collapsed" FontFamily="Segoe MDL2 Assets" Content="" 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="" 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=""/>
|
||||
<Button x:Name="NextButton" Content=""/>
|
||||
<Button x:Name="VolumeMenuButton" Content="">
|
||||
<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=""/>
|
||||
</Button>
|
||||
<Slider Foreground="Red" Orientation="Horizontal" Width="150" Margin="10,5,10,0" VerticalAlignment="Center" x:Name="VolumeSlider"/>
|
||||
<Button x:Name="AudioMuteButton" Content="" 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=""/>
|
||||
</Button>
|
||||
<Button x:Name="SkipForwardButton">
|
||||
<FontIcon Glyph=""/>
|
||||
</Button>
|
||||
|
||||
<StackPanel x:Name="RightFooterControls" Grid.Column="2" VerticalAlignment="Bottom" Orientation="Horizontal">
|
||||
<Button x:Name="SkipBackwardButton" Content=""/>
|
||||
<Button x:Name="SkipForwardButton" Content=""/>
|
||||
<Line Stroke="White" StrokeThickness="2" Y1="5" Y2="45"/>
|
||||
|
||||
<Button x:Name="cc">
|
||||
<SymbolIcon Symbol="ClosedCaption"/>
|
||||
<Button x:Name="CaptionsMenuButton" Content="">
|
||||
<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="">
|
||||
<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>
|
||||
|
||||