Archived
1
0
This repository has been archived on 2026-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FoxTube/FoxTube/Pages/VideoPage.xaml.cs
T
Michael Gordeev b875124c1a Region settings fixed
Channel page cover fixed
Livestream toasts added
2019-04-04 22:10:47 +03:00

587 lines
22 KiB
C#

using FoxTube.Controls;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
using Windows.System;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using YoutubeExplode;
using YoutubeExplode.Models.MediaStreams;
namespace FoxTube.Pages
{
public enum Rating { None, Like, Dislike }
public class VideoPlaylistItem
{
public int Number { get; set; }
public string Thumbnail { get; private set; } = "/Assets/videoThumbSample.png";
public string Id { get; private set; }
public string Title { get; private set; }
public VideoPlaylistItem(string image, string title, string id)
{
Thumbnail = image;
Title = title;
Id = id;
}
}
/// <summary>
/// Video page
/// </summary>
public sealed partial class VideoPage : Page
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("VideoPage");
public string videoId;
public string playlistId = null;
public Video item;
bool isExtended = false;
Rating userRating = Rating.None;
DispatcherTimer liveTimer;
DispatcherTimer countdownTimer;
public LoadingPage LoadingPage => loading;
public VideoPlayer Player => player;
public VideoPage()
{
InitializeComponent();
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
if (Window.Current.Bounds.Width <= 1000)
{
Debug.WriteLine("Correcting layout...");
mainContent.Children.Remove(descriptionPanel);
pivot.Items.Insert(0, descriptionPanel);
tabsPlaceholder.Children.Remove(pivot);
mainContent.Children.Add(pivot);
grid.ColumnDefinitions[1].Width = new GridLength(0);
pivot.SelectedIndex = 0;
}
}
private void Player_NextClicked()
{
if (playlistId != null)
playlistList.SelectedIndex++;
else
(relatedVideos.Children[0] as VideoCard).Button_Click(this, null);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter == null || string.IsNullOrWhiteSpace((e.Parameter as string[])[0]))
loading.Error("NullReferenceException", "Unable to initialize page. Video ID is not stated.");
else
Initialize(e.Parameter as string[]);
}
public async void Initialize(string[] ids)
{
loading.Refresh();
try
{
videoId = ids[0];
if (ids[1] != null)
LoadPlaylist(ids[1]);
else
pivot.Items.Remove(playlist);
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,statistics,status,contentDetails,liveStreamingDetails");
request.Id = ids[0];
item = (await request.ExecuteAsync()).Items[0];
if (item.Snippet.LiveBroadcastContent == "none")
LoadStats();
else
LoadStream();
if (item.Snippet.LiveBroadcastContent == "upcoming")
SetSchedule();
LoadInfo();
loading.Close();
}
catch (System.Net.Http.HttpRequestException)
{
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
loading.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Video loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", videoId }
});
}
}
void SetSchedule()
{
views.Visibility = Visibility.Collapsed;
upcoming.Visibility = Visibility.Visible;
if (item.LiveStreamingDetails.ScheduledEndTime.HasValue || item.LiveStreamingDetails.ScheduledStartTime.HasValue)
schedule.Visibility = Visibility.Visible;
if (item.LiveStreamingDetails.ScheduledEndTime.HasValue)
{
end.Text = $"{resources.GetString("/VideoPage/end")} {item.LiveStreamingDetails.ScheduledEndTime.Value}";
end.Visibility = Visibility.Visible;
}
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
{
start.Text = $"{resources.GetString("/VideoPage/start")} {item.LiveStreamingDetails.ScheduledStartTime.Value}";
start.Visibility = Visibility.Visible;
countdownPanel.Visibility = Visibility.Visible;
countdownTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
countdownTimer.Tick += (s, e) =>
{
countdown.Text = item.LiveStreamingDetails.ScheduledStartTime.Value > DateTime.Now ? "" : "-" + (item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss");
if (countdown.Text == "00:00:00")
refresh_Click(this, null);
};
countdownTimer.Start();
}
}
async void LoadPlaylist(string id)
{
playlistId = id;
List<VideoPlaylistItem> items = new List<VideoPlaylistItem>();
VideoPlaylistItem selection = null;
//Retrieving data
PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
playlistRequest.Id = id;
Playlist playlistItem = (await playlistRequest.ExecuteAsync()).Items[0];
PlaylistItemsResource.ListRequest listRequest = SecretsVault.Service.PlaylistItems.List("snippet");
listRequest.MaxResults = 50;
listRequest.PlaylistId = id;
PlaylistItemListResponse listResponse;
do
{
listResponse = await listRequest.ExecuteAsync();
listRequest.PageToken = listResponse.NextPageToken;
foreach(PlaylistItem i in listResponse.Items)
items.Add(new VideoPlaylistItem(i.Snippet.Thumbnails.Medium.Url, i.Snippet.Title, i.Snippet.ResourceId.VideoId));
}
while (!string.IsNullOrWhiteSpace(listRequest.PageToken));
selection = items.Find(i => i.Id == item.Id);
for (int k = 0; k < items.Count; k++)
items[k].Number = k + 1;
//Setting data
playlistName.Text = playlistItem.Snippet.Title;
playlistChannel.Text = playlistItem.Snippet.ChannelTitle;
playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{playlistItem.ContentDetails.ItemCount}";
playlistList.ItemsSource = items;
playlistList.SelectedItem = selection;
pivot.SelectedItem = playlist;
await Task.Delay(500);
playlistScroll.ChangeView(null, playlistList.SelectedIndex * 86 + 89, null, true);
if (playlistList.SelectedIndex == playlistList.Items.Count - 1)
player.Controls.IsNextTrackButtonVisible = false;
}
async void LoadInfo()
{
//Setting meta
title.Text = item.Snippet.Title;
date.Text = $"{resources.GetString("/VideoPage/publishedAt")}: {item.Snippet.PublishedAt} ({Methods.GetAgo(item.Snippet.PublishedAt.Value)})";
Methods.FormatText(ref description, item.Snippet.Description);
//Setting channel button
ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet, statistics");
channelRequest.Id = item.Snippet.ChannelId;
var item1 = (await channelRequest.ExecuteAsync()).Items[0];
channelAvatar.ProfilePicture = new BitmapImage(item1.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelHeight = 90, DecodePixelWidth = 90 };
channelName.Text = item.Snippet.ChannelTitle;
subscribers.Text = $"{item1.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
//Setting ratings
dislikes.Text = $"{item.Statistics.DislikeCount:0,0}";
likes.Text = $"{item.Statistics.LikeCount:0,0}";
rating.Value = (double)item.Statistics.DislikeCount / (double)(item.Statistics.DislikeCount + item.Statistics.LikeCount) * 100;
//Setting User's rate
if (SecretsVault.IsAuthorized)
{
VideoGetRatingResponse ratingResponse = await SecretsVault.Service.Videos.GetRating(videoId).ExecuteAsync();
if (ratingResponse.Items[0].Rating == "like")
{
userRating = Rating.Like;
like.Foreground = new SolidColorBrush(Colors.Green);
}
else if (ratingResponse.Items[0].Rating == "dislike")
{
userRating = Rating.Dislike;
dislike.Foreground = new SolidColorBrush(Colors.Red);
}
foreach (Subscription s in SecretsVault.Subscriptions)
{
if (s.Snippet.ResourceId.ChannelId == item.Snippet.ChannelId)
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe");
}
}
subscribe.Visibility = Visibility.Visible;
}
else
{
download.Visibility = Visibility.Collapsed;
addTo.Visibility = Visibility.Collapsed;
subscribe.Visibility = Visibility.Collapsed;
}
//Initializing player
player.Initialize(item, item1.Snippet.Thumbnails.Medium.Url);
LoadRelatedVideos();
}
void LoadStream()
{
liveTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(10) };
liveTimer.Tick += LiveStatsUpdate;
liveTimer.Start();
LiveStatsUpdate();
if(string.IsNullOrWhiteSpace(item.LiveStreamingDetails.ActiveLiveChatId))
pivot.Items.Remove(commentsPlaceholder);
else
{
commentsPlaceholder.Header = resources.GetString("/VideoPage/chat");
commentsPlaceholder.Content = new Chat(item.LiveStreamingDetails.ActiveLiveChatId);
pivot.SelectedItem = commentsPlaceholder;
}
download.Visibility = Visibility.Collapsed;
}
private async void LiveStatsUpdate(object sender = null, object e = null)
{
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("liveStreamingDetails");
request.Id = videoId;
views.Text = $"{(await request.ExecuteAsync()).Items[0].LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
}
void LoadStats()
{
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
comments.Initialize(item);
LoadDownloads();
}
async void LoadDownloads()
{
try
{
MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(videoId);
foreach (MuxedStreamInfo i in infoSet.Muxed)
{
MenuFlyoutItem menuItem = new MenuFlyoutItem()
{
Text = i.VideoQualityLabel,
Tag = new object[] { i, i.VideoQualityLabel }
};
menuItem.Click += downloadItemSelected;
downloadSelector.Items.Add(menuItem);
}
MenuFlyoutItem audioItem = new MenuFlyoutItem()
{
Text = resources.GetString("/VideoPage/audio"),
Tag = new object[] { infoSet.Audio[0], resources.GetString("/Cards/audioOnly") }
};
audioItem.Click += downloadItemSelected;
downloadSelector.Items.Add(audioItem);
}
catch
{
download.Visibility = Visibility.Collapsed;
}
}
private void downloadItemSelected(object sender, RoutedEventArgs e)
{
DownloadAgent.Add(((sender as MenuFlyoutItem).Tag as object[])[0] as MediaStreamInfo, item, ((sender as MenuFlyoutItem).Tag as object[])[1] as string);
}
async void LoadRelatedVideos()
{
SearchResource.ListRequest request = SecretsVault.Service.Search.List("snippet");
request.RegionCode = SettingsStorage.Region;
request.RelevanceLanguage = SettingsStorage.RelevanceLanguage;
request.RelatedToVideoId = videoId;
request.SafeSearch = (SearchResource.ListRequest.SafeSearchEnum)SettingsStorage.SafeSearch;
request.MaxResults = 20;
request.Type = "video";
SearchListResponse response = await request.ExecuteAsync();
foreach (SearchResult video in response.Items)
relatedVideos.Add(new VideoCard(video.Id.VideoId));
}
private void Player_Minimize(object sender, params object[] e)
{
if (isExtended == (bool)e[0])
return;
isExtended = (bool)e[0];
if(isExtended)
{
grid.ColumnDefinitions[1].Width = new GridLength(0);
commandbar.Visibility = Visibility.Collapsed;
mainScroll.ChangeView(0, 0, null);
mainScroll.Margin = new Thickness(0);
mainScroll.VerticalScrollMode = ScrollMode.Disabled;
Methods.MainPage.MinimizeVideo();
}
else
{
grid.ColumnDefinitions[1].Width = new GridLength(400);
commandbar.Visibility = Visibility.Visible;
mainScroll.Margin = new Thickness(0,0,0,50);
mainScroll.VerticalScrollMode = ScrollMode.Auto;
Methods.MainPage.MaximizeVideo();
}
}
private void gotoChannel_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
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;
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}{timecode}".ToUri());
}
public void refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToVideo(videoId, playlistId);
}
public void CloseVideo()
{
player.Controls_CloseRequested(this, null);
}
private void grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width > 1000 && mainContent.Children.Contains(pivot))
{
mainContent.Children.Remove(pivot);
tabsPlaceholder.Children.Add(pivot);
pivot.SelectedItem = descriptionPanel;
descriptionPanel.Opacity = 1;
pivot.Items.RemoveAt(0);
mainContent.Children.Add(descriptionPanel);
grid.ColumnDefinitions[1].Width = new GridLength(400);
pivot.SelectedIndex = 0;
}
else if (e.NewSize.Width <= 1000 && mainContent.Children.Contains(descriptionPanel))
{
mainContent.Children.Remove(descriptionPanel);
pivot.Items.Insert(0, descriptionPanel);
tabsPlaceholder.Children.Remove(pivot);
mainContent.Children.Add(pivot);
grid.ColumnDefinitions[1].Width = new GridLength(0);
pivot.SelectedIndex = 0;
}
}
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
{
player.Pause();
Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url,
item.Snippet.Title,
$"https://www.youtube.com/watch?v={videoId}",
resources.GetString("/Cards/videoShare"));
}
private void share_Click(object sender, RoutedEventArgs e)
{
DataTransferManager.ShowShareUI();
}
private async void dislike_Click(object sender, RoutedEventArgs e)
{
if (SecretsVault.IsAuthorized)
switch (userRating)
{
case Rating.Like:
like.Foreground = new SolidColorBrush(Colors.Gray);
likes.Text = (int.Parse(likes.Text, NumberStyles.AllowThousands) - 1).ToString("0,0");
dislike.Foreground = new SolidColorBrush(Colors.Red);
dislikes.Text = (int.Parse(dislikes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
userRating = Rating.Dislike;
break;
case Rating.None:
dislike.Foreground = new SolidColorBrush(Colors.Red);
dislikes.Text = (int.Parse(dislikes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Maximum++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
userRating = Rating.Dislike;
break;
case Rating.Dislike:
dislike.Foreground = new SolidColorBrush(Colors.Gray);
dislikes.Text = (int.Parse(dislikes.Text, NumberStyles.AllowThousands) - 1).ToString("0,0");
rating.Maximum--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
break;
}
}
private async void like_Click(object sender, RoutedEventArgs e)
{
if(SecretsVault.IsAuthorized)
switch (userRating)
{
case Rating.Dislike:
dislike.Foreground = new SolidColorBrush(Colors.Gray);
dislikes.Text = (int.Parse(dislikes.Text, NumberStyles.AllowThousands) - 1).ToString("0,0");
like.Foreground = new SolidColorBrush(Colors.Green);
likes.Text = (int.Parse(likes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
userRating = Rating.Like;
break;
case Rating.None:
like.Foreground = new SolidColorBrush(Colors.Green);
likes.Text = (int.Parse(likes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Maximum++;
rating.Value++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
userRating = Rating.Like;
break;
case Rating.Like:
like.Foreground = new SolidColorBrush(Colors.Gray);
likes.Text = (int.Parse(likes.Text, NumberStyles.AllowThousands) - 1).ToString("0,0");
rating.Maximum--;
rating.Value--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
break;
}
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
if ((e.AddedItems[0] as VideoPlaylistItem).Id != videoId)
Methods.MainPage.GoToVideo((e.AddedItems[0] as VideoPlaylistItem).Id, playlistId);
}
catch { }
}
private async void subscribe_Click(object sender, RoutedEventArgs e)
{
if (await SecretsVault.ChangeSubscriptionState(item.Snippet.ChannelId))
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe");
}
else
{
subscribe.Background = new SolidColorBrush(Colors.Red);
subscribe.Foreground = new SolidColorBrush(Colors.White);
subscribe.Content = resources.GetString("/Cards/subscribe/Content");
}
}
private async void NewPlaylist_Click(object sender, RoutedEventArgs e)
{
//TODO: Localize strings
await playlistDialog.ShowAsync();
}
private void Wl_Click(object sender, RoutedEventArgs e)
{
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
}
}