From 9bca44c5f4bfeadb91afc4922f0669c56c559467 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Sat, 6 Apr 2019 18:31:11 +0300 Subject: [PATCH] Added "Add to" option to video cards context menu Added "Download" option to video cards context menu Updated changelog Related Work Items: #192 --- FoxTube/Assets/Data/Patchnotes.xml | 4 + FoxTube/Controls/DownloadItem.xaml.cs | 16 +- FoxTube/Controls/VideoCard.xaml | 9 +- FoxTube/Controls/VideoCard.xaml.cs | 215 ++++++++++++++++++++++++++ FoxTube/Pages/VideoPage.xaml | 39 +---- 5 files changed, 239 insertions(+), 44 deletions(-) diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml index 90cfea9..69cf03f 100644 --- a/FoxTube/Assets/Data/Patchnotes.xml +++ b/FoxTube/Assets/Data/Patchnotes.xml @@ -5,10 +5,14 @@ ### What's new: - Changelog notification now pops up after update at first launch - Added ability to add videos to playlists on video page +- Added ability to add videos to playlists through card's context menu +- Added ability to download video through card's context menu ### Что нового: - Добавлено уведомление со списком изменений при первом запуске после обновления - Добавлена возможность добавлять видео в плейлисты на странице просмотра +- Добавлена возможность добавлять видео в плейлисты через контекстное меню карточки +- Добавлена возможность скачивать видео через контекстное меню карточки diff --git a/FoxTube/Controls/DownloadItem.xaml.cs b/FoxTube/Controls/DownloadItem.xaml.cs index 2f7f219..6678b2e 100644 --- a/FoxTube/Controls/DownloadItem.xaml.cs +++ b/FoxTube/Controls/DownloadItem.xaml.cs @@ -258,21 +258,29 @@ namespace FoxTube.Controls } cts.Cancel(); + DownloadAgent.Remove(this); status.Text = resources.GetString("/Downloads/cancelling"); progressBar.IsIndeterminate = true; } void SetMeta() { - thumbnail.Source = new BitmapImage(Container.Thumbnail) { DecodePixelHeight = (int)thumbnail.ActualHeight, DecodePixelWidth = (int)thumbnail.ActualWidth }; + try + { + thumbnail.Source = new BitmapImage(Container.Thumbnail) { DecodePixelHeight = (int)thumbnail.ActualHeight, DecodePixelWidth = (int)thumbnail.ActualWidth }; - title.Text = Container.Title; - path.Text = File.Path; + title.Text = Container.Title; + path.Text = File.Path; - meta.Text = $@"{resources.GetString("/Downloads/ext")}: {Container.Extension} + meta.Text = $@"{resources.GetString("/Downloads/ext")}: {Container.Extension} {resources.GetString("/Downloads/quality")}: {Container.Quality} {resources.GetString("/Downloads/duration")}: {Container.Duration} {resources.GetString("/Downloads/author")}: {Container.Channel}"; + } + catch + { + DownloadAgent.Remove(this); + } } private void Cancel_Click(object sender, RoutedEventArgs e) diff --git a/FoxTube/Controls/VideoCard.xaml b/FoxTube/Controls/VideoCard.xaml index de1749c..a54e13f 100644 --- a/FoxTube/Controls/VideoCard.xaml +++ b/FoxTube/Controls/VideoCard.xaml @@ -71,12 +71,17 @@ + + + + + - - + + diff --git a/FoxTube/Controls/VideoCard.xaml.cs b/FoxTube/Controls/VideoCard.xaml.cs index 4ceb0ca..68e0007 100644 --- a/FoxTube/Controls/VideoCard.xaml.cs +++ b/FoxTube/Controls/VideoCard.xaml.cs @@ -11,6 +11,7 @@ using Microsoft.AppCenter.Analytics; using System.Collections.Generic; using YoutubeExplode; using Windows.UI.Popups; +using YoutubeExplode.Models.MediaStreams; namespace FoxTube.Controls { @@ -61,6 +62,7 @@ namespace FoxTube.Controls else info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value); liveTag.Visibility = Visibility.Visible; + download.Visibility = Visibility.Collapsed; } else if (item.Snippet.LiveBroadcastContent == "upcoming") { @@ -74,12 +76,15 @@ namespace FoxTube.Controls 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 { } @@ -103,6 +108,42 @@ namespace FoxTube.Controls } } + 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; @@ -206,5 +247,179 @@ namespace FoxTube.Controls { thumbnail.Opacity = 1; } + + 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; + } + } + } } } diff --git a/FoxTube/Pages/VideoPage.xaml b/FoxTube/Pages/VideoPage.xaml index 846761f..7c0fda3 100644 --- a/FoxTube/Pages/VideoPage.xaml +++ b/FoxTube/Pages/VideoPage.xaml @@ -117,43 +117,6 @@ - @@ -201,7 +164,7 @@ - +