Archived
1
0

Merged PR 17: Version 0.2.181021

- Closed captions fixes
- Merged to YoutubeExplode instead of MyToolkit for video sources
- Video playback fixes
- Internal links support
- Subscription button fixed
- Comments threads fixes and improvements (highlighting author's and user's names)
- General bug fixes
This commit is contained in:
Michael Gordeev
2018-10-20 22:03:04 +00:00
parent cc117a161c
commit f637c18e22
32 changed files with 863 additions and 591 deletions
+7
View File
@@ -22,5 +22,12 @@ namespace FoxTube.Classes
Duration = TimeSpan.FromMilliseconds(duration);
Text = text;
}
public Caption(double startTime, double duration, string text)
{
Start = TimeSpan.FromSeconds(startTime);
Duration = TimeSpan.FromSeconds(duration);
Text = text;
}
}
}
+44 -40
View File
@@ -1,68 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.IO;
using MyToolkit.Multimedia;
using Windows.Storage;
using FoxTube.Classes;
using Newtonsoft.Json;
using Windows.UI.Popups;
namespace FoxTube.Controls
{
public class DownloadAgent
{
public List<DownloadItem> Items = new List<DownloadItem>();
public event ObjectEventHandler ListChanged;
public List<DownloadItem> items = new List<DownloadItem>();
StorageFolder roaming = ApplicationData.Current.RoamingFolder;
private ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
XmlDocument doc = new XmlDocument();
public DownloadAgent()
{
if (settings.Values["downloadHistory"] != null)
doc.LoadXml(settings.Values["downloadHistory"] as string);
else
{
doc.AppendChild(doc.CreateXmlDeclaration("1.0", "utf-8", null));
doc.AppendChild(doc.CreateElement("downloads"));
settings.Values.Add("downloadHistory", doc.InnerXml);
Initialize();
Windows.UI.Core.Preview.SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += (s, a) => QuitPrompt();
}
foreach(XmlElement e in doc["downloads"].ChildNodes)
public async void Initialize()
{
try
{
Items.Add(new DownloadItem(
e["details"]["id"].InnerText,
e["title"].InnerText,
e["snippet"]["author"].InnerText,
e["snippet"]["image"].InnerText,
e["snippet"]["duration"].InnerText,
e["snippet"]["quality"].InnerText,
e["details"]["path"].InnerText));
List<DownloadItemContainer> containers = JsonConvert.DeserializeObject<List<DownloadItemContainer>>(await FileIO.ReadTextAsync(await roaming.GetFileAsync("data.json")));
foreach (DownloadItemContainer i in containers)
try { items.Add(new DownloadItem(i)); }
catch (FileNotFoundException) { }
}
catch { }
}
public void Add(string id, YouTubeQuality quality)
public void Add(string url)
{
DownloadItem item = new DownloadItem(id, quality);
item.DownloadCanceled += Item_DownloadCanceled;
item.DownloadComplete += Item_DownloadComplete;
Items.Add(item);
ListChanged.Invoke(item, "add");
}
private void Item_DownloadComplete(object sender, params object[] e)
{
doc["downloads"].InnerXml += e[0];
settings.Values["downloadHistory"] = doc.InnerXml;
items.Add(new DownloadItem(url));
}
private void Item_DownloadCanceled(object sender, params object[] e)
{
Items.Remove(sender as DownloadItem);
ListChanged.Invoke(sender, "remove");
items.Remove(sender as DownloadItem);
}
public async void QuitPrompt()
{
if(items.Find(x => x.InProgress) != null)
{
MessageDialog dialog = new MessageDialog("You have some unfinished downloads. Quitting now will erase all downloaded data. Are you sure to continue?", "Some downloads are still pending");
dialog.Commands.Add(new UICommand("Yes", async (command) =>
{
foreach (DownloadItem i in items.FindAll(x => x.InProgress))
i.Cancel();
items.RemoveAll(x => x.InProgress);
List<DownloadItemContainer> containers = new List<DownloadItemContainer>();
foreach (DownloadItem i in items)
containers.Add(i.Container);
await FileIO.WriteTextAsync(
await roaming.CreateFileAsync("data.json", CreationCollisionOption.ReplaceExisting),
JsonConvert.SerializeObject(containers));
}));
dialog.Commands.Add(new UICommand("No"));
dialog.DefaultCommandIndex = 1;
await dialog.ShowAsync();
}
}
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
using YoutubeExplode.Models.MediaStreams;
namespace FoxTube.Classes
{
public class DownloadItemContainer
{
public string Title { get; set; }
public string Channel { get; set; }
public string Id { get; set; }
public Uri Path { get; set; }
public Uri Thumbnail { get; set; }
public VideoQuality Quality { get; set; }
public TimeSpan Duration { get; set; }
}
}
+76 -4
View File
@@ -1,9 +1,10 @@
using MyToolkit.Multimedia;
using Google.Apis.YouTube.v3;
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using Windows.ApplicationModel.Core;
using Windows.System;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@@ -14,6 +15,7 @@ namespace FoxTube
{
public static class Methods
{
public static bool NeedToResponse { get; set; } = false;
public static MainPage MainPage
{
get { return (Window.Current.Content as Frame).Content as MainPage; }
@@ -24,6 +26,11 @@ namespace FoxTube
CoreApplication.Exit();
}
public static Uri ToUri(this string url)
{
return new Uri(url);
}
public static string GetAgo(DateTime dateTime)
{
TimeSpan span = DateTime.Now - dateTime;
@@ -69,7 +76,8 @@ namespace FoxTube
{
try
{
Hyperlink link = new Hyperlink { NavigateUri = new Uri(item), Foreground = new SolidColorBrush(Colors.Red) };
Hyperlink link = new Hyperlink();
link.Click += (s, arg) => { ProcessLink(item); };
link.Inlines.Add(new Run { Text = item });
block.Inlines.Add(link);
}
@@ -98,7 +106,7 @@ namespace FoxTube
}
}
public static string QualityToString(YouTubeQuality quality)
/*public static string QualityToString(YouTubeQuality quality)
{
switch(quality)
{
@@ -131,6 +139,70 @@ namespace FoxTube
default:
return "Unknown";
}
}*/
public async static void ProcessLink(string url)
{
try
{
Debug.WriteLine($"Processing link: {url}");
if (url.Contains("youtube.com/") || url.Contains("youtu.be/"))
{
Debug.WriteLine("This is an internal youtube link");
url = url.Replace("https://", "").Replace("http://", "").Replace("wwww.", "").Replace("//", "");
Debug.WriteLine($"Prepared link: {url}");
if (url.Contains("/playlist"))
{
Debug.WriteLine($"This is a playlist link. ID: {HttpUtility.ParseQueryString(url).Get("list")}");
MainPage.GoToPlaylist(HttpUtility.ParseQueryString(url).Get("list"));
}
else if (url.Contains("youtu.be/"))
{
Debug.WriteLine($"This is obfuscated video link. Video ID: {url.Split('/')[1]}");
MainPage.GoToVideo(url.Split('/')[1]);
}
else if (url.Contains("/watch"))
{
Debug.WriteLine($"This is regular video link. Video ID: {HttpUtility.ParseQueryString(url).Get("v")}");
MainPage.GoToVideo(HttpUtility.ParseQueryString(url).Get(0), HttpUtility.ParseQueryString(url).Get("list"));
}
else if (url.Contains("/v/"))
{
Debug.WriteLine($"This is video link. ID: {url.Split('/')[2].Split('?')[0]}");
MainPage.GoToVideo(url.Split('/')[2].Split('?')[0]);
}
else if (url.Contains("/channel/"))
{
Debug.WriteLine($"This is channel link. ID: {url.Split('/')[2]}");
MainPage.GoToChannel(url.Split('/')[2]);
}
else if (url.Contains("/user/"))
{
Debug.WriteLine($"This is channel link with username. Username: {url.Split('/')[2]}");
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("id");
Debug.WriteLine(request.ForUsername = url.Split('/')[2]);
request.MaxResults = 1;
MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id);
}
else if (url.Contains("/c/"))
{
Debug.WriteLine($"This is channel link with custom url. Custom name: {url.Split('/')[2]}");
SearchResource.ListRequest request = SecretsVault.Service.Search.List("id");
Debug.WriteLine(request.Q = url.Split('/')[2]);
request.MaxResults = 1;
MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id.ChannelId);
}
else
throw new Exception();
}
else
throw new Exception();
}
catch
{
await Launcher.LaunchUriAsync(new Uri(url));
}
}
}
}
-1
View File
@@ -121,7 +121,6 @@ namespace FoxTube
public string Channel { get; private set; }
public Filters Filter { get; private set; } = new Filters();
public SearchParameters(string term)
{
Term = term;
+41
View File
@@ -57,6 +57,45 @@ namespace FoxTube
}
}
public static async Task<bool> ChangeSubscriptionState(string id)
{
if (!IsAuthorized)
return false;
if(Subscriptions.Find(x => x.Snippet.ResourceId.ChannelId == id) != null)
{
Subscription s = Subscriptions.Find(x => x.Snippet.ResourceId.ChannelId == id);
try { await Service.Subscriptions.Delete(s.Id).ExecuteAsync(); }
catch { return true; }
SubscriptionsChanged.Invoke(null, "remove", Subscriptions.IndexOf(s));
Subscriptions.Remove(s);
return false;
}
else
{
var request = Service.Subscriptions.Insert(new Subscription()
{
Snippet = new SubscriptionSnippet()
{
ResourceId = new ResourceId()
{
ChannelId = id,
Kind = "youtube#channel"
}
}
}, "snippet");
Subscription s = await request.ExecuteAsync();
if (s == null)
return false;
Subscriptions.Add(s);
SubscriptionsChanged.Invoke(null, "add", s);
return true;
}
}
public static async Task<bool> Subscribe(string id)
{
if (!IsAuthorized)
@@ -75,6 +114,8 @@ namespace FoxTube
}, "snippet");
Subscription s = await request.ExecuteAsync();
if (s == null)
return false;
Subscriptions.Add(s);
SubscriptionsChanged.Invoke(null, "add", s);
return true;
+1 -7
View File
@@ -91,18 +91,13 @@ namespace FoxTube.Controls
private async void subscribe_Click(object sender, RoutedEventArgs e)
{
if (subscribe.Background == new SolidColorBrush(Colors.Red))
{
if (await SecretsVault.Subscribe(channelId))
if (await SecretsVault.ChangeSubscriptionState(channelId))
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = "Subscribed";
}
}
else
{
if (await SecretsVault.Unsubscibe(channelId))
{
subscribe.Background = new SolidColorBrush(Colors.Red);
subscribe.Foreground = new SolidColorBrush(Colors.White);
@@ -111,4 +106,3 @@ namespace FoxTube.Controls
}
}
}
}
+13 -10
View File
@@ -23,15 +23,20 @@
<PersonPicture Tapped="avatar_Tapped" Name="avatar" Height="50" Margin="5" VerticalAlignment="Top"/>
<Grid Grid.Column="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Name="meta" TextWrapping="WrapWholeWords" Foreground="Gray" FontSize="13" Text="[Author's name] | [Published time span] [?](edited)"/>
<TextBlock Name="text" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords" Grid.Row="1" Text="[Content]"/>
<TextBlock Name="author" TextWrapping="WrapWholeWords" Foreground="Gray" FontSize="13" Text="[Author's name]"/>
<Border Name="authorBorder" HorizontalAlignment="Left" Background="Red" CornerRadius="5" Visibility="Collapsed">
<TextBlock Name="specialAuthor" TextWrapping="WrapWholeWords" Foreground="White" FontSize="13" Text="[Author's name]" Padding="2,0,2,2"/>
</Border>
<TextBlock Name="meta" Grid.Row="1" TextWrapping="WrapWholeWords" Foreground="Gray" FontSize="13" Text="[Published time span] [?](edited)"/>
<TextBlock Name="text" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords" Grid.Row="2" Text="[Content]"/>
<StackPanel Grid.Row="1" Name="editor" Visibility="Collapsed">
<TextBox Name="editorText" Text="[Content]" AcceptsReturn="True" Height="Auto" TextChanged="editorText_TextChanged"/>
<StackPanel Grid.Row="2" Name="editor" Visibility="Collapsed">
<TextBox Name="editorText" Text="[Content]" AcceptsReturn="True" TextWrapping="Wrap" TextChanged="editorText_TextChanged"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,5,0,5">
<Button Name="editorClose" Content="Cancel" Click="editorClose_Click"/>
<Button Name="editorSend" Content="Submit" Margin="5,0,0,0" Click="editorSend_Click"/>
@@ -39,7 +44,7 @@
<ProgressBar Name="editorSending" Foreground="Red" IsIndeterminate="True" Visibility="Collapsed"/>
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<StackPanel Grid.Row="3" Orientation="Horizontal">
<TextBlock Name="upvote" Foreground="Gray" Padding="0"
VerticalAlignment="Center" Margin="0,0,5,0"
FontFamily="Segoe MDL2 Assets" Text="&#xE19F;" FontSize="20"/>
@@ -77,10 +82,11 @@
</StackPanel>
</Button>
</StackPanel>
<ProgressBar Grid.Row="3" VerticalAlignment="Bottom" IsIndeterminate="True" Visibility="Collapsed" Name="commentsLoading"/>
</Grid>
</Grid>
<TextBox Grid.Row="1" Name="reply" TextChanged="reply_TextChanged" BorderThickness="0" Background="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" AcceptsReturn="True" MaxLength="500"
<TextBox Grid.Row="1" Name="reply" TextChanged="reply_TextChanged" TextWrapping="Wrap" BorderThickness="0" Background="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" AcceptsReturn="True" MaxLength="500"
Padding="5" Margin="0,0,32,0"
PlaceholderText="Enter your reply..."/>
<Button Grid.Row="1" Name="send" Click="send_Click" IsEnabled="False" HorizontalAlignment="Right" VerticalAlignment="Top"
@@ -91,9 +97,6 @@
Content="&#xE122;" />
<ProgressBar Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" IsIndeterminate="True" Foreground="Red" Name="sending" Visibility="Collapsed"/>
<StackPanel Grid.Row="2" Name="repliesPlaceholder">
<StackPanel Margin="60,0,0,0" Name="replies"/>
<local:ShowMore Clicked="more_Click"/>
</StackPanel>
<StackPanel Grid.Row="2" Margin="60,0,0,0" Name="replies"/>
</Grid>
</UserControl>
+75 -49
View File
@@ -26,21 +26,18 @@ namespace FoxTube.Controls
public enum CommentType { TopLevel, Reply }
public sealed partial class CommentCard : UserControl
{
ShowMore more;
Comment item;
CommentThread thread;
bool repliesLoaded = false;
CommentType type = CommentType.TopLevel;
string NextPageToken;
public CommentCard(CommentThread comment)
{
this.InitializeComponent();
more = repliesPlaceholder.Children[1] as ShowMore;
Initialize(comment);
}
public async void Initialize(CommentThread comment)
public void Initialize(CommentThread comment)
{
item = comment.Snippet.TopLevelComment;
thread = comment;
@@ -60,31 +57,26 @@ namespace FoxTube.Controls
rating.Text = comment.Snippet.TopLevelComment.Snippet.LikeCount.HasValue ? comment.Snippet.TopLevelComment.Snippet.LikeCount.ToString() : "";
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
{
specialAuthor.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
authorBorder.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
editBtn.Visibility = Visibility.Visible;
}
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == Methods.MainPage.GetCurrentItem().Snippet.ChannelId)
{
specialAuthor.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
authorBorder.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
}
else
author.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
meta.Text = string.Format("{0} | {1} {2}", comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName, Methods.GetAgo(comment.Snippet.TopLevelComment.Snippet.PublishedAt.Value), comment.Snippet.TopLevelComment.Snippet.UpdatedAt != comment.Snippet.TopLevelComment.Snippet.PublishedAt ? "(edited)" : "");
meta.Text = string.Format("{0} {1}", Methods.GetAgo(comment.Snippet.TopLevelComment.Snippet.PublishedAt.Value), comment.Snippet.TopLevelComment.Snippet.UpdatedAt != comment.Snippet.TopLevelComment.Snippet.PublishedAt ? "(edited)" : "");
Methods.FormatText(ref text, comment.Snippet.TopLevelComment.Snippet.TextDisplay);
try { avatar.ProfilePicture = new BitmapImage(new Uri(comment.Snippet.TopLevelComment.Snippet.AuthorProfileImageUrl)); }
catch { }
if(comment.Snippet.TotalReplyCount > 0)
{
var request = SecretsVault.Service.Comments.List("snippet");
request.ParentId = item.Id;
request.TextFormat = CommentsResource.ListRequest.TextFormatEnum.PlainText;
request.MaxResults = 10;
var response = await request.ExecuteAsync();
if (response.NextPageToken != null)
NextPageToken = response.NextPageToken;
else
more.Visibility = Visibility.Collapsed;
foreach (Comment c in response.Items.Reverse())
replies.Children.Add(new CommentCard(c));
}
}
public CommentCard(Comment comment)
@@ -110,7 +102,23 @@ namespace FoxTube.Controls
else
rating.Text = comment.Snippet.LikeCount.HasValue ? comment.Snippet.LikeCount.ToString() : "";
meta.Text = string.Format("{0} | {1} {2}", comment.Snippet.AuthorDisplayName, Methods.GetAgo(comment.Snippet.PublishedAt.Value), comment.Snippet.UpdatedAt.Value != comment.Snippet.PublishedAt.Value ? "(edited)" : "");
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
{
specialAuthor.Text = comment.Snippet.AuthorDisplayName;
authorBorder.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
editBtn.Visibility = Visibility.Visible;
}
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == Methods.MainPage.GetCurrentItem().Snippet.ChannelId)
{
specialAuthor.Text = comment.Snippet.AuthorDisplayName;
authorBorder.Visibility = Visibility.Visible;
author.Visibility = Visibility.Collapsed;
}
else
author.Text = comment.Snippet.AuthorDisplayName;
meta.Text = string.Format("{0} {1}", comment.Snippet.AuthorDisplayName, Methods.GetAgo(comment.Snippet.PublishedAt.Value), comment.Snippet.UpdatedAt.Value != comment.Snippet.PublishedAt.Value ? "(edited)" : "");
Methods.FormatText(ref text, comment.Snippet.TextDisplay);
try { avatar.ProfilePicture = new BitmapImage(new Uri(comment.Snippet.AuthorProfileImageUrl)); }
@@ -125,10 +133,45 @@ namespace FoxTube.Controls
grid.RowDefinitions[1].Height = new GridLength(0);
}
private void showReplies_Click(object sender, RoutedEventArgs e)
private async void showReplies_Click(object sender, RoutedEventArgs e)
{
if (grid.RowDefinitions[2].Height == new GridLength(0))
{
if(type == CommentType.TopLevel && !repliesLoaded)
{
commentsLoading.Visibility = Visibility.Visible;
var request = SecretsVault.Service.Comments.List("snippet");
request.ParentId = item.Id;
request.TextFormat = CommentsResource.ListRequest.TextFormatEnum.PlainText;
request.MaxResults = 50;
string token;
IList<Comment> list = new List<Comment>();
var response = await request.ExecuteAsync();
token = response.NextPageToken;
foreach (Comment i in response.Items)
list.Add(i);
while(token != null)
{
request.PageToken = token;
response = await request.ExecuteAsync();
token = response.NextPageToken;
foreach (Comment i in response.Items)
list.Add(i);
}
foreach (Comment c in list.Reverse())
replies.Children.Add(new CommentCard(c));
repliesLoaded = true;
commentsLoading.Visibility = Visibility.Collapsed;
}
grid.RowDefinitions[2].Height = GridLength.Auto;
}
else
grid.RowDefinitions[2].Height = new GridLength(0);
}
@@ -138,27 +181,6 @@ namespace FoxTube.Controls
Methods.MainPage.GoToChannel(item.Snippet.AuthorChannelId.ToString().Split('"')[3]);
}
private async void more_Click()
{
var request = SecretsVault.NoAuthService.Comments.List("snippet");
request.ParentId = item.Id;
request.MaxResults = 10;
request.PageToken = NextPageToken;
request.TextFormat = CommentsResource.ListRequest.TextFormatEnum.PlainText;
var response = await request.ExecuteAsync();
foreach (Comment c in response.Items.Reverse())
replies.Children.Add(new CommentCard(c));
if (response.NextPageToken != null)
{
NextPageToken = response.NextPageToken;
more.Complete();
}
else
more.Complete(true);
}
private void reply_TextChanged(object sender, TextChangedEventArgs e)
{
if (reply.Text.Length == 0)
@@ -196,7 +218,7 @@ namespace FoxTube.Controls
private void editorClose_Click(object sender, RoutedEventArgs e)
{
((grid.Children[0] as Grid).Children[1] as Grid).RowDefinitions[2].Height = GridLength.Auto;
((grid.Children[0] as Grid).Children[1] as Grid).RowDefinitions[3].Height = GridLength.Auto;
text.Visibility = Visibility.Visible;
editor.Visibility = Visibility.Collapsed;
}
@@ -214,11 +236,15 @@ namespace FoxTube.Controls
item.Snippet.UpdatedAt = DateTime.Now;
if (type == CommentType.Reply)
{
await SecretsVault.Service.Comments.Update(item, "snippet").ExecuteAsync();
Initialize(item);
}
else
{
thread.Snippet.TopLevelComment = item;
await SecretsVault.Service.CommentThreads.Update(thread, "snippet").ExecuteAsync();
Initialize(thread);
}
editorClose_Click(this, null);
@@ -243,7 +269,7 @@ namespace FoxTube.Controls
private void editBtn_Click(object sender, RoutedEventArgs e)
{
((grid.Children[0] as Grid).Children[1] as Grid).RowDefinitions[2].Height = new GridLength(0);
((grid.Children[0] as Grid).Children[1] as Grid).RowDefinitions[3].Height = new GridLength(0);
text.Visibility = Visibility.Collapsed;
editorText.Text = text.Text;
editor.Visibility = Visibility.Visible;
+1 -1
View File
@@ -28,7 +28,7 @@
<TextBlock Text="Author:" Foreground="Gray" Name="channel"/>
</StackPanel>
<StackPanel Name="donePanel" Grid.Column="3" Orientation="Horizontal" Visibility="Collapsed">
<StackPanel Name="donePanel" Grid.Column="3" Orientation="Horizontal" Visibility="Visible">
<Button Name="open" Click="open_Click" Width="80" Height="80" Padding="0" Background="Transparent">
<StackPanel>
<TextBlock Text="&#xED25;" FontFamily="Segoe MDL2 Assets" FontSize="30" HorizontalAlignment="Center"/>
+37 -84
View File
@@ -1,26 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using System.Net;
using Windows.UI.Xaml.Media.Imaging;
using Windows.System;
using MyToolkit.Multimedia;
using Windows.Storage;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Windows.UI.Popups;
using System.Xml;
using FoxTube.Classes;
using YoutubeExplode.Models.MediaStreams;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
@@ -28,55 +14,36 @@ namespace FoxTube.Controls
{
public sealed partial class DownloadItem : UserControl
{
string uri;
string Id;
public DownloadItemContainer Container { get; private set; }
public bool InProgress { get; set; } = false;
public event ObjectEventHandler DownloadCanceled;
public event ObjectEventHandler DownloadComplete;
public DownloadItem(string url)
{
this.InitializeComponent();
Download(url);
}
WebClient client = new WebClient();
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
public DownloadItem(string id, YouTubeQuality q)
public DownloadItem(DownloadItemContainer container)
{
this.InitializeComponent();
client.DownloadFileCompleted += DownloadCompleted;
client.DownloadProgressChanged += UpdateInfo;
}
async void Download(string id, YouTubeQuality q)
{
try
{
Id = id;
VideosResource.ListRequest request = SecretsVault.NoAuthService.Videos.List("snippet");
request.Id = id;
var response = (await request.ExecuteAsync()).Items[0];
Uri url = (await YouTube.GetVideoUriAsync(id, q)).Uri;
client.DownloadFileAsync(url, settings.Values["defaultDownload"] as string + $@"\{response.Snippet.Title} - {response.Snippet.ChannelTitle}.mp4");
thumbnail.Source = new BitmapImage(new Uri(response.Snippet.Thumbnails.Standard.Url));
title.Text = response.Snippet.Title;
channel.Text = $"Author: {response.Snippet.ChannelTitle}";
quality.Text = $"Quality: {Methods.QualityToString(q)}"; TimeSpan ts = XmlConvert.ToTimeSpan(response.ContentDetails.Duration);
duration.Text = string.Format("Duration: {0}{1:00}:{2:00} | ", ts.Hours == 0 ? "" : ts.Hours + ":", ts.Minutes, ts.Seconds);
uri = settings.Values["defaultDownload"] as string + $@"\{response.Snippet.Title} - {response.Snippet.ChannelTitle}.mp4";
status.Text = "Downloading...";
perc.Text = "0%";
}
catch
{
if (client.IsBusy)
client.CancelAsync();
await new MessageDialog("We were unable to download video due to connection problems or internal YouTube server error. Please, try again later.", "Failed to download").ShowAsync();
Container = container;
if (!File.Exists(container.Path.AbsolutePath))
throw new FileNotFoundException();
title.Text = Container.Title;
thumbnail.Source = new BitmapImage(Container.Thumbnail);
quality.Text = $"Quality: {Container.Quality.GetVideoQualityLabel()}";
duration.Text = $"Duration: {Container.Duration}";
channel.Text = $"Author: {Container.Channel}";
progressPanel.Visibility = Visibility.Collapsed;
donePanel.Visibility = Visibility.Visible;
}
void Download(string url)
{
}
private void UpdateInfo(object sender, DownloadProgressChangedEventArgs e)
@@ -87,7 +54,7 @@ namespace FoxTube.Controls
private void DownloadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
progressPanel.Visibility = Visibility.Collapsed;
/*progressPanel.Visibility = Visibility.Collapsed;
donePanel.Visibility = Visibility.Visible;
string node = $@"<item>
@@ -103,41 +70,27 @@ namespace FoxTube.Controls
</details>
</item>";
DownloadComplete.Invoke(this, node);
}
public DownloadItem(string videoId, string videoName, string channelName, string thumbUrl, string length, string videoQuality, string path)
{
if (!File.Exists(path))
throw new FileNotFoundException();
title.Text = videoName;
thumbnail.Source = new BitmapImage(new Uri(thumbUrl));
quality.Text = $"Quality: {videoQuality}";
duration.Text = $"Duration: {length}";
channel.Text = $"Author: {channelName}";
progressPanel.Visibility = Visibility.Collapsed;
donePanel.Visibility = Visibility.Visible;
uri = path;
Id = videoId;
DownloadComplete.Invoke(this, node);*/
}
private async void open_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(uri))
await Launcher.LaunchUriAsync(new Uri(uri));
await Launcher.LaunchUriAsync(Container.Path);
}
private void gotoOriginal_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToVideo(Id);
Methods.MainPage.GoToVideo(Container.Id);
}
private async void cancel_Click(object sender, RoutedEventArgs e)
public void Cancel()
{
if(client.IsBusy)
}
private void cancel_Click(object sender, RoutedEventArgs e)
{
/*if(client.IsBusy)
{
MessageDialog dialog = new MessageDialog("Are you sure?", "Cancelling download");
@@ -153,7 +106,7 @@ namespace FoxTube.Controls
dialog.DefaultCommandIndex = 1;
await dialog.ShowAsync();
}
}*/
}
}
}
+18 -1
View File
@@ -23,6 +23,7 @@ namespace FoxTube.Controls
public sealed partial class LiveCaptions : UserControl
{
public MediaElement Player { get; set; }
private bool isClosed = false;
DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
List<Caption> captions = new List<Caption>();
Caption currentCaption = null;
@@ -38,6 +39,7 @@ namespace FoxTube.Controls
TimeSpan currentPosition = Player.Position;
bool found = false;
if(!isClosed)
captions.ForEach((x) =>
{
if (Player.Position >= x.Start && Player.Position <= x.End)
@@ -56,13 +58,18 @@ namespace FoxTube.Controls
}
}
public void Initialize(string source)
public void Initialize(string source, bool isAutoGenerated = false)
{
captions.Clear();
XmlDocument doc = new XmlDocument();
doc.Load(source);
if (!isAutoGenerated)
foreach (XmlElement i in doc["timedtext"]["body"].ChildNodes)
captions.Add(new Caption(int.Parse(i.GetAttribute("t")), int.Parse(i.GetAttribute("d")), i.InnerText));
else
foreach (XmlElement i in doc["transcript"].ChildNodes)
captions.Add(new Caption(double.Parse(i.GetAttribute("start")), double.Parse(i.GetAttribute("dur")), i.InnerText.Replace("<font color=\"#E5E5E5\">", "").Replace("<font color=\"#CCCCCC\">", "").Replace("</font>", "").Replace("&#39;", "'")));
captions.ForEach((x) =>
{
@@ -84,5 +91,15 @@ namespace FoxTube.Controls
Visibility = Visibility.Collapsed;
timer.Stop();
}
public void Hide()
{
isClosed = true;
}
public void Show()
{
isClosed = false;
}
}
}
+8 -14
View File
@@ -13,11 +13,11 @@
RequestedTheme="Dark"
PointerMoved="UserControl_PointerMoved"
PointerExited="UserControl_PointerExited"
PointerEntered="UserControl_PointerEntered"
KeyUp="UserControl_KeyUp">
PointerEntered="UserControl_PointerEntered">
<Grid Background="White" Name="grid" Tapped="UserControl_Tapped">
<MediaElement IsDoubleTapEnabled="False" CurrentStateChanged="videoSource_CurrentStateChanged" AutoPlay="False" Name="videoSource" AreTransportControlsEnabled="False" PosterSource="ms-appx:///Assets/videoThumbSample.png"/>
<MediaElement Width="0" Height="0" VerticalAlignment="Top" HorizontalAlignment="Right" Name="audioSource" MediaOpened="videoSource_Opened" BufferingProgressChanged="videoSource_BufferingProgressChanged"/>
<MediaElement IsDoubleTapEnabled="False" CurrentStateChanged="videoSource_CurrentStateChanged" AutoPlay="False" MediaOpened="videoSource_Opened" BufferingProgressChanged="videoSource_BufferingProgressChanged" Volume="0" Name="videoSource" AreTransportControlsEnabled="False" PosterSource="ms-appx:///Assets/videoThumbSample.png"/>
<controls1:LiveCaptions Player="{x:Bind videoSource}" Visibility="Collapsed"/>
@@ -98,7 +98,10 @@
<Grid Grid.Column="1">
<TextBlock Name="elapsedTime" Foreground="White" Text="[Elapsed]" VerticalAlignment="Bottom" HorizontalAlignment="Left" ToolTipService.ToolTip="Time elapsed"/>
<TextBlock Name="remainingTime" Foreground="White" Text="[Remaining]" VerticalAlignment="Bottom" HorizontalAlignment="Right" ToolTipService.ToolTip="Time remaining"/>
<Slider PointerCaptureLost="seek_PointerCaptureLost" ValueChanged="seek_ValueChanged" Name="seek" VerticalAlignment="Top" ToolTipService.ToolTip="Seek" IsThumbToolTipEnabled="False" HorizontalAlignment="Stretch"/>
<Grid VerticalAlignment="Top" Margin="0,18,0,0" Height="2">
<ProgressBar Background="#66FFFFFF" Foreground="LightGray" Name="bufferingLevel"/>
</Grid>
<Slider PointerCaptureLost="seek_PointerCaptureLost" ValueChanged="seek_ValueChanged" Name="seek" VerticalAlignment="Top" ToolTipService.ToolTip="Seek" IsThumbToolTipEnabled="False" Background="Transparent" HorizontalAlignment="Stretch"/>
</Grid>
<StackPanel Grid.Column="2" Orientation="Horizontal">
@@ -120,16 +123,7 @@
<Button Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="&#xE713;" Foreground="White" Width="50" Height="50" FontSize="25" ToolTipService.ToolTip="Video quality">
<Button.Flyout>
<Flyout>
<ComboBox Width="225" Header="Quality" Name="quality" SelectionChanged="quality_SelectionChanged">
<!--<ComboBoxItem Content="Auto"/>-->
<ComboBoxItem Content="2160P" Visibility="Collapsed"/>
<ComboBoxItem Content="1080p" Visibility="Collapsed"/>
<ComboBoxItem Content="720p" Visibility="Collapsed"/>
<ComboBoxItem Content="480p" Visibility="Collapsed"/>
<ComboBoxItem Content="360p" Visibility="Collapsed"/>
<ComboBoxItem Content="240p" Visibility="Collapsed"/>
<ComboBoxItem Content="144p" Visibility="Collapsed"/>
</ComboBox>
<ComboBox Width="225" Header="Quality" Name="quality" SelectionChanged="quality_SelectionChanged"/>
</Flyout>
</Button.Flyout>
</Button>
+236 -74
View File
@@ -1,26 +1,17 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Timers;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Microsoft.Toolkit.Uwp.UI.Animations;
using Google.Apis.YouTube.v3.Data;
using Google.Apis.YouTube.v3;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Networking.Connectivity;
using System.Diagnostics;
using Windows.Media;
using Windows.Storage.Streams;
@@ -28,13 +19,13 @@ using Windows.UI.ViewManagement;
using System.Xml;
using Windows.ApplicationModel.Core;
using Windows.UI;
using Windows.Graphics.Display;
using Windows.Media.Casting;
using YoutubeExplode.Models.MediaStreams;
using YoutubeExplode;
using YoutubeExplode.Models.ClosedCaptions;
using System.Globalization;
using FoxTube.Controls;
using Windows.System;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
@@ -44,14 +35,31 @@ namespace FoxTube
{
public string videoId;
public bool miniView = false;
private bool miniview = false;
public bool MiniView
{
get { return miniview; }
set
{
miniview = value;
if (value)
captions.Hide();
else
captions.Show();
}
}
private bool fullScreen = false;
public bool pointerCaptured = false;
public event ObjectEventHandler SetFullSize;
public event ObjectEventHandler NextClicked;
bool isMuxed = false;
bool audioReady = false;
bool videoReady = false;
CoreCursor cursorBackup = Window.Current.CoreWindow.PointerCursor;
Point cursorPositionBackup;
public TimeSpan elapsed;
TimeSpan remaining;
@@ -65,7 +73,6 @@ namespace FoxTube
SystemMediaTransportControls systemControls = SystemMediaTransportControls.GetForCurrentView();
LiveCaptions captions;
YoutubeClient client = new YoutubeClient();
IReadOnlyList<ClosedCaptionTrackInfo> ccInfo;
MediaStreamInfoSet streamInfo;
@@ -86,13 +93,20 @@ namespace FoxTube
this.InitializeComponent();
Visibility = Visibility.Collapsed;
if (!ApplicationView.GetForCurrentView().IsViewModeSupported(ApplicationViewMode.CompactOverlay))
miniViewBtn.Visibility = Visibility.Collapsed;;
miniViewBtn.Visibility = Visibility.Collapsed;
captions = grid.Children[1] as LiveCaptions;
captions = grid.Children[2] as LiveCaptions;
volume.Value = Convert.ToDouble(settings.Values["volume"]);
videoSource.AutoPlay = (bool)settings.Values["videoAutoplay"];
videoSource.MediaEnded += (s, arg) =>
{
seek.Value = seek.Maximum;
seekIndicator.Value = seekIndicator.Maximum;
seekTimer.Stop();
};
audioSource.CurrentStateChanged += AudioSource_CurrentStateChanged;
t.Elapsed += T_Elapsed;
seekTimer.Elapsed += SeekTimer_Elapsed;
}
@@ -105,39 +119,36 @@ namespace FoxTube
#region Retrieving info for CC and Media streams
//Loading streams
streamInfo = await client.GetVideoMediaStreamInfosAsync(item.Id);
foreach(MuxedStreamInfo i in streamInfo.Muxed)
{
if (i.VideoQuality == VideoQuality.High2160)
(quality.Items[0] as ComboBoxItem).Visibility = Visibility.Visible;
if (i.VideoQuality == VideoQuality.High1080)
(quality.Items[1] as ComboBoxItem).Visibility = Visibility.Visible;
if (i.VideoQuality == VideoQuality.High720)
(quality.Items[2] as ComboBoxItem).Visibility = Visibility.Visible;
if (i.VideoQuality == VideoQuality.Medium480)
(quality.Items[3] as ComboBoxItem).Visibility = Visibility.Visible;
if (i.VideoQuality == VideoQuality.Medium360)
(quality.Items[4] as ComboBoxItem).Visibility = Visibility.Visible;
if (i.VideoQuality == VideoQuality.Low240)
(quality.Items[5] as ComboBoxItem).Visibility = Visibility.Visible;
if (i.VideoQuality == VideoQuality.Low144)
(quality.Items[6] as ComboBoxItem).Visibility = Visibility.Visible;
}
streamInfo = await new YoutubeClient().GetVideoMediaStreamInfosAsync(videoId);
streamInfo.Audio.ToList().ForEach(x => Debug.WriteLine($"{x.AudioEncoding} {x.Bitrate}"));
int k = (int)settings.Values["quality"];
if ((quality.Items[k] as ComboBoxItem).Visibility == Visibility.Visible)
quality.SelectedIndex = k;
List<VideoQuality> q = streamInfo.GetAllVideoQualities().ToList();
q.Sort();
q.Reverse();
foreach (VideoQuality i in q)
quality.Items.Add(new ComboBoxItem() { Content = i.GetVideoQualityLabel() });
string s;
if ((string)settings.Values["quality"] == "remember")
s = (string)settings.Values["rememberedQuality"];
else
quality.SelectedItem = quality.Items.First(x => (x as ComboBoxItem).Visibility == Visibility.Visible);
s = (string)settings.Values["quality"];
if (quality.Items.ToList().Find(x => (x as ComboBoxItem).Content as string == s) != null)
quality.SelectedItem = quality.Items.First(x => (x as ComboBoxItem).Content as string == s);
else
quality.SelectedItem = quality.Items.First();
//Loading captions
ccInfo = await client.GetVideoClosedCaptionTrackInfosAsync(item.Id);
ccInfo = await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(item.Id);
foreach (ClosedCaptionTrackInfo cc in ccInfo)
{
subsLang.Items.Add(new ComboBoxItem()
{
Content = string.Format("{1}{0}", CultureInfo.GetCultureInfo(cc.Language.Code).DisplayName, cc.IsAutoGenerated ? " (Auto-generated)" : ""),
Content = string.Format("{0}{1}", CultureInfo.GetCultureInfo(cc.Language.Code).DisplayName, cc.IsAutoGenerated ? " (Auto-generated)" : ""),
Tag = cc
});
}
if(ccInfo.Count > 0)
subsLang.SelectedIndex = 0;
else
@@ -232,22 +243,30 @@ namespace FoxTube
{
seek.Value = videoSource.Position.TotalSeconds;
seekIndicator.Value = seek.Value;
/*if (Math.Round(videoSource.Position.TotalSeconds, 1) != Math.Round(audioSource.Position.TotalSeconds, 1))
{
Debug.WriteLine($"Correcting tracks synchronization (Video track position: {videoSource.Position}; Audio track position: {audioSource.Position})");
audioSource.Position = videoSource.Position;
}*/
}
void Elapsed()
{
controls.Visibility = Visibility.Collapsed;
if (!miniView)
if (!MiniView)
touchCentral.Visibility = Visibility.Collapsed;
if (pointerCaptured)
{
cursorPositionBackup = Window.Current.CoreWindow.PointerPosition;
Window.Current.CoreWindow.PointerCursor = null;
}
seekIndicator.Visibility = Visibility.Collapsed;
t.Stop();
}
public void UpdateSize()
{
if(miniView)
if(MiniView)
{
Height = Window.Current.Bounds.Height;
Debug.WriteLine("Video player aspect ratio has been corrected.");
@@ -258,19 +277,19 @@ namespace FoxTube
{
double v = volume.Value;
if (v == 0)
muteBtn.Content = openVolume.Content = "";
muteBtn.Content = openVolume.Content = "\xE74F";
else if (v <= 25 && v > 0)
muteBtn.Content = openVolume.Content = "";
muteBtn.Content = openVolume.Content = "\xE992";
else if (v <= 50 && v > 25)
muteBtn.Content = openVolume.Content = "";
muteBtn.Content = openVolume.Content = "\xE993";
else if (v <= 75 && v > 50)
muteBtn.Content = openVolume.Content = "";
muteBtn.Content = openVolume.Content = "\xE994";
else if (v > 75)
muteBtn.Content = openVolume.Content = "";
muteBtn.Content = openVolume.Content = "\xE995";
settings.Values["volume"] = volume.Value;
videoSource.Volume = volume.Value * 0.01;
audioSource.Volume = videoSource.Volume = volume.Value * 0.01;
}
private void muteBtn_Click(object sender, RoutedEventArgs e)
@@ -298,13 +317,17 @@ namespace FoxTube
private void UserControl_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (cursorPositionBackup == null)
cursorPositionBackup = Window.Current.CoreWindow.PointerPosition;
else if (cursorPositionBackup == Window.Current.CoreWindow.PointerPosition)
return;
ShowControls();
}
void ShowControls()
{
controls.Visibility = Visibility.Visible;
if (miniView)
if (MiniView)
seekIndicator.Visibility = Visibility.Visible;
if (pointerCaptured)
Window.Current.CoreWindow.PointerCursor = cursorBackup;
@@ -317,32 +340,117 @@ namespace FoxTube
try
{
videoSource.Pause();
audioSource.Source = null;
timecodeBackup = videoSource.Position.TotalSeconds;
switch((quality.SelectedItem as ComboBoxItem).Content)
audioReady = false;
videoReady = false;
settings.Values["rememberedQuality"] = (quality.SelectedItem as ComboBoxItem).Content as string;
if(streamInfo.Muxed.ToList().Find(x => x.VideoQualityLabel == (quality.SelectedItem as ComboBoxItem).Content as string) != null)
{
isMuxed = true;
videoSource.Source = streamInfo.Muxed.First(x => x.VideoQualityLabel == (quality.SelectedItem as ComboBoxItem).Content as string).Url.ToUri();
}
else
{
isMuxed = false;
videoSource.Source = streamInfo.Video.First(x => x.VideoQualityLabel == (quality.SelectedItem as ComboBoxItem).Content as string).Url.ToUri();
audioSource.Source = streamInfo.Audio.First().Url.ToUri();
}
/*switch((quality.SelectedItem as ComboBoxItem).Content)
{
case "2160p":
videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.High2160).Url);
if(streamInfo.Muxed.First(x => x.VideoQuality.))
{
isMuxed = true;
}
else
{
isMuxed = false;
audioSource.Source = (streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityHigh && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityMedium && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityLow && !x.HasVideo) ??
streamInfo.Find(x => x.HasAudio && !x.HasVideo)).Uri;
}
videoSource.Source = streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality2160P).Uri;
break;
case "1080p":
videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.High1080).Url);
if (streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality1080P).HasAudio)
isMuxed = true;
else
{
isMuxed = false;
audioSource.Source = (streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityHigh && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityMedium && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityLow && !x.HasVideo) ??
streamInfo.Find(x => x.HasAudio && !x.HasVideo)).Uri;
}
videoSource.Source = streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality1080P).Uri;
break;
case "720p":
videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.High720).Url);
if (streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality720P).HasAudio)
isMuxed = true;
else
{
isMuxed = false;
audioSource.Source = (streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityHigh && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityMedium && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityLow && !x.HasVideo) ??
streamInfo.Find(x => x.HasAudio && !x.HasVideo)).Uri;
}
videoSource.Source = streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality720P).Uri;
break;
case "480p":
videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Medium480).Url);
if (streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality480P).HasAudio)
isMuxed = true;
else
{
isMuxed = false;
audioSource.Source = (streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityMedium && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityLow && !x.HasVideo) ??
streamInfo.Find(x => x.HasAudio && !x.HasVideo)).Uri;
}
videoSource.Source = streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality480P).Uri;
break;
case "360p":
videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Medium360).Url);
if (streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality360P).HasAudio)
isMuxed = true;
else
{
isMuxed = false;
audioSource.Source = (streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityMedium && !x.HasVideo) ??
streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityLow && !x.HasVideo) ??
streamInfo.Find(x => x.HasAudio && !x.HasVideo)).Uri;
}
videoSource.Source = streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality360P).Uri;
break;
case "240p":
videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Low240).Url);
if (streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality240P).HasAudio)
isMuxed = true;
else
{
isMuxed = false;
audioSource.Source = (streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityLow && !x.HasVideo) ??
streamInfo.Find(x => x.HasAudio && !x.HasVideo)).Uri;
}
videoSource.Source = streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality240P).Uri;
break;
case "144p":
videoSource.Source = new Uri(streamInfo.Muxed.First(x => x.VideoQuality == VideoQuality.Low144).Url);
break;
if (streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality144P).HasAudio)
isMuxed = true;
else
{
isMuxed = false;
audioSource.Source = (streamInfo.Find(x => x.AudioQuality == YouTubeQuality.QualityLow && !x.HasVideo) ??
streamInfo.Find(x => x.HasAudio && !x.HasVideo)).Uri;
}
videoSource.Source = streamInfo.First(x => x.VideoQuality == YouTubeQuality.Quality144P).Uri;
break;
}*/
needUpdateTimecode = true;
videoSource.Play();
@@ -367,7 +475,9 @@ namespace FoxTube
{
if (subsSwitch.IsOn)
{
if (ccInfo[subsLang.SelectedIndex].IsAutoGenerated)
captions.Initialize(ccInfo[subsLang.SelectedIndex].Url.Replace("format=3", "format=0"), true);
else
captions.Initialize(ccInfo[subsLang.SelectedIndex].Url);
}
else
@@ -399,9 +509,47 @@ namespace FoxTube
if (videoSource.CurrentState == MediaElementState.Playing)
videoSource.Pause();
else if (videoSource.CurrentState == MediaElementState.Paused)
if((audioReady && videoReady) || isMuxed)
videoSource.Play();
}
private void AudioSource_CurrentStateChanged(object sender, RoutedEventArgs e)
{
switch (audioSource.CurrentState)
{
case MediaElementState.Buffering:
if(videoSource.CurrentState != MediaElementState.Buffering)
videoSource.Pause();
break;
case MediaElementState.Playing:
if (videoSource.CurrentState == MediaElementState.Paused)
videoSource.Play();
else if (videoSource.CurrentState == MediaElementState.Buffering)
audioSource.Pause();
break;
}
}
private void videoSource_Opened(object sender, RoutedEventArgs arg)
{
if(!isMuxed)
{
if(sender == videoSource)
{
videoReady = true;
if (audioReady && ((timecodeBackup == 0 && (bool)settings.Values["videoAutoplay"]) || timecodeBackup > 0))
play_Click(this, null);
}
else if(sender == audioSource)
{
audioReady = true;
if (videoReady && ((timecodeBackup == 0 && (bool)settings.Values["videoAutoplay"]) || timecodeBackup > 0))
play_Click(this, null);
}
}
}
private void videoSource_CurrentStateChanged(object sender, RoutedEventArgs e)
{
if(videoSource.CurrentState == MediaElementState.Playing && needUpdateTimecode)
@@ -413,6 +561,8 @@ namespace FoxTube
switch(videoSource.CurrentState)
{
case MediaElementState.Buffering:
audioSource.Position = videoSource.Position;
audioSource.Pause();
bufferingBar.Visibility = Visibility.Visible;
seek.IsEnabled = false;
@@ -426,6 +576,8 @@ namespace FoxTube
break;
case MediaElementState.Paused:
if(audioSource.CurrentState != MediaElementState.Buffering)
audioSource.Pause();
bufferingBar.Visibility = Visibility.Collapsed;
seek.IsEnabled = true;
@@ -439,12 +591,15 @@ namespace FoxTube
break;
case MediaElementState.Playing:
audioSource.Play();
bufferingBar.Visibility = Visibility.Collapsed;
seek.IsEnabled = true;
play.IsEnabled = true;
touchPlay.IsEnabled = true;
seekTimer.Start();
play.Content = "\xE103";
touchPlay.Content = "\xE103";
@@ -452,6 +607,7 @@ namespace FoxTube
break;
default:
audioSource.Pause();
bufferingBar.Visibility = Visibility.Collapsed;
systemControls.PlaybackStatus = MediaPlaybackStatus.Closed;
break;
@@ -461,10 +617,10 @@ namespace FoxTube
private async void miniView_Click(object sender, RoutedEventArgs e)
{
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
miniView = !miniView;
SetFullSize(this, miniView);
MiniView = !MiniView;
SetFullSize(this, MiniView);
if (miniView)
if (MiniView)
{
if (fullScreen)
{
@@ -590,7 +746,7 @@ namespace FoxTube
Width = 432;
Height = 243;
miniView = true;
MiniView = true;
Methods.MainPage.MinimizeVideo();
mainControls.Visibility = Visibility.Collapsed;
@@ -607,6 +763,8 @@ namespace FoxTube
private void close_Click(object sender, RoutedEventArgs e)
{
systemControls.IsEnabled = false;
pointerCaptured = false;
Elapsed();
Methods.MainPage.CloseVideo();
}
@@ -617,7 +775,7 @@ namespace FoxTube
Width = double.NaN;
Height = double.NaN;
miniView = false;
MiniView = false;
Methods.MainPage.MaximizeVideo();
mainControls.Visibility = Visibility.Visible;
@@ -655,38 +813,38 @@ namespace FoxTube
private void playPauseArea_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
if (miniView && ApplicationView.GetForCurrentView().ViewMode == ApplicationViewMode.CompactOverlay)
if (MiniView && ApplicationView.GetForCurrentView().ViewMode == ApplicationViewMode.CompactOverlay)
miniView_Click(this, null);
else if (miniView && ApplicationView.GetForCurrentView().ViewMode == ApplicationViewMode.Default)
else if (MiniView && ApplicationView.GetForCurrentView().ViewMode == ApplicationViewMode.Default)
maximize_Click(this, null);
else if (fullScreen)
fullscreen_Click(this, null);
else if (!miniView && !fullScreen)
else if (!MiniView && !fullScreen)
fullscreen_Click(this, null);
}
private void playPauseArea_Tapped(object sender, TappedRoutedEventArgs e)
{
if (e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse && !miniView)
if (e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse && !MiniView)
play_Click(this, null);
}
private void UserControl_KeyUp(object sender, KeyRoutedEventArgs e)
public void KeyUpPressed(object sender, KeyRoutedEventArgs e)
{
switch(e.Key)
{
case Windows.System.VirtualKey.Escape:
case Windows.System.VirtualKey.F11:
case VirtualKey.Escape:
case VirtualKey.F11:
if (fullScreen)
fullscreen_Click(this, null);
break;
case Windows.System.VirtualKey.Space:
case VirtualKey.Space:
play_Click(this, null);
break;
case Windows.System.VirtualKey.Left:
case VirtualKey.Left:
back10_Click(this, null);
break;
case Windows.System.VirtualKey.Right:
case VirtualKey.Right:
fwd30_Click(this, null);
break;
}
@@ -694,8 +852,12 @@ namespace FoxTube
private void subsLang_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (subsSwitch.IsOn)
captions.Initialize(ccInfo[subsLang.SelectedIndex].Url);
LoadTrack();
}
private void videoSource_BufferingProgressChanged(object sender, RoutedEventArgs e)
{
bufferingLevel.Value = (audioSource.Source != null && videoSource.BufferingProgress > audioSource.BufferingProgress ? audioSource.BufferingProgress : videoSource.BufferingProgress) * 100;
}
}
}
+13 -12
View File
@@ -97,6 +97,7 @@
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Classes\Caption.cs" />
<Compile Include="Classes\DownloadItemContainer.cs" />
<Compile Include="Classes\InboxItem.cs" />
<Compile Include="Classes\Methods.cs" />
<Compile Include="Classes\ObjectEventArgs.cs" />
@@ -398,7 +399,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AdaptiveCards.Rendering.Uwp">
<Version>1.0.1</Version>
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Google.Apis">
<Version>1.30.0-beta02</Version>
@@ -419,25 +420,25 @@
<Version>10.1805.7001</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version>
<Version>6.1.9</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
<Version>3.0.0</Version>
<Version>4.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
<Version>3.0.0</Version>
</PackageReference>
<PackageReference Include="MyToolkit">
<Version>2.5.16</Version>
</PackageReference>
<PackageReference Include="MyToolkit.Extended">
<Version>2.5.16</Version>
<Version>4.0.0</Version>
</PackageReference>
<PackageReference Include="runtime.win10-arm64.runtime.native.System.IO.Compression">
<Version>4.3.1</Version>
<Version>4.3.2</Version>
</PackageReference>
<PackageReference Include="Syroot.Windows.IO.KnownFolders">
<Version>1.2.0</Version>
</PackageReference>
<PackageReference Include="YoutubeExplode">
<Version>4.3.1</Version>
<Version>4.4.0</Version>
</PackageReference>
<PackageReference Include="YoutubeExtractor">
<Version>0.10.11</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
+4 -10
View File
@@ -86,6 +86,9 @@ namespace FoxTube.Pages
try
{
channelId = id;
if (Methods.NeedToResponse)
Methods.MainPage.content_Navigated(this, null);
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
request.Id = id;
if (content.Items.Count == 4)
@@ -133,8 +136,6 @@ namespace FoxTube.Pages
if (SecretsVault.IsAuthorized)
{
SecretsVault.Subscriptions.ForEach(x => Debug.WriteLine($"{x.Snippet.Title}: {x.Snippet.ResourceId.ChannelId}"));
Debug.WriteLine($"Current channel ID: {item.Id}");
bool b = false;
foreach (Subscription s in SecretsVault.Subscriptions)
{
@@ -148,7 +149,6 @@ namespace FoxTube.Pages
break;
}
}
Debug.WriteLine($"Channel was found: {b}");
subscriptionPane.Visibility = Visibility.Visible;
}
@@ -250,25 +250,19 @@ namespace FoxTube.Pages
private async void subscribe_Click(object sender, RoutedEventArgs e)
{
if (subscribe.Background == new SolidColorBrush(Colors.Red))
{
if (await SecretsVault.Subscribe(channelId))
if(await SecretsVault.ChangeSubscriptionState(channelId))
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = "Subscribed";
}
}
else
{
if (await SecretsVault.Unsubscibe(channelId))
{
subscribe.Background = new SolidColorBrush(Colors.Red);
subscribe.Foreground = new SolidColorBrush(Colors.White);
subscribe.Content = "Subscribe";
}
}
}
private void Hyperlink_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args)
{
+1 -1
View File
@@ -18,7 +18,7 @@
<RowDefinition Height="auto"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox x:Uid="/CommentsPage/textbox" Margin="5,5,42,5" PlaceholderText="Add a public comment" Name="newComment" VerticalAlignment="Center" MinHeight="32" MaxHeight="100" Height="auto" AcceptsReturn="True"/>
<TextBox x:Uid="/CommentsPage/textbox" Margin="5,5,42,5" PlaceholderText="Add a public comment" Name="newComment" VerticalAlignment="Center" MinHeight="32" TextWrapping="Wrap" AcceptsReturn="True"/>
<Button HorizontalAlignment="Right" Name="send" Click="send_Click" VerticalAlignment="Top"
Height="32" Width="32"
Margin="0,5,5,0" Padding="0"
+4 -1
View File
@@ -5,6 +5,7 @@
xmlns:local="using:FoxTube.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:FoxTube.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
@@ -27,7 +28,9 @@
</Grid>
</StackPanel>
<ScrollViewer Grid.Row="1">
<StackPanel Margin="5" Name="stack"/>
<StackPanel Margin="5" Name="stack">
<ListView ItemsSource="{Binding Path=Methods.MainPage.agent.items}" SelectionMode="None"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Page>
+3 -21
View File
@@ -11,11 +11,6 @@ using Windows.Storage.Pickers;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
#pragma warning disable CS0252 // Possible unintended reference comparison; left hand side needs cast
@@ -28,33 +23,20 @@ namespace FoxTube.Pages
public sealed partial class Downloads : Page
{
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
DownloadAgent agent = Methods.MainPage.Agent;
public Downloads()
{
this.InitializeComponent();
path.Text = settings.Values["defaultDownload"] as string;
agent.ListChanged += UpdateList;
foreach (DownloadItem item in agent.Items)
stack.Children.Add(item);
}
private void UpdateList(object sender, params object[] e)
{
if (e[0] == "remove")
stack.Children.Remove(sender as DownloadItem);
else if (e[0] == "add")
stack.Children.Add(sender as DownloadItem);
}
private async void changePath_Click(object sender, RoutedEventArgs e)
{
FolderPicker picker = new FolderPicker()
{
SuggestedStartLocation = PickerLocationId.Desktop,
SuggestedStartLocation = PickerLocationId.Downloads,
ViewMode = PickerViewMode.Thumbnail
};
picker.FileTypeFilter.Add(".shit"); //Because overwise it trhows an exception
StorageFolder p = await picker.PickSingleFolderAsync();
if (p != null)
@@ -64,7 +46,7 @@ namespace FoxTube.Pages
private async void openFolder_Click(object sender, RoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri(settings.Values["defaultDownload"] as string));
await Launcher.LaunchFolderAsync( await StorageFolder.GetFolderFromPathAsync(settings.Values["defaultDownload"] as string));
}
}
}
+12 -6
View File
@@ -8,8 +8,10 @@
xmlns:ads="using:Microsoft.Advertising.WinRT.UI"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:pages="using:FoxTube.Pages"
xmlns:Windows10version1803="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 6)"
mc:Ignorable="d"
SizeChanged="Page_SizeChanged">
SizeChanged="Page_SizeChanged"
PreviewKeyUp="Page_PreviewKeyUp">
<Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
@@ -21,11 +23,15 @@
<VisualState.Setters>
<Setter Target="header.FontSize" Value="28"/>
<Setter Target="headerGrid.Margin" Value="10,25,0,0"/>
<Setter Target="account.Height" Value="50"/>
<Setter Target="notificationMenu.Height" Value="50"/>
<Setter Target="avatar.Height" Value="50"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<NavigationView SelectedItem="toHome" BackRequested="nav_BackRequested" PaneTitle="FoxTube" OpenPaneLength="300" Name="nav" SelectionChanged="nav_SelectionChanged">
<NavigationView SelectedItem="toHome" Windows10version1803:BackRequested="nav_BackRequested" Windows10version1803:PaneTitle="FoxTube" OpenPaneLength="300" Name="nav" SelectionChanged="nav_SelectionChanged">
<NavigationView.MenuItems>
<NavigationViewItem x:Uid="/Main/home" Icon="Home" Content="Home" Name="toHome"/>
@@ -65,7 +71,7 @@
</NavigationView.AutoSuggestBox>
<NavigationView.Header>
<Grid Margin="10,25,0,0">
<Grid Name="headerGrid" Margin="0,-10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
@@ -74,7 +80,7 @@
<StackPanel Orientation="Horizontal" Grid.Column="1">
<Button Name="notificationMenu" Click="notificationMenu_Click"
FontFamily="Segoe MDL2 Assets" Content="&#xED0D;"
Width="50" Height="50" Background="Transparent" x:Uid="notifications" ToolTipService.ToolTip="Notifications">
Width="50" Height="42" Background="Transparent" x:Uid="notifications" ToolTipService.ToolTip="Notifications">
<Button.Flyout>
<Flyout Content="{x:Bind notificationsCenter}"/>
</Button.Flyout>
@@ -82,7 +88,7 @@
<Button Name="account"
FontFamily="Segoe MDL2 Assets" Content="&#xE8FA;"
Width="50" Height="50" Background="Transparent" x:Uid="signIn" ToolTipService.ToolTip="Sign in">
Width="50" Height="42" Background="Transparent" x:Uid="signIn" ToolTipService.ToolTip="Sign in">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="/Main/signEx" Text="Sign in with existing account" Name="signIn" Click="signIn_Click"/>
@@ -91,7 +97,7 @@
</Button.Flyout>
</Button>
<Button Width="50" Background="Transparent" Height="50" Visibility="Collapsed" Name="avatar">
<Button Width="50" Background="Transparent" Height="42" Visibility="Collapsed" Name="avatar">
<PersonPicture Width="30" ToolTipService.ToolTip="My account" x:Uid="myAccount"/>
<Button.Flyout>
<MenuFlyout>
+60 -27
View File
@@ -1,48 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using System.Diagnostics;
using Microsoft.Toolkit.Uwp.Notifications;
using Windows.UI.Notifications;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Storage;
using System.Xml;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using System.Threading.Tasks;
using System.Threading;
using Google.Apis.Util.Store;
using System.Globalization;
using Windows.ApplicationModel.Core;
using Windows.System;
using Windows.UI.Xaml.Documents;
using Google.Apis.Oauth2.v2;
using Google.Apis.Oauth2.v2.Data;
using FoxTube.Controls;
using FoxTube.Pages;
using Microsoft.Toolkit.Uwp.UI.Controls;
using Windows.ApplicationModel;
using System.Net;
using Windows.UI.Popups;
using Windows.Networking.Connectivity;
using Windows.UI.Core;
using Syroot.Windows.IO;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
@@ -67,7 +49,9 @@ namespace FoxTube
this.InitializeComponent();
if (settings.Values["quality"] == null)
settings.Values.Add("quality", 1);
settings.Values.Add("quality", "remember");
if (settings.Values["rememberedQuality"] == null)
settings.Values.Add("rememberedQuality", "1080p");
if (settings.Values["newVideoNotification"] == null)
settings.Values.Add("newVideoNotification", true);
@@ -89,7 +73,7 @@ namespace FoxTube
settings.Values.Add("safeSearch", 0);
if (settings.Values["defaultDownload"] == null)
settings.Values.Add("defaultDownload", Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\DownloadedVideos");
settings.Values.Add("defaultDownload", Syroot.Windows.IO.KnownFolders.Downloads.Path + "\\DownloadedVideos");
if (settings.Values["notificationsHistory"] == null)
{
@@ -144,6 +128,12 @@ namespace FoxTube
SetTitleBar();
}
public Video GetCurrentItem()
{
try { return (videoPlaceholder.Content as VideoPage).item; }
catch { return null; }
}
public void SetTitleBar()
{
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
@@ -307,17 +297,22 @@ namespace FoxTube
public void GoToSearch(SearchParameters args)
{
nav.IsPaneOpen = false;
MinimizeAsInitializer();
content.Navigate(typeof(Search), args);
}
public void GoToChannel(string id)
{
Debug.WriteLine($"Channel id: {id}");
MinimizeAsInitializer();
content.Navigate(typeof(ChannelPage), id);
}
public async void GoToVideo(string id, string playlistId = null)
{
Debug.WriteLine($"Video id: {id}; Playlist id: {playlistId}");
try
{
var connection = NetworkInformation.GetInternetConnectionProfile().GetConnectionCost();
if ((bool)settings.Values["moblieWarning"] && (connection.NetworkCostType == NetworkCostType.Fixed || connection.NetworkCostType == NetworkCostType.Variable))
@@ -340,6 +335,8 @@ namespace FoxTube
if (cancel)
return;
}
}
catch { }
nav.IsPaneOpen = false;
@@ -358,6 +355,7 @@ namespace FoxTube
public void GoToPlaylist(string id)
{
Debug.WriteLine($"Playlist id: {id}");
MinimizeAsInitializer();
content.Navigate(typeof(PlaylistPage), id);
}
@@ -369,10 +367,18 @@ namespace FoxTube
}
public void MinimizeAsInitializer()
{
if(videoPlaceholder.Content != null)
{
if ((videoPlaceholder.Content as VideoPage).loading.State != LoadingState.Loaded)
CloseVideo();
else
{
try { (videoPlaceholder.Content as VideoPage).player.minimize_Click(this, null); }
catch { }
}
}
}
public void MinimizeVideo()
{
@@ -405,7 +411,7 @@ namespace FoxTube
{
nav.OpenPaneLength = 0;
nav.CompactPaneLength = 0;
if ((videoPlaceholder.Content as VideoPage).player.miniView)
if ((videoPlaceholder.Content as VideoPage).player.MiniView)
nav.Margin = new Thickness(0, -80, 0, 0);
else
nav.Margin = new Thickness(0, -91, 0, 0);
@@ -427,6 +433,7 @@ namespace FoxTube
}
(videoPlaceholder.Content as VideoPage).player.pointerCaptured = false;
videoPlaceholder.Content = null;
Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0);
MaximizeVideo();
if (content.CanGoBack)
@@ -437,6 +444,7 @@ namespace FoxTube
private void search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if(!string.IsNullOrWhiteSpace(search.Text))
GoToSearch(new SearchParameters(search.Text));
}
@@ -494,7 +502,7 @@ namespace FoxTube
s = Sender.None;
}
private void content_Navigated(object sender, NavigationEventArgs e)
public void content_Navigated(object sender, NavigationEventArgs e)
{
Dictionary<Type, Action> switchCase = new Dictionary<Type, Action>()
{
@@ -527,9 +535,16 @@ namespace FoxTube
} },
{ typeof(ChannelPage), () =>
{
if(sender.GetType() != typeof(ChannelPage))
{
Methods.NeedToResponse = true;
s = Sender.None;
return;
}
Methods.NeedToResponse = false;
if(SecretsVault.IsAuthorized)
{
if((content.Content as ChannelPage).channelId == SecretsVault.AccountId)
if((content.Content as ChannelPage).channelId == SecretsVault.UserChannel.Id)
{
if(nav.SelectedItem != toChannel)
nav.SelectedItem = toChannel;
@@ -551,14 +566,23 @@ namespace FoxTube
}
if(!found)
{
nav.SelectedItem = null;
}
}
}
else
nav.SelectedItem = null;
} },
{ typeof(PlaylistPage), () =>
{
if(sender.GetType() != typeof(PlaylistPage))
{
Methods.NeedToResponse = true;
s = Sender.None;
return;
}
Methods.NeedToResponse = false;
if((content.Content as PlaylistPage).playlistId == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
{
if(nav.SelectedItem != toLiked)
@@ -609,7 +633,7 @@ namespace FoxTube
} }
};
try { navCase[e.SourcePageType](); }
try { navCase[content.SourcePageType](); }
catch
{
nav.SelectedItem = null;
@@ -623,7 +647,7 @@ namespace FoxTube
else
nav.IsBackEnabled = false;
if (e.SourcePageType == typeof(Home) || e.SourcePageType == typeof(Settings) || e.SourcePageType == typeof(Subscriptions))
if (content.SourcePageType == typeof(Home) || content.SourcePageType == typeof(Settings) || content.SourcePageType == typeof(Subscriptions))
{
nav.ExpandedModeThresholdWidth = 1008;
if (nav.DisplayMode == NavigationViewDisplayMode.Expanded)
@@ -655,5 +679,14 @@ namespace FoxTube
else
content.GoBack();
}
private void Page_PreviewKeyUp(object sender, KeyRoutedEventArgs e)
{
if(videoPlaceholder.Content != null && FocusManager.GetFocusedElement().GetType() != typeof(TextBox))
{
e.Handled = true;
(videoPlaceholder.Content as VideoPage).player.KeyUpPressed(sender, e);
}
}
}
}
+3
View File
@@ -62,6 +62,9 @@ namespace FoxTube.Pages
try
{
playlistId = id;
if (Methods.NeedToResponse)
Methods.MainPage.content_Navigated(this, null);
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails");
request.Id = id;
-1
View File
@@ -1,5 +1,4 @@
<Page
NavigationCacheMode="Enabled"
x:Class="FoxTube.Search"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+6 -8
View File
@@ -92,13 +92,11 @@ namespace FoxTube
}
public async void Initialize(SearchParameters arg, bool forceInitialization = false)
public async void Initialize(SearchParameters arg)
{
if (Parameters != null && arg.Term == Parameters.Term && !forceInitialization)
return;
loading.Refresh();
//try
try
{
Parameters = arg;
request = SecretsVault.Service.Search.List("id,snippet");
@@ -161,14 +159,14 @@ namespace FoxTube
loading.Close();
}
/*catch (System.Net.Http.HttpRequestException)
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);
}*/
}
}
private void toggleFilters_Click(object sender, RoutedEventArgs e)
@@ -187,7 +185,7 @@ namespace FoxTube
private void AppBarButton_Click(object sender, RoutedEventArgs e)
{
Initialize(Parameters, true);
Initialize(Parameters);
}
private async void more_Clicked()
@@ -244,7 +242,7 @@ namespace FoxTube
Parameters.Filter.Date = (SearchParameters.Filters.Enumerations.Date)date.SelectedIndex;
Parameters.Filter.Duration = (SearchParameters.Filters.Enumerations.Duration)duration.SelectedIndex;
Methods.MainPage.GoToSearch(Parameters);
Initialize(Parameters);
}
}
}
+2 -2
View File
@@ -21,11 +21,11 @@
<settingspages:About/>
</ScrollViewer>
</PivotItem>
<PivotItem Header="Help us translate this app" x:Uid="/Settings/helpTranslate">
<!--<PivotItem Header="Help us translate this app" x:Uid="/Settings/helpTranslate">
<ScrollViewer>
<settingspages:Translate/>
</ScrollViewer>
</PivotItem>
</PivotItem>-->
<PivotItem Header="Inbox" x:Uid="/Settings/inbox">
<ScrollViewer>
<settingspages:Inbox/>
+3 -3
View File
@@ -40,7 +40,7 @@ namespace FoxTube
{
if ((e.Parameter as string).Contains("inbox"))
{
pivot.SelectedIndex = 4;
pivot.SelectedIndex = 3;
if ((string)e.Parameter != "inbox")
((pivot.SelectedItem as PivotItem).Content as Inbox).Open((e.Parameter as string).Split('&')[1]);
}
@@ -59,9 +59,9 @@ namespace FoxTube
private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (pivot.SelectedIndex == 3 && !inboxLoaded)
if (pivot.SelectedIndex == 2 && !inboxLoaded)
{
(((pivot.Items[3] as PivotItem).Content as ScrollViewer).Content as Inbox).LoadItems();
(((pivot.Items[2] as PivotItem).Content as ScrollViewer).Content as Inbox).LoadItems();
inboxLoaded = true;
}
}
+1 -1
View File
@@ -41,7 +41,7 @@
<TextBlock><Run x:Uid="/About/vk">Vkontakte:</Run> <Hyperlink NavigateUri="https://vk.com/XFox.Mike">@XFox.Mike</Hyperlink></TextBlock>
<TextBlock>YouTube: <Hyperlink NavigateUri="https://youtube.com/c/FoxGameStudioChannel">@FoxGameStudioChannel</Hyperlink></TextBlock>
<TextBlock>E-mail: <Hyperlink NavigateUri="mailto:michael.xfox@outlook.com">michael.xfox@outlook.com</Hyperlink></TextBlock>
<TextBlock Margin="0,0,0,10"> <Run x:Uid="/About/myBlog">My blog (Russian language only):</Run> <Hyperlink NavigateUri="https://michael-xfox.com">https://michael-xfox.com</Hyperlink></TextBlock>
<TextBlock Visibility="Collapsed" Margin="0,0,0,10"> <Run x:Uid="/About/myBlog">My blog (Russian language only):</Run> <Hyperlink NavigateUri="https://michael-xfox.com">https://michael-xfox.com</Hyperlink></TextBlock>
<TextBlock x:Uid="/About/legal" Text="Legal stuff" FontSize="22" FontWeight="SemiBold"/>
<HyperlinkButton x:Uid="/About/ourPrivacy" Content="Our Privacy Policy" NavigateUri="https://michael-xfox.com/foxtubepp.txt" Padding="0,5,0,0"/>
+8 -8
View File
@@ -28,14 +28,14 @@
<TextBlock x:Uid="/General/playback" Text="Playback" FontSize="22"/>
<ComboBox x:Uid="/General/quality" Width="250" Header="Default video playback quality" Name="quality" SelectionChanged="quality_SelectionChanged">
<ComboBoxItem x:Uid="/General/remember" Content="Remember my choice"/>
<ComboBoxItem Content="2160p"/>
<ComboBoxItem Content="1080p"/>
<ComboBoxItem Content="720p"/>
<ComboBoxItem Content="480p"/>
<ComboBoxItem Content="360p"/>
<ComboBoxItem Content="240p"/>
<ComboBoxItem Content="144p"/>
<ComboBoxItem Tag="remember" x:Uid="/General/remember" Content="Remember my choice"/>
<ComboBoxItem Tag="2160p" Content="2160p"/>
<ComboBoxItem Tag="1080p" Content="1080p"/>
<ComboBoxItem Tag="720p" Content="720p"/>
<ComboBoxItem Tag="480p" Content="480p"/>
<ComboBoxItem Tag="360p" Content="360p"/>
<ComboBoxItem Tag="240p" Content="240p"/>
<ComboBoxItem Tag="144p" Content="144p"/>
</ComboBox>
<ToggleSwitch x:Uid="/General/metered" OnContent="Notify when playing on metered connection" OffContent="Notify when playing on metered connection" Name="mobileWarning" Toggled="mobileWarning_Toggled"/>
<ToggleSwitch x:Uid="/General/autoplay" OnContent="Play videos automatically" OffContent="Play videos automatically" Name="autoplay" Toggled="autoplay_Toggled"/>
+1 -1
View File
@@ -76,7 +76,7 @@ namespace FoxTube.Pages.SettingsPages
private void quality_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
settings.Values["quality"] = quality.SelectedIndex;
settings.Values["quality"] = (quality.SelectedItem as ComboBoxItem).Tag as string;
}
private void mobileWarning_Toggled(object sender, RoutedEventArgs e)
@@ -119,6 +119,7 @@ namespace FoxTube.Pages.SettingsPages
{
content.Text = "";
title.Text = "";
list.SelectedItem = null;
block.Visibility = Visibility.Visible;
}
+4 -4
View File
@@ -34,7 +34,8 @@
<ScrollViewer Margin="0,0,0,50" Name="mainScroll">
<StackPanel Orientation="Vertical" Name="mainContent">
<local:VideoPlayer/>
<StackPanel Margin="10" Name="descriptionPanel">
<PivotItem Header="Description" Name="descriptionPanel">
<StackPanel Margin="0,10">
<TextBlock IsTextSelectionEnabled="True" Name="title" Text="[Video title]" FontSize="25" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Start"/>
<Grid>
<Grid.RowDefinitions>
@@ -95,15 +96,14 @@
<TextBlock Name="license" Grid.Column="2" Grid.Row="2" Text="[License type]"/>
</Grid>
</StackPanel>
</PivotItem>
</StackPanel>
</ScrollViewer>
<CommandBar VerticalAlignment="Bottom" Name="commandbar">
<AppBarButton Icon="Download" Label="Download video" Name="download">
<AppBarButton.Flyout>
<MenuFlyout x:Name="downloadSelector">
<MenuFlyoutItem Text="No items are available" IsEnabled="False"/>
</MenuFlyout>
<MenuFlyout x:Name="downloadSelector"/>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarButton Label="Add to" Icon="Add" IsEnabled="False"/>
+25 -61
View File
@@ -1,36 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.System;
using Google.Apis.YouTube.v3.Data;
using Google.Apis.YouTube.v3;
using Windows.UI.Xaml.Media.Imaging;
using System.Diagnostics;
using System.Timers;
using Windows.UI.Core;
using System.Threading;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
using Windows.ApplicationModel;
using Windows.Storage.Streams;
using Windows.UI.Popups;
using Windows.UI.Text;
using Windows.UI;
using FoxTube.Pages;
using MyToolkit.Multimedia;
using FoxTube.Controls;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
@@ -63,9 +48,7 @@ namespace FoxTube.Pages
public string videoId;
string playlistId = null;
Video item;
List<string> downloads = new List<string>();
public Video item;
bool wide;
bool isExtended = false;
@@ -92,11 +75,7 @@ namespace FoxTube.Pages
{
Debug.WriteLine("Correcting layout...");
mainContent.Children.Remove(descriptionPanel);
pivot.Items.Insert(0, new PivotItem()
{
Content = descriptionPanel,
Header = "Description"
});
pivot.Items.Insert(0, descriptionPanel);
tabsPlaceholder.Children.Remove(pivot);
mainContent.Children.Add(pivot);
@@ -256,40 +235,38 @@ namespace FoxTube.Pages
async void LoadDownloads()
{
downloadSelector.Items.Clear();
YouTubeUri[] uris = await YouTube.GetUrisAsync(item.Id);
if (uris.Length > 0)
/*List<YouTubeUri> uris = (await YouTube.GetUrisAsync(item.Id)).ToList();
if (uris.Count > 0)
foreach (YouTubeUri u in uris)
{
if (u.HasAudio && u.HasVideo)
{
downloads.Add(u.Uri.AbsoluteUri);
MenuFlyoutItem menuItem = new MenuFlyoutItem()
{
Text = Methods.QualityToString(u.VideoQuality)
Text = Methods.QualityToString(u.VideoQuality),
Tag = u.Uri.AbsoluteUri
};
menuItem.Click += downloadItemSelected;
downloadSelector.Items.Add(menuItem);
}
else if (u.HasAudio)
{
downloads.Add(u.Uri.AbsoluteUri);
MenuFlyoutItem menuItem = new MenuFlyoutItem()
{
Text = Methods.QualityToString(u.AudioQuality)
Text = Methods.QualityToString(u.AudioQuality),
Tag = u.Uri.AbsoluteUri
};
menuItem.Click += downloadItemSelected;
downloadSelector.Items.Add(menuItem);
}
}
else
download.Visibility = Visibility.Collapsed;
download.Visibility = Visibility.Collapsed;*/
}
private void downloadItemSelected(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
Methods.MainPage.Agent.Add((sender as MenuFlyoutItem).Tag.ToString());
}
async void LoadRelatedVideos()
@@ -360,40 +337,31 @@ namespace FoxTube.Pages
else
wide = false;
if (e.NewSize.Width > 1000 && e.PreviousSize.Width <= 1000 && !isExtended)
{
if (mainContent.Children.Contains(pivot))
if (e.NewSize.Width > 1000 && mainContent.Children.Contains(pivot) && !isExtended)
{
mainContent.Children.Remove(pivot);
tabsPlaceholder.Children.Add(pivot);
(pivot.Items[0] as PivotItem).Content = null;
pivot.Items.RemoveAt(0);
mainContent.Children.Add(descriptionPanel);
}
grid.ColumnDefinitions[1].Width = new GridLength(400);
pivot.SelectedIndex = 0;
}
else if (e.NewSize.Width <= 1000 & e.PreviousSize.Width > 1000)
{
if (mainContent.Children.Contains(descriptionPanel))
else if (e.NewSize.Width <= 1000 && mainContent.Children.Contains(descriptionPanel))
{
mainContent.Children.Remove(descriptionPanel);
pivot.Items.Insert(0, new PivotItem()
{
Content = descriptionPanel,
Header = "Description"
});
pivot.Items.Insert(0, descriptionPanel);
tabsPlaceholder.Children.Remove(pivot);
mainContent.Children.Add(pivot);
}
grid.ColumnDefinitions[1].Width = new GridLength(0);
}
pivot.SelectedIndex = 0;
}
}
private async void Share(DataTransferManager sender, DataRequestedEventArgs args)
{
@@ -450,7 +418,7 @@ namespace FoxTube.Pages
dislike.Foreground = new SolidColorBrush(Colors.Red);
dislikes.Text = (int.Parse(dislikes.Text, System.Globalization.NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value = (int)((item.Statistics.LikeCount - 1) / (item.Statistics.DislikeCount + item.Statistics.LikeCount) * 100);
rating.Value--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
userRating = Rating.Dislike;
@@ -459,7 +427,7 @@ namespace FoxTube.Pages
case Rating.None:
dislike.Foreground = new SolidColorBrush(Colors.Red);
dislikes.Text = (int.Parse(dislikes.Text, System.Globalization.NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value = (int)((item.Statistics.LikeCount) / (item.Statistics.DislikeCount + item.Statistics.LikeCount + 1) * 100);
rating.Maximum++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
userRating = Rating.Dislike;
@@ -468,7 +436,7 @@ namespace FoxTube.Pages
case Rating.Dislike:
dislike.Foreground = new SolidColorBrush(Colors.Gray);
dislikes.Text = (int.Parse(dislikes.Text, System.Globalization.NumberStyles.AllowThousands) - 1).ToString("0,0");
rating.Value = (int)((item.Statistics.LikeCount) / (item.Statistics.DislikeCount + item.Statistics.LikeCount - 1) * 100);
rating.Maximum--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
break;
}
@@ -485,7 +453,7 @@ namespace FoxTube.Pages
like.Foreground = new SolidColorBrush(Colors.Green);
likes.Text = (int.Parse(likes.Text, System.Globalization.NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value = (int)((item.Statistics.LikeCount + 1) / (item.Statistics.DislikeCount + item.Statistics.LikeCount) * 100);
rating.Value++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
userRating = Rating.Like;
@@ -494,7 +462,8 @@ namespace FoxTube.Pages
case Rating.None:
like.Foreground = new SolidColorBrush(Colors.Green);
likes.Text = (int.Parse(likes.Text, System.Globalization.NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value = (int)((item.Statistics.LikeCount + 1) / (item.Statistics.DislikeCount + item.Statistics.LikeCount + 1) * 100);
rating.Maximum++;
rating.Value++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
userRating = Rating.Like;
@@ -503,7 +472,8 @@ namespace FoxTube.Pages
case Rating.Like:
like.Foreground = new SolidColorBrush(Colors.Gray);
likes.Text = (int.Parse(likes.Text, System.Globalization.NumberStyles.AllowThousands) - 1).ToString("0,0");
rating.Value = (int)((item.Statistics.LikeCount - 1) / (item.Statistics.DislikeCount + item.Statistics.LikeCount - 1) * 100);
rating.Maximum--;
rating.Value--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
break;
}
@@ -521,18 +491,13 @@ namespace FoxTube.Pages
private async void subscribe_Click(object sender, RoutedEventArgs e)
{
if (subscribe.Background == new SolidColorBrush(Colors.Red))
{
if (await SecretsVault.Subscribe(item.Snippet.ChannelId))
if (await SecretsVault.ChangeSubscriptionState(item.Snippet.ChannelId))
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = "Subscribed";
}
}
else
{
if (await SecretsVault.Unsubscibe(item.Snippet.ChannelId))
{
subscribe.Background = new SolidColorBrush(Colors.Red);
subscribe.Foreground = new SolidColorBrush(Colors.White);
@@ -541,4 +506,3 @@ namespace FoxTube.Pages
}
}
}
}