Added "Add to" option to video cards context menu
Added "Download" option to video cards context menu Updated changelog Related Work Items: #192
This commit is contained in:
@@ -5,10 +5,14 @@
|
|||||||
<en-US>### What's new:
|
<en-US>### What's new:
|
||||||
- Changelog notification now pops up after update at first launch
|
- 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 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
|
||||||
</en-US>
|
</en-US>
|
||||||
<ru-RU>### Что нового:
|
<ru-RU>### Что нового:
|
||||||
- Добавлено уведомление со списком изменений при первом запуске после обновления
|
- Добавлено уведомление со списком изменений при первом запуске после обновления
|
||||||
- Добавлена возможность добавлять видео в плейлисты на странице просмотра
|
- Добавлена возможность добавлять видео в плейлисты на странице просмотра
|
||||||
|
- Добавлена возможность добавлять видео в плейлисты через контекстное меню карточки
|
||||||
|
- Добавлена возможность скачивать видео через контекстное меню карточки
|
||||||
</ru-RU>
|
</ru-RU>
|
||||||
</content>
|
</content>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -258,11 +258,14 @@ namespace FoxTube.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
cts.Cancel();
|
cts.Cancel();
|
||||||
|
DownloadAgent.Remove(this);
|
||||||
status.Text = resources.GetString("/Downloads/cancelling");
|
status.Text = resources.GetString("/Downloads/cancelling");
|
||||||
progressBar.IsIndeterminate = true;
|
progressBar.IsIndeterminate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMeta()
|
void SetMeta()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
thumbnail.Source = new BitmapImage(Container.Thumbnail) { DecodePixelHeight = (int)thumbnail.ActualHeight, DecodePixelWidth = (int)thumbnail.ActualWidth };
|
thumbnail.Source = new BitmapImage(Container.Thumbnail) { DecodePixelHeight = (int)thumbnail.ActualHeight, DecodePixelWidth = (int)thumbnail.ActualWidth };
|
||||||
|
|
||||||
@@ -274,6 +277,11 @@ namespace FoxTube.Controls
|
|||||||
{resources.GetString("/Downloads/duration")}: {Container.Duration}
|
{resources.GetString("/Downloads/duration")}: {Container.Duration}
|
||||||
{resources.GetString("/Downloads/author")}: {Container.Channel}";
|
{resources.GetString("/Downloads/author")}: {Container.Channel}";
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
DownloadAgent.Remove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Cancel_Click(object sender, RoutedEventArgs e)
|
private void Cancel_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,12 +71,17 @@
|
|||||||
</MenuFlyoutItem.Icon>
|
</MenuFlyoutItem.Icon>
|
||||||
</MenuFlyoutItem>
|
</MenuFlyoutItem>
|
||||||
<MenuFlyoutItem x:Uid="/Cards/channel" Icon="Contact" Text="View channel" Name="viewChannel" Click="ViewChannel_Click"/>
|
<MenuFlyoutItem x:Uid="/Cards/channel" Icon="Contact" Text="View channel" Name="viewChannel" Click="ViewChannel_Click"/>
|
||||||
|
<MenuFlyoutSubItem Icon="Add" Text="Add to" Name="addTo">
|
||||||
|
<MenuFlyoutItem Text="New playlist" Name="newPlaylist" Click="NewPlaylist_Click" Icon="Add"/>
|
||||||
|
<ToggleMenuFlyoutItem Text="Watch later" Name="wl" Click="Wl_Click" Icon="Clock"/>
|
||||||
|
<MenuFlyoutSeparator/>
|
||||||
|
</MenuFlyoutSubItem>
|
||||||
<MenuFlyoutSeparator/>
|
<MenuFlyoutSeparator/>
|
||||||
<MenuFlyoutItem x:Uid="/Cards/getLink" Icon="Link" Text="Copy link" Name="getLink" Click="GetLink_Click"/>
|
<MenuFlyoutItem x:Uid="/Cards/getLink" Icon="Link" Text="Copy link" Name="getLink" Click="GetLink_Click"/>
|
||||||
<MenuFlyoutItem x:Uid="/Cards/openWeb" Icon="Globe" Text="Open in browser" Name="inBrowser" Click="InBrowser_Click"/>
|
<MenuFlyoutItem x:Uid="/Cards/openWeb" Icon="Globe" Text="Open in browser" Name="inBrowser" Click="InBrowser_Click"/>
|
||||||
<MenuFlyoutItem x:Uid="/Cards/share" Icon="Share" Text="Share" Name="share" Visibility="Collapsed"/>
|
<MenuFlyoutItem x:Uid="/Cards/share" Icon="Share" Text="Share" Name="share" Visibility="Collapsed"/>
|
||||||
<MenuFlyoutSeparator Visibility="Collapsed"/>
|
<MenuFlyoutSeparator Visibility="{x:Bind download.Visibility}"/>
|
||||||
<MenuFlyoutItem Icon="Download" Text="Download" Visibility="Collapsed"/>
|
<MenuFlyoutSubItem Icon="Download" Text="Download" Visibility="Visible" Name="download"/>
|
||||||
</MenuFlyout>
|
</MenuFlyout>
|
||||||
</UserControl.ContextFlyout>
|
</UserControl.ContextFlyout>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using Microsoft.AppCenter.Analytics;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using YoutubeExplode;
|
using YoutubeExplode;
|
||||||
using Windows.UI.Popups;
|
using Windows.UI.Popups;
|
||||||
|
using YoutubeExplode.Models.MediaStreams;
|
||||||
|
|
||||||
namespace FoxTube.Controls
|
namespace FoxTube.Controls
|
||||||
{
|
{
|
||||||
@@ -61,6 +62,7 @@ namespace FoxTube.Controls
|
|||||||
else
|
else
|
||||||
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
|
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
|
||||||
liveTag.Visibility = Visibility.Visible;
|
liveTag.Visibility = Visibility.Visible;
|
||||||
|
download.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
else if (item.Snippet.LiveBroadcastContent == "upcoming")
|
else if (item.Snippet.LiveBroadcastContent == "upcoming")
|
||||||
{
|
{
|
||||||
@@ -74,12 +76,15 @@ namespace FoxTube.Controls
|
|||||||
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
|
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");
|
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");
|
else liveContent.Text = resources.GetString("/Cards/upcoming");
|
||||||
|
download.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
|
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
|
||||||
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
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()); }
|
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
|
||||||
catch { }
|
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()
|
public async void LoadMeta()
|
||||||
{
|
{
|
||||||
videoId = item.Id;
|
videoId = item.Id;
|
||||||
@@ -206,5 +247,179 @@ namespace FoxTube.Controls
|
|||||||
{
|
{
|
||||||
thumbnail.Opacity = 1;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,43 +117,6 @@
|
|||||||
<ToggleMenuFlyoutItem Text="Watch later" Name="wl" Click="Wl_Click" Icon="Clock"/>
|
<ToggleMenuFlyoutItem Text="Watch later" Name="wl" Click="Wl_Click" Icon="Clock"/>
|
||||||
<MenuFlyoutSeparator/>
|
<MenuFlyoutSeparator/>
|
||||||
</MenuFlyout>
|
</MenuFlyout>
|
||||||
<!--<Flyout>
|
|
||||||
<ScrollViewer Margin="-12" MaxHeight="300">
|
|
||||||
<NavigationViewList Width="200" IsMultiSelectCheckBoxEnabled="True" SelectionMode="Multiple">
|
|
||||||
<NavigationViewItem Content="Watch later">
|
|
||||||
<NavigationViewItem.Icon>
|
|
||||||
<FontIcon Glyph=""/>
|
|
||||||
</NavigationViewItem.Icon>
|
|
||||||
</NavigationViewItem>
|
|
||||||
<NavigationViewItem Content="New playlist">
|
|
||||||
<NavigationViewItem.Icon>
|
|
||||||
<FontIcon Glyph=""/>
|
|
||||||
</NavigationViewItem.Icon>
|
|
||||||
</NavigationViewItem>
|
|
||||||
<NavigationViewItemHeader Content="Other playlists"/>
|
|
||||||
<NavigationViewItem Content="My playlist">
|
|
||||||
<NavigationViewItem.Icon>
|
|
||||||
<FontIcon Glyph=""/>
|
|
||||||
</NavigationViewItem.Icon>
|
|
||||||
</NavigationViewItem>
|
|
||||||
<NavigationViewItem Content="Cats">
|
|
||||||
<NavigationViewItem.Icon>
|
|
||||||
<FontIcon Glyph=""/>
|
|
||||||
</NavigationViewItem.Icon>
|
|
||||||
</NavigationViewItem>
|
|
||||||
<NavigationViewItem Content="Dogs">
|
|
||||||
<NavigationViewItem.Icon>
|
|
||||||
<FontIcon Glyph=""/>
|
|
||||||
</NavigationViewItem.Icon>
|
|
||||||
</NavigationViewItem>
|
|
||||||
<NavigationViewItem Content="Another playlist">
|
|
||||||
<NavigationViewItem.Icon>
|
|
||||||
<FontIcon Glyph=""/>
|
|
||||||
</NavigationViewItem.Icon>
|
|
||||||
</NavigationViewItem>
|
|
||||||
</NavigationViewList>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Flyout>-->
|
|
||||||
</AppBarButton.Flyout>
|
</AppBarButton.Flyout>
|
||||||
</AppBarButton>
|
</AppBarButton>
|
||||||
<AppBarButton x:Uid="/VideoPage/refresh" Name="refresh" Click="refresh_Click" Icon="Refresh" Label="Refresh page"/>
|
<AppBarButton x:Uid="/VideoPage/refresh" Name="refresh" Click="refresh_Click" Icon="Refresh" Label="Refresh page"/>
|
||||||
@@ -201,7 +164,7 @@
|
|||||||
</Pivot>
|
</Pivot>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ContentDialog PrimaryButtonText="Create and add" Title="New playlist" DefaultButton="Primary" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" Name="playlistDialog">
|
<ContentDialog PrimaryButtonText="Create and add" Title="New playlist" CloseButtonText="Cancel" DefaultButton="Primary" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" Name="playlistDialog">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBox PlaceholderText="Enter playlist name" Name="newListName"/>
|
<TextBox PlaceholderText="Enter playlist name" Name="newListName"/>
|
||||||
<ComboBox Header="Availablity" SelectedIndex="0" HorizontalAlignment="Stretch" Name="newListDisc">
|
<ComboBox Header="Availablity" SelectedIndex="0" HorizontalAlignment="Stretch" Name="newListDisc">
|
||||||
|
|||||||
Reference in New Issue
Block a user