242 lines
8.6 KiB
C#
242 lines
8.6 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Windows.UI.Core;
|
|
using Windows.UI.Xaml;
|
|
using Windows.UI.Xaml.Controls;
|
|
using Google.Apis.YouTube.v3.Data;
|
|
using Windows.UI.Xaml.Media.Imaging;
|
|
using Windows.Media;
|
|
using Windows.Storage.Streams;
|
|
using YoutubeExplode.Models.MediaStreams;
|
|
using YoutubeExplode;
|
|
using System.Diagnostics;
|
|
|
|
namespace FoxTube
|
|
{
|
|
public sealed partial class VideoPlayer : UserControl
|
|
{
|
|
public Video item;
|
|
public string avatar;
|
|
|
|
public event Event NextClicked;
|
|
public event ObjectEventHandler MiniMode;
|
|
public PlayerControls Controls => videoSource.TransportControls as PlayerControls;
|
|
public TimeSpan Position => videoSource.Position;
|
|
|
|
bool audioLoaded = false;
|
|
bool videoLoaded = false;
|
|
bool isMuxed = false;
|
|
DispatcherTimer muxedTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
|
|
|
|
TimeSpan timecodeBackup;
|
|
bool needUpdateTimecode = false;
|
|
|
|
SystemMediaTransportControls systemControls;
|
|
|
|
public VideoPlayer()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
public async void Initialize(Video meta, string channelAvatar, bool privateMode = false)
|
|
{
|
|
item = meta;
|
|
avatar = channelAvatar;
|
|
|
|
videoSource.PosterSource = new BitmapImage((meta.Snippet.Thumbnails.Maxres ?? meta.Snippet.Thumbnails.Medium).Url.ToUri());
|
|
Controls.SetMeta(meta.Snippet.Title, meta.Snippet.ChannelTitle);
|
|
|
|
if (item.Snippet.LiveBroadcastContent == "none")
|
|
{
|
|
InitializeContols();
|
|
if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5)
|
|
videoSource.Markers.Add(new Windows.UI.Xaml.Media.TimelineMarker { Time = Methods.GetDuration(item.ContentDetails.Duration) - TimeSpan.FromMinutes(1) });
|
|
Controls.SetQualities(await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id));
|
|
Controls.SetCaptions(await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(item.Id));
|
|
}
|
|
else if (item.Snippet.LiveBroadcastContent == "live")
|
|
{
|
|
InitializeContols();
|
|
Controls.IsSkipBackwardButtonVisible = false;
|
|
Controls.IsSkipForwardButtonVisible = false;
|
|
Controls.LiveRequested += Controls_LiveRequested;
|
|
Controls.SetStream((await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id)).HlsLiveStreamUrl);
|
|
}
|
|
else
|
|
videoSource.AreTransportControlsEnabled = false;
|
|
|
|
if (!privateMode)
|
|
Debug.WriteLine("TODO: history entry creation");
|
|
// TODO: Create history entry
|
|
|
|
Visibility = Visibility.Visible;
|
|
}
|
|
|
|
private void Controls_LiveRequested(object sender, RoutedEventArgs e)
|
|
{
|
|
videoSource.Position = videoSource.NaturalDuration.TimeSpan;
|
|
}
|
|
|
|
public void InitializeContols()
|
|
{
|
|
videoSource.Volume = SettingsStorage.Volume;
|
|
|
|
muxedTimer.Tick += (s, e) =>
|
|
{
|
|
if (!Enumerable.Range(-100, 100).Contains((int)(videoSource.Position - audioSource.Position).TotalMilliseconds))
|
|
audioSource.Position = videoSource.Position;
|
|
};
|
|
|
|
Controls.CloseRequested += Controls_CloseRequested;
|
|
Controls.NextRequested += (s, e) => NextClicked?.Invoke();
|
|
Controls.QualityChanged += Controls_QualityChanged;
|
|
Controls.MiniModeChanged += Controls_MiniModeChanged;
|
|
Controls.Player = videoSource;
|
|
|
|
#region System Media Transport Controls
|
|
systemControls = SystemMediaTransportControls.GetForCurrentView();
|
|
systemControls.IsNextEnabled = true;
|
|
systemControls.IsPauseEnabled = true;
|
|
systemControls.IsPlayEnabled = true;
|
|
|
|
systemControls.DisplayUpdater.Type = MediaPlaybackType.Video;
|
|
systemControls.DisplayUpdater.VideoProperties.Title = item.Snippet.Title;
|
|
systemControls.DisplayUpdater.VideoProperties.Subtitle = item.Snippet.ChannelTitle;
|
|
systemControls.DisplayUpdater.Thumbnail = RandomAccessStreamReference.CreateFromUri(avatar.ToUri());
|
|
systemControls.DisplayUpdater.Update();
|
|
|
|
systemControls.ButtonPressed += SystemControls_Engaged;
|
|
systemControls.IsEnabled = true;
|
|
#endregion
|
|
}
|
|
|
|
public void Controls_MiniModeChanged(object sender, bool e)
|
|
{
|
|
videoSource.IsFullWindow = false;
|
|
|
|
MiniMode?.Invoke(this, e);
|
|
}
|
|
|
|
public void Minimize()
|
|
{
|
|
Controls.Minimize();
|
|
}
|
|
|
|
private void Controls_QualityChanged(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list)
|
|
{
|
|
videoSource.Pause();
|
|
|
|
timecodeBackup = videoSource.Position;
|
|
needUpdateTimecode = true;
|
|
|
|
audioLoaded = false;
|
|
videoLoaded = false;
|
|
muxedTimer.Stop();
|
|
|
|
videoSource.Source = requestedQuality.Url.ToUri();
|
|
if(requestedQuality is MuxedStreamInfo)
|
|
{
|
|
isMuxed = true;
|
|
audioSource.Source = null;
|
|
}
|
|
else
|
|
audioSource.Source = list.Audio.First().Url.ToUri();
|
|
}
|
|
|
|
public void Controls_CloseRequested(object sender, RoutedEventArgs e)
|
|
{
|
|
if(systemControls != null)
|
|
systemControls.IsEnabled = false;
|
|
|
|
videoSource.Stop();
|
|
audioSource.Stop();
|
|
|
|
Methods.MainPage.CloseVideo();
|
|
}
|
|
|
|
private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
|
|
{
|
|
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
|
{
|
|
if (args.Button == SystemMediaTransportControlsButton.Next)
|
|
NextClicked?.Invoke();
|
|
|
|
switch (args.Button)
|
|
{
|
|
case SystemMediaTransportControlsButton.Play:
|
|
videoSource.Play();
|
|
break;
|
|
case SystemMediaTransportControlsButton.Pause:
|
|
videoSource.Pause();
|
|
break;
|
|
case SystemMediaTransportControlsButton.Next:
|
|
NextClicked.Invoke();
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
public void Pause()
|
|
{
|
|
videoSource.Pause();
|
|
}
|
|
|
|
private void VideoSource_CurrentStateChanged(object sender, RoutedEventArgs e)
|
|
{
|
|
switch(videoSource.CurrentState)
|
|
{
|
|
case Windows.UI.Xaml.Media.MediaElementState.Buffering:
|
|
case Windows.UI.Xaml.Media.MediaElementState.Paused:
|
|
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
|
|
// TODO: Create history entry
|
|
audioSource?.Pause();
|
|
break;
|
|
case Windows.UI.Xaml.Media.MediaElementState.Playing:
|
|
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
|
|
audioSource?.Play();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void AudioSource_CurrentStateChanged(object sender, RoutedEventArgs e)
|
|
{
|
|
if(audioSource.CurrentState == Windows.UI.Xaml.Media.MediaElementState.Playing)
|
|
muxedTimer.Start();
|
|
else
|
|
muxedTimer.Stop();
|
|
}
|
|
|
|
private void VideoSource_MediaOpened(object sender, RoutedEventArgs e)
|
|
{
|
|
videoLoaded = true;
|
|
if (needUpdateTimecode)
|
|
{
|
|
videoSource.Position = timecodeBackup;
|
|
needUpdateTimecode = false;
|
|
}
|
|
if (audioLoaded || isMuxed)
|
|
videoSource.Play();
|
|
}
|
|
|
|
private void AudioSource_MediaOpened(object sender, RoutedEventArgs e)
|
|
{
|
|
audioLoaded = true;
|
|
if (needUpdateTimecode)
|
|
audioSource.Position = timecodeBackup;
|
|
if (videoLoaded)
|
|
videoSource.Play();
|
|
}
|
|
|
|
private void VideoSource_VolumeChanged(object sender, RoutedEventArgs e)
|
|
{
|
|
audioSource.Volume = videoSource.Volume;
|
|
SettingsStorage.Volume = videoSource.Volume;
|
|
}
|
|
|
|
private void VideoSource_MarkerReached(object sender, Windows.UI.Xaml.Media.TimelineMarkerRoutedEventArgs e)
|
|
{
|
|
Controls.Advert.PushAdvert();
|
|
}
|
|
}
|
|
}
|