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(); } } }