b0ecac7c02
Fixed ChannelCard Added "Share" button to VideoCard
449 lines
18 KiB
C#
449 lines
18 KiB
C#
using System;
|
|
using Windows.UI.Xaml;
|
|
using Windows.UI.Xaml.Controls;
|
|
using Google.Apis.YouTube.v3;
|
|
using Google.Apis.YouTube.v3.Data;
|
|
using Windows.UI.Xaml.Media.Imaging;
|
|
using Windows.System;
|
|
using Windows.ApplicationModel.DataTransfer;
|
|
using Windows.ApplicationModel.Resources;
|
|
using Microsoft.AppCenter.Analytics;
|
|
using System.Collections.Generic;
|
|
using YoutubeExplode;
|
|
using Windows.UI.Popups;
|
|
using YoutubeExplode.Models.MediaStreams;
|
|
using Windows.Foundation;
|
|
|
|
namespace FoxTube.Controls
|
|
{
|
|
/// <summary>
|
|
/// Video item card
|
|
/// </summary>
|
|
public sealed partial class VideoCard : UserControl
|
|
{
|
|
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
|
|
|
|
public string playlistId;
|
|
public string videoId;
|
|
Video item;
|
|
|
|
public VideoCard(string id, string playlist = null)
|
|
{
|
|
InitializeComponent();
|
|
Initialize(id, playlist);
|
|
}
|
|
|
|
public VideoCard(Video meta, string playlist = null)
|
|
{
|
|
InitializeComponent();
|
|
item = meta;
|
|
playlistId = playlist;
|
|
LoadMeta();
|
|
}
|
|
|
|
public async void Initialize(string id, string playlist = null)
|
|
{
|
|
try
|
|
{
|
|
videoId = id;
|
|
playlistId = playlist;
|
|
|
|
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails");
|
|
request.Id = id;
|
|
item = (await request.ExecuteAsync()).Items[0];
|
|
|
|
title.Text = item.Snippet.Title;
|
|
channelName.Text = item.Snippet.ChannelTitle;
|
|
|
|
if (item.Snippet.Title == "Deleted video")
|
|
{
|
|
ContextFlyout = null;
|
|
show.Begin();
|
|
return;
|
|
}
|
|
|
|
if (item.Snippet.LiveBroadcastContent == "live")
|
|
{
|
|
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
|
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
|
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}";
|
|
else
|
|
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
|
|
liveTag.Visibility = Visibility.Visible;
|
|
download.Visibility = Visibility.Collapsed;
|
|
}
|
|
else if (item.Snippet.LiveBroadcastContent == "upcoming")
|
|
{
|
|
views.Text = "";
|
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
|
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}";
|
|
else
|
|
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
|
liveTag.Visibility = Visibility.Visible;
|
|
|
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
|
|
liveContent.Text = resources.GetString("/Cards/goesLive") + (item.LiveStreamingDetails.ScheduledStartTime.Value > DateTime.Now ? " " : " -") + (item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss");
|
|
else liveContent.Text = resources.GetString("/Cards/upcoming");
|
|
download.Visibility = Visibility.Collapsed;
|
|
}
|
|
else
|
|
{
|
|
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
|
|
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
|
LoadDownloads();
|
|
}
|
|
LoadAddTo();
|
|
|
|
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
|
|
catch { }
|
|
try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 }; }
|
|
catch { }
|
|
|
|
if(SecretsVault.History.Contains(videoId))
|
|
watched.Visibility = Visibility.Visible;
|
|
|
|
show.Begin();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Visibility = Visibility.Collapsed;
|
|
Analytics.TrackEvent("VideoCard loading failed", new Dictionary<string, string>()
|
|
{
|
|
{ "Exception", e.GetType().ToString() },
|
|
{ "Message", e.Message },
|
|
{ "Video ID", videoId }
|
|
});
|
|
}
|
|
}
|
|
|
|
async void LoadDownloads()
|
|
{
|
|
try
|
|
{
|
|
// TODO: Localize context menu
|
|
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;
|
|
download.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;
|
|
download.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);
|
|
}
|
|
|
|
public async void LoadMeta()
|
|
{
|
|
videoId = item.Id;
|
|
title.Text = item.Snippet.Title;
|
|
channelName.Text = item.Snippet.ChannelTitle;
|
|
|
|
if (item.Snippet.LiveBroadcastContent == "live")
|
|
{
|
|
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
|
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
|
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}";
|
|
else
|
|
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
|
|
liveTag.Visibility = Visibility.Visible;
|
|
}
|
|
else if (item.Snippet.LiveBroadcastContent == "upcoming")
|
|
{
|
|
views.Text = "";
|
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
|
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}";
|
|
else
|
|
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
|
liveTag.Visibility = Visibility.Visible;
|
|
|
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
|
|
liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {item.LiveStreamingDetails.ScheduledStartTime - DateTime.Now}";
|
|
else liveContent.Text = resources.GetString("/Cards/upcoming");
|
|
}
|
|
else
|
|
{
|
|
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
|
|
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
|
}
|
|
|
|
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
|
|
catch { }
|
|
try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 }; }
|
|
catch { }
|
|
|
|
if (SecretsVault.History.Contains(videoId))
|
|
watched.Visibility = Visibility.Visible;
|
|
|
|
show.Begin();
|
|
}
|
|
|
|
public async void Button_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (item.ContentDetails.ContentRating != null)
|
|
{
|
|
if (SecretsVault.IsAuthorized)
|
|
{
|
|
if (SettingsStorage.Mature == MatureState.Blocked)
|
|
{
|
|
MessageDialog dialog = new MessageDialog(resources.GetString("/VideoPage/matureText"), resources.GetString("/VideoPage/wantContinue"));
|
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/yes"), null, true));
|
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/always"), (command) => SettingsStorage.Mature = MatureState.Allowed, true));
|
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/no"), null, false));
|
|
|
|
dialog.CancelCommandIndex = 2;
|
|
dialog.DefaultCommandIndex = 0;
|
|
|
|
if (!(bool)(await dialog.ShowAsync()).Id)
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageDialog dialog = new MessageDialog(resources.GetString("/VideoPage/matureText"), resources.GetString("/VideoPage/signRequired"));
|
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/signin"), (command) => SecretsVault.Authorize()));
|
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/cancel")));
|
|
|
|
dialog.CancelCommandIndex = 1;
|
|
dialog.DefaultCommandIndex = 0;
|
|
|
|
await dialog.ShowAsync();
|
|
return;
|
|
}
|
|
}
|
|
|
|
Methods.MainPage.GoToVideo(videoId, playlistId);
|
|
}
|
|
|
|
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
|
|
{
|
|
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.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
|
|
DataTransferManager.ShowShareUI();
|
|
}
|
|
|
|
private void ViewChannel_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
|
|
}
|
|
|
|
private void GetLink_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
DataPackage data = new DataPackage();
|
|
data.SetText($"https://www.youtube.com/watch?v={videoId}");
|
|
Clipboard.SetContent(data);
|
|
}
|
|
|
|
private async void InBrowser_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}".ToUri());
|
|
}
|
|
|
|
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e)
|
|
{
|
|
showThumb.Begin();
|
|
}
|
|
|
|
private async void NewPlaylist_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
// TODO: Localize strings
|
|
StackPanel stack = new StackPanel();
|
|
stack.Children.Add(new TextBox
|
|
{
|
|
PlaceholderText = "Enter playlist name"
|
|
});
|
|
|
|
ComboBox comboBox = new ComboBox
|
|
{
|
|
Header = "Availablity",
|
|
SelectedIndex = 0,
|
|
HorizontalAlignment = HorizontalAlignment.Stretch
|
|
};
|
|
comboBox.Items.Add(new ComboBoxItem { Content = "Public" });
|
|
comboBox.Items.Add(new ComboBoxItem { Content = "Private" });
|
|
comboBox.Items.Add(new ComboBoxItem { Content = "Direct link" });
|
|
|
|
stack.Children.Add(comboBox);
|
|
|
|
ContentDialog playlistDialog = new ContentDialog
|
|
{
|
|
PrimaryButtonText = "Create and add",
|
|
CloseButtonText = "Cancel",
|
|
DefaultButton = ContentDialogButton.Primary,
|
|
Title = "New playlist",
|
|
Content = stack
|
|
};
|
|
playlistDialog.PrimaryButtonClick += PlaylistDialog_PrimaryButtonClick;
|
|
await playlistDialog.ShowAsync();
|
|
}
|
|
|
|
private async void PlaylistDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
|
{
|
|
string privacy = "private";
|
|
switch (((sender.Content as StackPanel).Children[1] as ComboBox).SelectedIndex)
|
|
{
|
|
case 0:
|
|
privacy = "public";
|
|
break;
|
|
case 1:
|
|
privacy = "private";
|
|
break;
|
|
case 2:
|
|
privacy = "unlisted";
|
|
break;
|
|
}
|
|
|
|
Playlist newItem = new Playlist
|
|
{
|
|
Snippet = new PlaylistSnippet
|
|
{
|
|
Title = ((sender.Content as StackPanel).Children[0] as TextBox).Text
|
|
},
|
|
Status = new PlaylistStatus
|
|
{
|
|
PrivacyStatus = privacy,
|
|
}
|
|
};
|
|
|
|
Playlist i;
|
|
|
|
try { i = await SecretsVault.Service.Playlists.Insert(newItem, "snippet,status").ExecuteAsync(); }
|
|
catch
|
|
{
|
|
return;
|
|
}
|
|
|
|
ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem
|
|
{
|
|
Text = i.Snippet.Title,
|
|
IsChecked = true,
|
|
Tag = i,
|
|
Icon = new FontIcon
|
|
{
|
|
Glyph = "\xE728"
|
|
}
|
|
};
|
|
menuItem.Click += Item_Click;
|
|
addTo.Items.Add(menuItem);
|
|
|
|
Item_Click(menuItem, null);
|
|
}
|
|
|
|
private void Wl_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
async void LoadAddTo()
|
|
{
|
|
if (SecretsVault.UserChannel == null)
|
|
{
|
|
addTo.Visibility = Visibility.Collapsed;
|
|
return;
|
|
}
|
|
|
|
if (SecretsVault.WatchLater.Contains(item.Id))
|
|
(addTo.Items[1] as ToggleMenuFlyoutItem).IsChecked = true;
|
|
|
|
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet");
|
|
request.Mine = true;
|
|
request.MaxResults = 50;
|
|
|
|
PlaylistListResponse response = await request.ExecuteAsync();
|
|
|
|
PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet");
|
|
itemRequest.VideoId = item.Id;
|
|
|
|
foreach (Playlist i in response.Items)
|
|
{
|
|
itemRequest.PlaylistId = i.Id;
|
|
ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem
|
|
{
|
|
Text = i.Snippet.Title,
|
|
IsChecked = (await itemRequest.ExecuteAsync()).Items.Count > 0,
|
|
Tag = i,
|
|
Icon = new FontIcon
|
|
{
|
|
Glyph = "\xE728"
|
|
}
|
|
};
|
|
menuItem.Click += Item_Click;
|
|
addTo.Items.Add(menuItem);
|
|
}
|
|
}
|
|
|
|
private async void Item_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (((ToggleMenuFlyoutItem)sender).IsChecked)
|
|
{
|
|
try
|
|
{
|
|
PlaylistItem playlist = new PlaylistItem
|
|
{
|
|
Snippet = new PlaylistItemSnippet
|
|
{
|
|
PlaylistId = (((ToggleMenuFlyoutItem)sender).Tag as Playlist).Id,
|
|
ResourceId = new ResourceId
|
|
{
|
|
VideoId = item.Id,
|
|
Kind = "youtube#video"
|
|
}
|
|
}
|
|
};
|
|
PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet");
|
|
|
|
await request.ExecuteAsync();
|
|
}
|
|
catch
|
|
{
|
|
((ToggleMenuFlyoutItem)sender).IsChecked = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet");
|
|
itemRequest.VideoId = item.Id;
|
|
itemRequest.PlaylistId = ((Playlist)((ToggleMenuFlyoutItem)sender).Tag).Id;
|
|
|
|
PlaylistItemsResource.DeleteRequest request = SecretsVault.Service.PlaylistItems.Delete((await itemRequest.ExecuteAsync()).Items[0].Id);
|
|
|
|
await request.ExecuteAsync();
|
|
}
|
|
catch
|
|
{
|
|
((ToggleMenuFlyoutItem)sender).IsChecked = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|