diff --git a/FoxTube/Classes/Caption.cs b/FoxTube/Classes/Caption.cs
new file mode 100644
index 0000000..14567bc
--- /dev/null
+++ b/FoxTube/Classes/Caption.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FoxTube.Classes
+{
+ public class Caption
+ {
+ public TimeSpan Start { get; private set; }
+ public TimeSpan Duration { get; private set; }
+ public TimeSpan End
+ {
+ get { return Start + Duration; }
+ }
+ public string Text { get; private set; }
+
+ public Caption(int startTime, int duration, string text)
+ {
+ Start = TimeSpan.FromMilliseconds(startTime);
+ Duration = TimeSpan.FromMilliseconds(duration);
+ Text = text;
+ }
+ }
+}
diff --git a/FoxTube/Controls/LiveCaptions.xaml b/FoxTube/Controls/LiveCaptions.xaml
index a65d4f8..20c54fb 100644
--- a/FoxTube/Controls/LiveCaptions.xaml
+++ b/FoxTube/Controls/LiveCaptions.xaml
@@ -8,9 +8,9 @@
mc:Ignorable="d"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
- Margin="0,75">
+ Margin="0,55">
-
+
diff --git a/FoxTube/Controls/LiveCaptions.xaml.cs b/FoxTube/Controls/LiveCaptions.xaml.cs
index e468565..7231128 100644
--- a/FoxTube/Controls/LiveCaptions.xaml.cs
+++ b/FoxTube/Controls/LiveCaptions.xaml.cs
@@ -1,4 +1,5 @@
-using System;
+using FoxTube.Classes;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -12,6 +13,8 @@ using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
+using System.Xml;
+using System.Diagnostics;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
@@ -19,9 +22,67 @@ namespace FoxTube.Controls
{
public sealed partial class LiveCaptions : UserControl
{
+ public MediaElement Player { get; set; }
+ DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
+ List
captions = new List();
+ Caption currentCaption = null;
+
public LiveCaptions()
{
this.InitializeComponent();
+ timer.Tick += UpdateCaption;
+ }
+
+ private void UpdateCaption(object sender, object e)
+ {
+ TimeSpan currentPosition = Player.Position;
+
+ bool found = false;
+ captions.ForEach((x) =>
+ {
+ if (Player.Position >= x.Start && Player.Position <= x.End)
+ {
+ currentCaption = x;
+ text.Text = currentCaption.Text;
+ Visibility = Visibility.Visible;
+ found = true;
+ }
+ });
+
+ if (!found)
+ {
+ currentCaption = null;
+ Visibility = Visibility.Collapsed;
+ }
+ }
+
+ public void Initialize(string source)
+ {
+ XmlDocument doc = new XmlDocument();
+ doc.Load(source);
+
+ foreach (XmlElement i in doc["timedtext"]["body"].ChildNodes)
+ captions.Add(new Caption(int.Parse(i.GetAttribute("t")), int.Parse(i.GetAttribute("d")), i.InnerText));
+
+ captions.ForEach((x) =>
+ {
+ if(Player.Position > x.Start && Player.Position < x.End)
+ {
+ currentCaption = x;
+ text.Text = currentCaption.Text;
+ Visibility = Visibility.Visible;
+ }
+ });
+
+ timer.Start();
+ }
+
+ public void Close()
+ {
+ captions.Clear();
+ currentCaption = null;
+ Visibility = Visibility.Collapsed;
+ timer.Stop();
}
}
}
diff --git a/FoxTube/Controls/VideoPlayer.xaml b/FoxTube/Controls/VideoPlayer.xaml
index f9bafc7..70cb15c 100644
--- a/FoxTube/Controls/VideoPlayer.xaml
+++ b/FoxTube/Controls/VideoPlayer.xaml
@@ -19,7 +19,7 @@
-
+
@@ -36,7 +36,7 @@
-
+
@@ -83,14 +83,17 @@
-
+
-
-
-
-
-
-
@@ -104,34 +107,34 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/FoxTube/Controls/VideoPlayer.xaml.cs b/FoxTube/Controls/VideoPlayer.xaml.cs
index 5cf1e14..b769eb6 100644
--- a/FoxTube/Controls/VideoPlayer.xaml.cs
+++ b/FoxTube/Controls/VideoPlayer.xaml.cs
@@ -20,7 +20,6 @@ using Microsoft.Toolkit.Uwp.UI.Animations;
using Google.Apis.YouTube.v3.Data;
using Google.Apis.YouTube.v3;
using Windows.UI.Xaml.Media.Imaging;
-using MyToolkit.Multimedia;
using Windows.Networking.Connectivity;
using System.Diagnostics;
using Windows.Media;
@@ -32,6 +31,10 @@ using Windows.UI;
using Windows.Graphics.Display;
using Windows.Media.Casting;
using YoutubeExplode.Models.MediaStreams;
+using YoutubeExplode;
+using YoutubeExplode.Models.ClosedCaptions;
+using System.Globalization;
+using FoxTube.Controls;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
@@ -56,11 +59,15 @@ namespace FoxTube
Video item;
string avatar;
- double timecodeBackup;
+ double timecodeBackup = 0;
+ bool needUpdateTimecode = false;
SystemMediaTransportControls systemControls = SystemMediaTransportControls.GetForCurrentView();
-
- List uris = new List();
+ LiveCaptions captions;
+
+ YoutubeClient client = new YoutubeClient();
+ IReadOnlyList ccInfo;
+ MediaStreamInfoSet streamInfo;
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
Timer t = new Timer()
@@ -81,6 +88,8 @@ namespace FoxTube
if (!ApplicationView.GetForCurrentView().IsViewModeSupported(ApplicationViewMode.CompactOverlay))
miniViewBtn.Visibility = Visibility.Collapsed;;
+ captions = grid.Children[1] as LiveCaptions;
+
volume.Value = Convert.ToDouble(settings.Values["volume"]);
videoSource.AutoPlay = (bool)settings.Values["videoAutoplay"];
@@ -94,121 +103,48 @@ namespace FoxTube
avatar = channelAvatar;
videoId = item.Id;
- #region Checking qualities availability
- uris = new List(await YouTube.GetUrisAsync(videoId));
-
- foreach (YouTubeUri i in uris.ToList())
- if (!i.HasAudio || !i.HasVideo)
- uris.Remove(i);
-
- foreach (YouTubeUri u in uris)
+ #region Retrieving info for CC and Media streams
+ //Loading streams
+ streamInfo = await client.GetVideoMediaStreamInfosAsync(item.Id);
+ foreach(MuxedStreamInfo i in streamInfo.Muxed)
{
- Debug.WriteLine("-----------------");
- Debug.WriteLine("URI: " + u.Uri);
- Debug.WriteLine("Has video: " + u.HasVideo);
- Debug.WriteLine("Has audio: " + u.HasAudio);
- Debug.WriteLine("Type: " + u.Type);
- Debug.WriteLine("Video quality: " + u.VideoQuality);
- Debug.WriteLine("Audio quality: " + u.AudioQuality);
- Debug.WriteLine("-----------------");
+ if (i.VideoQuality == VideoQuality.High2160)
+ (quality.Items[0] as ComboBoxItem).Visibility = Visibility.Visible;
+ if (i.VideoQuality == VideoQuality.High1080)
+ (quality.Items[1] as ComboBoxItem).Visibility = Visibility.Visible;
+ if (i.VideoQuality == VideoQuality.High720)
+ (quality.Items[2] as ComboBoxItem).Visibility = Visibility.Visible;
+ if (i.VideoQuality == VideoQuality.Medium480)
+ (quality.Items[3] as ComboBoxItem).Visibility = Visibility.Visible;
+ if (i.VideoQuality == VideoQuality.Medium360)
+ (quality.Items[4] as ComboBoxItem).Visibility = Visibility.Visible;
+ if (i.VideoQuality == VideoQuality.Low240)
+ (quality.Items[5] as ComboBoxItem).Visibility = Visibility.Visible;
+ if (i.VideoQuality == VideoQuality.Low144)
+ (quality.Items[6] as ComboBoxItem).Visibility = Visibility.Visible;
}
-
- foreach(YouTubeUri i in uris)
- switch(i.VideoQuality)
- {
- case YouTubeQuality.Quality2160P:
- (quality.Items[0] as ComboBoxItem).Visibility = Visibility.Visible;
- break;
- case YouTubeQuality.Quality1080P:
- (quality.Items[1] as ComboBoxItem).Visibility = Visibility.Visible;
- break;
- case YouTubeQuality.Quality720P:
- (quality.Items[2] as ComboBoxItem).Visibility = Visibility.Visible;
- break;
- case YouTubeQuality.Quality480P:
- (quality.Items[3] as ComboBoxItem).Visibility = Visibility.Visible;
- break;
- case YouTubeQuality.Quality360P:
- (quality.Items[4] as ComboBoxItem).Visibility = Visibility.Visible;
- break;
- case YouTubeQuality.Quality240P:
- (quality.Items[5] as ComboBoxItem).Visibility = Visibility.Visible;
- break;
- case YouTubeQuality.Quality144P:
- (quality.Items[6] as ComboBoxItem).Visibility = Visibility.Visible;
- break;
- }
int k = (int)settings.Values["quality"];
- switch(k)
- {
- case 0:
- foreach(YouTubeUri i in uris)
- if(i.VideoQuality == YouTubeQuality.Quality2160P)
- {
- quality.SelectedIndex = 0;
- break;
- }
- SetFirstQuality();
- break;
- case 1:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality1080P)
- {
- quality.SelectedIndex = 1;
- break;
- }
- SetFirstQuality();
- break;
- case 2:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality720P)
- {
- quality.SelectedIndex = 2;
- break;
- }
- SetFirstQuality();
- break;
- case 3:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality480P)
- {
- quality.SelectedIndex = 3;
- break;
- }
- SetFirstQuality();
- break;
- case 4:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality360P)
- {
- quality.SelectedIndex = 4;
- break;
- }
- SetFirstQuality();
- break;
- case 5:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality240P)
- {
- quality.SelectedIndex = 5;
- break;
- }
- SetFirstQuality();
- break;
- case 6:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality144P)
- {
- quality.SelectedIndex = 6;
- break;
- }
- SetFirstQuality();
- break;
- }
+ if ((quality.Items[k] as ComboBoxItem).Visibility == Visibility.Visible)
+ quality.SelectedIndex = k;
+ else
+ quality.SelectedItem = quality.Items.First(x => (x as ComboBoxItem).Visibility == Visibility.Visible);
+
+ //Loading captions
+ ccInfo = await client.GetVideoClosedCaptionTrackInfosAsync(item.Id);
+ foreach (ClosedCaptionTrackInfo cc in ccInfo)
+ subsLang.Items.Add(new ComboBoxItem()
+ {
+ Content = string.Format("{1}{0}", CultureInfo.GetCultureInfo(cc.Language.Code).DisplayName, cc.IsAutoGenerated ? " (Auto-generated)" : ""),
+ Tag = cc
+ });
+ if(ccInfo.Count > 0)
+ subsLang.SelectedIndex = 0;
+ else
+ captionsBtn.Visibility = Visibility.Collapsed;
#endregion
- if(item.ContentDetails.ContentRating != null)
+ if (item.ContentDetails.ContentRating != null)
{
if(SecretsVault.IsAuthorized && settings.Values["showMature"] != null)
{
@@ -261,34 +197,6 @@ namespace FoxTube
Visibility = Visibility.Visible;
}
- void SetFirstQuality()
- {
- switch (uris[0].VideoQuality)
- {
- case YouTubeQuality.Quality2160P:
- quality.SelectedIndex = 0;
- break;
- case YouTubeQuality.Quality1080P:
- quality.SelectedIndex = 1;
- break;
- case YouTubeQuality.Quality720P:
- quality.SelectedIndex = 2;
- break;
- case YouTubeQuality.Quality480P:
- quality.SelectedIndex = 3;
- break;
- case YouTubeQuality.Quality360P:
- quality.SelectedIndex = 4;
- break;
- case YouTubeQuality.Quality240P:
- quality.SelectedIndex = 5;
- break;
- case YouTubeQuality.Quality144P:
- quality.SelectedIndex = 6;
- break;
- }
- }
-
private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
@@ -328,16 +236,13 @@ namespace FoxTube
void Elapsed()
{
- if(!volumePane.IsOpen && !qualityPane.IsOpen && !subsPane.IsOpen)
- {
- controls.Visibility = Visibility.Collapsed;
- if(!miniView)
- touchCentral.Visibility = Visibility.Collapsed;
- if (pointerCaptured)
- Window.Current.CoreWindow.PointerCursor = null;
- seekIndicator.Visibility = Visibility.Collapsed;
- t.Stop();
- }
+ controls.Visibility = Visibility.Collapsed;
+ if (!miniView)
+ touchCentral.Visibility = Visibility.Collapsed;
+ if (pointerCaptured)
+ Window.Current.CoreWindow.PointerCursor = null;
+ seekIndicator.Visibility = Visibility.Collapsed;
+ t.Stop();
}
public void UpdateSize()
@@ -349,11 +254,6 @@ namespace FoxTube
}
}
- private void openVolume_Click(object sender, RoutedEventArgs e)
- {
- volumePane.IsOpen = true;
- }
-
private void volume_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
double v = volume.Value;
@@ -373,17 +273,6 @@ namespace FoxTube
videoSource.Volume = volume.Value * 0.01;
}
- private void openSets_Click(object sender, RoutedEventArgs e)
- {
- qualityPane.IsOpen = true;
- }
-
- private void openSubs_Click(object sender, RoutedEventArgs e)
- {
- subsPane.IsOpen = true;
- subsSwitch.IsOn = !subsSwitch.IsOn;
- }
-
private void muteBtn_Click(object sender, RoutedEventArgs e)
{
if(volume.Value != 0)
@@ -397,7 +286,7 @@ namespace FoxTube
private void UserControl_Tapped(object sender, TappedRoutedEventArgs e)
{
- if (e.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Mouse)
+ if (e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
{
touchCentral.Visibility = Visibility.Visible;
if (t.Enabled)
@@ -405,7 +294,6 @@ namespace FoxTube
else
ShowControls();
}
- //else play_Click(this, null);
}
private void UserControl_PointerMoved(object sender, PointerRoutedEventArgs e)
@@ -426,91 +314,64 @@ namespace FoxTube
private void quality_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
- if(uris.Count > 0)
+ try
{
videoSource.Pause();
timecodeBackup = videoSource.Position.TotalSeconds;
-
- switch(quality.SelectedIndex)
+
+ switch((quality.SelectedItem as ComboBoxItem).Content)
{
- case 0:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality2160P)
- {
- videoSource.Source = i.Uri;
- break;
- }
+ case "2160p":
+ videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.High2160).Url);
break;
- case 1:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality1080P)
- {
- videoSource.Source = i.Uri;
- break;
- }
+ case "1080p":
+ videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.High1080).Url);
break;
- case 2:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality720P)
- {
- videoSource.Source = i.Uri;
- break;
- }
+ case "720p":
+ videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.High720).Url);
break;
- case 3:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality480P)
- {
- videoSource.Source = i.Uri;
- break;
- }
+ case "480p":
+ videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Medium480).Url);
break;
- case 4:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality360P)
- {
- videoSource.Source = i.Uri;
- break;
- }
+ case "360p":
+ videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Medium360).Url);
break;
- case 5:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality240P)
- {
- videoSource.Source = i.Uri;
- break;
- }
+ case "240p":
+ videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Low240).Url);
break;
- case 6:
- foreach (YouTubeUri i in uris)
- if (i.VideoQuality == YouTubeQuality.Quality144P)
- {
- videoSource.Source = i.Uri;
- break;
- }
+ case "144p":
+ videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Low144).Url);
break;
}
+ needUpdateTimecode = true;
videoSource.Play();
-
- Timer timer = new Timer(1000);
- timer.Elapsed += Timer_Elapsed;
- timer.Start();
}
- }
-
- private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
- {
- await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ catch
{
- (sender as Timer).Stop();
- videoSource.Position = TimeSpan.FromSeconds(timecodeBackup);
- });
+
+ }
}
private void subsSwitch_Toggled(object sender, RoutedEventArgs e)
{
+ if (subsSwitch.IsOn)
+ subsLang.Visibility = Visibility.Visible;
+ else
+ subsLang.Visibility = Visibility.Collapsed;
+ LoadTrack();
+ }
+
+ void LoadTrack()
+ {
+ if (subsSwitch.IsOn)
+ {
+
+ captions.Initialize(ccInfo[subsLang.SelectedIndex].Url);
+ }
+ else
+ captions.Close();
}
private void fullscreen_Click(object sender, RoutedEventArgs e)
@@ -543,6 +404,12 @@ namespace FoxTube
private void videoSource_CurrentStateChanged(object sender, RoutedEventArgs e)
{
+ if(videoSource.CurrentState == MediaElementState.Playing && needUpdateTimecode)
+ {
+ videoSource.Position = TimeSpan.FromSeconds(timecodeBackup);
+ needUpdateTimecode = false;
+ }
+
switch(videoSource.CurrentState)
{
case MediaElementState.Buffering:
@@ -824,5 +691,11 @@ namespace FoxTube
break;
}
}
+
+ private void subsLang_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (subsSwitch.IsOn)
+ captions.Initialize(ccInfo[subsLang.SelectedIndex].Url);
+ }
}
}
diff --git a/FoxTube/FoxTube.csproj b/FoxTube/FoxTube.csproj
index eefc35c..3110a13 100644
--- a/FoxTube/FoxTube.csproj
+++ b/FoxTube/FoxTube.csproj
@@ -96,6 +96,7 @@
App.xaml
+