Archived
1
0

New arch (not working)

This commit is contained in:
Michael Gordeev
2019-07-19 17:19:43 +03:00
parent ac0262b47e
commit 3ded1f4f18
84 changed files with 2816 additions and 3159 deletions
+23 -232
View File
@@ -1,30 +1,16 @@
using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter;
using FoxTube.Classes;
using Microsoft.AppCenter.Analytics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Background;
using Windows.Globalization;
using Windows.Storage;
using Windows.System.Power;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FoxTube
{
sealed partial class App : Application
{
public static string[] AvailableLanguages => new[] { "en-US", "ru-RU" };
Stopwatch sw = new Stopwatch();
public App()
{
SettingsStorage.LoadData();
@@ -40,113 +26,30 @@ namespace FoxTube
}
ApplicationLanguages.PrimaryLanguageOverride = SettingsStorage.Language;
CheckVersion();
InitializeComponent();
Suspending += OnSuspending;
UnhandledException += UnhandledError;
AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics));
AppCenter.SetCountryCode(SettingsStorage.Region);
sw.Start();
}
/// <summary>
/// Comparing current version with last recorded version. If doesn't match, poping up changelog notification
/// </summary>
public async void CheckVersion()
Suspending += (s, e) => Processes.SuspendApp();
UnhandledException += (s, e) => Analytics.TrackEvent("The app crashed", new Dictionary<string, string>()
{
PackageVersion ver = Package.Current.Id.Version;
if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}.{ver.Build}")
{
try
{
XmlDocument changelog = new XmlDocument();
StorageFile file = await (await Package.Current.InstalledLocation.GetFolderAsync(@"Assets\Data")).GetFileAsync("Patchnotes.xml");
changelog.Load(await file.OpenStreamForReadAsync());
XmlElement e = changelog["items"].ChildNodes[0] as XmlElement;
ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(e.GetAttribute("version")));
SettingsStorage.Version = $"{ver.Major}.{ver.Minor}.{ver.Build}";
}
catch (Exception e)
{
Analytics.TrackEvent("Unable to retrieve changelog", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "App version", $"{ver.Major}.{ver.Minor}.{ver.Revision}.{ver.Build}" },
{ "StackTrace", e.StackTrace }
{ "Exception", e.Exception.GetType().ToString() },
{ "Details", e.Message },
{ "StackTrace", e.Exception.StackTrace }
});
}
}
Processes.InitializeApp();
}
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
if (!(Window.Current.Content is Frame rootFrame))
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
}
if (!(Window.Current.Content is Frame))
Window.Current.Content = new Frame();
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage), e.Arguments);
if ((Window.Current.Content as Frame).Content == null)
(Window.Current.Content as Frame).Navigate(typeof(MainPage), e.Arguments);
Window.Current.Activate();
}
ActivateToastBackgoundTask();
ActivateBackgoundTask();
}
/// <summary>
/// Initializes background task for processing toast notifications' clicks
/// </summary>
public async void ActivateToastBackgoundTask()
{
const string taskName = "FoxtubeToastBackground";
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
return;
var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser)
return;
BackgroundTaskBuilder builder = new BackgroundTaskBuilder() { Name = taskName };
builder.SetTrigger(new ToastNotificationActionTrigger());
BackgroundTaskRegistration registration = builder.Register();
}
/// <summary>
/// Initializes background task for checking user's subscriptions and poping toast notifications when new video is uploaded
/// </summary>
public async void ActivateBackgoundTask()
{
const string taskName = "FoxtubeBackgound";
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
return;
var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
var saverRequest = PowerManager.EnergySaverStatus;
if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser || saverRequest == EnergySaverStatus.On)
return;
BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
{
Name = taskName,
IsNetworkRequested = true,
TaskEntryPoint = "FoxTube.Background.BackgroundProcessor"
};
builder.SetTrigger(new TimeTrigger(15, false));
BackgroundTaskRegistration registration = builder.Register();
}
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
@@ -154,50 +57,10 @@ namespace FoxTube
var deferral = args.TaskInstance.GetDeferral();
base.OnBackgroundActivated(args);
if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" && args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details)
{
string[] arguments = details.Argument.Split('|');
if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" || !(args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details))
return;
switch (arguments[0])
{
case "later":
try
{
SecretsVault.AuthorizationStateChanged += async (s, e) =>
{
if ((bool)e[0])
{
PlaylistItem item = new PlaylistItem()
{
Snippet = new PlaylistItemSnippet()
{
ResourceId = new ResourceId()
{
Kind = "youtube#video",
VideoId = arguments[1]
},
PlaylistId = "WL"
}
};
await SecretsVault.Service.PlaylistItems.Insert(item, "snippet").ExecuteAsync();
}
};
SecretsVault.CheckAuthorization(false);
}
catch (Exception e)
{
Analytics.TrackEvent("Failed to add video to WL from toast", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", arguments[1] },
{ "StackTrace", e.StackTrace }
});
}
break;
}
}
Processes.ProcessToast(details.Argument);
deferral.Complete();
}
@@ -206,7 +69,8 @@ namespace FoxTube
{
base.OnActivated(e);
if (!(Window.Current.Content is Frame rootFrame))
//TODO: Check this shit
/*if (!(Window.Current.Content is Frame rootFrame))
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
@@ -217,88 +81,15 @@ namespace FoxTube
if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage));
Window.Current.Activate();
Window.Current.Activate();*/
if (e.Kind != ActivationKind.ToastNotification)
return;
switch (e.Kind)
{
case ActivationKind.ToastNotification:
if (SecretsVault.IsAuthorized)
ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
Processes.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
else
SecretsVault.AuthorizationStateChanged += (s, arg) => ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
break;
}
}
private void ProcessToast(string arg)
{
string[] args = arg.Split('|');
switch (args[0])
{
case "changelog":
case "inbox":
Methods.MainPage.GoToDeveloper(args[1]);
break;
case "video":
Methods.MainPage.GoToVideo(args[1]);
break;
case "channel":
Methods.MainPage.GoToChannel(args[1]);
break;
case "download":
Methods.MainPage.GoToDownloads();
break;
case "dcancel":
DownloadAgent.Cancel(args[1]);
break;
case "clipboard":
switch (args[1])
{
case "video":
Methods.MainPage.GoToVideo(args[2]);
break;
case "channel":
Methods.MainPage.GoToChannel(args[2]);
break;
case "playlist":
Methods.MainPage.GoToPlaylist(args[2]);
break;
}
break;
}
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
sw.Stop();
SettingsStorage.Uptime += sw.Elapsed;
HistorySet.Save();
SettingsStorage.SaveData();
DownloadAgent.QuitPrompt();
Controls.Player.ManifestGenerator.ClearRoaming();
deferral.Complete();
Analytics.TrackEvent("Session terminated");
}
private void UnhandledError(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
{
Analytics.TrackEvent("The app crashed", new Dictionary<string, string>()
{
{ "Exception", e.Exception.GetType().ToString() },
{ "Details", e.Message },
{ "StackTrace", e.Exception.StackTrace }
});
SecretsVault.AuthorizationStateChanged += (arg) => Processes.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
}
}
}
+1 -3
View File
@@ -4,9 +4,7 @@ namespace FoxTube.Classes
{
class AdaptiveCommandBar : CommandBar
{
public AdaptiveCommandBar()
{
public AdaptiveCommandBar() =>
ClosedDisplayMode = SettingsStorage.AppBarClosedMode;
}
}
}
+5 -7
View File
@@ -3,26 +3,26 @@ using System.Collections.Generic;
using Windows.Storage;
using Newtonsoft.Json;
using YoutubeExplode.Models.MediaStreams;
using Google.Apis.YouTube.v3.Data;
using FoxTube.Controls;
using FoxTube.Pages;
using Microsoft.AppCenter.Analytics;
using YoutubeExplode.Models;
namespace FoxTube
{
public static class DownloadAgent
{
public static List<DownloadItem> Items { get; set; } = new List<DownloadItem>();
private static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
public static Downloads Page { get; set; }
public static StorageFolder Downloads { get; set; }
public static async void Initialize()
{
Downloads = await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
Items.Clear();
try
{
Downloads = await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
List<DownloadItemContainer> containers = JsonConvert.DeserializeObject<List<DownloadItemContainer>>((string)settings.Values[$"downloads"]);
containers.ForEach(i => Items.Add(new DownloadItem(i)));
}
@@ -38,10 +38,8 @@ namespace FoxTube
}
}
public static void Add(MediaStreamInfo info, Video meta, string qualty)
{
Items.Insert(0, new DownloadItem(info, meta, qualty));
}
public static void Add((MediaStreamInfo info, Video meta, string qualty) e) =>
Items.Insert(0, new DownloadItem(e.info, e.meta, e.qualty));
public static void Remove(DownloadItem item)
{
+34
View File
@@ -0,0 +1,34 @@
using Google.Apis.YouTube.v3.Data;
using System;
using System.Threading.Tasks;
namespace FoxTube.Classes
{
public delegate void SubscriptionsChangedEventHandler(string action, Subscription subscription);
public delegate void AuthorizationChangedEventHandler(bool? isAuthorized);
public delegate void MinimodeChangedEventHandler(bool isOn);
public delegate void SimpleEventHandler();
public delegate void PlaylistItemChangedEventHandler(string id);
public delegate void NavigationEventHanlder(Type sourcePageType, object parameter);
public static class Extensions
{
public static bool Belongs(this double num, double x1, double x2)
{
return num > x1 && num < x2;
}
}
public interface INavigationPage
{
object Parameter { get; set; }
}
public interface ICard
{
Task Initialize();
void ItemClicked();
}
public enum PlayerDisplayState { Normal, Minimized, Compact }
}
+4 -41
View File
@@ -9,16 +9,7 @@ namespace FoxTube.Classes
{
public InboxItemType Type { get; set; } = InboxItemType.Default;
public DateTime TimeStamp { get; set; }
public string TimeStampString
{
get
{
if (Type == InboxItemType.PatchNote)
return TimeStamp.ToShortDateString();
else
return TimeStamp.ToString();
}
}
public string TimeStampString => Type == InboxItemType.PatchNote ? TimeStamp.ToShortDateString() : TimeStamp.ToString();
public string Subject { get; set; }
public string Content { get; set; }
@@ -26,38 +17,10 @@ namespace FoxTube.Classes
private ResourceLoader resources = ResourceLoader.GetForCurrentView("Inbox");
public string Icon
{
get
{
if (Type == InboxItemType.PatchNote)
return "\xE728";
else
return "\xE119";
}
}
public string Subtitle
{
get
{
if (Type == InboxItemType.PatchNote)
return resources.GetString("changelog");
else
return resources.GetString("dev");
}
}
public string Title
{
get
{
if (Type == InboxItemType.PatchNote)
return $"{resources.GetString("whatsnew")}{Id}";
else
return Subject;
}
}
public string Icon => Type == InboxItemType.PatchNote ? "\xE728" : "\xE119";
public string Subtitle => Type == InboxItemType.PatchNote ? resources.GetString("changelog") : resources.GetString("dev");
public string Title => Type == InboxItemType.PatchNote ? $"{resources.GetString("whatsnew")}{Id}" : Subject;
public InboxItem(string version, string content, DateTime timeStamp)
{
+6 -43
View File
@@ -16,7 +16,7 @@ using Windows.ApplicationModel.Resources;
using Windows.Storage;
using YoutubeExplode.Models.MediaStreams;
namespace FoxTube.Controls.Player
namespace FoxTube.Classes
{
public static class ManifestGenerator
{
@@ -82,7 +82,7 @@ namespace FoxTube.Controls.Player
}
}
private static XmlElement GetVideoPresentation(XmlDocument doc, StreamInfo.VideoInfo info)
static XmlElement GetVideoPresentation(XmlDocument doc, StreamInfo.VideoInfo info)
{
XmlElement representation = doc.CreateElement("Representation");
representation.SetAttribute("bandwidth", GetBandwidth(info.Label));
@@ -108,7 +108,7 @@ namespace FoxTube.Controls.Player
return representation;
}
private static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info)
static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info)
{
XmlElement audio = doc.CreateElement("Representation");
audio.SetAttribute("bandwidth", "200000");
@@ -133,7 +133,7 @@ namespace FoxTube.Controls.Player
return audio;
}
private static async Task<StreamInfo> GetInfoAsync(Video info, VideoStreamInfo requestedQuality)
static async Task<StreamInfo> GetInfoAsync(Video info, VideoStreamInfo requestedQuality)
{
try
{
@@ -195,7 +195,7 @@ namespace FoxTube.Controls.Player
}
}
public static Dictionary<string, string> SplitQuery(string query)
static Dictionary<string, string> SplitQuery(string query)
{
Dictionary<string, string> dic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string[] paramsEncoded = query.TrimStart('?').Split("&");
@@ -221,7 +221,7 @@ namespace FoxTube.Controls.Player
return dic;
}
private static string GetBandwidth(string quality)
static string GetBandwidth(string quality)
{
string parsed = quality.Split('p')[0];
switch (quality)
@@ -301,41 +301,4 @@ namespace FoxTube.Controls.Player
await f.DeleteAsync(StorageDeleteOption.PermanentDelete);
}
}
public class StreamInfo
{
public class VideoInfo
{
public string IndexRange { get; set; }
public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}";
public string Itag { get; set; }
public string Fps { get; set; }
public string Url { get; set; }
public string Codecs { get; set; }
public string MimeType { get; set; }
public string Height { get; set; }
public string Width { get; set; }
public string Label { get; set; }
}
public class AudioInfo
{
public string IndexRange { get; set; }
public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}";
public string SampleRate { get; set; }
public string ChannelsCount { get; set; }
public string Codecs { get; set; }
public string MimeType { get; set; }
public string Url { get; set; }
public string Itag { get; set; }
}
public List<VideoInfo> Video { get; } = new List<VideoInfo>();
public List<AudioInfo> Audio { get; } = new List<AudioInfo>();
}
public class StreamQuality
{
public Uri Url { get; set; }
public string Resolution { get; set; }
}
}
+57 -71
View File
@@ -1,6 +1,7 @@
using FoxTube.Pages;
using FoxTube.Classes;
using FoxTube.Controls.VideoPage;
using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
@@ -9,7 +10,6 @@ using System.Net.Mail;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using Windows.ApplicationModel.Core;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Storage.Streams;
@@ -26,21 +26,47 @@ namespace FoxTube
public delegate void ObjectEventHandler(object sender = null, params object[] args);
public interface INavigationPage
{
object Parameter { get; set; }
}
public static class Methods
{
private static readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods");
public static CommentsPage CommentsPage { get; set; }
public static Comments CommentsPage { get; set; }
public static MainPage MainPage => (Window.Current.Content as Frame).Content as MainPage;
public static void CloseApp()
public static async Task<bool> AddItemToWL(string id)
{
CoreApplication.Exit();
try
{
SecretsVault.RefreshToken();
PlaylistItem item = new PlaylistItem
{
Snippet = new PlaylistItemSnippet
{
ResourceId = new ResourceId
{
Kind = "youtube#video",
VideoId = id
},
PlaylistId = "WL"
}
};
await SecretsVault.Service.PlaylistItems.Insert(item, "snippet").ExecuteAsync();
return true;
}
catch (Exception e)
{
Analytics.TrackEvent("Failed to add video to WL", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", id },
{ "StackTrace", e.StackTrace }
});
return false;
}
}
public static Uri ToUri(this string url)
@@ -49,10 +75,8 @@ namespace FoxTube
catch { return null; }
}
public static string GuardFromNull(string str)
{
return str ?? string.Empty;
}
public static string GuardFromNull(string str) =>
str ?? string.Empty;
public static void SendMail(string content)
{
@@ -68,22 +92,6 @@ namespace FoxTube
client.Send(msg);
}
[Obsolete]
public static string GetChars(this string str, int count)
{
try
{
string s = "";
for (int i = 0; i < count; i++)
s += str[i];
return s;
}
catch
{
return "";
}
}
public static List<object> ToReversedList(this Array array)
{
List<object> list = new List<object>();
@@ -93,22 +101,15 @@ namespace FoxTube
return list;
}
public static void ForEach<T>(this IEnumerable<T> array, Action<T> action)
{
public static void ForEach<T>(this IEnumerable<T> array, Action<T> action) =>
array.ToList().ForEach(action);
}
public static T Find<T>(this IEnumerable<T> array, Predicate<T> match)
{
return array.ToList().Find(match);
}
public static T Find<T>(this IEnumerable<T> array, Predicate<T> match) =>
array.ToList().Find(match);
public static List<T> FindAll<T>(this IEnumerable<T> array, Predicate<T> match)
{
return array.ToList().FindAll(match);
}
public static List<T> FindAll<T>(this IEnumerable<T> array, Predicate<T> match) =>
array.ToList().FindAll(match);
[Obsolete]
public static string ReplaceInvalidChars(this string str, char newValue)
{
foreach (char i in Path.GetInvalidFileNameChars())
@@ -116,12 +117,7 @@ namespace FoxTube
return str;
}
[Obsolete]
public static string Last(this string[] arr)
{
return arr[arr.Length - 1];
}
[Obsolete("Use *YoutubeExplode.Models.Video instead*")]
public static TimeSpan GetDuration(this string str)
{
try
@@ -243,6 +239,8 @@ namespace FoxTube
}
public async static void ProcessLink(string url)
{
try
{
string type;
@@ -274,19 +272,21 @@ namespace FoxTube
switch (type)
{
case "channel":
MainPage.GoToChannel(output);
Navigation.GoToChannel(output);
break;
case "video":
MainPage.GoToVideo(output);
Navigation.GoToVideo(output);
break;
case "playlist":
MainPage.GoToPlaylist(output);
Navigation.GoToPlaylist(output);
break;
case "user":
MainPage.GoToChannel(await new YoutubeClient().GetChannelIdAsync(output));
Navigation.GoToChannel(await new YoutubeClient().GetChannelIdAsync(output));
break;
}
}
catch { }
}
public static void Share(DataRequestedEventArgs args, string thumbnail, string title, string url, string type)
{
@@ -302,32 +302,18 @@ namespace FoxTube
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri()));
}
public static async Task<List<string>> GetHistory()
public static async Task<YoutubeExplode.Models.Playlist> GetHistory()
{
SecretsVault.RefreshToken();
List<string> list = new List<string>();
string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=HL&hl={SettingsStorage.RelevanceLanguage}");
dynamic raw = JsonConvert.DeserializeObject(output);
foreach (dynamic i in raw.video)
list.Add(i.encrypted_id.ToString());
return list;
return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("HL");
}
public static async Task<List<string>> GetLater()
public static async Task<YoutubeExplode.Models.Playlist> GetLater()
{
SecretsVault.RefreshToken();
List<string> list = new List<string>();
string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=WL&hl={SettingsStorage.RelevanceLanguage}");
dynamic raw = JsonConvert.DeserializeObject(output);
foreach (dynamic i in raw.video)
list.Add(i.encrypted_id.ToString());
return list;
return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("WL");
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using FoxTube.Pages;
using Google.Apis.YouTube.v3.Data;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Classes
{
public static class Navigation
{
public static MainFrame Frame => ((Window.Current.Content as Frame).Content as MainPage).Content;
public static void GoToSearch(SearchParameters args) =>
Frame.NavigateTo(typeof(Search), new object[] { args, Frame.Frame });
public static void GoToChannel(string id) =>
Frame.NavigateTo(typeof(ChannelPage), id);
public static void GoToHome() =>
Frame.NavigateTo(typeof(Home));
public static void GoToVideo(string id, string playlistId = null, bool incognito = false) =>
Frame.OpenVideo(id, playlistId, incognito);
public static void GoToDeveloper(string id) =>
Frame.NavigateTo(typeof(Settings), id);
public static void GoToPlaylist(string id) =>
Frame.NavigateTo(typeof(PlaylistPage), id);
public static void GoToHistory() =>
Frame.NavigateTo(typeof(History));
public static void GoToDownloads() =>
Frame.NavigateTo(typeof(Downloads));
}
}
+299
View File
@@ -0,0 +1,299 @@
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.Services.Store.Engagement;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Storage;
using Windows.System;
using Windows.System.Power;
using Windows.UI.Notifications;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Core;
namespace FoxTube.Classes
{
public static class Processes
{
static Stopwatch sw = new Stopwatch();
public static async void InitializeApp() => await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
CheckVersion();
RegisterToastTask();
ActivateBackgoundTask();
AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics));
AppCenter.SetCountryCode(SettingsStorage.Region);
sw.Start();
SecretsVault.Initialize();
DownloadAgent.Initialize();
if (SettingsStorage.ProcessClipboard)
{
Clipboard.ContentChanged += ParseClipboard;
ParseClipboard();
}
PromptFeedback();
});
public static void SuspendApp()
{
sw.Stop();
SettingsStorage.Uptime += sw.Elapsed;
HistorySet.Save();
SettingsStorage.SaveData();
DownloadAgent.QuitPrompt();
ManifestGenerator.ClearRoaming();
Analytics.TrackEvent("Session terminated", new Dictionary<string, string>
{
{ "Uptime", sw.Elapsed.ToString() },
{ "Total time", SettingsStorage.Uptime.ToString() }
});
}
public static async void ProcessToast(string arg)
{
string[] args = arg.Split('|');
switch (args[0])
{
case "changelog":
case "inbox":
Navigation.GoToDeveloper(args[1]);
break;
case "video":
Navigation.GoToVideo(args[1]);
break;
case "channel":
Navigation.GoToChannel(args[1]);
break;
case "download":
Navigation.GoToDownloads();
break;
case "dcancel":
DownloadAgent.Cancel(args[1]);
break;
case "clipboard":
switch (args[1])
{
case "video":
Navigation.GoToVideo(args[2]);
break;
case "channel":
Navigation.GoToChannel(args[2]);
break;
case "playlist":
Navigation.GoToPlaylist(args[2]);
break;
}
break;
case "later":
if (SecretsVault.IsAuthorized)
await Methods.AddItemToWL(args[1]);
else
{
SecretsVault.AuthorizationStateChanged += async (e) =>
{
if (e.Value)
await Methods.AddItemToWL(args[1]);
};
SecretsVault.CheckAuthorization(false);
}
break;
}
}
public static async void PromptFeedback()
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
if (SettingsStorage.Uptime.TotalHours >= 12 && SettingsStorage.PromptFeedback)
{
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/feedbackMessage"));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptFeedback = false));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater")));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) =>
{
SettingsStorage.PromptFeedback = false;
if (StoreServicesFeedbackLauncher.IsSupported())
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
else
{
MessageDialog message = new MessageDialog(resources.GetString("/Main/feedbackFail"));
message.Commands.Add(new UICommand(resources.GetString("/Main/sendEmail"), async (c) => await Launcher.LaunchUriAsync("mailto:michael.xfox@outlook.com".ToUri())));
message.Commands.Add(new UICommand(resources.GetString("/Main/goBack")));
message.CancelCommandIndex = 1;
message.DefaultCommandIndex = 0;
await message.ShowAsync();
}
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
if (SettingsStorage.Uptime.TotalHours >= 24 && SettingsStorage.PromptReview)
{
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/rate"));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptReview = false));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater")));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) =>
{
SettingsStorage.PromptReview = false;
await Launcher.LaunchUriAsync("ms-windows-store://review/?ProductId=9NCQQXJTDLFH".ToUri());
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
}
public static async void ParseClipboard(object sender = null, object e = null)
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
if (Window.Current.CoreWindow.ActivationMode != Windows.UI.Core.CoreWindowActivationMode.ActivatedInForeground || !SettingsStorage.ProcessClipboard)
return;
try
{
string link = await Clipboard.GetContent().GetTextAsync();
if (!link.Contains("youtube") && !link.Contains("youtu.be"))
return;
string id;
string type = string.Empty;
string name = string.Empty;
if (YoutubeExplode.YoutubeClient.TryParseChannelId(link, out id))
{
type = "channel";
name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
}
else if (YoutubeExplode.YoutubeClient.TryParsePlaylistId(link, out id))
{
type = "playlist";
name = (await new YoutubeExplode.YoutubeClient().GetPlaylistAsync(id)).Title;
}
else if (YoutubeExplode.YoutubeClient.TryParseUsername(link, out id))
{
id = await new YoutubeExplode.YoutubeClient().GetChannelIdAsync(id);
type = "channel";
name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
}
else if (YoutubeExplode.YoutubeClient.TryParseVideoId(link, out id))
{
type = "video";
name = (await new YoutubeExplode.YoutubeClient().GetVideoAsync(id)).Title;
}
if(string.IsNullOrWhiteSpace(id))
return;
Windows.Data.Xml.Dom.XmlDocument toastXml = new Windows.Data.Xml.Dom.XmlDocument();
toastXml.LoadXml($@"<toast launch='clipboard|{type}|{id}'>
<visual>
<binding template='ToastGeneric'>
<text>{resources.GetString("/Main/clipboardHead")}</text>
<text>{name}</text>
<text>{resources.GetString($"/Main/{type}")}</text>
</binding>
</visual>
<actions>
<action content='{resources.GetString("/Main/clipboardOpen")}' arguments='clipboard|{type}|{id}'/>
</actions>
</toast>");
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
catch { }
}
/// <summary>
/// Comparing current version with last recorded version. If doesn't match, poping up changelog notification
/// </summary>
static async void CheckVersion()
{
PackageVersion ver = Package.Current.Id.Version;
if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}.{ver.Build}")
try
{
XmlDocument changelog = new XmlDocument();
StorageFile file = await (await Package.Current.InstalledLocation.GetFolderAsync(@"Assets\Data")).GetFileAsync("Patchnotes.xml");
changelog.Load(await file.OpenStreamForReadAsync());
ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(changelog["items"].FirstChild.Attributes["version"].Value));
SettingsStorage.Version = $"{ver.Major}.{ver.Minor}.{ver.Build}";
}
catch (Exception e)
{
Analytics.TrackEvent("Unable to retrieve changelog", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "App version", $"{ver.Major}.{ver.Minor}.{ver.Revision}.{ver.Build}" },
{ "StackTrace", e.StackTrace }
});
}
}
/// <summary>
/// Initializes background task for processing toast notifications' clicks
/// </summary>
static async void RegisterToastTask()
{
const string taskName = "FoxtubeToastBackground";
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
return;
var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser)
return;
BackgroundTaskBuilder builder = new BackgroundTaskBuilder() { Name = taskName };
builder.SetTrigger(new ToastNotificationActionTrigger());
BackgroundTaskRegistration registration = builder.Register();
}
/// <summary>
/// Initializes background task for checking user's subscriptions and poping toast notifications when new video is uploaded
/// </summary>
static async void ActivateBackgoundTask()
{
const string taskName = "FoxtubeBackgound";
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
return;
var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
var saverRequest = PowerManager.EnergySaverStatus;
if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser || saverRequest == EnergySaverStatus.On)
return;
BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
{
Name = taskName,
IsNetworkRequested = true,
TaskEntryPoint = "FoxTube.Background.BackgroundProcessor"
};
builder.SetTrigger(new TimeTrigger(15, false));
BackgroundTaskRegistration registration = builder.Register();
}
}
}
+30
View File
@@ -0,0 +1,30 @@
using System.Collections.Generic;
namespace FoxTube.Classes
{
public class QualityComparer : IComparer<string>
{
public int Compare(string x, string y)
{
string[] xArr = x.Split('p');
string[] yArr = y.Split('p');
int qualityA = int.Parse(xArr[0]);
int qualityB = int.Parse(yArr[0]);
int framerateA = 30;
int framerateB = 30;
if (!string.IsNullOrWhiteSpace(xArr[1]))
framerateA = int.Parse(xArr[1]);
if (!string.IsNullOrWhiteSpace(yArr[1]))
framerateB = int.Parse(yArr[1]);
if (qualityA > qualityB)
return 1;
else if (qualityA < qualityB)
return -1;
else
return framerateA - framerateB > 0 ? 1 : -1;
}
}
}
+37 -95
View File
@@ -6,8 +6,6 @@ using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Newtonsoft.Json;
using Windows.Storage;
using Windows.Services.Store;
using System.Net.Http;
using Google.Apis.Oauth2.v2.Data;
@@ -15,6 +13,8 @@ using Google.Apis.Oauth2.v2;
using static Google.Apis.Auth.OAuth2.UwpCodeReceiver;
using Microsoft.AppCenter.Analytics;
using System.Net;
using FoxTube.Classes;
using System.Linq;
namespace FoxTube
{
@@ -22,12 +22,12 @@ namespace FoxTube
{
#region Properties
//Events
public static event ObjectEventHandler AuthorizationStateChanged;
public static event ObjectEventHandler SubscriptionsChanged;
public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version
public static event AuthorizationChangedEventHandler AuthorizationStateChanged;
public static event SubscriptionsChangedEventHandler SubscriptionsChanged;
public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version or after purchase
//Properties
public static NetworkCredential EmailCredential => new NetworkCredential("mikhailagord@gmail.com", "JkY39w$.7?bT57O,8k3a");
public static NetworkCredential EmailCredential => new NetworkCredential("foxtube.bot@xfox111.net", "JkY39w$.7?bT57O,8k3a");
private static ClientSecrets Secrets => new ClientSecrets
{
ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com",
@@ -38,7 +38,7 @@ namespace FoxTube
ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0",
ApplicationName = "FoxTube"
});
public static BaseClientService.Initializer Initializer => new BaseClientService.Initializer
private static BaseClientService.Initializer Initializer => new BaseClientService.Initializer
{
HttpClientInitializer = Credential,
ApplicationName = "FoxTube"
@@ -46,7 +46,7 @@ namespace FoxTube
public static YouTubeService Service => IsAuthorized ? new YouTubeService(Initializer) : NoAuthService;
public static HttpClient HttpClient { get; } = new HttpClient();
private static bool TestAds => false; //TODO: Change this bool
private static bool TestAds => true; //TODO: Change this bool
public static string AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh";
public static string AdUnitId => TestAds ? "test" : "1100037769";
public static bool AdsDisabled { get; private set; } = true;
@@ -60,15 +60,13 @@ namespace FoxTube
public static Userinfoplus UserInfo { get; private set; }
public static List<Subscription> Subscriptions { get; } = new List<Subscription>();
public static List<string> History { get; set; } = new List<string>();
public static List<string> WatchLater { get; set; } = new List<string>();
public static YoutubeExplode.Models.Playlist History { get; set; }
public static YoutubeExplode.Models.Playlist WatchLater { get; set; }
#endregion
#region Methods
public static void RefreshToken()
{
public static void RefreshToken() =>
Credential?.RefreshTokenAsync(CancellationToken.None);
}
/// <summary>
/// Subscribes or unsibscribes authorized user from the channel
@@ -77,26 +75,23 @@ namespace FoxTube
/// <returns>Returns 'true' if channel is in subscriptions now; 'false' if it's not</returns>
public static async Task<bool> ChangeSubscriptionState(string id)
{
if(Subscriptions.Exists(x => x.Snippet.ResourceId.ChannelId == id))
if(Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == id) is Subscription subscription)
{
Subscription s = Subscriptions.Find(x => x.Snippet.ResourceId.ChannelId == id);
try { await Service.Subscriptions.Delete(s.Id).ExecuteAsync(); }
try { await Service.Subscriptions.Delete(subscription.Id).ExecuteAsync(); }
catch { return true; }
SubscriptionsChanged?.Invoke(null, "remove", s);
Subscriptions.Remove(s);
SubscriptionsChanged?.Invoke("remove", subscription);
Subscriptions.Remove(subscription);
SaveSubscriptions();
return false;
}
else
{
var request = Service.Subscriptions.Insert(new Subscription()
var request = Service.Subscriptions.Insert(new Subscription
{
Snippet = new SubscriptionSnippet()
Snippet = new SubscriptionSnippet
{
ResourceId = new ResourceId()
ResourceId = new ResourceId
{
ChannelId = id,
Kind = "youtube#channel"
@@ -104,13 +99,12 @@ namespace FoxTube
}
}, "snippet");
Subscription s = await request.ExecuteAsync();
if (s == null)
if (!(await request.ExecuteAsync() is Subscription sub))
return false;
Subscriptions.Add(s);
SubscriptionsChanged?.Invoke(null, "add", s);
SaveSubscriptions();
Subscriptions.Add(sub);
SubscriptionsChanged?.Invoke("add", sub);
return true;
}
}
@@ -140,23 +134,17 @@ namespace FoxTube
Oauth2Service.Scope.UserinfoProfile,
Oauth2Service.Scope.UserinfoEmail,
YouTubeService.Scope.YoutubeForceSsl,
YouTubeService.Scope.Youtube,
YouTubeService.Scope.YoutubeUpload,
YouTubeService.Scope.YoutubeReadonly,
YouTubeService.Scope.Youtubepartner
YouTubeService.Scope.YoutubeUpload
},
"user",
CancellationToken.None);
await Credential.RefreshTokenAsync(CancellationToken.None);
}
catch (AuthenticateException e)
catch (AuthenticateException e) when (e.Message.Contains("UserCancel")) { }
catch(Exception e)
{
if (e.Message.Contains("UserCancel"))
return;
else
{
AuthorizationStateChanged?.Invoke(args: new bool?[] { null });
AuthorizationStateChanged?.Invoke(null);
Analytics.TrackEvent("Failed to authorize", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
@@ -164,7 +152,6 @@ namespace FoxTube
{ "StackTrace", e.StackTrace }
});
}
}
if (Credential == null || !retrieveSubs)
return;
@@ -188,7 +175,6 @@ namespace FoxTube
subRequest.Order = SubscriptionsResource.ListRequest.OrderEnum.Relevance;
SubscriptionListResponse subResponse;
string nextToken = null;
Subscriptions.Clear();
do
{
@@ -200,22 +186,17 @@ namespace FoxTube
} while (!string.IsNullOrWhiteSpace(nextToken));
var request = Service.Channels.List("snippet,contentDetails");
ChannelsResource.ListRequest request = Service.Channels.List("snippet,contentDetails");
request.Mine = true;
UserChannel = (await request.ExecuteAsync()).Items[0];
UserChannel = (await request.ExecuteAsync()).Items.FirstOrDefault();
#endregion
//Saving user's subscriptions for background task
SaveSubscriptions();
AuthorizationStateChanged?.Invoke(args: true);
AuthorizationStateChanged?.Invoke(true);
}
catch (Exception e)
{
AuthorizationStateChanged?.Invoke(args: new bool?[] { null });
Methods.SendMail($@"Exception: {e.GetType()}
Message: {e.Message}
Stack trace: {e.StackTrace}");
AuthorizationStateChanged?.Invoke(null);
Methods.SendMail(e.ToString());
Analytics.TrackEvent("Failed to retrieve user's info", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
@@ -225,43 +206,6 @@ Stack trace: {e.StackTrace}");
}
}
/// <summary>
/// Saves user's subscriptions keypairs (channel ID: avatar URL) into "background.json" file for concurrent background processing
/// </summary>
public static void SaveSubscriptions()
{
try
{
Dictionary<string, string> subs = new Dictionary<string, string>();
foreach (Subscription i in Subscriptions)
try
{
subs.Add(i.Snippet.ResourceId.ChannelId, i.Snippet.Thumbnails.Default__.Url);
}
catch (Exception e)
{
Analytics.TrackEvent("Failed to save user's subscription", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Channel ID", i.Snippet.ResourceId.ChannelId },
{ "StackTrace", e.StackTrace }
});
continue;
}
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = JsonConvert.SerializeObject(subs);
}
catch (Exception e)
{
Analytics.TrackEvent("Failed to write user's subscriptions", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "StackTrace", e.StackTrace }
});
}
}
/// <summary>
/// Deauthenticates current user
/// </summary>
@@ -273,12 +217,13 @@ Stack trace: {e.StackTrace}");
Credential = null;
UserChannel = null;
UserInfo = null;
History.Clear();
WatchLater.Clear();
History = null;
WatchLater = null;
Subscriptions.Clear();
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
AuthorizationStateChanged?.Invoke(args: false);
HttpClient.DefaultRequestHeaders.Authorization = null;
AuthorizationStateChanged?.Invoke(false);
SettingsStorage.HasAccount = false;
}
@@ -291,10 +236,7 @@ Stack trace: {e.StackTrace}");
if (SettingsStorage.HasAccount)
Authorize(retrieveSubs);
else
{
AuthorizationStateChanged.Invoke(args: false);
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
}
AuthorizationStateChanged.Invoke(false);
}
/// <summary>
+1 -1
View File
@@ -41,7 +41,7 @@ namespace FoxTube
private static string GetLanguage()
{
if (App.AvailableLanguages.Contains(CultureInfo.InstalledUICulture.Name))
if (new string[] { "ru-RU", "en-US" }.Contains(CultureInfo.InstalledUICulture.Name))
return CultureInfo.InstalledUICulture.Name;
else if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName))
return "ru-RU";
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
namespace FoxTube.Classes
{
public class StreamInfo
{
public class VideoInfo
{
public string IndexRange { get; set; }
public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}";
public string Itag { get; set; }
public string Fps { get; set; }
public string Url { get; set; }
public string Codecs { get; set; }
public string MimeType { get; set; }
public string Height { get; set; }
public string Width { get; set; }
public string Label { get; set; }
}
public class AudioInfo
{
public string IndexRange { get; set; }
public string InitRange => $"0-{int.Parse(IndexRange.Split('-')[0]) - 1}";
public string SampleRate { get; set; }
public string ChannelsCount { get; set; }
public string Codecs { get; set; }
public string MimeType { get; set; }
public string Url { get; set; }
public string Itag { get; set; }
}
public List<VideoInfo> Video { get; } = new List<VideoInfo>();
public List<AudioInfo> Audio { get; } = new List<AudioInfo>();
}
public class StreamQuality
{
public Uri Url { get; set; }
public string Resolution { get; set; }
}
}
+2 -14
View File
@@ -6,10 +6,9 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
VerticalAlignment="Stretch"
d:DesignHeight="290"
d:DesignWidth="384"
Visibility="Collapsed"
Opacity="0"
Name="card">
@@ -17,19 +16,9 @@
<Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hide">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="image" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hideThumb">
<DoubleAnimation Storyboard.TargetName="image" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources>
<Button Padding="0" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid Name="grid">
<Grid Name="grid" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="20"/>
@@ -61,5 +50,4 @@
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
</Grid>
</Button>
</UserControl>
+7 -12
View File
@@ -12,8 +12,8 @@ namespace FoxTube.Controls.Adverts
/// </summary>
public sealed partial class CardAdvert : UserControl
{
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
public NativeAdV2 advert;
readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
NativeAdV2 advert;
public CardAdvert()
{
InitializeComponent();
@@ -22,22 +22,21 @@ namespace FoxTube.Controls.Adverts
manager.RequestAd();
}
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e)
void ErrorOccurred(object sender, NativeAdErrorEventArgs e)
{
(Parent as AdaptiveGridView)?.Items.Remove(this);
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad");
}
private void AdReady(object sender, NativeAdReadyEventArgs e)
void AdReady(object sender, NativeAdReadyEventArgs e)
{
advert = e.NativeAd;
Initialize();
e.NativeAd.RegisterAdContainer(grid);
}
public void Initialize()
void Initialize()
{
Visibility = Visibility.Visible;
title.Text = advert.Title;
image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri());
@@ -57,13 +56,9 @@ namespace FoxTube.Controls.Adverts
if (!string.IsNullOrWhiteSpace(advert.Rating))
desc.Text += " " + advert.Rating;
}
void Image_ImageOpened(object sender, RoutedEventArgs e) =>
show.Begin();
}
private void Image_ImageOpened(object sender, RoutedEventArgs e)
{
showThumb.Begin();
}
}
}
+48
View File
@@ -0,0 +1,48 @@
<ListViewItem
x:Class="FoxTube.Controls.Adverts.ChatAdvert"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="grid">
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="5" HorizontalAlignment="Stretch" Margin="0,27,0,2">
<Border.Background>
<SolidColorBrush Color="Red" Opacity=".2"/>
</Border.Background>
<Grid Margin="0,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="5,0">
<PersonPicture Height="20" Name="icon"/>
<FontIcon Glyph="&#xE735;" Margin="2,0">
<ToolTipService.ToolTip>
<TextBlock x:Uid="/Chat/sponsor"/>
</ToolTipService.ToolTip>
</FontIcon>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="0,0,2,0" Grid.Column="1">
<HyperlinkButton Margin="0,-6,0,0" FontWeight="Bold">
<TextBlock TextTrimming="CharacterEllipsis" MaxWidth="150" Name="name"/>
</HyperlinkButton>
<TextBlock Text=":"/>
</StackPanel>
<StackPanel Padding="2,0,0,0" Grid.Column="2" VerticalAlignment="Top">
<TextBlock TextWrapping="WrapWholeWords" FontWeight="Bold" Name="title"/>
<TextBlock TextWrapping="WrapWholeWords" Name="description"/>
<Button x:Name="ctaBtn">
<TextBlock TextWrapping="WrapWholeWords" Name="cta"/>
</Button>
</StackPanel>
</Grid>
</Border>
</Grid>
</ListViewItem>
@@ -0,0 +1,60 @@
using Microsoft.Advertising.WinRT.UI;
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;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace FoxTube.Controls.Adverts
{
public sealed partial class ChatAdvert : ListViewItem
{
readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
NativeAdV2 advert;
public ChatAdvert()
{
InitializeComponent();
manager.AdReady += AdReady;
manager.RequestAd();
}
void AdReady(object sender, NativeAdReadyEventArgs e)
{
advert = e.NativeAd;
Initialize();
if (cta.Visibility == Visibility.Collapsed)
e.NativeAd.RegisterAdContainer(grid);
else
e.NativeAd.RegisterAdContainer(grid, new List<FrameworkElement> { cta });
}
void Initialize()
{
name.Text = Methods.GuardFromNull(advert.SponsoredBy);
ToolTipService.SetToolTip(name, name.Text);
icon.ProfilePicture = advert.AdIcon == null ? null : advert.AdIcon.Source;
title.Text = advert.Title;
description.Text = Methods.GuardFromNull(advert.Description);
cta.Text = Methods.GuardFromNull(advert.CallToActionText);
cta.Visibility = string.IsNullOrWhiteSpace(advert.CallToActionText) ? Visibility.Collapsed : Visibility.Visible;
Visibility = Visibility.Visible;
}
}
}
+4 -10
View File
@@ -10,23 +10,17 @@ namespace FoxTube.Controls.Adverts
/// </summary>
public sealed partial class CommentAdvert : UserControl
{
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
public NativeAdV2 advert;
readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
NativeAdV2 advert;
public CommentAdvert()
{
InitializeComponent();
manager.AdReady += AdReady;
manager.ErrorOccurred += ErrorOccurred;
manager.RequestAd();
}
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad");
}
private void AdReady(object sender, NativeAdReadyEventArgs e)
void AdReady(object sender, NativeAdReadyEventArgs e)
{
advert = e.NativeAd;
Initialize();
@@ -36,7 +30,7 @@ namespace FoxTube.Controls.Adverts
e.NativeAd.RegisterAdContainer(grid, new List<FrameworkElement> { cta });
}
private void Initialize()
void Initialize()
{
title.Text = advert.Title;
description.Text = Methods.GuardFromNull(advert.Description);
+5 -14
View File
@@ -8,10 +8,9 @@ namespace FoxTube.Controls.Adverts
{
public sealed partial class PlayerAdvert : UserControl
{
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
NativeAdV2 advert;
DispatcherTimer timer = new DispatcherTimer()
readonly DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(10000)
};
@@ -20,7 +19,6 @@ namespace FoxTube.Controls.Adverts
InitializeComponent();
manager.AdReady += AdReady;
manager.ErrorOccurred += ErrorOccurred;
timer.Tick += (s, e) =>
{
@@ -29,12 +27,7 @@ namespace FoxTube.Controls.Adverts
};
}
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad");
}
private void AdReady(object sender, NativeAdReadyEventArgs arg)
void AdReady(object sender, NativeAdReadyEventArgs arg)
{
advert = arg.NativeAd;
@@ -68,12 +61,10 @@ namespace FoxTube.Controls.Adverts
show.Begin();
}
public void PushAdvert()
{
public void PushAdvert() =>
manager.RequestAd();
}
private void Button_Click(object sender, RoutedEventArgs e)
void Button_Click(object sender, RoutedEventArgs e)
{
timer.Stop();
hide.Begin();
+6 -10
View File
@@ -6,25 +6,21 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
VerticalAlignment="Stretch"
d:DesignHeight="290"
d:DesignWidth="384"
MaxWidth="700"
MaxHeight="600"
Opacity="0"
Name="card"
SizeChanged="Card_SizeChanged">
Name="card">
<UserControl.Resources>
<Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="cover" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
<Grid Name="grid">
<Grid Name="grid" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="50"/>
@@ -33,7 +29,7 @@
</Grid.RowDefinitions>
<Image Source="/Assets/ChannelCoverTemplate.png" Stretch="UniformToFill" Opacity=".0001"/>
<Image Name="cover" Source="/Assets/ChannelCoverTemplate.png" Stretch="UniformToFill" ImageOpened="Cover_ImageOpened" Opacity="0"/>
<Image Name="cover" Source="/Assets/ChannelCoverTemplate.png" Stretch="UniformToFill" ImageOpened="Cover_ImageOpened"/>
<StackPanel Name="liveTag" Margin="5" Background="Red" BorderBrush="White" BorderThickness="1" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3" Orientation="Horizontal" Visibility="Collapsed">
<TextBlock Text="&#xEC44; " VerticalAlignment="Center" Foreground="White" FontSize="12" FontFamily="Segoe MDL2 Assets" FontWeight="Black"/>
@@ -68,7 +64,7 @@
<ToggleButton Name="notify" Height="50" Width="50" Visibility="Collapsed" FontFamily="Segoe MDL2 Assets" FontSize="18" FontWeight="SemiBold" Content="&#xE7ED;" Foreground="White" Background="Red" HorizontalAlignment="Right"/>
</Grid>
</Grid>
</Button>
<UserControl.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="/Cards/channel" Icon="Contact" Text="View channel" Click="Button_Click"/>
+26 -36
View File
@@ -1,7 +1,9 @@
using Google.Apis.YouTube.v3;
using FoxTube.Classes;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Microsoft.Toolkit.Uwp.UI.Controls;
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.System;
@@ -16,32 +18,33 @@ namespace FoxTube.Controls
/// <summary>
/// Channel item card
/// </summary>
public sealed partial class ChannelCard : UserControl
public sealed partial class ChannelCard : UserControl, ICard
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
string channelId;
readonly string channelId;
readonly string live;
Channel item;
public ChannelCard(string id, string live = null)
{
InitializeComponent();
Initialize(id, live);
channelId = id;
this.live = live;
}
public async void Initialize(string id, string live)
public async Task Initialize()
{
try
{
if (id == SecretsVault.AccountId)
if (channelId == SecretsVault.AccountId)
grid.RowDefinitions[3].Height = new GridLength(0);
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
request.Id = id;
request.Id = channelId;
ChannelListResponse response = await request.ExecuteAsync();
item = response.Items[0];
channelId = id;
title.Text = item.Snippet.Title;
description.Text = item.Snippet.Description;
@@ -54,7 +57,7 @@ namespace FoxTube.Controls
if (SecretsVault.IsAuthorized)
{
if (SecretsVault.Subscriptions.Exists(i => i.Snippet.ResourceId.ChannelId == id))
if (SecretsVault.Subscriptions.Exists(i => i.Snippet.ResourceId.ChannelId == channelId))
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
@@ -63,10 +66,9 @@ namespace FoxTube.Controls
subscriptionPane.Visibility = Visibility.Visible;
}
try { avatar.ProfilePicture = new BitmapImage(new Uri(item.Snippet.Thumbnails.Medium.Url)) { DecodePixelWidth = 74, DecodePixelHeight = 74 }; }
catch { }
try
{
avatar.ProfilePicture = new BitmapImage(new Uri(item.Snippet.Thumbnails.Medium.Url)) { DecodePixelWidth = 74, DecodePixelHeight = 74 };
if (!item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
cover.Source = new BitmapImage(item.BrandingSettings.Image.BannerMobileImageUrl.ToUri());
}
@@ -80,25 +82,22 @@ namespace FoxTube.Controls
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", id },
{ "Video ID", channelId },
{ "StackTrace", e.StackTrace }
});
}
show.Begin();
}
public void Button_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(channelId);
}
public void ItemClicked() =>
Navigation.GoToChannel(channelId);
private void Hyperlink_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args)
{
void Button_Click(object sender, RoutedEventArgs e) =>
ItemClicked();
void Hyperlink_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args) =>
SecretsVault.Authorize();
}
private async void subscribe_Click(object sender, RoutedEventArgs e)
async void subscribe_Click(object sender, RoutedEventArgs e)
{
if (await SecretsVault.ChangeSubscriptionState(channelId))
{
@@ -114,26 +113,17 @@ namespace FoxTube.Controls
}
}
private void GetLink_Click(object sender, RoutedEventArgs e)
void GetLink_Click(object sender, RoutedEventArgs e)
{
DataPackage data = new DataPackage();
data.SetText($"https://www.youtube.com/channel/{item.Id}");
Clipboard.SetContent(data);
}
private async void InBrowser_Click(object sender, RoutedEventArgs e)
{
async void InBrowser_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync($"https://www.youtube.com/channel/{item.Id}".ToUri());
}
private void Cover_ImageOpened(object sender, RoutedEventArgs e)
{
showThumb.Begin();
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
void Cover_ImageOpened(object sender, RoutedEventArgs e) =>
show.Begin();
}
}
+8 -16
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
@@ -10,6 +9,7 @@ using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Popups;
using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using FoxTube.Classes;
namespace FoxTube.Controls
{
@@ -135,12 +135,10 @@ namespace FoxTube.Controls
catch { }
}
private void replyBtn_Click(object sender, RoutedEventArgs e)
{
replyEditor.Visibility = replyEditor.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
void replyBtn_Click(object sender, RoutedEventArgs e) =>
replyEditor.Visibility = replyEditor.Visibility == Visibility.Visible? Visibility.Collapsed : Visibility.Visible;
private async void showReplies_Click(object sender, RoutedEventArgs e)
async void showReplies_Click(object sender, RoutedEventArgs e)
{
replies.Visibility = replies.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
@@ -169,15 +167,11 @@ namespace FoxTube.Controls
processing.Visibility = Visibility.Collapsed;
}
private void avatar_Tapped(object sender, TappedRoutedEventArgs e)
{
Methods.MainPage.GoToChannel(item.Snippet.AuthorChannelId.ToString().Split('"')[3]);
}
void avatar_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel(item.Snippet.AuthorChannelId.ToString().Split('"')[3]);
private void reply_TextChanged(object sender, TextChangedEventArgs e)
{
private void reply_TextChanged(object sender, TextChangedEventArgs e) =>
send.IsEnabled = reply.Text.Length == 0 ? false : true;
}
private async void send_Click(object sender, RoutedEventArgs args)
{
@@ -283,10 +277,8 @@ namespace FoxTube.Controls
processing.Visibility = Visibility.Collapsed;
}
private void editorText_TextChanged(object sender, TextChangedEventArgs e)
{
private void editorText_TextChanged(object sender, TextChangedEventArgs e) =>
editorSend.IsEnabled = editorText.Text.Length == 0 ? false : true;
}
private void editBtn_Click(object sender, RoutedEventArgs e)
{
@@ -0,0 +1,35 @@
<Grid
x:Class="FoxTube.Controls.Common.AccountManager"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Button Background="Transparent" Visibility="Collapsed" Name="manager" Height="41" Width="60" FontSize="15" Padding="0">
<Button.Flyout>
<Flyout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<PersonPicture Width="65" Name="avatar" VerticalAlignment="Top"/>
<StackPanel Grid.Column="1" Margin="5">
<TextBlock Name="name"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Name="email"/>
<HyperlinkButton x:Uid="/Main/signOut" Content="Log out" Click="Logout_Click"/>
</StackPanel>
</Grid>
</Flyout>
</Button.Flyout>
<Ellipse Width="25" Height="25" Name="icon">
<Ellipse.Fill>
<ImageBrush ImageSource="/Assets/Icons/profile.png"/>
</Ellipse.Fill>
</Ellipse>
</Button>
<Button x:Uid="/Main/signIn" Name="account" Click="SignIn_Click" Visibility="Collapsed" FontFamily="Segoe MDL2 Assets" Content="&#xE8FA;" Background="Transparent" Height="41" Width="60" FontSize="15"/>
</Grid>
@@ -0,0 +1,48 @@
using Microsoft.AppCenter.Analytics;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace FoxTube.Controls.Common
{
public sealed partial class AccountManager : Grid
{
public AccountManager()
{
InitializeComponent();
}
void SignIn_Click(object sender, RoutedEventArgs e)
{
SecretsVault.Authorize();
Analytics.TrackEvent("Initialized authorization sequence");
}
void Logout_Click(object sender, RoutedEventArgs e)
{
manager.Flyout.Hide();
SecretsVault.Deauthenticate();
}
public async void Logged() => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
account.Visibility = Visibility.Collapsed;
ToolTipService.SetToolTip(avatar, $"{SecretsVault.UserInfo.Name} ({SecretsVault.UserInfo.Email})");
name.Text = SecretsVault.UserInfo.Name;
email.Text = SecretsVault.UserInfo.Email;
avatar.ProfilePicture = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 65, DecodePixelWidth = 65 };
(icon.Fill as ImageBrush).ImageSource = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 25, DecodePixelWidth = 25 };
manager.Visibility = Visibility.Visible;
});
public void Quit()
{
manager.Visibility = Visibility.Collapsed;
account.Visibility = Visibility.Visible;
}
}
}
+129
View File
@@ -0,0 +1,129 @@
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.ApplicationModel.Resources;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Controls.Common
{
class AddToPlaylist
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("VideoPage");
IList<MenuFlyoutItemBase> Items { get; }
string videoId;
public AddToPlaylist(IList<MenuFlyoutItemBase> items) =>
Items = items;
public async void Initialize(string id)
{
Items.Clear();
LoadDefaultItems();
videoId = id;
if (SecretsVault.WatchLater.Videos.Any(i => i.Id == id))
(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 = 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 SymbolIcon(Symbol.List)
};
menuItem.Click += Item_Click;
Items.Add(menuItem);
}
}
void LoadDefaultItems()
{
MenuFlyoutItem createPlaylist = new MenuFlyoutItem
{
Text = resources.GetString("/VideoPage/newPlaylist/Text"),
Icon = new SymbolIcon(Symbol.Add)
};
createPlaylist.Click += NewPlaylist_Click;
Items.Add(createPlaylist);
ToggleMenuFlyoutItem watchLater = new ToggleMenuFlyoutItem
{
Text = resources.GetString("/VideoPage/wl"),
Icon = new SymbolIcon(Symbol.Clock)
};
watchLater.Click += Wl_Click;
Items.Add(watchLater);
Items.Add(new MenuFlyoutSeparator());
}
async void NewPlaylist_Click(object sender, RoutedEventArgs e)
{
ToggleMenuFlyoutItem menuItem = await new CreateAndAddPlaylist().GetItem();
if (menuItem == null)
return;
menuItem.Click += Item_Click;
Items.Add(menuItem);
Item_Click(menuItem, null);
}
async void Wl_Click(object sender, RoutedEventArgs e) =>
(sender as ToggleMenuFlyoutItem).IsChecked = await Methods.AddItemToWL(videoId);
async void Item_Click(object sender, RoutedEventArgs e)
{
try
{
if(((ToggleMenuFlyoutItem)sender).IsChecked)
{
PlaylistItem playlist = new PlaylistItem
{
Snippet = new PlaylistItemSnippet
{
PlaylistId = (((ToggleMenuFlyoutItem)sender).Tag as Playlist).Id,
ResourceId = new ResourceId
{
VideoId = videoId,
Kind = "youtube#video"
}
}
};
PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet");
await request.ExecuteAsync();
}
else
{
PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet");
itemRequest.VideoId = videoId;
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 = !((ToggleMenuFlyoutItem)sender).IsChecked;
}
}
}
};
@@ -0,0 +1,22 @@
<ContentDialog
x:Class="FoxTube.Controls.CreateAndAddPlaylist"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Uid="/VideoPage/dialog"
PrimaryButtonText="Create and add" Title="New playlist" CloseButtonText="Cancel"
DefaultButton="Primary"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
Name="playlistDialog">
<StackPanel>
<TextBox x:Uid="/VideoPage/newPlaylistName" PlaceholderText="Enter playlist name" Name="newListName"/>
<ComboBox x:Uid="/VideoPage/privacy" Header="Availablity" SelectedIndex="0" HorizontalAlignment="Stretch" Name="newListDisc">
<ComboBoxItem x:Uid="/VideoPage/public" Content="Public"/>
<ComboBoxItem x:Uid="/VideoPage/private" Content="Private"/>
<ComboBoxItem x:Uid="/VideoPage/direct" Content="Direct link"/>
</ComboBox>
</StackPanel>
</ContentDialog>
@@ -0,0 +1,75 @@
using Google.Apis.YouTube.v3.Data;
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Controls
{
public sealed partial class CreateAndAddPlaylist : ContentDialog
{
ToggleMenuFlyoutItem menuItem;
public CreateAndAddPlaylist()
{
InitializeComponent();
}
public async Task<ToggleMenuFlyoutItem> GetItem()
{
if (await ShowAsync() != ContentDialogResult.Primary)
return null;
while (menuItem == null)
await Task.Delay(100);
return menuItem;
}
private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
string privacy = "private";
switch (newListDisc.SelectedIndex)
{
case 0:
privacy = "public";
break;
case 1:
privacy = "private";
break;
case 2:
privacy = "unlisted";
break;
}
Playlist newItem = new Playlist
{
Snippet = new PlaylistSnippet
{
Title = newListName.Text
},
Status = new PlaylistStatus
{
PrivacyStatus = privacy,
}
};
Playlist i;
try { i = await SecretsVault.Service.Playlists.Insert(newItem, "snippet,status").ExecuteAsync(); }
catch
{
return;
}
menuItem = new ToggleMenuFlyoutItem
{
Text = i.Snippet.Title,
IsChecked = true,
Tag = i,
Icon = new FontIcon
{
Glyph = "\xE728"
}
};
}
}
}
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using Windows.ApplicationModel.Resources;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using YoutubeExplode;
using YoutubeExplode.Models;
using YoutubeExplode.Models.MediaStreams;
namespace FoxTube.Controls.Common
{
public class DownloadSelector
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("VideoPage");
IList<MenuFlyoutItemBase> Items { get; }
public DownloadSelector(IList<MenuFlyoutItemBase> items) =>
Items = items;
public async void Initialize(Video item)
{
Items.Clear();
MediaStreamInfoSet infoSet = await new YoutubeClient(SecretsVault.HttpClient).GetVideoMediaStreamInfosAsync(item.Id);
foreach (MuxedStreamInfo i in infoSet.Muxed)
{
MenuFlyoutItem menuItem = new MenuFlyoutItem()
{
Text = i.VideoQualityLabel,
Tag = (i, item, i.VideoQualityLabel)
};
menuItem.Click += downloadItemSelected;
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;
Items.Add(audioItem);
}
void downloadItemSelected(object sender, RoutedEventArgs e) =>
DownloadAgent.Add(((MuxedStreamInfo, Video, string))(sender as MenuFlyoutItemBase).Tag);
}
}
+12 -9
View File
@@ -1,4 +1,6 @@
using Windows.UI.Xaml;
using FoxTube.Classes;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
@@ -10,26 +12,27 @@ namespace FoxTube.Controls
public LoadingPage LoadingPage => loading;
public event NavigatedEventHandler Navigated;
public new object Content => content.Content;
public ContentFrame()
{
InitializeComponent();
content.Navigated += (s, e) => Navigated?.Invoke(s, e);
}
private void Content_Navigating(object sender, NavigatingCancelEventArgs e)
{
loading.Refresh();
}
public void Navigate(Type sourcePageType, object parameter) =>
content.Navigate(sourcePageType, parameter);
private void Loading_RefreshPage(object sender, RoutedEventArgs e)
void Content_Navigating(object sender, NavigatingCancelEventArgs e) =>
loading.Refresh();
void Loading_RefreshPage(object sender, RoutedEventArgs e)
{
content.Navigate(content.CurrentSourcePageType, (content.Content as INavigationPage).Parameter);
content.BackStack.RemoveAt(content.BackStack.Count - 1);
}
public void Refresh()
{
public void Refresh() =>
Loading_RefreshPage(this, null);
}
}
}
-1
View File
@@ -9,7 +9,6 @@
d:DesignHeight="100"
d:DesignWidth="1500">
<!--<Button HorizontalAlignment="Stretch" Background="WhiteSmoke" Height="100" Padding="0" HorizontalContentAlignment="Stretch"/>-->
<Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Height="100" HorizontalAlignment="Stretch" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
+18 -23
View File
@@ -7,7 +7,6 @@ using Windows.System;
using YoutubeExplode.Models.MediaStreams;
using YoutubeExplode;
using Windows.Storage;
using Google.Apis.YouTube.v3.Data;
using System.Threading;
using Windows.UI.Popups;
using Windows.UI.Notifications;
@@ -17,6 +16,8 @@ using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
using Newtonsoft.Json;
using YoutubeExplode.Models;
using FoxTube.Classes;
namespace FoxTube.Controls
{
@@ -52,14 +53,14 @@ namespace FoxTube.Controls
Container = new DownloadItemContainer()
{
Channel = meta.Snippet.ChannelTitle,
Duration = Methods.GetDuration(meta.ContentDetails.Duration),
Channel = meta.Author,
Duration = meta.Duration,
Extension = info.Container.GetFileExtension(),
Id = meta.Id,
IsDownloaded = false,
Quality = q,
Thumbnail = meta.Snippet.Thumbnails.Medium.Url.ToUri(),
Title = meta.Snippet.Title
Thumbnail = meta.Thumbnails.MediumResUrl.ToUri(),
Title = meta.Title
};
Download(info);
@@ -102,16 +103,16 @@ namespace FoxTube.Controls
timer.Tick += (s, e) => UpdateInfo();
#region Polling notification
ToastContent toastContent = new ToastContent()
ToastContent toastContent = new ToastContent
{
Visual = new ToastVisual()
Visual = new ToastVisual
{
BindingGeneric = new ToastBindingGeneric()
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText() { Text = resources.GetString("/Downloads/toastStartHeader") },
new AdaptiveProgressBar()
new AdaptiveText { Text = resources.GetString("/Downloads/toastStartHeader") },
new AdaptiveProgressBar
{
Title = Container.Title,
Status = resources.GetString("/Downloads/downloading/Text"),
@@ -121,7 +122,7 @@ namespace FoxTube.Controls
}
},
Actions = new ToastActionsCustom()
Actions = new ToastActionsCustom
{
Buttons =
{
@@ -197,7 +198,7 @@ namespace FoxTube.Controls
}
}
private void UpdateInfo()
void UpdateInfo()
{
progressBar.Value = percentage;
progressText.Text = Math.Round(percentage * 100, 1) + "%";
@@ -206,7 +207,7 @@ namespace FoxTube.Controls
ToastNotificationManager.CreateToastNotifier().Update(data, $"download|{Container.Id}");
}
private void DownloadCompleted()
void DownloadCompleted()
{
Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
template.LoadXml($@"<toast activationType='foreground' launch='download'>
@@ -229,15 +230,11 @@ namespace FoxTube.Controls
progressPanel.Visibility = Visibility.Collapsed;
}
private async void open_Click(object sender, RoutedEventArgs e)
{
async void open_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchFileAsync(File);
}
private void gotoOriginal_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToVideo(Container.Id);
}
void gotoOriginal_Click(object sender, RoutedEventArgs e) =>
Navigation.GoToVideo(Container.Id);
public async void Cancel(bool prompt = true)
{
@@ -283,9 +280,7 @@ namespace FoxTube.Controls
}
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
private void Cancel_Click(object sender, RoutedEventArgs e) =>
Cancel();
}
}
}
+92 -168
View File
@@ -1,4 +1,5 @@
using FoxTube.Controls;
using FoxTube.Classes;
using FoxTube.Controls;
using FoxTube.Controls.Adverts;
using FoxTube.Controls.Player;
using Google.Apis.YouTube.v3.Data;
@@ -6,57 +7,29 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
using Windows.Graphics.Display;
using Windows.Media;
using Windows.Media.Core;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
using YoutubeExplode;
using YoutubeExplode.Models.ClosedCaptions;
using YoutubeExplode.Models.MediaStreams;
namespace FoxTube
namespace FoxTube.Controls.Player
{
public delegate void MinimodeChangedEventHandler(object sender, bool isOn);
public enum PlayerDisplayState { Normal, Minimized, Compact }
public class QualityComparer : IComparer<string>
{
public int Compare(string x, string y)
{
string[] xArr = x.Split('p');
string[] yArr = y.Split('p');
int qualityA = int.Parse(xArr[0]);
int qualityB = int.Parse(yArr[0]);
int framerateA = 30;
int framerateB = 30;
if (!string.IsNullOrWhiteSpace(xArr[1]))
framerateA = int.Parse(xArr[1]);
if (!string.IsNullOrWhiteSpace(yArr[1]))
framerateB = int.Parse(yArr[1]);
if (qualityA > qualityB)
return 1;
else if (qualityA < qualityB)
return -1;
else
return framerateA - framerateB > 0 ? 1 : -1;
}
}
/// <summary>
/// Custom controls for media player. MARKUP IS IN **Themes/Generic.xaml**!!!
/// </summary>
public sealed class PlayerControls : MediaTransportControls
public sealed partial class PlayerControls : MediaTransportControls
{
public event RoutedEventHandler CloseRequested;
public event MinimodeChangedEventHandler MiniModeChanged;
public event RoutedEventHandler NextRequested;
#region Controls variables
Button minimize;
@@ -64,6 +37,7 @@ namespace FoxTube
Button miniview;
Button play;
Button next;
Button prev;
Button volumeMenu;
Button mute;
Button live;
@@ -97,17 +71,12 @@ namespace FoxTube
Grid centerTrigger;
#endregion
PlayerDisplayState State { get; set; } = PlayerDisplayState.Normal;
public MediaElement Player { get; set; }
public PlayerAdvert Advert;
public LiveCaptions Caption;
TimeSpan timecodeBackup;
bool needUpdateTimecode = false;
public Video Meta { get; set; }
public IReadOnlyList<ClosedCaptionTrackInfo> ClosedCaptions { get; set; }
public MediaStreamInfoSet MediaStreams { get; set; }
@@ -125,11 +94,22 @@ namespace FoxTube
isReady = true;
Player.MediaOpened += Player_MediaOpened;
Player.MarkerReached += (s, e) => PushAdvert();
Player.CurrentStateChanged += Player_CurrentStateChanged;
Caption.Player = Player;
minimize.Click += Minimize_Click;
close.Click += Close_Click;
miniview.Click += Miniview_Click;
mute.Click += async (s, e) =>
{
await Task.Delay(10);
Volume_ValueChanged(this, null);
};
next.Click += Next_Click;
prev.Click += Prev_Click;
volume.ValueChanged += Volume_ValueChanged;
playbackSpeed.ValueChanged += PlaybackSpeed_ValueChanged;
live.Click += Live_Click;
@@ -139,8 +119,19 @@ namespace FoxTube
quality.SelectionChanged += Quality_SelectionChanged;
seek.ValueChanged += Seek_ValueChanged;
Player.Tapped += (s, e) =>
IsCompactOverlayButtonVisible = true;
IsCompactOverlayEnabled = true;
IsFullWindowButtonVisible = true;
IsFullWindowEnabled = true;
IsSkipBackwardButtonVisible = true;
IsSkipBackwardEnabled = true;
IsSkipForwardButtonVisible = true;
IsSkipForwardEnabled = true;
Player.Tapped += async (s, e) =>
{
await Task.Delay(10);
Rect view = new Rect(0, 0, centerTrigger.ActualWidth, centerTrigger.ActualHeight);
Point p = e.GetPosition(centerTrigger);
@@ -160,10 +151,20 @@ namespace FoxTube
base.OnApplyTemplate();
}
private void PlaybackSpeed_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
private void Player_CurrentStateChanged(object sender, RoutedEventArgs e)
{
Player.PlaybackRate = playbackSpeed.Value;
systemControls.PlaybackStatus = Player.CurrentState == MediaElementState.Playing ? MediaPlaybackStatus.Playing : MediaPlaybackStatus.Paused;
if (Player.CurrentState == MediaElementState.Paused)
if (!incognito && Item.Snippet.LiveBroadcastContent == "none")
{
History.LeftOn = Player.Position;
HistorySet.Update(History);
}
}
private void PlaybackSpeed_ValueChanged(object sender, RangeBaseValueChangedEventArgs e) =>
Player.PlaybackRate = playbackSpeed.Value;
void AssignControls()
{
@@ -172,6 +173,7 @@ namespace FoxTube
miniview = GetTemplateChild("CompactOverlayButton") as Button;
play = GetTemplateChild("PlayPauseButton") as Button;
next = GetTemplateChild("NextButton") as Button;
prev = GetTemplateChild("PreviousButton") as Button;
volumeMenu = GetTemplateChild("VolumeMenuButton") as Button;
mute = GetTemplateChild("AudioMuteButton") as Button;
live = GetTemplateChild("PlayLiveButton") as Button;
@@ -207,14 +209,14 @@ namespace FoxTube
centerTrigger = GetTemplateChild("centerTrigger") as Grid;
}
private void Seek_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
private void Seek_ValueChanged(object sender, RangeBaseValueChangedEventArgs e) =>
seekIndicator.Value = seek.Value;
}
private async void Quality_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (Meta.Snippet.LiveBroadcastContent == "live")
if (e.AddedItems.Count == 0)
return;
if (Item.Snippet.LiveBroadcastContent == "live")
goto SetQuality;
if(!needUpdateTimecode)
timecodeBackup = Player.Position;
@@ -227,7 +229,7 @@ namespace FoxTube
if (info is MuxedStreamInfo)
Player.SetPlaybackSource(MediaSource.CreateFromUri((info as MuxedStreamInfo).Url.ToUri()));
else if (info is VideoStreamInfo || info == null)
Player.SetPlaybackSource(MediaSource.CreateFromUri(await ManifestGenerator.GetManifest(Meta, info as VideoStreamInfo, MediaStreams)));
Player.SetPlaybackSource(MediaSource.CreateFromUri(await ManifestGenerator.GetManifest(Item, info as VideoStreamInfo, MediaStreams)));
else if (info is StreamQuality)
Player.SetPlaybackSource(MediaSource.CreateFromUri((info as StreamQuality).Url));
}
@@ -256,9 +258,15 @@ namespace FoxTube
private void Next_Click(object sender, RoutedEventArgs e)
{
NextRequested.Invoke(sender, e);
if (Playlist == null)
NextRequested?.Invoke();
else
Playlist.Next();
}
private void Prev_Click(object sender, RoutedEventArgs e) =>
Playlist.Previous();
private void Miniview_Click(object sender, RoutedEventArgs e)
{
if (State == PlayerDisplayState.Compact)
@@ -267,11 +275,6 @@ namespace FoxTube
EnterMiniview();
}
public void UpdateVolumeIcon()
{
Volume_ValueChanged(this, null);
}
private void Volume_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
double v = volume.Value;
@@ -289,6 +292,13 @@ namespace FoxTube
private void Player_MediaOpened(object sender, RoutedEventArgs args)
{
if ((timecodeBackup == null || timecodeBackup.TotalSeconds < 3) && History.LeftOn.TotalSeconds.Belongs(30, Player.NaturalDuration.TimeSpan.TotalSeconds - 120))
{
Player.Position = History.LeftOn;
needUpdateTimecode = false;
return;
}
if (!needUpdateTimecode)
return;
@@ -298,7 +308,10 @@ namespace FoxTube
private void Close_Click(object sender, RoutedEventArgs e)
{
CloseRequested?.Invoke(sender, e);
systemControls.IsEnabled = false;
Player.Stop();
Player.Source = null;
//Methods.MainPage.CloseVideo();
}
private void Minimize_Click(object sender, RoutedEventArgs e)
@@ -314,6 +327,8 @@ namespace FoxTube
if (State == PlayerDisplayState.Minimized)
return;
//Methods.MainPage.MinimizeVideo(true);
header.Children.Remove(minimize);
center.Children.Add(minimize);
rightHeader.Children.Remove(close);
@@ -331,7 +346,7 @@ namespace FoxTube
minimize.Content = "\xE010";
MiniModeChanged.Invoke(this, true);
MiniModeChanged?.Invoke(true);
Caption.Minimize();
State = PlayerDisplayState.Minimized;
@@ -342,6 +357,8 @@ namespace FoxTube
if (State == PlayerDisplayState.Normal)
return;
//Methods.MainPage.MaximizeVideo(true);
if(State == PlayerDisplayState.Compact)
{
center.Children.Remove(miniview);
@@ -370,7 +387,7 @@ namespace FoxTube
centerStack.Children.Remove(bwd);
rightFooter.Children.Insert(0, bwd);
MiniModeChanged.Invoke(this, false);
MiniModeChanged?.Invoke(false);
}
drag.Visibility = Visibility.Collapsed;
@@ -413,120 +430,27 @@ namespace FoxTube
State = PlayerDisplayState.Compact;
}
public async void Load(Video meta)
{
if(!isReady)
{
queue.Enqueue(() => Load(meta));
return;
}
Player.MediaOpened += Player_MediaOpened;
Meta = meta;
title.Text = meta.Snippet.Title;
channel.Text = meta.Snippet.ChannelTitle;
MediaStreams = await new YoutubeClient().GetVideoMediaStreamInfosAsync(meta.Id);
if (meta.Snippet.LiveBroadcastContent == "none")
{
ClosedCaptions = await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(meta.Id);
uint screenHeight = DisplayInformation.GetForCurrentView().ScreenHeightInRawPixels;
List<string> qualityList = MediaStreams.GetAllVideoQualityLabels().ToList();
qualityList.Sort(new QualityComparer());
qualityList.Reverse();
quality.Items.Add(new ComboBoxItem
{
Content = ResourceLoader.GetForCurrentView("VideoPage").GetString("/VideoPage/auto")
});
foreach (string i in qualityList)
{
object tag;
if (MediaStreams.Muxed.Any(m => m.VideoQualityLabel == i && m.Resolution.Height <= screenHeight))
tag = MediaStreams.Muxed.Find(m => m.VideoQualityLabel == i);
else if (MediaStreams.Video.Any(m => m.VideoQualityLabel == i && m.Resolution.Height <= screenHeight && m.Container.GetFileExtension() == "mp4"))
tag = MediaStreams.Video.Find(m => m.VideoQualityLabel == i && m.Container.GetFileExtension() == "mp4");
else
continue;
quality.Items.Add(new ComboBoxItem
{
Content = i,
Tag = tag
});
}
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (quality.Items.Any(i => ((i as ComboBoxItem).Content as string).Contains(s)))
quality.SelectedItem = quality.Items.Find(i => ((i as ComboBoxItem).Content as string).Contains(s));
else
quality.SelectedIndex = 0;
if (ClosedCaptions.Count == 0)
{
captionsMenu.Visibility = Visibility.Collapsed;
return;
}
foreach (ClosedCaptionTrackInfo i in ClosedCaptions)
captions.Items.Add(new ComboBoxItem
{
Content = string.Format("{0} {1}", CultureInfo.GetCultureInfo(i.Language.Code).DisplayName, i.IsAutoGenerated ? ResourceLoader.GetForCurrentView("VideoPage").GetString("/VideoPage/generatedCaption") : ""),
Tag = i
});
ClosedCaptionTrackInfo item = ClosedCaptions.Find(i => SettingsStorage.RelevanceLanguage.Contains(i.Language.Code)) ?? ClosedCaptions.Find(i => "en-US".Contains(i.Language.Code));
if (item == null)
item = ClosedCaptions.First();
captions.SelectedItem = captions.Items.Find(i => (i as ComboBoxItem).Tag as ClosedCaptionTrackInfo == item);
Caption.Player = Player;
}
else
{
captionsMenu.Visibility = Visibility.Collapsed;
seek.Visibility = Visibility.Collapsed;
live.Visibility = Visibility.Visible;
remain.Visibility = Visibility.Collapsed;
elapsed.FontSize = 24;
Grid.SetRow(elapsed, 0);
Grid.SetRowSpan(elapsed, 2);
elapsed.HorizontalAlignment = HorizontalAlignment.Right;
fwd.Visibility = Visibility.Collapsed;
bwd.Visibility = Visibility.Collapsed;
List<StreamQuality> list = await ManifestGenerator.ResolveLiveSteream(MediaStreams.HlsLiveStreamUrl);
foreach (StreamQuality i in list)
quality.Items.Add(new ComboBoxItem
{
Content = i.Resolution,
Tag = i
});
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (quality.Items.Any(i => (i as ComboBoxItem).Content as string == s))
quality.SelectedItem = quality.Items.Find(i => (i as ComboBoxItem).Content as string == s);
else
quality.SelectedIndex = 0;
}
Focus(FocusState.Programmatic);
}
public void PushAdvert()
void PushAdvert()
{
if(State == PlayerDisplayState.Normal)
Advert.PushAdvert();
}
private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args) =>
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
Player.Play();
break;
case SystemMediaTransportControlsButton.Pause:
Player.Pause();
break;
case SystemMediaTransportControlsButton.Next:
Next_Click(this, null);
break;
}
});
}
}
+208
View File
@@ -0,0 +1,208 @@
using FoxTube.Classes;
using Google.Apis.YouTube.v3.Data;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Resources;
using Windows.Graphics.Display;
using Windows.Media;
using Windows.Storage.Streams;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using YoutubeExplode;
using YoutubeExplode.Models.ClosedCaptions;
using YoutubeExplode.Models.MediaStreams;
using FoxTube.Controls.VideoPage;
using Windows.UI.Xaml.Media;
namespace FoxTube.Controls.Player
{
public partial class PlayerControls : MediaTransportControls
{
public event SimpleEventHandler CloseRequested;
public event MinimodeChangedEventHandler MiniModeChanged;
public event SimpleEventHandler NextRequested;
public PlayerDisplayState State { get; set; } = PlayerDisplayState.Normal;
public Video Item { get; private set; }
HistoryItem History { get; set; }
VideoPlaylist Playlist { get; set; }
public MediaElement Player { get; set; }
SystemMediaTransportControls systemControls;
bool incognito;
public void Initialize(Video item, string avatarUrl, bool incognito = false, VideoPlaylist playlist = null)
{
this.incognito = incognito;
Playlist = playlist;
Item = item;
History = HistorySet.Items.Find(i => i.Id == Item.Id);
if (History == null && !incognito)
{
History = new HistoryItem { Id = Item.Id };
HistorySet.Update(History);
}
if (item.Snippet.LiveBroadcastContent == "upcoming")
Player.AreTransportControlsEnabled = false;
else
{
LoadControls(avatarUrl);
if (item.Snippet.LiveBroadcastContent == "none")
LoadVideo();
else
LoadStream();
}
}
void LoadControls(string avatar)
{
if (!isReady)
{
queue.Enqueue(() => LoadControls(avatar));
return;
}
prev.Visibility = Visibility.Collapsed;
captionsSwitch.IsOn = false;
Player.Volume = SettingsStorage.Volume;
Player.AutoPlay = SettingsStorage.Autoplay;
title.Text = Item.Snippet.Title;
channel.Text = Item.Snippet.ChannelTitle;
#region System Media Transport Controls
systemControls = SystemMediaTransportControls.GetForCurrentView();
systemControls.IsPauseEnabled = true;
systemControls.IsPlayEnabled = true;
if(Playlist != null)
{
next.IsEnabled = Playlist.SelectedIndex + 1 < Playlist.Items.Count;
prev.Visibility = Visibility.Visible;
prev.IsEnabled = Playlist.SelectedIndex != 0;
systemControls.IsNextEnabled = systemControls.IsPreviousEnabled = false;
}
else
next.IsEnabled = systemControls.IsNextEnabled = true;
systemControls.DisplayUpdater.Type = MediaPlaybackType.Video;
systemControls.DisplayUpdater.VideoProperties.Title = Item.Snippet.Title;
systemControls.DisplayUpdater.VideoProperties.Subtitle = Item.Snippet.ChannelTitle;
systemControls.DisplayUpdater.Thumbnail = RandomAccessStreamReference.CreateFromUri(avatar.ToUri());
systemControls.DisplayUpdater.Update();
systemControls.ButtonPressed += SystemControls_Engaged;
systemControls.IsEnabled = true;
#endregion
quality.Items.Clear();
captions.Items.Clear();
Player.Markers.Clear();
}
async void LoadVideo()
{
if (Methods.GetDuration(Item.ContentDetails.Duration).TotalMinutes > 5)
Player.Markers.Add(new TimelineMarker { Time = Methods.GetDuration(Item.ContentDetails.Duration) - TimeSpan.FromMinutes(1) });
if (Methods.GetDuration(Item.ContentDetails.Duration).TotalMinutes >= 60)
for (int k = 1; k < Methods.GetDuration(Item.ContentDetails.Duration).TotalMinutes / 30; k++)
Player.Markers.Add(new TimelineMarker { Time = TimeSpan.FromMinutes(k * 30) });
MediaStreamInfoSet set = await new YoutubeClient().GetVideoMediaStreamInfosAsync(Item.Id);
uint screenHeight = DisplayInformation.GetForCurrentView().ScreenHeightInRawPixels;
List<string> qualityList = set.GetAllVideoQualityLabels().ToList();
qualityList.Sort(new QualityComparer());
qualityList.Reverse();
quality.Items.Add(new ComboBoxItem
{
Content = ResourceLoader.GetForCurrentView("VideoPage").GetString("/VideoPage/auto")
});
foreach (string i in qualityList)
{
object tag = (object)set.Muxed.Find(m => m.VideoQualityLabel == i && m.Resolution.Height <= screenHeight) ?? set.Video.Find(m => m.VideoQualityLabel == i && m.Container.GetFileExtension() == "mp4");
if (tag == null)
continue;
quality.Items.Add(new ComboBoxItem
{
Content = i,
Tag = tag
});
}
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (quality.Items.Find(i => ((i as ComboBoxItem).Content as string).Contains(s)) is ComboBoxItem item)
quality.SelectedItem = item;
else
quality.SelectedIndex = 0;
IReadOnlyList<ClosedCaptionTrackInfo> cc = await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(Item.Id);
captionsMenu.Visibility = cc.Count == 0 ? Visibility.Collapsed : Visibility.Visible;
foreach (ClosedCaptionTrackInfo i in cc)
captions.Items.Add(new ComboBoxItem
{
Content = $"{CultureInfo.GetCultureInfo(i.Language.Code).DisplayName}{(i.IsAutoGenerated ? $" ({ResourceLoader.GetForCurrentView("VideoPage").GetString("/VideoPage/generatedCaption")})" : "")})",
Tag = i
});
ClosedCaptionTrackInfo ccItem = cc.Find(i => SettingsStorage.RelevanceLanguage.Contains(i.Language.Code)) ?? cc.Find(i => "en-US".Contains(i.Language.Code));
ccItem = ccItem ?? cc.FirstOrDefault();
captions.SelectedItem = captions.Items.Find(i => (i as ComboBoxItem).Tag as ClosedCaptionTrackInfo == ccItem);
}
async void LoadStream()
{
List<StreamQuality> qualities = await ManifestGenerator.ResolveLiveSteream((await new YoutubeClient().GetVideoMediaStreamInfosAsync(Item.Id)).HlsLiveStreamUrl);
foreach (StreamQuality i in qualities)
quality.Items.Add(new ComboBoxItem
{
Content = i.Resolution,
Tag = i
});
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (quality.Items.Find(i => (i as ComboBoxItem).Content as string == s) is ComboBoxItem item)
quality.SelectedItem = item;
else
quality.SelectedIndex = 0;
}
public void Pause() =>
Player.Pause();
public async void OpenBrowser()
{
Player.Pause();
string timecode = Player.Position.TotalSeconds > 10 ?
"&t=" + (int)Player.Position.TotalSeconds + "s" : string.Empty;
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={Item.Id}{timecode}".ToUri());
}
public void SetMargin(double offset) =>
Player.Margin = new Thickness(Player.Margin.Left, offset * -1, Player.Margin.Right, Player.Margin.Bottom);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:foxtube="using:FoxTube"
xmlns:foxtube="using:FoxTube.Controls.Player"
mc:Ignorable="d"
d:DesignHeight="1080"
d:DesignWidth="1920"
+10 -9
View File
@@ -7,6 +7,7 @@ using Windows.UI.Xaml.Media.Imaging;
using Windows.Media;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media;
using FoxTube.Controls.Player;
namespace FoxTube
{
@@ -45,7 +46,7 @@ namespace FoxTube
if (item.Snippet.LiveBroadcastContent == "none")
{
InitializeContols();
Controls.Load(item);
//Controls.Load(item);
if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5)
videoSource.Markers.Add(new TimelineMarker { Time = Methods.GetDuration(item.ContentDetails.Duration) - TimeSpan.FromMinutes(1) });
@@ -59,7 +60,7 @@ namespace FoxTube
else if (item.Snippet.LiveBroadcastContent == "live")
{
InitializeContols();
Controls.Load(item);
//Controls.Load(item);
}
else
videoSource.AreTransportControlsEnabled = false;
@@ -73,9 +74,9 @@ namespace FoxTube
videoSource.AutoPlay = SettingsStorage.Autoplay;
Controls.CloseRequested += Controls_CloseRequested;
Controls.NextRequested += (s, e) => NextClicked?.Invoke();
Controls.NextRequested += () => NextClicked?.Invoke();
Controls.MiniModeChanged += Controls_MiniModeChanged;
Controls.Player = videoSource;
//Controls.Player = videoSource;
#region System Media Transport Controls
systemControls = SystemMediaTransportControls.GetForCurrentView();
@@ -113,7 +114,7 @@ namespace FoxTube
});
}
public void Controls_MiniModeChanged(object sender, bool e)
public void Controls_MiniModeChanged(bool e)
{
videoSource.IsFullWindow = false;
@@ -125,7 +126,7 @@ namespace FoxTube
Controls.Minimize();
}
public void Controls_CloseRequested(object sender, RoutedEventArgs e)
public void Controls_CloseRequested()
{
videoSource.Pause();
systemControls.IsEnabled = false;
@@ -137,7 +138,7 @@ namespace FoxTube
HistorySet.Update(history);
}
Methods.MainPage.CloseVideo();
//Methods.MainPage.CloseVideo();
}
public void Pause()
@@ -161,12 +162,12 @@ namespace FoxTube
{
if(videoSource.Volume != 0)
SettingsStorage.Volume = videoSource.Volume;
Controls.UpdateVolumeIcon();
//Controls.UpdateVolumeIcon();
}
private void VideoSource_MarkerReached(object sender, TimelineMarkerRoutedEventArgs e)
{
Controls.PushAdvert();
//Controls.PushAdvert();
}
}
}
+6 -10
View File
@@ -6,25 +6,21 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
VerticalAlignment="Stretch"
d:DesignHeight="290"
d:DesignWidth="384"
MaxWidth="700"
MaxHeight="600"
Opacity="0"
Name="card"
SizeChanged="Card_SizeChanged">
Name="card">
<UserControl.Resources>
<Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
<Grid>
<Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="20"/>
@@ -32,7 +28,7 @@
</Grid.RowDefinitions>
<Image Source="/Assets/videoPlaceholder.png" Opacity=".0001" Stretch="Fill"/>
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill" Opacity="0" ImageOpened="Thumbnail_ImageOpened"/>
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill" ImageOpened="Thumbnail_ImageOpened"/>
<Grid HorizontalAlignment="Right" Width="100">
<Grid.Background>
@@ -58,7 +54,7 @@
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" TextTrimming="CharacterEllipsis" Margin="5" MaxLines="2"/>
</Grid>
</Button>
<UserControl.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="/Cards/playlist" Icon="List" Text="View playlist" Click="Button_Click"/>
+19 -35
View File
@@ -1,9 +1,11 @@
using Google.Apis.YouTube.v3;
using FoxTube.Classes;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics;
using Microsoft.Toolkit.Uwp.UI.Controls;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using Windows.System;
using Windows.UI.Xaml;
@@ -16,24 +18,21 @@ namespace FoxTube.Controls
/// <summary>
/// Playlist card control
/// </summary>
public sealed partial class PlaylistCard : UserControl
public sealed partial class PlaylistCard : UserControl, ICard
{
Playlist item;
public string playlistId;
public bool NeedInitialize { get; set; } = true;
readonly string playlistId;
public PlaylistCard(string id)
{
InitializeComponent();
Initialize(id);
playlistId = id;
}
public async void Initialize(string id)
public async Task Initialize()
{
try
{
playlistId = id;
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails");
request.Id = playlistId;
request.Hl = SettingsStorage.RelevanceLanguage;
@@ -44,17 +43,12 @@ namespace FoxTube.Controls
counter.Text = item.ContentDetails.ItemCount.ToString();
date.Text = Methods.GetAgo(item.Snippet.PublishedAt.Value);
ChannelsResource.ListRequest r = SecretsVault.Service.Channels.List("snippet");
r.Id = item.Snippet.ChannelId;
try
{
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 };
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient(SecretsVault.HttpClient).GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 46, DecodePixelWidth = 46 };
}
catch { }
show.Begin();
}
catch (Exception e)
{
@@ -70,36 +64,26 @@ namespace FoxTube.Controls
}
}
public void Button_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToPlaylist(item.Id);
}
public void ItemClicked() =>
Navigation.GoToPlaylist(item.Id);
private void OpenChannel_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
public void Button_Click(object sender, RoutedEventArgs e) =>
ItemClicked();
private void GetLink_Click(object sender, RoutedEventArgs e)
void OpenChannel_Click(object sender, RoutedEventArgs e) =>
Navigation.GoToChannel(item.Snippet.ChannelId);
void GetLink_Click(object sender, RoutedEventArgs e)
{
DataPackage data = new DataPackage();
data.SetText($"https://www.youtube.com/playlist?list={playlistId}");
Clipboard.SetContent(data);
}
private async void InBrowser_Click(object sender, RoutedEventArgs e)
{
async void InBrowser_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync($"https://www.youtube.com/playlist?list={playlistId}".ToUri());
}
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e)
{
showThumb.Begin();
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) =>
show.Begin();
}
}
+1 -14
View File
@@ -6,10 +6,8 @@ namespace FoxTube.Controls
public sealed partial class ShowMore : UserControl
{
public event Event Clicked;
public ShowMore()
{
public ShowMore() =>
InitializeComponent();
}
private void btn_Click(object sender, RoutedEventArgs e)
{
@@ -18,17 +16,6 @@ namespace FoxTube.Controls
Clicked.Invoke();
}
public void Invoke()
{
btn_Click(this, null);
}
public void Show()
{
btn.Visibility = Visibility.Collapsed;
bar.Visibility = Visibility.Visible;
}
public void Complete()
{
bar.Visibility = Visibility.Collapsed;
+12 -21
View File
@@ -6,25 +6,21 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
VerticalAlignment="Stretch"
d:DesignHeight="290"
d:DesignWidth="384"
MaxWidth="700"
MaxHeight="600"
Opacity="0"
Name="card"
SizeChanged="Card_SizeChanged">
Name="card">
<UserControl.Resources>
<Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
<Grid>
<Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="20"/>
@@ -32,20 +28,20 @@
</Grid.RowDefinitions>
<Image Source="/Assets/videoPlaceholder.png" Opacity=".0001" Stretch="Fill"/>
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill" ImageOpened="Thumbnail_ImageOpened" Opacity="0"/>
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill" ImageOpened="Thumbnail_ImageOpened"/>
<Grid Background="#7F000000" Name="watched" Visibility="Collapsed">
<StackPanel Margin="5" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" VerticalAlignment="Top" HorizontalAlignment="Left" Padding="5,2,5,2" BorderBrush="Gray" BorderThickness="1">
<Border Margin="5" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" VerticalAlignment="Top" HorizontalAlignment="Left" Padding="5,2" BorderBrush="Gray" BorderThickness="1">
<TextBlock x:Uid="/Cards/watched" Text="Watched" Foreground="Gray" FontSize="12"/>
</StackPanel>
<ProgressBar VerticalAlignment="Bottom" Margin="54,0,0,0" Foreground="Red" Name="leftOn"/>
</Border>
<ProgressBar VerticalAlignment="Bottom" Margin="54,0,0,0" Name="leftOn"/>
</Grid>
<StackPanel Margin="0,0,5,5" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3">
<Border Margin="5" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3">
<TextBlock Name="info" Text="[Duration] | [Published at]" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Gray" FontSize="12"/>
</StackPanel>
</Border>
<StackPanel Name="liveTag" Margin="0,0,5,30" Background="Red" BorderBrush="White" BorderThickness="1" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3" Orientation="Horizontal" Visibility="Collapsed">
<StackPanel Name="liveTag" Margin="5,30" Background="Red" BorderBrush="White" BorderThickness="1" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3" Orientation="Horizontal" Visibility="Collapsed">
<TextBlock Text="&#xEC44; " VerticalAlignment="Center" Foreground="White" FontSize="12" FontFamily="Segoe MDL2 Assets" FontWeight="Black"/>
<TextBlock x:Uid="/Cards/live" Name="liveContent" Text="LIVE" VerticalAlignment="Center" Foreground="White" FontSize="12" FontWeight="Bold"/>
</StackPanel>
@@ -64,7 +60,6 @@
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
</Grid>
</Button>
<UserControl.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="/Cards/play" Icon="Play" Text="Play" Name="play" Click="Button_Click"/>
@@ -80,11 +75,7 @@
<MenuFlyoutItem x:Uid="/Cards/share" Icon="Share" Text="Share" Name="share" Click="share_Click"/>
<MenuFlyoutSeparator/>
<MenuFlyoutSubItem x:Uid="/Cards/downloads" Icon="Download" Text="Download" Name="download"/>
<MenuFlyoutSubItem x:Uid="/Cards/addTo" Icon="Add" Text="Add to" Name="addTo">
<MenuFlyoutItem x:Uid="/VideoPage/newPlaylist" Text="New playlist" Name="newPlaylist" Click="NewPlaylist_Click" Icon="Add"/>
<ToggleMenuFlyoutItem x:Uid="/VideoPage/wl" Text="Watch later" Name="wl" Click="Wl_Click" Icon="Clock"/>
<MenuFlyoutSeparator/>
</MenuFlyoutSubItem>
<MenuFlyoutSubItem x:Uid="/Cards/addTo" Icon="Add" Text="Add to" Name="addTo"/>
<MenuFlyoutItem x:Uid="/Cards/delete" Text="Remove from playlist" Icon="Delete" Visibility="Collapsed" Name="delete" Click="Delete_Click"/>
</MenuFlyout>
</UserControl.ContextFlyout>
+83 -375
View File
@@ -2,7 +2,6 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Windows.UI.Xaml.Media.Imaging;
using Windows.System;
using Windows.ApplicationModel.DataTransfer;
@@ -10,112 +9,108 @@ using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
using YoutubeExplode;
using YoutubeExplode.Models;
using Windows.UI.Popups;
using YoutubeExplode.Models.MediaStreams;
using Windows.Foundation;
using FoxTube.Pages;
using Windows.Networking.Connectivity;
using Microsoft.Toolkit.Uwp.UI.Controls;
using FoxTube.Classes;
using FoxTube.Controls.Common;
using System.Threading.Tasks;
using System.Linq;
namespace FoxTube.Controls
{
/// <summary>
/// Video item card
/// </summary>
public sealed partial class VideoCard : UserControl
public sealed partial class VideoCard : UserControl, ICard
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
public string playlistId;
readonly string playlistId;
readonly string id;
Video item;
HistoryItem history;
Google.Apis.YouTube.v3.Data.Video liveItem;
public VideoCard(string id, string playlist = null)
{
InitializeComponent();
Initialize(id, playlist);
}
public VideoCard(Video meta, string playlist = null)
{
InitializeComponent();
item = meta;
this.id = id;
playlistId = playlist;
LoadMeta();
}
public async void Initialize(string id, string playlist = null)
public async Task Initialize()
{
try
{
playlistId = playlist;
delete.Visibility = string.IsNullOrWhiteSpace(playlistId) ? Visibility.Collapsed : Visibility.Visible;
item = await new YoutubeClient(SecretsVault.HttpClient).GetVideoAsync(id);
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails");
title.Text = item.Title;
channelName.Text = item.Author;
ValidatePlaylist();
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("liveStreamingDetails,contentDetails");
request.Id = id;
request.Hl = SettingsStorage.RelevanceLanguage;
item = (await request.ExecuteAsync()).Items[0];
liveItem = (await request.ExecuteAsync()).Items[0];
title.Text = item.Snippet.Localized.Title;
channelName.Text = item.Snippet.ChannelTitle;
if (item.Snippet.Title == "Deleted video")
if(liveItem.LiveStreamingDetails.ConcurrentViewers != null)
{
ContextFlyout = null;
show.Begin();
return;
}
if (item.Snippet.LiveBroadcastContent == "live")
if (liveItem.LiveStreamingDetails.ActualStartTime.HasValue)
{
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}";
views.Text = $"{liveItem.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue && liveItem.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{liveItem.LiveStreamingDetails.ScheduledEndTime - liveItem.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(liveItem.LiveStreamingDetails.ActualStartTime.Value)}";
else
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
liveTag.Visibility = Visibility.Visible;
download.Visibility = Visibility.Collapsed;
info.Text = Methods.GetAgo(liveItem.LiveStreamingDetails.ActualStartTime.Value);
}
else if (item.Snippet.LiveBroadcastContent == "upcoming")
else
{
views.Text = "";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}";
if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue && liveItem.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{liveItem.LiveStreamingDetails.ScheduledEndTime - liveItem.LiveStreamingDetails.ScheduledStartTime} | {liveItem.LiveStreamingDetails.ScheduledStartTime}";
else
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
liveTag.Visibility = Visibility.Visible;
info.Text = Methods.GetAgo(item.UploadDate.DateTime);
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");
if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue)
liveContent.Text = resources.GetString("/Cards/goesLive") + (liveItem.LiveStreamingDetails.ScheduledStartTime.Value > DateTime.Now ? " " : " -") + (liveItem.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss");
else liveContent.Text = resources.GetString("/Cards/upcoming");
}
liveTag.Visibility = Visibility.Visible;
download.Visibility = Visibility.Collapsed;
}
else
{
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
LoadDownloads();
info.Text = $"{item.Duration} | {item.UploadDate}";
try { new DownloadSelector(download.Items).Initialize(item); }
catch { download.Visibility = Visibility.Collapsed; }
}
LoadAddTo();
try { new AddToPlaylist(addTo.Items).Initialize(id); }
catch { addTo.Visibility = Visibility.Collapsed; }
try
{
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 };
thumbnail.Source = new BitmapImage(item.Thumbnails.MediumResUrl.ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient(SecretsVault.HttpClient).GetVideoAuthorChannelAsync(id)).LogoUrl.ToUri()) { DecodePixelHeight = 46, DecodePixelWidth = 46 };
}
catch { }
if (SecretsVault.History.Contains(item.Id))
if (SecretsVault.History.Videos.Any(i => i.Id == id))
watched.Visibility = Visibility.Visible;
if (HistorySet.Items.Exists(i => i.Id == item.Id))
{
history = HistorySet.Items.Find(i => i.Id == item.Id);
watched.Visibility = Visibility.Visible;
leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds;
leftOn.Value = 100 * history.LeftOn.TotalSeconds / item.Duration.TotalSeconds;
}
show.Begin();
}
catch (Exception e)
{
@@ -133,95 +128,30 @@ namespace FoxTube.Controls
}
}
async void LoadDownloads()
async void ValidatePlaylist()
{
try
{
MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id);
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()
{
title.Text = item.Snippet.Title;
channelName.Text = item.Snippet.ChannelTitle;
if (item.Snippet.Title == "Deleted video")
{
ContextFlyout = null;
show.Begin();
if (string.IsNullOrWhiteSpace(playlistId) || !SecretsVault.IsAuthorized)
return;
}
if (item.Snippet.LiveBroadcastContent == "live")
{
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}";
else
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
liveTag.Visibility = Visibility.Visible;
}
else if (item.Snippet.LiveBroadcastContent == "upcoming")
{
views.Text = "";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}";
else
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
liveTag.Visibility = Visibility.Visible;
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {item.LiveStreamingDetails.ScheduledStartTime - DateTime.Now}";
else liveContent.Text = resources.GetString("/Cards/upcoming");
}
if (playlistId == "WL" || playlistId == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
delete.Visibility = Visibility.Visible;
else
{
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet");
playlistRequest.Id = playlistId;
var response = await playlistRequest.ExecuteAsync();
if (response.Items[0].Snippet.ChannelId == SecretsVault.AccountId)
delete.Visibility = Visibility.Visible;
}
}
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 };
public void ItemClicked() =>
ItemClicked(false);
if (SecretsVault.History.Contains(item.Id))
watched.Visibility = Visibility.Visible;
if (HistorySet.Items.Exists(i => i.Id == item.Id))
leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds;
show.Begin();
}
public async void Button_Click(object sender, RoutedEventArgs e)
async void ItemClicked(bool incognito = false)
{
if (item.ContentDetails.ContentRating != null)
if (liveItem.ContentDetails.ContentRating != null)
{
if (SecretsVault.IsAuthorized)
{
@@ -268,11 +198,11 @@ namespace FoxTube.Controls
{
try
{
PlaylistItem playlistItem = new PlaylistItem()
Google.Apis.YouTube.v3.Data.PlaylistItem playlistItem = new Google.Apis.YouTube.v3.Data.PlaylistItem
{
Snippet = new PlaylistItemSnippet()
Snippet = new Google.Apis.YouTube.v3.Data.PlaylistItemSnippet
{
ResourceId = new ResourceId()
ResourceId = new Google.Apis.YouTube.v3.Data.ResourceId
{
Kind = "youtube#video",
VideoId = item.Id
@@ -290,251 +220,37 @@ namespace FoxTube.Controls
return;
}
Methods.MainPage.GoToVideo(item.Id, playlistId == "HL" ? null : playlistId, ((FrameworkElement)sender).Name == "incognito" ? true : false);
Navigation.GoToVideo(id, playlistId == "HL" ? null : playlistId, incognito);
}
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
{
void Share(DataTransferManager sender, DataRequestedEventArgs args) =>
Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url,
item.Snippet.Title,
$"https://www.youtube.com/watch?v={item.Id}",
item.Thumbnails.MediumResUrl,
item.Title,
item.GetShortUrl(),
resources.GetString("/Cards/videoShare"));
}
private void share_Click(object sender, RoutedEventArgs e)
void share_Click(object sender, RoutedEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
DataTransferManager.ShowShareUI();
}
private void ViewChannel_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
async void ViewChannel_Click(object sender, RoutedEventArgs e) =>
Navigation.GoToChannel((await new YoutubeClient(SecretsVault.HttpClient).GetVideoAuthorChannelAsync(id)).Id);
private void GetLink_Click(object sender, RoutedEventArgs e)
void GetLink_Click(object sender, RoutedEventArgs e)
{
DataPackage data = new DataPackage();
data.SetText($"https://www.youtube.com/watch?v={item.Id}");
data.SetText(item.GetShortUrl());
Clipboard.SetContent(data);
}
private async void InBrowser_Click(object sender, RoutedEventArgs e)
{
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={item.Id}".ToUri());
}
async void InBrowser_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync(item.GetShortUrl().ToUri());
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e)
{
showThumb.Begin();
}
private async void NewPlaylist_Click(object sender, RoutedEventArgs e)
{
StackPanel stack = new StackPanel();
stack.Children.Add(new TextBox
{
PlaceholderText = resources.GetString("/VideoPage/newPlaylistName/PlaceholderText")
});
ComboBox comboBox = new ComboBox
{
Header = resources.GetString("/VideoPage/privacy/Header"),
SelectedIndex = 0,
HorizontalAlignment = HorizontalAlignment.Stretch
};
comboBox.Items.Add(new ComboBoxItem { Content = resources.GetString("/VideoPage/public/Content") });
comboBox.Items.Add(new ComboBoxItem { Content = resources.GetString("/VideoPage/private/Content") });
comboBox.Items.Add(new ComboBoxItem { Content = resources.GetString("/VideoPage/direct/Content") });
stack.Children.Add(comboBox);
ContentDialog playlistDialog = new ContentDialog
{
PrimaryButtonText = resources.GetString("/VideoPage/dialog/PrimaryButtonText"),
CloseButtonText = resources.GetString("/VideoPage/dialog/CloseButtonText"),
DefaultButton = ContentDialogButton.Primary,
Title = resources.GetString("/VideoPage/dialog/Title"),
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 async void Wl_Click(object sender, RoutedEventArgs e)
{
if (wl.IsChecked)
{
try
{
PlaylistItem playlist = new PlaylistItem
{
Snippet = new PlaylistItemSnippet
{
PlaylistId = "WL",
ResourceId = new ResourceId
{
VideoId = item.Id,
Kind = "youtube#video"
}
}
};
PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet");
await request.ExecuteAsync();
}
catch
{
wl.IsChecked = false;
}
}
else
wl.IsChecked = true;
}
async void LoadAddTo()
{
try
{
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);
}
}
catch
{
addTo.Visibility = Visibility.Collapsed;
}
}
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;
}
}
}
void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) =>
show.Begin();
private async void Delete_Click(object sender, RoutedEventArgs e)
{
@@ -550,7 +266,7 @@ namespace FoxTube.Controls
else
{
HistorySet.Delete(history);
(Methods.MainPage.PageContent.Frame.Content as History).Delete(this);
(Navigation.Frame.Frame.Content as History).Delete(this);
}
return;
}
@@ -559,23 +275,15 @@ namespace FoxTube.Controls
PlaylistItemsResource.ListRequest request = SecretsVault.Service.PlaylistItems.List("snippet");
request.PlaylistId = playlistId;
request.VideoId = item.Id;
PlaylistItemListResponse response = await request.ExecuteAsync();
PlaylistItem playlistItem = response.Items.Find(i => i.Snippet.PlaylistId == playlistId);
await SecretsVault.Service.PlaylistItems.Delete(playlistItem.Id).ExecuteAsync();
(Methods.MainPage.PageContent.Frame.Content as PlaylistPage).DeleteItem(this);
}
catch
{
await SecretsVault.Service.PlaylistItems.Delete((await request.ExecuteAsync()).Items[0].Id).ExecuteAsync();
(Navigation.Frame.Frame.Content as PlaylistPage).DeleteItem(this);
}
catch { }
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
private void Button_Click(object sender, RoutedEventArgs e) =>
ItemClicked(((FrameworkElement)sender).Name == "incognito" ? true : false);
}
}
@@ -1,10 +1,10 @@
<UserControl
x:Class="FoxTube.Controls.Chat"
x:Class="FoxTube.Controls.VideoPage.Chat"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:FoxTube.Controls"
xmlns:controls="using:FoxTube.Controls.VideoPage"
mc:Ignorable="d"
d:DesignHeight="600"
d:DesignWidth="400">
@@ -10,10 +10,11 @@ using System.Collections.Generic;
using Windows.UI.Popups;
using Windows.ApplicationModel.Resources;
using Microsoft.Advertising.WinRT.UI;
using Windows.UI.Xaml.Media.Imaging;
using System.Linq;
using FoxTube.Classes;
using FoxTube.Controls.Adverts;
namespace FoxTube.Controls
namespace FoxTube.Controls.VideoPage
{
public class ChatMessage
{
@@ -63,16 +64,12 @@ namespace FoxTube.Controls
private LiveChatMessage message;
public ChatMessage(LiveChatMessage item)
{
public ChatMessage(LiveChatMessage item) =>
message = item;
}
}
public sealed partial class Chat : UserControl
{
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
public NativeAdV2 advert;
string chatId;
DateTime lastInsert;
@@ -81,7 +78,7 @@ namespace FoxTube.Controls
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
Interval = TimeSpan.FromSeconds(3)
};
DispatcherTimer adTimer = new DispatcherTimer
@@ -104,130 +101,8 @@ namespace FoxTube.Controls
if (SecretsVault.AdsDisabled)
return;
adTimer.Tick += (s, e) => manager.RequestAd();
adTimer.Tick += (s, e) => list.Items.Add(new ChatAdvert());
adTimer.Start();
manager.AdReady += AdReady;
manager.ErrorOccurred += ErrorOccurred;
}
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad");
}
private void AdReady(object sender, NativeAdReadyEventArgs e)
{
advert = e.NativeAd;
Grid grid = new Grid
{
Margin = new Thickness(0, 5, 5, 5),
};
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
StackPanel iconStack = new StackPanel
{
Orientation = Orientation.Horizontal,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(5, 0, 5, 0)
};
iconStack.Children.Add(new PersonPicture
{
Height = 20,
ProfilePicture = advert.AdIcon == null ? null : advert.AdIcon.Source
});
FontIcon sponsor = new FontIcon
{
Glyph = "\xE735",
Margin = new Thickness(2, 0, 2, 0)
};
ToolTipService.SetToolTip(sponsor, ResourceLoader.GetForCurrentView("Chat").GetString("/Chat/sponsor/Text"));
iconStack.Children.Add(sponsor);
StackPanel nameStack = new StackPanel
{
Orientation = Orientation.Horizontal,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, 0, 2, 0)
};
Grid.SetColumn(nameStack, 1);
HyperlinkButton sponsorName = new HyperlinkButton
{
Margin = new Thickness(0, -6, 0, 0),
FontWeight = Windows.UI.Text.FontWeights.Bold,
Content = new TextBlock
{
TextTrimming = TextTrimming.CharacterEllipsis,
MaxWidth = 150,
Text = Methods.GuardFromNull(advert.SponsoredBy)
}
};
ToolTipService.SetToolTip(sponsorName, Methods.GuardFromNull(advert.SponsoredBy));
nameStack.Children.Add(sponsorName);
nameStack.Children.Add(new TextBlock { Text = ":" });
StackPanel contentStack = new StackPanel
{
Padding = new Thickness(2, 0, 0, 0)
};
Grid.SetColumn(contentStack, 2);
contentStack.Children.Add(new TextBlock
{
VerticalAlignment = VerticalAlignment.Top,
TextWrapping = TextWrapping.WrapWholeWords,
FontWeight = Windows.UI.Text.FontWeights.Bold,
Text = advert.Title
});
if(!string.IsNullOrWhiteSpace(advert.Description))
contentStack.Children.Add(new TextBlock
{
VerticalAlignment = VerticalAlignment.Top,
TextWrapping = TextWrapping.WrapWholeWords,
Text = advert.Description
});
if (!string.IsNullOrWhiteSpace(advert.CallToActionText))
contentStack.Children.Add(new HyperlinkButton
{
VerticalAlignment = VerticalAlignment.Top,
Content = new TextBlock
{
TextWrapping = TextWrapping.WrapWholeWords,
Text = advert.CallToActionText
}
});
grid.Children.Add(iconStack);
grid.Children.Add(nameStack);
grid.Children.Add(contentStack);
Grid mainGrid = new Grid();
mainGrid.Children.Add(new Border
{
BorderBrush = new SolidColorBrush(Colors.Red),
BorderThickness = new Thickness(2),
CornerRadius = new CornerRadius(5),
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(0, 27, 0, 2),
Background = new SolidColorBrush(Colors.Red) { Opacity = .2 },
Child = grid
});
ListViewItem item = new ListViewItem
{
Content = mainGrid
};
list.Items.Insert(0, item);
if (contentStack.Children.Last() is HyperlinkButton)
advert.RegisterAdContainer(mainGrid, new List<FrameworkElement> { contentStack.Children.Last() as HyperlinkButton });
else
advert.RegisterAdContainer(mainGrid);
}
public async void Update(object sender, object e)
@@ -241,10 +116,8 @@ namespace FoxTube.Controls
timer.Start();
}
private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(((HyperlinkButton)sender).Tag as string);
}
private void HyperlinkButton_Click(object sender, RoutedEventArgs e) =>
Navigation.GoToChannel(((HyperlinkButton)sender).Tag as string);
private async void send_Click(object sender, RoutedEventArgs args)
{
@@ -1,5 +1,5 @@
<Page
x:Class="FoxTube.Pages.CommentsPage"
<UserControl
x:Class="FoxTube.Controls.VideoPage.Comments"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -50,4 +50,4 @@
</StackPanel>
</ScrollViewer>
</Grid>
</Page>
</UserControl>
@@ -9,12 +9,12 @@ using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
namespace FoxTube.Pages
namespace FoxTube.Controls.VideoPage
{
/// <summary>
/// Comments placeholder
/// </summary>
public sealed partial class CommentsPage : Page
public sealed partial class Comments : UserControl
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage");
@@ -24,7 +24,7 @@ namespace FoxTube.Pages
CommentThreadsResource.ListRequest.OrderEnum order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
CommentThreadsResource.ListRequest request;
public CommentsPage()
public Comments()
{
InitializeComponent();
}
@@ -0,0 +1,15 @@
<UserControl
x:Class="FoxTube.Controls.VideoPage.RelatedVideos"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube.Controls.VideoPage"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:FoxTube.Pages"
mc:Ignorable="d"
d:DesignWidth="400">
<ScrollViewer>
<pages:VideoGrid x:Name="list"/>
</ScrollViewer>
</UserControl>
@@ -0,0 +1,35 @@
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Controls.VideoPage
{
public sealed partial class RelatedVideos : UserControl
{
public RelatedVideos() =>
InitializeComponent();
public async void Initialize(string id)
{
list.Clear();
SearchResource.ListRequest request = SecretsVault.Service.Search.List("id");
request.RegionCode = SettingsStorage.Region;
request.RelevanceLanguage = SettingsStorage.RelevanceLanguage;
request.RelatedToVideoId = id;
request.SafeSearch = (SearchResource.ListRequest.SafeSearchEnum)SettingsStorage.SafeSearch;
request.MaxResults = 10;
request.Type = "video";
SearchListResponse response = await request.ExecuteAsync();
foreach (SearchResult video in response.Items)
list.Add(new VideoCard(video.Id.VideoId));
list.Children.Insert(1, new Adverts.CardAdvert());
}
public void OpenNext() =>
(list.Children[0] as VideoCard).ItemClicked();
}
}
@@ -0,0 +1,35 @@
<UserControl
x:Class="FoxTube.Controls.VideoPage.VideoPlaylist"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignWidth="400">
<ScrollViewer x:Name="scroll">
<StackPanel>
<StackPanel Padding="8" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<TextBlock Text="[Playlsit name]" FontSize="26" TextWrapping="WrapWholeWords" Name="playlistName"/>
<TextBlock Text="[Channel name]" Name="playlistChannel"/>
<TextBlock Text="[Counter]" Name="playlistCounter"/>
</StackPanel>
<ListBox Background="Transparent" Name="list">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Number}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<Image Grid.Column="1" Source="{Binding Path=Thumbnail}" Height="65"/>
<TextBlock Grid.Column="2" Margin="8,0,0,0" VerticalAlignment="Center" TextWrapping="WrapWholeWords" Text="{Binding Path=Title}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -0,0 +1,124 @@
using FoxTube.Classes;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.Resources;
using Windows.UI.Xaml.Controls;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace FoxTube.Controls.VideoPage
{
public sealed partial class VideoPlaylist : UserControl
{
public class PlaylistItem
{
public int Number { get; }
public string Id { get; }
public string Title { get; }
public string Thumbnail { get; }
public PlaylistItem(int number, Google.Apis.YouTube.v3.Data.PlaylistItem item)
{
Number = number;
Id = item.Snippet.ResourceId.VideoId;
Title = item.Snippet.Title;
Thumbnail = item.Snippet.Thumbnails.Medium.Url;
}
public PlaylistItem(int number, Video item)
{
Number = number;
Id = item.Id;
Title = item.Snippet.Title;
Thumbnail = item.Snippet.Thumbnails.Medium.Url;
}
}
ResourceLoader resources = ResourceLoader.GetForCurrentView("VideoPage");
public event PlaylistItemChangedEventHandler ItemChanged;
public VideoPlaylist() =>
InitializeComponent();
public ItemCollection Items => list.Items;
public int SelectedIndex => list.SelectedIndex;
public async Task Initialize(Video video, string playlist)
{
if (video.Id == "WL")
{
List<Video> items = new List<Video>();
SecretsVault.WatchLater = await Methods.GetLater();
/*if (SecretsVault.WatchLater.Count > 100)
throw new Exception("Too large playlist");*/
/*foreach (string i in SecretsVault.WatchLater)
{
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet");
request.Id = i;
items.Add((await request.ExecuteAsync()).Items[0]);
}*/
for (int k = 1; k <= items.Count; k++)
list.Items.Add(new PlaylistItem(k, items[k - 1]));
playlistName.Text = resources.GetString("/Main/later/Content");
playlistChannel.Text = SecretsVault.UserChannel.Snippet.Title;
list.SelectedItem = list.Items.Find(i => ((PlaylistItem)i).Id == video.Id);
//playlistCounter.Text = $"{list.SelectedIndex + 1}/{SecretsVault.WatchLater.Count}";
}
else
{
//Retrieving data
PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
playlistRequest.Id = playlist;
playlistRequest.Hl = SettingsStorage.RelevanceLanguage;
Playlist playlistItem = (await playlistRequest.ExecuteAsync()).Items[0];
if (playlistItem.ContentDetails.ItemCount > 100)
throw new Exception("Too large playlist");
PlaylistItemsResource.ListRequest listRequest = SecretsVault.Service.PlaylistItems.List("snippet");
listRequest.MaxResults = 50;
listRequest.PlaylistId = playlist;
PlaylistItemListResponse listResponse;
do
{
listResponse = await listRequest.ExecuteAsync();
listRequest.PageToken = listResponse.NextPageToken;
foreach (Google.Apis.YouTube.v3.Data.PlaylistItem i in listResponse.Items)
list.Items.Add(new PlaylistItem((int)i.Snippet.Position + 1, i));
}
while (!string.IsNullOrWhiteSpace(listRequest.PageToken));
//Setting data
playlistName.Text = playlistItem.Snippet.Localized.Title;
playlistChannel.Text = Methods.GuardFromNull(playlistItem.Snippet.ChannelTitle);
list.SelectedItem = list.Items.Find(i => ((PlaylistItem)i).Id == video.Id);
playlistCounter.Text = $"{list.SelectedIndex + 1}/{playlistItem.ContentDetails.ItemCount}";
}
list.SelectionChanged += ListBox_SelectionChanged;
await Task.Delay(500);
scroll.ChangeView(null, list.SelectedIndex * 86 + 89, null, true);
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
ItemChanged.Invoke((e.AddedItems[0] as PlaylistItem).Id);
public void Next() =>
list.SelectedIndex++;
public void Previous() =>
list.SelectedIndex--;
}
}
+57 -12
View File
@@ -2,6 +2,7 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{2597B816-7316-4D20-BA6C-D78001E89C1A}</ProjectGuid>
@@ -83,6 +84,7 @@
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<LangVersion>default</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
@@ -104,15 +106,27 @@
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Classes\AdaptiveCommandBar.cs" />
<Compile Include="Classes\Extensions.cs" />
<Compile Include="Classes\HistorySet.cs" />
<Compile Include="Classes\InboxItem.cs" />
<Compile Include="Classes\Methods.cs" />
<Compile Include="Classes\Navigation.cs" />
<Compile Include="Classes\Processes.cs" />
<Compile Include="Classes\QualityComparer.cs" />
<Compile Include="Classes\SearchParameters.cs" />
<Compile Include="Classes\SettingsStorage.cs" />
<Compile Include="Classes\ManifestGenerator.cs" />
<Compile Include="Classes\StreamInfo.cs" />
<Compile Include="Controls\Common\AccountManager.xaml.cs">
<DependentUpon>AccountManager.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Common\AddToPlaylist.cs" />
<Compile Include="Controls\Adverts\CardAdvert.xaml.cs">
<DependentUpon>CardAdvert.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Adverts\ChatAdvert.xaml.cs">
<DependentUpon>ChatAdvert.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Adverts\CommentAdvert.xaml.cs">
<DependentUpon>CommentAdvert.xaml</DependentUpon>
</Compile>
@@ -122,7 +136,8 @@
<Compile Include="Controls\ChannelCard.xaml.cs">
<DependentUpon>ChannelCard.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Chat.xaml.cs">
<Compile Include="Controls\Common\DownloadSelector.cs" />
<Compile Include="Controls\VideoPage\Chat.xaml.cs">
<DependentUpon>Chat.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\CommentCard.xaml.cs">
@@ -132,34 +147,44 @@
<Compile Include="Controls\ContentFrame.xaml.cs">
<DependentUpon>ContentFrame.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Common\CreateAndAddPlaylist.xaml.cs">
<DependentUpon>CreateAndAddPlaylist.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\DownloadItem.xaml.cs">
<DependentUpon>DownloadItem.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\LiveCaptions.xaml.cs">
<Compile Include="Controls\Player\LiveCaptions.xaml.cs">
<DependentUpon>LiveCaptions.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Player\PlayerControls.cs" />
<Compile Include="Controls\Player\PlayerMain.cs" />
<Compile Include="Controls\PlaylistCard.xaml.cs">
<DependentUpon>PlaylistCard.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ReportVideo.xaml.cs">
<Compile Include="Controls\VideoPage\ReportVideo.xaml.cs">
<DependentUpon>ReportVideo.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ShowMore.xaml.cs">
<DependentUpon>ShowMore.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\Browser.xaml.cs">
<DependentUpon>Browser.xaml</DependentUpon>
<Compile Include="Controls\VideoPage\RelatedVideos.xaml.cs">
<DependentUpon>RelatedVideos.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\VideoPage\VideoPlaylist.xaml.cs">
<DependentUpon>VideoPlaylist.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ChannelPage.xaml.cs">
<DependentUpon>ChannelPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\CommentsPage.xaml.cs">
<DependentUpon>CommentsPage.xaml</DependentUpon>
<Compile Include="Controls\VideoPage\Comments.xaml.cs">
<DependentUpon>Comments.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\Downloads.xaml.cs">
<DependentUpon>Downloads.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MainFrame.xaml.cs">
<DependentUpon>MainFrame.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\SettingsPages\About.xaml.cs">
<DependentUpon>About.xaml</DependentUpon>
</Compile>
@@ -293,10 +318,18 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Controls\Common\AccountManager.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Adverts\CardAdvert.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Adverts\ChatAdvert.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Adverts\CommentAdvert.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -309,7 +342,7 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Chat.xaml">
<Page Include="Controls\VideoPage\Chat.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -321,11 +354,15 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Common\CreateAndAddPlaylist.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\DownloadItem.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\LiveCaptions.xaml">
<Page Include="Controls\Player\LiveCaptions.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -333,7 +370,7 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ReportVideo.xaml">
<Page Include="Controls\VideoPage\ReportVideo.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -341,7 +378,11 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\Browser.xaml">
<Page Include="Controls\VideoPage\RelatedVideos.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\VideoPage\VideoPlaylist.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -349,7 +390,7 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\CommentsPage.xaml">
<Page Include="Controls\VideoPage\Comments.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -357,6 +398,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainFrame.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\SettingsPages\About.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
-25
View File
@@ -1,25 +0,0 @@
<Page
x:Class="FoxTube.Pages.Home1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<AutoSuggestBox Grid.ColumnSpan="2" Text="https://youtube.com/" QueryIcon="Forward" QuerySubmitted="Adress_QuerySubmitted" Name="adress"/>
<ScrollViewer Grid.Row="1">
<TextBlock TextWrapping="Wrap" Name="code" IsTextSelectionEnabled="True"/>
</ScrollViewer>
<WebView Name="view" Grid.Row="1" Grid.Column="1"/>
</Grid>
</Page>
-49
View File
@@ -1,49 +0,0 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Pages
{
public sealed partial class Home1 : Page
{
public Home1()
{
InitializeComponent();
Methods.MainPage.PageContent.LoadingPage.Close();
}
public async void Initialize()
{
Dictionary<string, string> postContent = new Dictionary<string, string>
{
{ "sej", "{\"clickTrackingParams\":\"COQBEJQ1GAIiEwi028HJkdrhAhWCU5sKHQVmCS0ojh4=\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/service_ajax\",\"sendPost\":true}},\"playlistEditEndpoint\":{\"playlistId\":\"WL\",\"actions\":[{\"action\":\"ACTION_REMOVE_VIDEO_BY_VIDEO_ID\",\"removedVideoId\":\"_j6Os3lIQ4U\"}]}}" },
{ "csn", "2K-4XLSPG4Kn7QSFzKXoAg" },
{ "session_token", "QUFFLUhqbVF6RkxPeWI3Z2s0bGJhMExMcjNmQ3BNRG9LUXxBQ3Jtc0tuR1pwOFJGMVd2ZGplSC1tTlZXd3M5bGxiZmdCR0pDZUEta0QyN3poejZCMlI2T1pfQXFnRU1yYkc4bzk5d0hrRUZ3bWlCbVZtWFl2U0h4UUp6RS1QRmlHVmNmTDVaeExGSF9ORXFCTko4ZEp0VnBlRVJnaWNRN2VQV05GaksyZm9MZTBnLTVRYWxnandGclVpSmZteU9fTjVxNUE=" }
};
FormUrlEncodedContent encoded = new FormUrlEncodedContent(postContent);
string encodedString = await encoded.ReadAsStringAsync();
//TODO: Fuck this shit
Dictionary<string, string> header = new Dictionary<string, string>
{
{ "Cookie", "CONSENT=YES+RU.ru+20180708-12-1; PREF=f6=400&al=ru&f1=50000000; VISITOR_INFO1_LIVE=ZDgD76zHoGM; SID=TwcUCkXNM6kCpxo-8TS_8h1W5Mc7v9InTeaSc2pUhTrhtJYT3370p3EGjMt7V_zIQCsT7w.; HSID=AKgfrRj-NlJVFHs_f; SSID=A8Ix_30UlJTHAZ_wi; APISID=mhnP6tU_JCLaQbsf/AcDBnYqdpqugTs5Uv; SAPISID=Y8OdALP87rADrHbx/Ak4TOmVQHJrSt5k9G; LOGIN_INFO=AFmmF2swRQIhALLDYwBvigvNfzj2iZ5FSy4EL9BhyUqFOuTKSu00jFJfAiAi6ej1ruClPwY6h8yMALu8zplkxU1g9HO8b1gMjdXJdg:QUQ3MjNmd2RFZFNfanB2OHpBVXRTWEs5cUo1Q1NVS0NjTV9ZUG1FQzJuam95S2t4Mm0yazU1ckYxdFN2S3dSTnlRaWZnWXBhYmFTWm1PWlAzVXNXX2dzYWtLUWdJSHgycG5Ed1R4bUJEdG1Hb2ZlR1JtRkI5QnhjYzhCZkNQNDhydWdkWUtjYl9MdVBOM0Y0RDRNNEdHRXZTNllKek9UT1hQMFJIdFpMcnFWNTh2NEtDS0FFdFNJ; SIDCC=AN0-TYv_HcQWprTqyxz1eROShbpRLucFOUPyiHv8XagpVwb_GvBvRAN4MClzKZ5dUj2ptf1NWyE; YSC=6ZnsQM5SW1A; ST-893xe3=itct=COQBEJQ1GAIiEwi028HJkdrhAhWCU5sKHQVmCS0ojh4yCmctaGlnaC1yZWNaD0ZFd2hhdF90b193YXRjaA%3D%3D&csn=2K-4XLSPG4Kn7QSFzKXoAg; ST-1w9u2il=itct=COQBEJQ1GAIiEwi028HJkdrhAhWCU5sKHQVmCS0ojh4%3D&csn=2K-4XLSPG4Kn7QSFzKXoAg" },
};
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://www.youtube.com/service_ajax?name=playlistEditEndpoint");
request.Content = encoded;
//request.Headers.Add("Cookie", "CONSENT=YES+RU.ru+20180708-12-1; PREF=f6=400&al=ru&f1=50000000; VISITOR_INFO1_LIVE=ZDgD76zHoGM; SID=TwcUCkXNM6kCpxo-8TS_8h1W5Mc7v9InTeaSc2pUhTrhtJYT3370p3EGjMt7V_zIQCsT7w.; HSID=AKgfrRj-NlJVFHs_f; SSID=A8Ix_30UlJTHAZ_wi; APISID=mhnP6tU_JCLaQbsf/AcDBnYqdpqugTs5Uv; SAPISID=Y8OdALP87rADrHbx/Ak4TOmVQHJrSt5k9G; LOGIN_INFO=AFmmF2swRQIhALLDYwBvigvNfzj2iZ5FSy4EL9BhyUqFOuTKSu00jFJfAiAi6ej1ruClPwY6h8yMALu8zplkxU1g9HO8b1gMjdXJdg:QUQ3MjNmd2RFZFNfanB2OHpBVXRTWEs5cUo1Q1NVS0NjTV9ZUG1FQzJuam95S2t4Mm0yazU1ckYxdFN2S3dSTnlRaWZnWXBhYmFTWm1PWlAzVXNXX2dzYWtLUWdJSHgycG5Ed1R4bUJEdG1Hb2ZlR1JtRkI5QnhjYzhCZkNQNDhydWdkWUtjYl9MdVBOM0Y0RDRNNEdHRXZTNllKek9UT1hQMFJIdFpMcnFWNTh2NEtDS0FFdFNJ; SIDCC=AN0-TYv_HcQWprTqyxz1eROShbpRLucFOUPyiHv8XagpVwb_GvBvRAN4MClzKZ5dUj2ptf1NWyE; YSC=6ZnsQM5SW1A; ST-893xe3=itct=COQBEJQ1GAIiEwi028HJkdrhAhWCU5sKHQVmCS0ojh4yCmctaGlnaC1yZWNaD0ZFd2hhdF90b193YXRjaA%3D%3D&csn=2K-4XLSPG4Kn7QSFzKXoAg; ST-1w9u2il=itct=COQBEJQ1GAIiEwi028HJkdrhAhWCU5sKHQVmCS0ojh4%3D&csn=2K-4XLSPG4Kn7QSFzKXoAg");
//request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", SecretsVault.Credential.Token.AccessToken);
HttpResponseMessage response = await new HttpClient().SendAsync(request);
Debug.WriteLine("Done");
}
private void Adress_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
Initialize();
}
}
}
+6 -5
View File
@@ -15,6 +15,7 @@ using Windows.UI;
using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
using FoxTube.Classes;
namespace FoxTube.Pages
{
@@ -105,21 +106,21 @@ namespace FoxTube.Pages
ScrollViewer_ViewChanged(this, null);
Methods.MainPage.PageContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Close();
}
catch (System.Net.Http.HttpRequestException)
{
Methods.MainPage.PageContent.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
Navigation.Frame.Frame.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
if(item == null)
{
Methods.MainPage.PageContent.LoadingPage.Error("ChannelNotFound", "Such channel doesn't exist");
Navigation.Frame.Frame.LoadingPage.Error("ChannelNotFound", "Such channel doesn't exist");
return;
}
Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message);
Navigation.Frame.Frame.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Channel loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
@@ -267,7 +268,7 @@ namespace FoxTube.Pages
private void Refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.PageContent.Refresh();
Navigation.Frame.Frame.Refresh();
}
private async void InBrowser_Click(object sender, RoutedEventArgs e)
+4 -5
View File
@@ -1,4 +1,5 @@
using FoxTube.Controls;
using FoxTube.Classes;
using FoxTube.Controls;
using System;
using Windows.System;
using Windows.UI.Xaml;
@@ -24,7 +25,7 @@ namespace FoxTube.Pages
empty.Visibility = list.Children.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
Methods.MainPage.PageContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Close();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
@@ -34,10 +35,8 @@ namespace FoxTube.Pages
DownloadAgent.Page = null;
}
private async void Open_Click(object sender, RoutedEventArgs e)
{
async void Open_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchFolderAsync(DownloadAgent.Downloads);
}
public void Remove(DownloadItem item)
{
-4
View File
@@ -18,7 +18,6 @@
</Grid.RowDefinitions>
<Pivot SelectionChanged="Pivot_SelectionChanged">
<PivotItem x:Uid="/Playlist/appHistory" Header="Application">
<Grid>
<ScrollViewer>
<StackPanel>
<Grid Margin="0,10" BorderBrush="Red" BorderThickness="5" CornerRadius="10" Padding="10">
@@ -39,9 +38,6 @@
<controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/>
</StackPanel>
</ScrollViewer>
<foxtube:LoadingPage x:Name="loading" Visibility="Collapsed" RefreshPage="Refresh_Click"/>
</Grid>
</PivotItem>
<PivotItem x:Uid="/Playlist/webHistory" Header="Website">
<Grid>
+19 -36
View File
@@ -1,4 +1,5 @@
using FoxTube.Controls;
using FoxTube.Classes;
using FoxTube.Controls;
using Microsoft.AppCenter.Analytics;
using System;
using System.Collections.Generic;
@@ -20,10 +21,8 @@ namespace FoxTube.Pages
int page = 1;
int webPage = 0;
public History()
{
public History() =>
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
@@ -31,15 +30,14 @@ namespace FoxTube.Pages
Parameter = e.Parameter;
Initialize();
Methods.MainPage.PageContent.LoadingPage.Close();
}
public async void Initialize()
{
Methods.MainPage.VideoContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Close();
try
{
loading.Refresh();
Navigation.Frame.Frame.LoadingPage.Refresh();
await Dispatcher.RunIdleAsync((command) =>
{
@@ -53,11 +51,11 @@ namespace FoxTube.Pages
clear.Visibility = Visibility.Collapsed;
});
loading.Close();
Navigation.Frame.Frame.LoadingPage.Close();
}
catch (Exception e)
{
loading.Error(e.GetType().ToString(), e.Message);
Navigation.Frame.Frame.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Local history loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
@@ -67,19 +65,13 @@ namespace FoxTube.Pages
}
}
private async void toBrowser_Click(object sender, RoutedEventArgs e)
{
private async void toBrowser_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync("https://www.youtube.com/feed/history".ToUri());
}
private void Refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.PageContent.Refresh();
}
private void Refresh_Click(object sender, RoutedEventArgs e) =>
Navigation.Frame.Refresh();
private async void ShowMore_Clicked()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
private async void ShowMore_Clicked() => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
for (int k = 25 * page++; k < 25 * page && k < HistorySet.Items.Count; k++)
list.Add(new VideoCard(HistorySet.Items[k].Id, "HL"));
@@ -89,7 +81,6 @@ namespace FoxTube.Pages
else
more.Complete();
});
}
private async void Clear_Click(object sender, RoutedEventArgs e)
{
@@ -117,8 +108,6 @@ namespace FoxTube.Pages
}
async void LoadWeb()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
try
{
@@ -126,10 +115,10 @@ namespace FoxTube.Pages
SecretsVault.History = await Methods.GetHistory();
for (int k = 0; k < 25 && k < SecretsVault.History.Count; k++)
websiteList.Add(new VideoCard(SecretsVault.History[k], "HL"));
for (int k = 0; k < 25 && k < SecretsVault.History.Videos.Count; k++)
websiteList.Add(new VideoCard(SecretsVault.History.Videos[k].Id, "HL"));
if (websiteList.Count >= SecretsVault.History.Count)
if (websiteList.Count >= SecretsVault.History.Videos.Count)
websiteMore.Visibility = Visibility.Collapsed;
websiteLoading.Close();
@@ -144,26 +133,20 @@ namespace FoxTube.Pages
{ "StackTrace", e.StackTrace }
});
}
});
}
private async void WebsiteMore_Clicked()
private async void WebsiteMore_Clicked() => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
for (int k = 25 * webPage++; k < 25 * webPage && k < SecretsVault.History.Count; k++)
websiteList.Add(new VideoCard(SecretsVault.History[k], "HL"));
for (int k = 25 * webPage++; k < 25 * webPage && k < SecretsVault.History.Videos.Count; k++)
websiteList.Add(new VideoCard(SecretsVault.History.Videos[k].Id, "HL"));
if (websiteList.Count >= SecretsVault.History.Count)
if (websiteList.Count >= SecretsVault.History.Videos.Count)
websiteMore.Visibility = Visibility.Collapsed;
else
websiteMore.Complete();
});
}
public void Delete (VideoCard item)
{
public void Delete (VideoCard item) =>
list.DeleteItem(item);
}
}
}
+7 -8
View File
@@ -8,6 +8,7 @@ using FoxTube.Controls;
using System.Net.Http;
using System.Text.RegularExpressions;
using Microsoft.AppCenter.Analytics;
using FoxTube.Classes;
namespace FoxTube
{
@@ -42,7 +43,7 @@ namespace FoxTube
private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Methods.MainPage.PageContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Close();
if (pivot.SelectedItem == recommended && !recLoaded)
LoadRecommendations();
@@ -96,7 +97,7 @@ namespace FoxTube
{
trendsLoading.Refresh();
trendsRequest = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails");
trendsRequest = SecretsVault.Service.Videos.List("id");
trendsRequest.MaxResults = 25;
trendsRequest.Chart = VideosResource.ListRequest.ChartEnum.MostPopular;
@@ -108,7 +109,7 @@ namespace FoxTube
trendingMore.Visibility = string.IsNullOrWhiteSpace(response.NextPageToken) ? Visibility.Collapsed : Visibility.Visible;
foreach (Video i in response.Items)
trendGrid.Add(new VideoCard(i));
trendGrid.Add(new VideoCard(i.Id));
trendsLoading.Close();
trendLoaded = true;
@@ -170,8 +171,7 @@ namespace FoxTube
#region More requests
private void Recommended_More()
{
int l = 25 + recGrid.Count;
for (int k = recGrid.Count; k < l && k < homeList.Count; k++)
for (int k = recGrid.Count; k < 25 + recGrid.Count && k < homeList.Count; k++)
recGrid.Add(new VideoCard(homeList[k]));
recommendedMore.Visibility = recGrid.Count >= homeList.Count ? Visibility.Collapsed : Visibility.Visible;
@@ -185,14 +185,13 @@ namespace FoxTube
trendingMore.Visibility = string.IsNullOrWhiteSpace(response.NextPageToken) ? Visibility.Collapsed : Visibility.Visible;
foreach (Video i in response.Items)
trendGrid.Add(new VideoCard(i));
trendGrid.Add(new VideoCard(i.Id));
trendingMore.Complete();
}
private void Subscriptions_More()
{
int l = 25 + subsGrid.Count;
for (int k = subsGrid.Count; k < l && k < subsList.Count; k++)
for (int k = subsGrid.Count; k < 25 + subsGrid.Count && k < subsList.Count; k++)
subsGrid.Add(new VideoCard(subsList[k]));
subscriptionsMore.Visibility = subsGrid.Count >= subsList.Count ? Visibility.Collapsed : Visibility.Visible;
+30 -37
View File
@@ -1,5 +1,6 @@
using System;
using Windows.ApplicationModel.Resources;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@@ -12,15 +13,13 @@ namespace FoxTube
/// </summary>
public sealed partial class LoadingPage : Page
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("LoadingPage");
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("LoadingPage");
public event RoutedEventHandler RefreshPage;
public LoadingState State { get; private set; } = LoadingState.Loadnig;
public LoadingPage()
{
public LoadingPage() =>
InitializeComponent();
}
public void Error(string exceptionId = "Unknown", string details = "N/A", bool isWifiTrouble = false)
{
@@ -47,39 +46,6 @@ namespace FoxTube
State = LoadingState.Error;
}
public void Block()
{
Visibility = Visibility.Visible;
ring.IsActive = false;
trouble.Visibility = Visibility.Collapsed;
wifiTrouble.Visibility = Visibility.Collapsed;
blockIcon.Visibility = Visibility.Visible;
State = LoadingState.Blocked;
}
private async void openWifi_Click(object sender, RoutedEventArgs e)
{
await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings:network"));
}
private async void openTroubleshoot_Click(object sender, RoutedEventArgs e)
{
await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings:troubleshoot"));
}
private async void feedback_Click(object sender, RoutedEventArgs e)
{
await Windows.System.Launcher.LaunchUriAsync(new Uri("feedback-hub:"));
}
private void wifiRefresh_Click(object sender, RoutedEventArgs e)
{
Refresh();
RefreshPage.Invoke(this, null);
}
public void Refresh()
{
Visibility = Visibility.Visible;
@@ -97,5 +63,32 @@ namespace FoxTube
State = LoadingState.Loaded;
}
public void Block()
{
Visibility = Visibility.Visible;
ring.IsActive = false;
trouble.Visibility = Visibility.Collapsed;
wifiTrouble.Visibility = Visibility.Collapsed;
blockIcon.Visibility = Visibility.Visible;
State = LoadingState.Blocked;
}
async void openWifi_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync("ms-settings:network".ToUri());
async void openTroubleshoot_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync("ms-settings:troubleshoot".ToUri());
async void feedback_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync("feedback-hub:".ToUri());
void wifiRefresh_Click(object sender, RoutedEventArgs e)
{
Refresh();
RefreshPage.Invoke(this, null);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
<Page
x:Class="FoxTube.Pages.MainFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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}">
<Grid>
<controls:ContentFrame x:Name="main"/>
<controls:ContentFrame x:Name="video"/>
</Grid>
</Page>
+153
View File
@@ -0,0 +1,153 @@
using FoxTube.Classes;
using FoxTube.Controls;
using System;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Pages
{
public sealed partial class MainFrame : Page
{
public event NavigationEventHanlder Navigated;
public bool CanGoBack => !VideoMinimized || main.Frame.CanGoBack;
public new ContentFrame Frame => main;
public ContentFrame Video => video;
public bool VideoMinimized => video.Width != double.NaN;
public MainFrame() =>
InitializeComponent();
public void NavigateTo(Type pageType, object parameter = null, bool isNavigationPage = false)
{
if (main.Frame.SourcePageType == pageType && parameter == (main.Content as INavigationPage).Parameter)
return;
main.Navigate(pageType, parameter);
if (video.Content != null && !VideoMinimized)
MinimizeVideo();
Methods.MainPage.UpdateView();
if (!isNavigationPage)
Navigated?.Invoke(pageType, parameter);
}
public void Refresh() =>
main.Refresh();
#region Video control methods
public void OpenVideo(string id, string playlistId = null, bool incognito = false)
{
if (VideoMinimized)
MaximizeVideo();
video.Navigate(typeof(VideoPage), (id, playlistId, incognito));
Methods.MainPage.UpdateView();
}
public void MinimizeVideo(bool player = false)
{
if (video.Content == null)
return;
video.Margin = new Thickness(0, 0, 25, 50);
video.HorizontalAlignment = HorizontalAlignment.Right;
video.VerticalAlignment = VerticalAlignment.Bottom;
video.Width = 432;
if (!player)
(video.Content as VideoPage).Minimize();
Methods.MainPage.UpdateView();
}
public void MaximizeVideo(bool player = false)
{
if (video.Content == null)
return;
video.Margin = new Thickness(0);
video.VerticalAlignment = VerticalAlignment.Stretch;
video.HorizontalAlignment = HorizontalAlignment.Stretch;
video.Width = double.NaN;
if (!player)
(video.Content as VideoPage).Maximize();
Methods.MainPage.UpdateView();
}
public void CloseVideo()
{
if (ApplicationView.GetForCurrentView().IsFullScreenMode)
ApplicationView.GetForCurrentView().ExitFullScreenMode();
video.Frame.Content = null;
video.Frame.BackStack.Clear();
if (VideoMinimized)
MaximizeVideo();
GC.Collect();
Methods.MainPage.UpdateView();
}
public void RefreshVideo()
{
if (video.Content == null)
return;
if (VideoMinimized)
MaximizeVideo();
video.Refresh();
Methods.MainPage.UpdateView();
}
#endregion
public void AuthorizationChanged(bool? isAuthorized)
{
switch (isAuthorized)
{
case true:
if (main.Content == null)
NavigateTo(typeof(Home));
else if (main.Frame.CurrentSourcePageType != typeof(Downloads) && main.Frame.CurrentSourcePageType != typeof(Settings))
main.Refresh();
break;
case false:
NavigateTo(typeof(Home));
main.Frame.BackStack.Clear();
video.Frame.BackStack.Clear();
break;
}
RefreshVideo();
}
public void BackRequested()
{
if (video.Content != null && !VideoMinimized)
{
if (video.Frame.CanGoBack)
video.Frame.GoBack();
else if (video.LoadingPage.State != LoadingState.Loaded)
CloseVideo();
else
MinimizeVideo();
}
else if (main.Frame.CanGoBack)
main.Frame.GoBack();
}
}
}
+17 -37
View File
@@ -6,7 +6,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:ui="using:Microsoft.UI.Xaml.Controls"
xmlns:controls="using:FoxTube.Controls">
xmlns:controls="using:FoxTube.Controls"
xmlns:data="using:Google.Apis.YouTube.v3.Data"
xmlns:foxtube="using:FoxTube.Controls.Player"
xmlns:common="using:FoxTube.Controls.Common"
xmlns:pages="using:FoxTube.Pages">
<Grid Name="grid">
<VisualStateManager.VisualStateGroups>
@@ -35,43 +39,23 @@
Style="{StaticResource CaptionTextBlockStyle}" />
</Border>
<ui:NavigationView SelectedItem="toHome" BackRequested="Nav_BackRequested" PaneClosing="Nav_PaneClosing" PaneOpened="Nav_PaneOpened" OpenPaneLength="300" Name="nav" SelectionChanged="Nav_SelectionChanged">
<ui:NavigationView Name="nav" SelectionChanged="Nav_SelectionChanged"
PaneClosing="Nav_PaneClosing" PaneOpened="Nav_PaneOpened"
BackRequested="Nav_BackRequested"
IsBackEnabled="{x:Bind content.CanGoBack}">
<ui:NavigationView.Header>
<Grid>
<TextBlock Name="Title" Style="{StaticResource TitleTextBlockStyle}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Uid="/Main/feedback" Name="feedback" Click="Feedback_Click" Visibility="Collapsed" FontFamily="Segoe MDL2 Assets" Content="&#xED15;" Background="Transparent" Height="41" Width="60" FontSize="15"/>
<Button x:Uid="/Main/signIn" Name="account" Click="SignIn_Click" Visibility="Collapsed" FontFamily="Segoe MDL2 Assets" Content="&#xE8FA;" Background="Transparent" Height="41" Width="60" FontSize="15"/>
<Button Background="Transparent" Visibility="Collapsed" Name="avatar" Height="41" Width="60" FontSize="15" Padding="0">
<Button.Flyout>
<Flyout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<PersonPicture Width="65" Name="avatarFlyout" VerticalAlignment="Top"/>
<StackPanel Grid.Column="1" Margin="5">
<TextBlock Name="myNameFlyout"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Name="myEmail"/>
<HyperlinkButton x:Uid="/Main/signOut" Content="Log out" Click="Logout_Click"/>
</StackPanel>
</Grid>
</Flyout>
</Button.Flyout>
<Ellipse Width="25" Height="25">
<Ellipse.Fill>
<ImageBrush ImageSource="/Assets/Icons/profile.png"/>
</Ellipse.Fill>
</Ellipse>
</Button>
<common:AccountManager x:Name="manager"/>
</StackPanel>
</Grid>
</ui:NavigationView.Header>
<ui:NavigationView.MenuItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Padding="5" Margin="-5,0,0,0" Tag="{Binding Snippet.ResourceId.ChannelId}">
<DataTemplate x:DataType="data:Subscription">
<StackPanel Orientation="Horizontal" Padding="5" Margin="-5,0,0,0">
<PersonPicture Height="20" Margin="-5,0,15,0">
<PersonPicture.ProfilePicture>
<BitmapImage UriSource="{Binding Snippet.Thumbnails.Medium.Url}" DecodePixelHeight="20" DecodePixelWidth="20"/>
@@ -99,21 +83,17 @@
</ui:NavigationView.MenuItems>
<ui:NavigationView.PaneFooter>
<ui:NavigationViewItem Content="Remove ads" Visibility="Collapsed" Tapped="RemoveAds_Tapped" Name="removeAds">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE14D;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewList>
<ui:NavigationViewItemSeparator/>
<ui:NavigationViewItem Content="Remove ads" Visibility="Collapsed" Tapped="RemoveAds_Tapped" Name="removeAds" Icon="Shop"/>
</ui:NavigationViewList>
</ui:NavigationView.PaneFooter>
<ui:NavigationView.AutoSuggestBox>
<AutoSuggestBox x:Name="search" QueryIcon="Find" QuerySubmitted="Search_QuerySubmitted" TextChanged="Search_TextChanged" x:Uid="/Main/searchPlaceholder" PlaceholderText="Search"/>
</ui:NavigationView.AutoSuggestBox>
<Grid>
<controls:ContentFrame x:Name="content" Navigated="Content_Navigated"/>
<controls:ContentFrame x:Name="videoPlaceholder"/>
</Grid>
<pages:MainFrame x:Name="content" Navigated="Content_Navigated"/>
</ui:NavigationView>
</Grid>
+131 -449
View File
@@ -1,27 +1,19 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Media.Imaging;
using Google.Apis.YouTube.v3.Data;
using Windows.ApplicationModel.Core;
using Windows.System;
using FoxTube.Pages;
using Windows.UI.Popups;
using Windows.ApplicationModel.Resources;
using Microsoft.Services.Store.Engagement;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Media;
using FoxTube.Controls;
using Microsoft.AppCenter.Analytics;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Notifications;
using System.Xml;
using System.Linq;
using FoxTube.Classes;
namespace FoxTube
{
@@ -30,12 +22,10 @@ namespace FoxTube
/// </summary>
public sealed partial class MainPage : Page
{
bool wasInvoked = false;
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
Dictionary<Type, string> headers;
readonly Dictionary<Type, string> headers;
public ContentFrame PageContent => content;
public ContentFrame VideoContent => videoPlaceholder;
public new MainFrame Content => content;
public MainPage()
{
@@ -70,129 +60,21 @@ namespace FoxTube
}
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/purchaseSuccess"));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/close"), (command) => Methods.CloseApp()));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/close"), (command) => CoreApplication.Exit()));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/delay")));
dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0;
await dialog.ShowAsync();
};
SecretsVault.Initialize();
if (SecretsVault.IsAuthorized)
AuthorizationStateChanged(true);
if (!SecretsVault.AdsDisabled)
SecretsVault.CheckAddons();
if(StoreServicesFeedbackLauncher.IsSupported())
feedback.Visibility = Visibility.Visible;
if(SettingsStorage.ProcessClipboard)
{
Clipboard.ContentChanged += ParseClipboard;
ParseClipboard(this);
}
PromptFeedback();
}
async void ParseClipboard(object sender = null, object e = null)
{
if (sender == null && Window.Current.CoreWindow.ActivationMode != Windows.UI.Core.CoreWindowActivationMode.Deactivated)
return;
try
{
string link = await Clipboard.GetContent().GetTextAsync();
if (!link.Contains("youtube") && !link.Contains("youtu.be"))
return;
string id;
string type;
string name;
if (YoutubeExplode.YoutubeClient.TryParseChannelId(link, out id))
{
type = "channel";
name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
goto Complete;
}
else if (YoutubeExplode.YoutubeClient.TryParsePlaylistId(link, out id))
{
type = "playlist";
name = (await new YoutubeExplode.YoutubeClient().GetPlaylistAsync(id)).Title;
goto Complete;
}
else if (YoutubeExplode.YoutubeClient.TryParseUsername(link, out id))
{
id = await new YoutubeExplode.YoutubeClient().GetChannelIdAsync(id);
type = "channel";
name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
goto Complete;
}
else if (YoutubeExplode.YoutubeClient.TryParseVideoId(link, out id))
{
type = "video";
name = (await new YoutubeExplode.YoutubeClient().GetVideoAsync(id)).Title;
goto Complete;
}
return;
Complete:
Windows.Data.Xml.Dom.XmlDocument toastXml = new Windows.Data.Xml.Dom.XmlDocument();
toastXml.LoadXml($@"<toast launch='clipboard|{type}|{id}'>
<visual>
<binding template='ToastGeneric'>
<text>{resources.GetString("/Main/clipboardHead")}</text>
<text>{name}</text>
<text>{resources.GetString($"/Main/{type}")}</text>
</binding>
</visual>
<actions>
<action content='{resources.GetString("/Main/clipboardOpen")}' arguments='clipboard|{type}|{id}'/>
</actions>
</toast>");
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
return;
}
catch { }
}
async void PromptFeedback()
{
if (SettingsStorage.Uptime.TotalHours >= 12 && SettingsStorage.PromptFeedback)
{
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/feedbackMessage"));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptFeedback = false));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater")));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) =>
{
SettingsStorage.PromptFeedback = false;
if (StoreServicesFeedbackLauncher.IsSupported())
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
else
{
MessageDialog message = new MessageDialog(resources.GetString("/Main/feedbackFail"));
message.Commands.Add(new UICommand(resources.GetString("/Main/sendEmail"), async (c) => await Launcher.LaunchUriAsync("mailto:michael.xfox@outlook.com".ToUri())));
message.Commands.Add(new UICommand(resources.GetString("/Main/goBack")));
message.CancelCommandIndex = 1;
message.DefaultCommandIndex = 0;
await message.ShowAsync();
}
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
if (SettingsStorage.Uptime.TotalHours >= 24 && SettingsStorage.PromptReview)
{
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/rate"));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptReview = false));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater")));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) =>
{
SettingsStorage.PromptReview = false;
await Launcher.LaunchUriAsync("ms-windows-store://review/?ProductId=9NCQQXJTDLFH".ToUri());
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
}
public void SetTitleBar(CoreApplicationViewTitleBar coreTitleBar = null)
@@ -212,49 +94,38 @@ namespace FoxTube
titleBar.ButtonPressedBackgroundColor = Colors.DarkRed;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveForegroundColor = Colors.Gray;
if(RequestedTheme == ElementTheme.Dark || (RequestedTheme == ElementTheme.Default && Application.Current.RequestedTheme == ApplicationTheme.Dark))
titleBar.ButtonForegroundColor = Colors.White;
else
titleBar.ButtonForegroundColor = Colors.Black;
titleBar.ButtonForegroundColor = Application.Current.RequestedTheme == ApplicationTheme.Dark ? Colors.Black : Colors.White;
}
private void SecretsVault_SubscriptionsChanged(object sender, params object[] args)
void SecretsVault_SubscriptionsChanged(string action, Subscription subscription)
{
switch((string)args[0])
switch(action)
{
case "add":
subsHeader.Visibility = Visibility.Visible;
if (nav.MenuItems.Count < 19)
nav.MenuItems.Add(args[1] as Subscription);
nav.MenuItems.Add(subscription);
break;
case "remove":
if(nav.MenuItems.Contains((Subscription)args[1]))
if(nav.MenuItems.Contains(subscription))
{
nav.MenuItems.Remove(args[1]);
nav.MenuItems.Remove(subscription);
if (SecretsVault.Subscriptions.Count >= 10)
nav.MenuItems.Add(SecretsVault.Subscriptions[9]);
else if (SecretsVault.Subscriptions.Count == 0)
subsHeader.Visibility = Visibility.Collapsed;
}
break;
}
}
private async void AuthorizationStateChanged(object sender, params object[] e)
async void AuthorizationStateChanged(bool? isAuthozied)
{
wasInvoked = false;
switch (e[0] as bool?)
switch (isAuthozied)
{
case true:
account.Visibility = Visibility.Collapsed;
ToolTipService.SetToolTip(avatar, $"{SecretsVault.UserInfo.Name} ({SecretsVault.UserInfo.Email})");
myNameFlyout.Text = SecretsVault.UserInfo.Name;
myEmail.Text = SecretsVault.UserInfo.Email;
avatarFlyout.ProfilePicture = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 65, DecodePixelWidth = 65 };
((avatar.Content as Ellipse).Fill as ImageBrush).ImageSource = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 25, DecodePixelWidth = 25 };
avatar.Visibility = Visibility.Visible;
manager.Logged();
toChannel.Visibility = Visibility.Visible;
toSubscriptions.Visibility = Visibility.Visible;
@@ -270,21 +141,10 @@ namespace FoxTube
nav.MenuItems.Add(SecretsVault.Subscriptions[k]);
}
HistorySet.Load();
if (content.Frame.Content != null)
content.Refresh();
else
content.Frame.Navigate(typeof(Home));
break;
case false:
content.Frame.Navigate(typeof(Home));
for (int k = nav.MenuItems.Count - 1; k > 8; k--)
nav.MenuItems.RemoveAt(k);
account.Visibility = Visibility.Visible;
avatar.Visibility = Visibility.Collapsed;
manager.Quit();
toChannel.Visibility = Visibility.Collapsed;
toSubscriptions.Visibility = Visibility.Collapsed;
@@ -296,8 +156,9 @@ namespace FoxTube
subsHeader.Visibility = Visibility.Collapsed;
content.Frame.BackStack.Clear();
content.Frame.ForwardStack.Clear();
for (int k = nav.MenuItems.Count - 1; k > 8; k--)
nav.MenuItems.RemoveAt(k);
HistorySet.Items.Clear();
break;
default:
@@ -305,14 +166,8 @@ namespace FoxTube
if (StoreServicesFeedbackLauncher.IsSupported())
dialog.Commands.Add(new UICommand(resources.GetString("/About/feedback/Content"), async (command) => await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync()));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/tryAgain"), (command) =>
{
SecretsVault.Authorize();
}));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/quit"), (command) =>
{
Methods.CloseApp();
}));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/tryAgain"), (command) => SecretsVault.Authorize()));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/quit"), (command) => CoreApplication.Exit()));
dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0;
@@ -321,114 +176,18 @@ namespace FoxTube
break;
}
if (videoPlaceholder.Frame.Content != null)
{
MaximizeVideo();
videoPlaceholder.Refresh();
content.AuthorizationChanged(isAuthozied);
}
DownloadAgent.Initialize();
}
private async void Feedback_Click(object sender, RoutedEventArgs e)
public void UpdateView()
{
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
}
private void SignIn_Click(object sender, RoutedEventArgs e)
{
SecretsVault.Authorize();
Analytics.TrackEvent("Initialized authorization sequence");
}
private void Logout_Click(object sender, RoutedEventArgs e)
{
avatar.Flyout.Hide();
SecretsVault.Deauthenticate();
}
public void GoToSearch(SearchParameters args)
{
content.Frame.Navigate(typeof(Search), new object[] { args, content });
}
public void GoToChannel(string id)
{
content.Frame.Navigate(typeof(ChannelPage), id);
}
public void GoToHome()
{
content.Frame.Navigate(typeof(Home));
}
public void GoToVideo(string id, string playlistId = null, bool incognito = false)
{
MaximizeVideo();
nav.IsBackEnabled = true;
nav.ExpandedModeThresholdWidth = short.MaxValue;
nav.IsPaneOpen = false;
videoPlaceholder.Frame.Navigate(typeof(VideoPage), new object[3] { id, playlistId, incognito });
if(content.Video.Content != null && !content.VideoMinimized)
Title.Text = resources.GetString("/Main/video");
}
public void GoToDeveloper(string id)
{
content.Frame.Navigate(typeof(Settings), id);
}
public void GoToPlaylist(string id)
{
content.Frame.Navigate(typeof(PlaylistPage), id);
}
public void GoToHistory()
{
content.Frame.Navigate(typeof(History));
}
public void GoToDownloads()
{
content.Frame.Navigate(typeof(Downloads));
}
public void MinimizeAsInitializer()
{
if (videoPlaceholder.Frame.Content == null)
return;
if (videoPlaceholder.LoadingPage.State != LoadingState.Loaded)
CloseVideo();
else
(videoPlaceholder.Frame.Content as VideoPage).Player.Minimize();
Title.Text = headers[content.Frame.Frame.SourcePageType];
nav.IsBackEnabled = content.CanGoBack;
Title.Text = headers[content.Frame.SourcePageType];
}
public void MinimizeVideo()
{
videoPlaceholder.Width = 432;
videoPlaceholder.Height = 432 * (videoPlaceholder.Frame.Content as VideoPage).Player.ActualHeight / (videoPlaceholder.Frame.Content as VideoPage).Player.ActualWidth;
videoPlaceholder.VerticalAlignment = VerticalAlignment.Bottom;
videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Right;
videoPlaceholder.Margin = new Thickness(0, 0, 25, 50);
if (content.Frame.CanGoBack)
nav.IsBackEnabled = true;
else
nav.IsBackEnabled = false;
SetNavigationMenu();
Title.Text = headers[content.Frame.SourcePageType];
}
void SetNavigationMenu()
{
if (content.Frame.SourcePageType == typeof(Home) || content.Frame.SourcePageType == typeof(Settings) || content.Frame.SourcePageType == typeof(Subscriptions))
if (new[] { typeof(Home), typeof(Settings), typeof(Subscriptions) }.Contains(content.Frame.Frame.SourcePageType))
{
nav.ExpandedModeThresholdWidth = 1008;
nav.IsPaneOpen = nav.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded ? true : false;
@@ -440,199 +199,122 @@ namespace FoxTube
}
}
public void MaximizeVideo()
object GetChannelMenuItem(object parameter)
{
videoPlaceholder.Width = double.NaN;
videoPlaceholder.Height = double.NaN;
videoPlaceholder.VerticalAlignment = VerticalAlignment.Stretch;
videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Stretch;
videoPlaceholder.Margin = new Thickness(0);
if (!SecretsVault.IsAuthorized)
return null;
if (videoPlaceholder.Frame.Content == null)
if (parameter.ToString() == SecretsVault.AccountId)
return toChannel;
else
return SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == parameter.ToString());
}
object GetPlaylistMenuItem(object parameter)
{
if (!SecretsVault.IsAuthorized)
return null;
if (parameter.ToString() == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
return toLiked;
else if (parameter.Equals("WL"))
return toLater;
else
return null;
}
void Content_Navigated(Type sourcePageType, object parameter)
{
switch (sourcePageType.Name)
{
case "Settings":
nav.SelectedItem = nav.SettingsItem;
break;
case "Subscriptions":
nav.SelectedItem = toSubscriptions;
break;
case "Downloads":
nav.SelectedItem = toDownloads;
break;
case "Home":
nav.SelectedItem = toHome;
break;
case "History":
nav.SelectedItem = toHistory;
break;
case "ChannelPage":
nav.SelectedItem = GetChannelMenuItem(parameter);
break;
case "PlaylistPage":
nav.SelectedItem = GetPlaylistMenuItem(parameter);
break;
default:
nav.SelectedItem = null;
break;
}
}
void Nav_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
{
if (args.IsSettingsSelected)
content.NavigateTo(typeof(Settings));
else if (args.SelectedItem == toHome)
content.NavigateTo(typeof(Home));
else if (args.SelectedItem == toHistory)
content.NavigateTo(typeof(History));
else if (args.SelectedItem == toLiked)
content.NavigateTo(typeof(PlaylistPage), SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes);
else if (args.SelectedItem == toLater)
content.NavigateTo(typeof(PlaylistPage), "WL");
else if (args.SelectedItem == toSubscriptions)
content.NavigateTo(typeof(Subscriptions));
else if (args.SelectedItem == toDownloads)
content.NavigateTo(typeof(Downloads));
else if (args.SelectedItem == toChannel)
content.NavigateTo(typeof(ChannelPage), SecretsVault.UserChannel.Id);
else
content.NavigateTo(typeof(ChannelPage), (args.SelectedItem as Subscription).Snippet.ResourceId.ChannelId);
}
void Nav_BackRequested(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewBackRequestedEventArgs args) =>
content.BackRequested();
async void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
if (search.Text.Length < 3 || args.Reason != AutoSuggestionBoxTextChangeReason.UserInput)
return;
nav.IsBackEnabled = true;
Title.Text = resources.GetString("/Main/video");
nav.ExpandedModeThresholdWidth = short.MaxValue;
nav.IsPaneOpen = false;
}
public void CloseVideo()
{
if (ApplicationView.GetForCurrentView().IsFullScreenMode)
ApplicationView.GetForCurrentView().ExitFullScreenMode();
videoPlaceholder.Frame.Content = null;
GC.Collect();
MaximizeVideo();
nav.IsBackEnabled = content.Frame.CanGoBack;
SetNavigationMenu();
Title.Text = headers[content.Frame.SourcePageType];
}
private void Search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if(!string.IsNullOrWhiteSpace(search.Text))
GoToSearch(new SearchParameters(search.Text));
}
private async void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
if (search.Text.Length > 2 && args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
try
{
XmlDocument doc = new XmlDocument();
doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}&hl={SettingsStorage.RelevanceLanguage}");
List<string> suggestions = new List<string>();
search.Items.Clear();
for (int i = 0; i < doc["toplevel"].ChildNodes.Count && i < 5; i++)
suggestions.Add(doc["toplevel"].ChildNodes[i]["suggestion"].GetAttribute("data"));
search.ItemsSource = suggestions;
}
catch { search.ItemsSource = new List<string>(); }
}
});
}
void SetNavigationItem(object item)
{
try
{
if (nav.SelectedItem != item)
nav.SelectedItem = item;
else
wasInvoked = false;
search.Items.Add(doc["toplevel"].ChildNodes[i]["suggestion"].GetAttribute("data"));
}
catch { }
}
});
public void Content_Navigated(object sender, NavigationEventArgs e)
{
Title.Text = headers[content.Frame.SourcePageType];
#region Simple UI interaction events
async void Feedback_Click(object sender, RoutedEventArgs e) =>
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
if (!wasInvoked)
{
wasInvoked = true;
if (e.SourcePageType == typeof(Settings))
SetNavigationItem(nav.SettingsItem);
else if (e.SourcePageType == typeof(Subscriptions))
SetNavigationItem(toSubscriptions);
else if (e.SourcePageType == typeof(Downloads))
SetNavigationItem(toDownloads);
else if (e.SourcePageType == typeof(Home))
SetNavigationItem(toHome);
else if (e.SourcePageType == typeof(Search))
SetNavigationItem(null);
else if(e.SourcePageType == typeof(ChannelPage))
{
if (SecretsVault.IsAuthorized)
{
if (e.Parameter.ToString() == SecretsVault.AccountId)
SetNavigationItem(toChannel);
else if (nav.MenuItems.Contains(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString())))
SetNavigationItem(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString()));
else
SetNavigationItem(null);
}
else
SetNavigationItem(null);
}
else if(e.SourcePageType == typeof(History))
SetNavigationItem(toHistory);
else if(e.SourcePageType == typeof(PlaylistPage))
{
if (SecretsVault.IsAuthorized)
{
if (e.Parameter.ToString() == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
SetNavigationItem(toLiked);
else if (e.Parameter.Equals("WL"))
SetNavigationItem(toLater);
}
else
SetNavigationItem(null);
}
}
else
wasInvoked = false;
nav.IsBackEnabled = content.Frame.CanGoBack;
SetNavigationMenu();
if (videoPlaceholder.Frame.Content != null && videoPlaceholder.HorizontalAlignment == HorizontalAlignment.Stretch)
MinimizeAsInitializer();
}
private void RemoveAds_Tapped(object sender, TappedRoutedEventArgs e)
{
void RemoveAds_Tapped(object sender, TappedRoutedEventArgs e) =>
SecretsVault.GetAdblock();
}
private void Nav_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
{
if (!wasInvoked)
{
if (content == null)
return;
wasInvoked = true;
if (args.IsSettingsSelected)
content.Frame.Navigate(typeof(Settings));
else
{
if (args.SelectedItem == toHome)
content.Frame.Navigate(typeof(Home));
else if (args.SelectedItem == toHistory)
content.Frame.Navigate(typeof(History));
else if (args.SelectedItem == toLiked)
content.Frame.Navigate(typeof(PlaylistPage), SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes);
else if (args.SelectedItem == toLater)
content.Frame.Navigate(typeof(PlaylistPage), "WL");
else if (args.SelectedItem == toSubscriptions)
content.Frame.Navigate(typeof(Subscriptions));
else if (args.SelectedItem == toDownloads)
content.Frame.Navigate(typeof(Downloads));
else if (args.SelectedItem == toChannel)
content.Frame.Navigate(typeof(ChannelPage), SecretsVault.UserChannel.Id);
else
content.Frame.Navigate(typeof(ChannelPage), (args.SelectedItem as Subscription).Snippet.ResourceId.ChannelId);
}
}
else
wasInvoked = false;
}
private void Nav_BackRequested(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewBackRequestedEventArgs args)
{
if (videoPlaceholder.Frame.Content != null && double.IsNaN(videoPlaceholder.Width))
{
if (videoPlaceholder.Frame.CanGoBack)
videoPlaceholder.Frame.GoBack();
else if (videoPlaceholder.LoadingPage.State != LoadingState.Loaded)
CloseVideo();
else
MinimizeAsInitializer();
}
else
content.Frame.GoBack();
}
private void Nav_PaneClosing(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewPaneClosingEventArgs args)
{
void Nav_PaneClosing(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewPaneClosingEventArgs args) =>
AppTitle.Visibility = Visibility.Collapsed;
}
private void Nav_PaneOpened(Microsoft.UI.Xaml.Controls.NavigationView sender, object args)
{
void Nav_PaneOpened(Microsoft.UI.Xaml.Controls.NavigationView sender, object args) =>
AppTitle.Visibility = Visibility.Visible;
void Search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (!string.IsNullOrWhiteSpace(search.Text))
Navigation.GoToSearch(new SearchParameters(search.Text));
}
#endregion
}
}
+2 -9
View File
@@ -40,12 +40,12 @@
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="10" Grid.Column="1" x:Name="cover" HorizontalAlignment="Left">
<StackPanel Margin="10" Grid.Column="1" x:Name="cover" HorizontalAlignment="Left">
<Image Source="/Assets/videoThumbSample.png" Name="thumbnail"/>
<TextBlock FontSize="20" Text="[Title]" TextWrapping="WrapWholeWords" Name="title"/>
<TextBlock Foreground="Gray" Text="# videos | # views | Updated at: ##-##-## ##:##:##" TextWrapping="WrapWholeWords" Name="info"/>
<TextBlock Foreground="Gray" Text="description" TextWrapping="WrapWholeWords" Name="description"/>
<Button Margin="10" Background="Transparent" Name="toChannel" Click="toChannel_Click">
<Button Margin="0,10" Background="Transparent" Name="toChannel" Click="toChannel_Click">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@@ -79,13 +79,6 @@
<classes:AdaptiveCommandBar Grid.Row="2">
<AppBarButton x:Uid="/Playlist/openWeb" Icon="Globe" Label="Open in browser" Name="inBrowser" Click="inBrowser_Click"/>
<AppBarButton x:Uid="/Playlist/addTo" Icon="Add" Label="Add to" IsEnabled="False" Visibility="Collapsed">
<AppBarButton.Flyout>
<MenuFlyout>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarButton x:Uid="/Playlist/refresh" Icon="Refresh" Label="Refresh" Name="refresh" Click="refresh_Click"/>
<AppBarButton x:Uid="/Playlist/share" Icon="Share" Label="Share" Name="share" Click="share_Click"/>
</classes:AdaptiveCommandBar>
+52 -128
View File
@@ -1,10 +1,8 @@
using FoxTube.Controls;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using FoxTube.Classes;
using FoxTube.Controls;
using Microsoft.AppCenter.Analytics;
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
@@ -13,6 +11,8 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using YoutubeExplode.Models;
using YoutubeExplode;
namespace FoxTube.Pages
{
@@ -22,196 +22,120 @@ namespace FoxTube.Pages
public sealed partial class PlaylistPage : Page, INavigationPage
{
public object Parameter { get; set; } = null;
public string playlistId;
string authorId;
Playlist item;
PlaylistItemsResource.ListRequest request;
string token;
int page = 1;
public PlaylistPage()
{
public PlaylistPage() =>
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Parameter = e.Parameter;
if (e.Parameter as string == "WL")
LoadWL();
else
Initialize(e.Parameter as string);
}
public async void Initialize(string id)
async void Initialize(string id)
{
try
{
playlistId = id;
SecretsVault.RefreshToken();
item = await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync(id);
PlaylistsResource.ListRequest infoRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
if (id == "WL")
{
SecretsVault.WatchLater = item;
share.Visibility = Visibility.Collapsed;
wlAlert.Visibility = Visibility.Visible;
}
title.Text = item.Title;
info.Text = $"{item.Videos.Count} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")} | {item.Statistics.ViewCount} {ResourceLoader.GetForCurrentView("Cards").GetString("/Cards/views")}";
description.Text = item.Description;
channelName.Text = item.Author;
Google.Apis.YouTube.v3.PlaylistsResource.ListRequest infoRequest = SecretsVault.Service.Playlists.List("snippet");
infoRequest.Id = id;
infoRequest.Hl = SettingsStorage.RelevanceLanguage;
item = (await infoRequest.ExecuteAsync()).Items[0];
title.Text = item.Snippet.Localized.Title;
info.Text = $"{item.ContentDetails.ItemCount} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}";
description.Text = item.Snippet.Localized.Description;
channelName.Text = Methods.GuardFromNull(item.Snippet.ChannelTitle);
authorId = (await infoRequest.ExecuteAsync()).Items[0].Snippet.ChannelId;
try
{
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 };
thumbnail.Source = new BitmapImage((item.Videos.Count > 0 ? item.Videos[0].Thumbnails.MediumResUrl : "/Assets/videoThumbSample.png").ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient(SecretsVault.HttpClient).GetChannelAsync(authorId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 };
}
catch { }
request = SecretsVault.Service.PlaylistItems.List("contentDetails");
request.PlaylistId = id;
request.MaxResults = 25;
for (int k = 0; k < 25 && k < item.Videos.Count; k++)
list.Add(new VideoCard(item.Videos[k].Id, "WL"));
PlaylistItemListResponse response = await request.ExecuteAsync();
token = response.NextPageToken;
if (string.IsNullOrWhiteSpace(token))
if (list.Count >= item.Videos.Count)
more.Visibility = Visibility.Collapsed;
foreach (PlaylistItem i in response.Items)
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
Methods.MainPage.PageContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Close();
}
catch (System.Net.Http.HttpRequestException)
{
Methods.MainPage.PageContent.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Playlist loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Playlist ID", playlistId },
{ "StackTrace", e.StackTrace }
});
}
}
public async void LoadWL()
{
try
{
playlistId = "WL";
share.Visibility = Visibility.Collapsed;
wlAlert.Visibility = Visibility.Visible;
SecretsVault.WatchLater = await Methods.GetLater();
title.Text = ResourceLoader.GetForCurrentView("Main").GetString("/Main/later/Content");
info.Text = $"{SecretsVault.WatchLater.Count} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}";
description.Text = "";
channelName.Text = SecretsVault.UserChannel.Snippet.Title;
avatar.ProfilePicture = new BitmapImage(SecretsVault.UserChannel.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 };
thumbnail.Source = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetVideoAsync(SecretsVault.WatchLater.First())).Thumbnails.HighResUrl.ToUri());
for (int k = 0; k < 25 && k < SecretsVault.WatchLater.Count; k++)
list.Add(new VideoCard(SecretsVault.WatchLater[k], "WL"));
if (list.Count >= SecretsVault.WatchLater.Count)
more.Visibility = Visibility.Collapsed;
Methods.MainPage.PageContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
if (item == null)
{
Methods.MainPage.PageContent.LoadingPage.Error("PlaylistNotFound", "Such playlist doesn't exist");
Navigation.Frame.Frame.LoadingPage.Error("PlaylistNotFound", "Such playlist doesn't exist");
return;
}
Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("WL playlist loading error", new Dictionary<string, string>()
Navigation.Frame.Frame.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Playlist loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Playlist ID", item.Id },
{ "StackTrace", e.StackTrace }
});
}
}
private void toChannel_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
void toChannel_Click(object sender, RoutedEventArgs e) =>
Navigation.GoToChannel(authorId);
private async void inBrowser_Click(object sender, RoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri($"https://www.youtube.com/playlist?list={item.Id}"));
}
async void inBrowser_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync(item.GetUrl().ToUri());
private void refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.PageContent.Refresh();
}
void refresh_Click(object sender, RoutedEventArgs e) =>
Navigation.Frame.Refresh();
private void share_Click(object sender, RoutedEventArgs e)
void share_Click(object sender, RoutedEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
DataTransferManager.ShowShareUI();
}
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
void Share(DataTransferManager sender, DataRequestedEventArgs args)
{
Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url,
item.Snippet.Title,
$"https://www.youtube.com/playlist?list={item.Id}",
(thumbnail.Source as BitmapImage).UriSource.AbsoluteUri,
item.Title,
item.GetUrl(),
ResourceLoader.GetForCurrentView("Cards").GetString("/Cards/playlistShare"));
}
private async void ShowMore_Clicked()
void ShowMore_Clicked()
{
if(playlistId == "WL")
{
MoreWL();
return;
}
for (int k = 25 * page++; k < 25 * page && k < item.Videos.Count; k++)
list.Add(new VideoCard(item.Videos[k].Id, "WL"));
request.PageToken = token;
PlaylistItemListResponse response = await request.ExecuteAsync();
if (string.IsNullOrWhiteSpace(request.PageToken))
more.Visibility = Visibility.Collapsed;
else
{
token = response.NextPageToken;
more.Complete();
}
foreach (PlaylistItem i in response.Items)
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
}
private void MoreWL()
{
for (int k = 25 * page++; k < 25 * page && k < SecretsVault.WatchLater.Count; k++)
list.Add(new VideoCard(SecretsVault.WatchLater[k], "WL"));
if (list.Count >= SecretsVault.WatchLater.Count)
if (list.Count >= item.Videos.Count)
more.Visibility = Visibility.Collapsed;
else
more.Complete();
}
public void DeleteItem(FrameworkElement card)
{
public void DeleteItem(FrameworkElement card) =>
list.DeleteItem(card);
}
}
}
-1
View File
@@ -2,7 +2,6 @@
x:Class="FoxTube.Search"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube"
xmlns:pages="using:FoxTube.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+46 -50
View File
@@ -10,6 +10,7 @@ using Windows.System;
using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
using FoxTube.Classes;
namespace FoxTube
{
@@ -26,41 +27,9 @@ namespace FoxTube
SearchResource.ListRequest request;
string nextToken;
public Search()
{
public Search() =>
InitializeComponent();
}
public string SetResults(int? count)
{
if (count == 1000000)
return count + "+";
else if (count == null)
return "0";
else
return count.ToString();
}
public void AddItem(SearchResult result)
{
switch (result.Id.Kind)
{
case "youtube#video":
VideoCard vCard = new VideoCard(result.Id.VideoId);
list.Add(vCard);
break;
case "youtube#channel":
if(Parameters.Channel == null)
list.Add(new ChannelCard(result.Id.ChannelId, result.Snippet.LiveBroadcastContent));
break;
case "youtube#playlist":
PlaylistCard pCard = new PlaylistCard(result.Id.PlaylistId);
list.Add(pCard);
break;
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
@@ -128,7 +97,7 @@ namespace FoxTube
features.SelectedItems.Add(features.Items[4]);
SearchListResponse response = await request.ExecuteAsync();
resultsCount.Text = $"{resources.GetString("/Search/found")}: {SetResults(response.PageInfo.TotalResults)} {resources.GetString("/Search/items")}";
resultsCount.Text = $"{resources.GetString("/Search/found")}: {GetResultsCout(response.PageInfo.TotalResults)} {resources.GetString("/Search/items")}";
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
nextToken = response.NextPageToken;
@@ -158,7 +127,38 @@ namespace FoxTube
}
}
private void ToggleFilters_Click(object sender, RoutedEventArgs e)
public void AddItem(SearchResult result)
{
switch (result.Id.Kind)
{
case "youtube#video":
VideoCard vCard = new VideoCard(result.Id.VideoId);
list.Add(vCard);
break;
case "youtube#channel":
if (Parameters.Channel == null)
list.Add(new ChannelCard(result.Id.ChannelId, result.Snippet.LiveBroadcastContent));
break;
case "youtube#playlist":
PlaylistCard pCard = new PlaylistCard(result.Id.PlaylistId);
list.Add(pCard);
break;
}
}
public string GetResultsCout(int? count)
{
if (count == 1000000)
return count + "+";
else if (count == null)
return "0";
else
return count.ToString();
}
void ToggleFilters_Click(object sender, RoutedEventArgs e)
{
if(filters.Visibility == Visibility.Collapsed)
{
@@ -172,28 +172,25 @@ namespace FoxTube
}
}
private void AppBarButton_Click(object sender, RoutedEventArgs e)
{
void AppBarButton_Click(object sender, RoutedEventArgs e) =>
frame.Refresh();
}
private async void More_Clicked()
async void More_Clicked()
{
request.PageToken = nextToken;
SearchListResponse response = await request.ExecuteAsync();
nextToken = response.NextPageToken;
foreach (SearchResult item in response.Items)
AddItem(item);
if (response.NextPageToken != null)
{
nextToken = response.NextPageToken;
if (!string.IsNullOrWhiteSpace(nextToken))
more.Complete();
}
else
more.Visibility = Visibility.Collapsed;
}
private void Type_SelectionChanged(object sender, SelectionChangedEventArgs e)
void Type_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
@@ -213,12 +210,7 @@ namespace FoxTube
catch (NullReferenceException) { }
}
private async void InBrowser_Click(object sender, RoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri($"https://www.youtube.com/results?search_query={searchTerm}"));
}
private void Apply_Click(object sender, RoutedEventArgs e)
void Apply_Click(object sender, RoutedEventArgs e)
{
Parameters.Filter.HD = features.SelectedItems.Contains(features.Items[0]);
Parameters.Filter.Is3D = features.SelectedItems.Contains(features.Items[1]);
@@ -231,7 +223,11 @@ namespace FoxTube
Parameters.Filter.Date = (SearchParameters.Filters.Enumerations.Date)date.SelectedIndex;
Parameters.Filter.Duration = (SearchParameters.Filters.Enumerations.Duration)duration.SelectedIndex;
frame.LoadingPage.Refresh();
Initialize(Parameters);
}
async void InBrowser_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync($"https://www.youtube.com/results?search_query={searchTerm}".ToUri());
}
}
+2 -3
View File
@@ -5,9 +5,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:settingspages="using:FoxTube.Pages.SettingsPages"
mc:Ignorable="d">
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot SelectedIndex="0" Name="pivot" IsHeaderItemsCarouselEnabled="False" SelectionChanged="Pivot_SelectionChanged">
<PivotItem Name="generalTab" Header="General" x:Uid="/Settings/general">
<ScrollViewer>
@@ -30,5 +30,4 @@
</ScrollViewer>
</PivotItem>
</Pivot>
</Grid>
</Page>
+4 -5
View File
@@ -1,6 +1,7 @@
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using FoxTube.Pages.SettingsPages;
using FoxTube.Classes;
namespace FoxTube
{
@@ -13,10 +14,8 @@ namespace FoxTube
bool inboxLoaded = false;
string inboxId = null;
public Settings()
{
public Settings() =>
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
@@ -38,10 +37,10 @@ namespace FoxTube
break;
}
Methods.MainPage.PageContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Close();
}
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (pivot.SelectedItem == inboxTab && !inboxLoaded)
{
+1 -3
View File
@@ -24,9 +24,7 @@ namespace FoxTube.Pages.SettingsPages
feedback.Visibility = Visibility.Visible;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
async void Button_Click(object sender, RoutedEventArgs e) =>
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
}
}
}
+13 -35
View File
@@ -79,7 +79,7 @@ namespace FoxTube.Pages.SettingsPages
relLanguage.SelectedItem = relLanguage.Items.Find(i => ((ComboBoxItem)i).Tag as string == SettingsStorage.RelevanceLanguage);
}
private void language_SelectionChanged(object sender, SelectionChangedEventArgs e)
void language_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if ((language.SelectedItem as ComboBoxItem).Tag.ToString() == SettingsStorage.Language)
return;
@@ -88,47 +88,31 @@ namespace FoxTube.Pages.SettingsPages
restartNote.Visibility = Visibility.Visible;
}
private void quality_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
void quality_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
SettingsStorage.VideoQuality = (quality.SelectedItem as ComboBoxItem).Tag as string;
}
private void mobileWarning_Toggled(object sender, RoutedEventArgs e)
{
void mobileWarning_Toggled(object sender, RoutedEventArgs e) =>
SettingsStorage.CheckConnection = mobileWarning.IsOn;
}
private void autoplay_Toggled(object sender, RoutedEventArgs e)
{
void autoplay_Toggled(object sender, RoutedEventArgs e) =>
SettingsStorage.Autoplay = autoplay.IsOn;
}
private void notification_IsEnabledChanged(object sender, RoutedEventArgs e)
{
void notification_IsEnabledChanged(object sender, RoutedEventArgs e) =>
SettingsStorage.VideoNotifications = newVideo.IsOn;
}
private void devNews_Toggled(object sender, RoutedEventArgs e)
{
void devNews_Toggled(object sender, RoutedEventArgs e) =>
SettingsStorage.DevNotifications = devNews.IsOn;
}
private void RelLanguage_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
void RelLanguage_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
SettingsStorage.RelevanceLanguage = ((ComboBoxItem)relLanguage.SelectedItem).Tag.ToString();
}
private void region_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
void region_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
SettingsStorage.Region = ((ComboBoxItem)region.SelectedItem).Tag.ToString();
}
private void safeSearch_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
void safeSearch_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
SettingsStorage.SafeSearch = safeSearch.SelectedIndex;
}
private void RadioButton_Checked(object sender, RoutedEventArgs e)
void RadioButton_Checked(object sender, RoutedEventArgs e)
{
if (sender == light)
{
@@ -151,19 +135,13 @@ namespace FoxTube.Pages.SettingsPages
Methods.MainPage.SetTitleBar();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
void Button_Click(object sender, RoutedEventArgs e) =>
CoreApplication.Exit();
}
private void MinimizedCB_Toggled(object sender, RoutedEventArgs e)
{
void MinimizedCB_Toggled(object sender, RoutedEventArgs e) =>
SettingsStorage.AppBarClosedMode = minimizedCB.IsOn ? AppBarClosedDisplayMode.Minimal : AppBarClosedDisplayMode.Compact;
}
private void ClipboardProcessing_Toggled(object sender, RoutedEventArgs e)
{
void ClipboardProcessing_Toggled(object sender, RoutedEventArgs e) =>
SettingsStorage.ProcessClipboard = clipboardProcessing.IsOn;
}
}
}
+5 -9
View File
@@ -72,7 +72,7 @@ namespace FoxTube.Pages.SettingsPages
list.SelectedItem = items.Find(i => i.Id == id);
}
private void filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
void filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (list == null)
return;
@@ -98,7 +98,7 @@ namespace FoxTube.Pages.SettingsPages
}
}
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!(list.SelectedItem is InboxItem item))
return;
@@ -133,20 +133,16 @@ namespace FoxTube.Pages.SettingsPages
}
}
private void close_Click(object sender, RoutedEventArgs e)
{
void close_Click(object sender, RoutedEventArgs e) =>
CloseView();
}
private void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
if (e.NewState == null)
CloseView();
}
private void Content_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e)
{
void Content_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) =>
Methods.ProcessLink(e.Link);
}
}
}
@@ -20,7 +20,6 @@ namespace FoxTube.Pages.SettingsPages
{
public sealed partial class Translate : Page
{
//TODO: Localize page
ResourceLoader resources = ResourceLoader.GetForCurrentView("Translate");
StorageFile submission;
public Translate()
@@ -31,7 +30,7 @@ namespace FoxTube.Pages.SettingsPages
LangList.Items.Add(culture);
}
private async void export_Click(object sender, RoutedEventArgs e)
async void export_Click(object sender, RoutedEventArgs e)
{
FileSavePicker picker = new FileSavePicker();
picker.CommitButtonText = resources.GetString("/Translate/exportPicker");
@@ -49,7 +48,7 @@ namespace FoxTube.Pages.SettingsPages
await Launcher.LaunchFileAsync(file);
}
private void LangList_SelectionChanged(object sender, SelectionChangedEventArgs e)
void LangList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
greenResult.Visibility = Visibility.Collapsed;
certification.Visibility = Visibility.Collapsed;
@@ -59,7 +58,7 @@ namespace FoxTube.Pages.SettingsPages
export.IsEnabled = true;
}
private async void upload_Click(object sender, RoutedEventArgs e)
async void upload_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker picker = new FileOpenPicker()
{
@@ -156,12 +155,10 @@ namespace FoxTube.Pages.SettingsPages
return report;
}
private async void Log_Click(object sender, RoutedEventArgs e)
{
async void Log_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchFileAsync(((Button)sender).Tag as StorageFile);
}
private async void Submit_Click(object sender, RoutedEventArgs e)
async void Submit_Click(object sender, RoutedEventArgs e)
{
uploadingProgress.Visibility = Visibility.Visible;
submit.IsEnabled = false;
+2 -6
View File
@@ -8,12 +8,10 @@
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer>
<controls:AdaptiveGridView ItemsSource="{x:Bind list}" DesiredWidth="250">
<controls:AdaptiveGridView ItemsSource="{x:Bind list}" DesiredWidth="250" ItemClick="AdaptiveGridView_ItemClick">
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate>
<Button HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Padding="5" Tag="{Binding Path=Snippet.ResourceId.ChannelId}" Click="Button_Click">
<Grid>
<Grid HorizontalAlignment="Stretch" Padding="5" Background="{StaticResource ButtonBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="55"/>
<ColumnDefinition/>
@@ -25,9 +23,7 @@
</PersonPicture>
<TextBlock Grid.Column="1" TextWrapping="Wrap" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Text="{Binding Path=Snippet.Title}"/>
</Grid>
</Button>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
</ScrollViewer>
</Page>
+5 -7
View File
@@ -1,6 +1,6 @@
using Google.Apis.YouTube.v3.Data;
using FoxTube.Classes;
using Google.Apis.YouTube.v3.Data;
using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Pages
@@ -16,12 +16,10 @@ namespace FoxTube.Pages
{
InitializeComponent();
Methods.MainPage.PageContent.LoadingPage.Close();
Navigation.Frame.Frame.LoadingPage.Close();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(((Button)sender).Tag.ToString());
}
void AdaptiveGridView_ItemClick(object sender, ItemClickEventArgs e) =>
Navigation.GoToChannel(((Subscription)e.ClickedItem).Snippet.ResourceId.ChannelId);
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ui:AdaptiveGridView Name="list" DesiredWidth="400" SelectionMode="None" HorizontalContentAlignment="Left">
<ui:AdaptiveGridView Name="list" DesiredWidth="400" SelectionMode="None" ItemClick="List_ItemClick" HorizontalContentAlignment="Left">
<ui:AdaptiveGridView.ItemContainerTransitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
+32 -9
View File
@@ -1,5 +1,7 @@
using FoxTube.Controls;
using FoxTube.Classes;
using FoxTube.Controls.Adverts;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@@ -12,17 +14,27 @@ namespace FoxTube.Pages
{
public int Count => list.Items.Count;
public ItemCollection Children => list.Items;
private int ItemsCount => Children.FindAll(i => i is VideoCard).Count + Children.FindAll(i => i is ChannelCard).Count + Children.FindAll(i => i is PlaylistCard).Count + Children.FindAll(i => i is CardAdvert).Count;
int ItemsCount => Children.FindAll(i => i is ICard).Count;
public VideoGrid()
{
Queue<ICard> queue = new Queue<ICard>();
public VideoGrid() =>
InitializeComponent();
public void Add(ICard card)
{
if (card == null)
return;
queue.Enqueue(card);
if (LoadItems().Status != TaskStatus.Running)
LoadItems().Start();
}
public void Add(UIElement card)
void Insert(ICard item)
{
list.Items.Add(card);
if ((list.Items.Count - 5) % 25 == 0)
list.Items.Add(item);
if ((ItemsCount - 5) % 25 == 0)
list.Items.Add(new CardAdvert());
empty.Visibility = Visibility.Collapsed;
}
@@ -33,9 +45,20 @@ namespace FoxTube.Pages
empty.Visibility = Visibility.Visible;
}
public void DeleteItem(FrameworkElement item)
{
public void DeleteItem(FrameworkElement item) =>
Children.Remove(item);
void List_ItemClick(object sender, ItemClickEventArgs e) =>
(e.ClickedItem as ICard).ItemClicked();
async Task LoadItems()
{
while(queue.Count > 0)
{
ICard item = queue.Dequeue();
await item.Initialize();
Insert(item);
}
}
}
}
+15 -57
View File
@@ -2,11 +2,11 @@
x:Class="FoxTube.Pages.VideoPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:FoxTube.Pages"
xmlns:classes="using:FoxTube.Classes"
xmlns:videopage="using:FoxTube.Controls.VideoPage"
xmlns:controls="using:FoxTube.Controls"
mc:Ignorable="d">
<Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" SizeChanged="grid_SizeChanged">
@@ -25,6 +25,7 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="400"/>
@@ -34,7 +35,7 @@
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ScrollViewer Name="mainScroll" VerticalScrollBarVisibility="Hidden">
<ScrollViewer Name="mainScroll" VerticalScrollBarVisibility="Hidden" ViewChanged="MainScroll_ViewChanged">
<StackPanel Name="mainContent">
<Border BorderBrush="Red" BorderThickness="5" CornerRadius="10" Margin="10" Name="upcoming" Visibility="Collapsed">
<Grid Margin="10">
@@ -61,10 +62,8 @@
</StackPanel>
</Grid>
</Border>
<local:VideoPlayer x:Name="player" NextClicked="Player_NextClicked" MiniMode="Player_Minimize"/>
<PivotItem Header="Description" Name="descriptionPanel">
<StackPanel Margin="0,10">
<Button FontFamily="Segoe UI, Segoe MDL2 Assets" Content="&#xE122; Continue from: 00:10:37" Name="left" Click="Left_Click" Visibility="Collapsed"/>
<TextBlock IsTextSelectionEnabled="True" Name="title" Text="[Video title]" FontSize="25" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Start"/>
<TextBlock Text="Published at: " Name="meta"/>
<Grid>
@@ -72,13 +71,13 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Padding="0" Background="Transparent" Margin="5" Name="gotoChannel" Click="gotoChannel_Click">
<Button Padding="0" Background="Transparent" Margin="5" Click="gotoChannel_Click">
<StackPanel Orientation="Horizontal">
<PersonPicture Name="channelAvatar" Width="90"/>
<StackPanel Orientation="Vertical" Grid.Column="1" Padding="5" VerticalAlignment="Center">
<StackPanel Padding="5" VerticalAlignment="Center">
<TextBlock Name="channelName" Text="[Channel name]" FontSize="18"/>
<TextBlock Name="subscribers" Text="[subscribers]" Foreground="Gray" Margin="0,0,0,5"/>
<Button x:Uid="/Cards/subscribe" Click="subscribe_Click" Grid.Column="2" Height="30" Width="200" Background="Red" Foreground="White" FontSize="14" FontWeight="SemiBold" Content="Subscirbe" Name="subscribe"/>
<Button x:Uid="/Cards/subscribe" Click="subscribe_Click" Height="30" Width="200" Background="Red" Foreground="White" FontSize="14" FontWeight="SemiBold" Content="Subscirbe" Name="subscribe"/>
</StackPanel>
</StackPanel>
</Button>
@@ -89,7 +88,7 @@
<TextBlock Foreground="Gray" Text="[dislikes]" Name="dislikes"/>
<TextBlock HorizontalAlignment="Right" Foreground="Gray" Text="[likes]" Name="likes"/>
</Grid>
<Grid BorderBrush="{StaticResource SystemControlBackgroundListMediumRevealBorderBrush}">
<Grid>
<FontIcon Foreground="Gray"
HorizontalAlignment="Left"
FontSize="40"
@@ -113,16 +112,12 @@
<classes:AdaptiveCommandBar Grid.Row="1" VerticalAlignment="Bottom" x:Name="commandbar">
<AppBarButton x:Uid="/VideoPage/download" Icon="Download" Label="Download video" Name="download">
<AppBarButton.Flyout>
<MenuFlyout x:Name="downloadSelector"/>
<Flyout x:Name="downloadSelector"/>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarButton x:Uid="/VideoPage/addTo" Name="addTo" Label="Add to" Icon="Add" Visibility="Visible">
<AppBarButton.Flyout>
<MenuFlyout x:Name="addList">
<MenuFlyoutItem x:Uid="/VideoPage/newPlaylist" Text="New playlist" Name="newPlaylist" Click="NewPlaylist_Click" Icon="Add"/>
<ToggleMenuFlyoutItem x:Uid="/VideoPage/wl" Text="Watch later" Name="wl" Click="Wl_Click" Icon="Clock"/>
<MenuFlyoutSeparator/>
</MenuFlyout>
<Flyout x:Name="addToSelector"/>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarButton x:Uid="/VideoPage/refresh" Name="refresh" Click="refresh_Click" Icon="Refresh" Label="Refresh page"/>
@@ -138,52 +133,15 @@
<Grid Grid.Column="1" Name="tabsPlaceholder">
<Pivot Name="pivot" SelectedIndex="0" IsHeaderItemsCarouselEnabled="False">
<PivotItem x:Uid="/VideoPage/related" Header="Suggestions">
<ScrollViewer>
<pages:VideoGrid x:Name="relatedVideos"/>
</ScrollViewer>
<videopage:RelatedVideos x:Name="suggestions"/>
</PivotItem>
<PivotItem x:Uid="/VideoPage/comments" Header="Comments" Name="commentsPlaceholder">
<pages:CommentsPage x:Name="comments"/>
<PivotItem x:Uid="/VideoPage/comments" Header="Comments" Name="commentsTab">
<videopage:Comments x:Name="comments"/>
</PivotItem>
<PivotItem x:Uid="/VideoPage/playlist" Header="Playlist" Name="playlist">
<ScrollViewer Name="playlistScroll">
<StackPanel>
<StackPanel Padding="8" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<TextBlock Text="[Playlsit name]" FontSize="26" TextWrapping="WrapWholeWords" Name="playlistName"/>
<TextBlock Text="[Channel name]" Name="playlistChannel"/>
<TextBlock Text="[Counter]" Name="playlistCounter"/>
</StackPanel>
<ListBox Background="Transparent" SelectionChanged="ListBox_SelectionChanged" Name="playlistList">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Id}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Number}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<Image Grid.Column="1" Source="{Binding Path=Thumbnail}" Height="65"/>
<TextBlock Grid.Column="2" Margin="8,0,0,0" VerticalAlignment="Center" TextWrapping="WrapWholeWords" Text="{Binding Path=Title}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ScrollViewer>
<PivotItem x:Uid="/VideoPage/playlist" Header="Playlist" Name="playlistTab">
<videopage:VideoPlaylist x:Name="playlist" ItemChanged="Playlist_ItemChanged"/>
</PivotItem>
</Pivot>
</Grid>
<ContentDialog x:Uid="/VideoPage/dialog" PrimaryButtonText="Create and add" Title="New playlist" CloseButtonText="Cancel" DefaultButton="Primary" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" Name="playlistDialog">
<StackPanel>
<TextBox x:Uid="/VideoPage/newPlaylistName" PlaceholderText="Enter playlist name" Name="newListName"/>
<ComboBox x:Uid="/VideoPage/privacy" Header="Availablity" SelectedIndex="0" HorizontalAlignment="Stretch" Name="newListDisc">
<ComboBoxItem x:Uid="/VideoPage/public" Content="Public"/>
<ComboBoxItem x:Uid="/VideoPage/private" Content="Private"/>
<ComboBoxItem x:Uid="/VideoPage/direct" Content="Direct link"/>
</ComboBox>
</StackPanel>
</ContentDialog>
</Grid>
</Page>
+129 -469
View File
@@ -1,4 +1,6 @@
using FoxTube.Controls;
using FoxTube.Classes;
using FoxTube.Controls;
using FoxTube.Controls.VideoPage;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics;
@@ -25,21 +27,6 @@ namespace FoxTube.Pages
{
public enum Rating { None, Like, Dislike }
public class VideoPlaylistItem
{
public int Number { get; set; }
public string Thumbnail { get; private set; } = "/Assets/videoThumbSample.png";
public string Id { get; private set; }
public string Title { get; private set; }
public VideoPlaylistItem(string image, string title, string id)
{
Thumbnail = image;
Title = title;
Id = id;
}
}
/// <summary>
/// Video page
/// </summary>
@@ -49,27 +36,25 @@ namespace FoxTube.Pages
public object Parameter { get; set; } = null;
public string playlistId = null;
public Video item;
public HistoryItem history;
public bool incognito = false;
bool isExtended = false;
string playlistItem = null;
Video item;
Channel channelItem;
bool incognito = false;
Rating userRating = Rating.None;
DispatcherTimer liveTimer;
DispatcherTimer countdownTimer;
public VideoPlayer Player => player;
public VideoPage()
{
InitializeComponent();
if (Window.Current.Bounds.Width <= 1000)
{
Debug.WriteLine("Correcting layout...");
//Methods.Player.SizeChanged += Player_SizeChanged;
if (Window.Current.Bounds.Width > 1000)
return;
mainContent.Children.Remove(descriptionPanel);
pivot.Items.Insert(0, descriptionPanel);
@@ -78,65 +63,87 @@ namespace FoxTube.Pages
grid.ColumnDefinitions[1].Width = new GridLength(0);
}
}
private void Player_NextClicked()
{
if (playlistId != null && playlistList.SelectedIndex + 1 < playlistList.Items.Count)
playlistList.SelectedIndex++;
else
(relatedVideos.Children[0] as VideoCard).Button_Click(this, null);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Parameter = e.Parameter;
Initialize(e.Parameter as object[]);
Initialize(((object, Channel, bool, string))e.Parameter);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
Player.Player.Stop();
}
public async void Initialize(object[] ids)
async void Initialize((object video, Channel channel, bool incognito, string playlist) parameter, bool playlistItemSwitch = false)
{
try
{
incognito = (bool)ids[2];
incognito = parameter.incognito;
channelItem = parameter.channel;
if (ids[1] != null)
LoadPlaylist(ids[1] as string);
if (parameter.video is Video)
item = parameter.video as Video;
else
pivot.Items.Remove(playlist);
{
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,statistics,status,contentDetails,liveStreamingDetails");
request.Id = ids[0] as string;
request.Id = parameter.video as string;
request.Hl = SettingsStorage.RelevanceLanguage;
item = (await request.ExecuteAsync()).Items[0];
ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet, statistics");
channelRequest.Id = item.Snippet.ChannelId;
channelItem = (await channelRequest.ExecuteAsync()).Items[0];
}
/*Methods.Player.Player.Visibility = Visibility.Visible;
Methods.Player.Player.PosterSource = new BitmapImage((item.Snippet.Thumbnails.Maxres ?? item.Snippet.Thumbnails.Medium).Url.ToUri());
Methods.Player.NextRequested += suggestions.OpenNext;*/
LoadDescription();
if (!playlistItemSwitch)
{
if (parameter.playlist != null)
{
try
{
await playlist.Initialize(item, parameter.playlist);
playlistItem = parameter.playlist;
pivot.SelectedItem = playlistTab;
}
catch
{ pivot.Items.Remove(playlistTab); }
}
else
pivot.Items.Remove(playlistTab);
}
//Initializing player
// Methods.MainPage.Player.Initialize(item, channelItem.Snippet.Thumbnails.Medium.Url, incognito, parameter.playlist == null ? null : playlist);
if (item.Snippet.LiveBroadcastContent == "none")
LoadStats();
{
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
comments.Initialize(item);
/*try { downloadSelector.Initialize(item); }
catch { download.Visibility = Visibility.Collapsed; }*/
}
else
LoadStream();
if (item.Snippet.LiveBroadcastContent == "upcoming")
SetSchedule();
LoadInfo();
LoadAddTo();
suggestions.Initialize(item.Id);
Methods.MainPage.VideoContent.LoadingPage.Close();
//Methods.MainPage.VideoContent.LoadingPage.Close();
}
catch (System.Net.Http.HttpRequestException)
{
Methods.MainPage.VideoContent.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
//Methods.MainPage.VideoContent.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
/*Methods.Player.Player.Visibility = Visibility.Collapsed;
if (item == null)
{
Methods.MainPage.PageContent.LoadingPage.Error("VideoNotFound", "Such video doesn't exist");
@@ -150,7 +157,7 @@ namespace FoxTube.Pages
{ "Message", e.Message },
{ "Video ID", item.Id },
{ "StackTrace", e.StackTrace }
});
});*/
}
}
@@ -183,95 +190,35 @@ namespace FoxTube.Pages
}
}
async void LoadPlaylist(string id)
{
playlistId = id;
List<VideoPlaylistItem> items = new List<VideoPlaylistItem>();
VideoPlaylistItem selection = null;
if (id == "WL")
{
SecretsVault.WatchLater = await Methods.GetLater();
foreach (string i in SecretsVault.WatchLater)
{
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet");
request.Id = i;
Video v = (await request.ExecuteAsync()).Items[0];
items.Add(new VideoPlaylistItem(v.Snippet.Thumbnails.Medium.Url, v.Snippet.Title, i));
}
selection = items.Find(i => i.Id == item.Id);
for (int k = 0; k < items.Count; k++)
items[k].Number = k + 1;
playlistName.Text = resources.GetString("/Main/later/Content");
playlistChannel.Text = SecretsVault.UserChannel.Snippet.Title;
playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{SecretsVault.WatchLater.Count}";
}
else
{
//Retrieving data
PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
playlistRequest.Id = id;
playlistRequest.Hl = SettingsStorage.RelevanceLanguage;
Playlist playlistItem = (await playlistRequest.ExecuteAsync()).Items[0];
PlaylistItemsResource.ListRequest listRequest = SecretsVault.Service.PlaylistItems.List("snippet");
listRequest.MaxResults = 50;
listRequest.PlaylistId = id;
PlaylistItemListResponse listResponse;
do
{
listResponse = await listRequest.ExecuteAsync();
listRequest.PageToken = listResponse.NextPageToken;
foreach (PlaylistItem i in listResponse.Items)
items.Add(new VideoPlaylistItem(i.Snippet.Thumbnails.Medium.Url, i.Snippet.Title, i.Snippet.ResourceId.VideoId));
}
while (!string.IsNullOrWhiteSpace(listRequest.PageToken));
selection = items.Find(i => i.Id == item.Id);
for (int k = 0; k < items.Count; k++)
items[k].Number = k + 1;
//Setting data
playlistName.Text = playlistItem.Snippet.Localized.Title;
playlistChannel.Text = Methods.GuardFromNull(playlistItem.Snippet.ChannelTitle);
playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{playlistItem.ContentDetails.ItemCount}";
}
playlistList.ItemsSource = items;
playlistList.SelectedItem = selection;
pivot.SelectedItem = playlist;
await Task.Delay(500);
playlistScroll.ChangeView(null, playlistList.SelectedIndex * 86 + 89, null, true);
}
async void LoadInfo()
async void LoadDescription()
{
//Setting meta
title.Text = item.Snippet.Localized.Title;
description.FormatText(item.Snippet.Localized.Description);
//Setting channel button
channelAvatar.ProfilePicture = new BitmapImage(channelItem.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelHeight = 90, DecodePixelWidth = 90 };
channelName.Text = item.Snippet.ChannelTitle;
subscribers.Text = $"{channelItem.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
//Setting ratings
dislikes.Text = $"{item.Statistics.DislikeCount:0,0}";
likes.Text = $"{item.Statistics.LikeCount:0,0}";
try { rating.Value = (double)item.Statistics.DislikeCount / (double)(item.Statistics.DislikeCount + item.Statistics.LikeCount) * 100; }
catch { rating.Visibility = Visibility.Collapsed; }
meta.Text = "";
meta.Inlines.Add(new Run
{
Text = $"{resources.GetString("/VideoPage/publishedAt")}: {item.Snippet.PublishedAt} ({Methods.GetAgo(item.Snippet.PublishedAt.Value)})"
});
if(!string.IsNullOrWhiteSpace(item.Snippet.CategoryId))
if (!string.IsNullOrWhiteSpace(item.Snippet.CategoryId))
{
VideoCategoriesResource.ListRequest request = SecretsVault.Service.VideoCategories.List("snippet,id");
request.Id = item.Snippet.CategoryId;
request.Hl = SettingsStorage.RelevanceLanguage;
VideoCategoryListResponse response = await request.ExecuteAsync();
if(response.Items.Count != 0)
{
meta.Inlines.Add(new Run
{
Text = $" {resources.GetString("/VideoPage/inCat")} "
@@ -281,26 +228,9 @@ namespace FoxTube.Pages
{
Text = response.Items[0].Snippet.Title
});
hl.Click += (s, e) => Methods.MainPage.GoToSearch(new SearchParameters(response.Items[0]));
hl.Click += (s, e) => Navigation.GoToSearch(new SearchParameters(response.Items[0]));
meta.Inlines.Add(hl);
}
}
description.FormatText(item.Snippet.Localized.Description);
//Setting channel button
ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet, statistics");
channelRequest.Id = item.Snippet.ChannelId;
var item1 = (await channelRequest.ExecuteAsync()).Items[0];
channelAvatar.ProfilePicture = new BitmapImage(item1.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelHeight = 90, DecodePixelWidth = 90 };
channelName.Text = item.Snippet.ChannelTitle;
subscribers.Text = $"{item1.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
//Setting ratings
dislikes.Text = $"{item.Statistics.DislikeCount:0,0}";
likes.Text = $"{item.Statistics.LikeCount:0,0}";
try { rating.Value = (double)item.Statistics.DislikeCount / (double)(item.Statistics.DislikeCount + item.Statistics.LikeCount) * 100; }
catch { rating.Visibility = Visibility.Collapsed; }
//Setting User's rate
if (SecretsVault.IsAuthorized)
@@ -323,8 +253,11 @@ namespace FoxTube.Pages
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe");
}
if (item.Snippet.ChannelId == SecretsVault.AccountId)
else if (item.Snippet.ChannelId == SecretsVault.AccountId)
subscribe.Visibility = Visibility.Collapsed;
/*try { addToSelector.Initialize(item); }
catch { addTo.Visibility = Visibility.Collapsed; }*/
}
else
{
@@ -332,34 +265,22 @@ namespace FoxTube.Pages
addTo.Visibility = Visibility.Collapsed;
subscribe.Visibility = Visibility.Collapsed;
}
if ((history = HistorySet.Items.Find(i => i.Id == item.Id)) != null && history.LeftOn.TotalSeconds >= 30 && Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds - history.LeftOn.TotalSeconds >= 30)
{
left.Visibility = Visibility.Visible;
left.Content = $"\xE122 {resources.GetString("/VideoPage/continue")}: {history.LeftOn.ToString(@"hh\:mm\:ss")}";
}
//Initializing player
player.Initialize(item, item1.Snippet.Thumbnails.Medium.Url, incognito);
LoadRelatedVideos();
}
void LoadStream()
{
liveTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(10) };
liveTimer = new DispatcherTimer() { Interval = TimeSpan.FromMinutes(1) };
liveTimer.Tick += LiveStatsUpdate;
liveTimer.Start();
LiveStatsUpdate();
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
if(string.IsNullOrWhiteSpace(item.LiveStreamingDetails.ActiveLiveChatId))
pivot.Items.Remove(commentsPlaceholder);
if (string.IsNullOrWhiteSpace(item.LiveStreamingDetails.ActiveLiveChatId))
comments.Initialize(item);
else
{
commentsPlaceholder.Header = resources.GetString("/VideoPage/chat");
commentsPlaceholder.Content = new Chat(item.LiveStreamingDetails.ActiveLiveChatId);
pivot.SelectedItem = commentsPlaceholder;
commentsTab.Header = resources.GetString("/VideoPage/chat");
commentsTab.Content = new Chat(item.LiveStreamingDetails.ActiveLiveChatId);
pivot.SelectedItem = commentsTab;
}
download.Visibility = Visibility.Collapsed;
}
@@ -372,117 +293,14 @@ namespace FoxTube.Pages
views.Text = $"{(await request.ExecuteAsync()).Items[0].LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
}
void LoadStats()
{
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
private void gotoChannel_Click(object sender, RoutedEventArgs e) =>
Navigation.GoToChannel(channelItem.Id);
comments.Initialize(item);
LoadDownloads();
}
private void openBrowser_Click(object sender, RoutedEventArgs e) => Methods.GuardFromNull("");
//Methods.Player.OpenBrowser();
async void LoadDownloads()
{
try
{
MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id);
foreach (MuxedStreamInfo i in infoSet.Muxed)
{
MenuFlyoutItem menuItem = new MenuFlyoutItem()
{
Text = i.VideoQualityLabel,
Tag = new object[] { i, i.VideoQualityLabel }
};
menuItem.Click += downloadItemSelected;
downloadSelector.Items.Add(menuItem);
}
MenuFlyoutItem audioItem = new MenuFlyoutItem()
{
Text = resources.GetString("/VideoPage/audio"),
Tag = new object[] { infoSet.Audio[0], resources.GetString("/Cards/audioOnly") }
};
audioItem.Click += downloadItemSelected;
downloadSelector.Items.Add(audioItem);
}
catch
{
download.Visibility = Visibility.Collapsed;
}
}
private void downloadItemSelected(object sender, RoutedEventArgs e)
{
DownloadAgent.Add(((sender as MenuFlyoutItem).Tag as object[])[0] as MediaStreamInfo, item, ((sender as MenuFlyoutItem).Tag as object[])[1] as string);
}
async void LoadRelatedVideos()
{
SearchResource.ListRequest request = SecretsVault.Service.Search.List("snippet");
request.RegionCode = SettingsStorage.Region;
request.RelevanceLanguage = SettingsStorage.RelevanceLanguage;
request.RelatedToVideoId = item.Id;
request.SafeSearch = (SearchResource.ListRequest.SafeSearchEnum)SettingsStorage.SafeSearch;
request.MaxResults = 20;
request.Type = "video";
SearchListResponse response = await request.ExecuteAsync();
foreach (SearchResult video in response.Items)
relatedVideos.Add(new VideoCard(video.Id.VideoId));
relatedVideos.Children.Insert(1, new Controls.Adverts.CardAdvert());
}
private void Player_Minimize(object sender, params object[] e)
{
isExtended = (bool)e[0];
if(isExtended)
{
grid.ColumnDefinitions[1].Width = new GridLength(0);
commandbar.Visibility = Visibility.Collapsed;
mainScroll.ChangeView(0, 0, null);
mainScroll.Margin = new Thickness(0);
mainScroll.VerticalScrollMode = ScrollMode.Disabled;
Methods.MainPage.MinimizeVideo();
}
else
{
grid.ColumnDefinitions[1].Width = new GridLength(400);
commandbar.Visibility = Visibility.Visible;
mainScroll.Margin = new Thickness(0,0,0,50);
mainScroll.VerticalScrollMode = ScrollMode.Auto;
Methods.MainPage.MaximizeVideo();
}
}
private void gotoChannel_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
private async void openBrowser_Click(object sender, RoutedEventArgs e)
{
player.Pause();
string timecode = player.Player.Position.TotalSeconds > 10 ?
"&t=" + (int)player.Player.Position.TotalSeconds + "s" : string.Empty;
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={item.Id}{timecode}".ToUri());
}
public void refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.VideoContent.Refresh();
}
public void CloseVideo()
{
player.Controls_CloseRequested(this, null);
}
void refresh_Click(object sender, RoutedEventArgs e) => Methods.GuardFromNull("");
//Methods.MainPage.VideoContent.Refresh();
private void grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
@@ -516,7 +334,7 @@ namespace FoxTube.Pages
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
{
player.Pause();
//Methods.Player.Pause();
Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url,
@@ -531,6 +349,9 @@ namespace FoxTube.Pages
DataTransferManager.ShowShareUI();
}
private async void Report_Click(object sender, RoutedEventArgs e) =>
await new ReportVideo(item.Id).ShowAsync();
private async void dislike_Click(object sender, RoutedEventArgs e)
{
if (SecretsVault.IsAuthorized)
@@ -603,16 +424,6 @@ namespace FoxTube.Pages
}
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
if ((e.AddedItems[0] as VideoPlaylistItem).Id != item.Id)
Methods.MainPage.GoToVideo((e.AddedItems[0] as VideoPlaylistItem).Id, playlistId);
}
catch { }
}
private async void subscribe_Click(object sender, RoutedEventArgs e)
{
if (await SecretsVault.ChangeSubscriptionState(item.Snippet.ChannelId))
@@ -629,194 +440,43 @@ namespace FoxTube.Pages
}
}
async void LoadAddTo()
private void MainScroll_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) => Methods.GuardFromNull("");
//Methods.Player.SetMargin(mainScroll.VerticalOffset);
private void Player_SizeChanged(object sender, SizeChangedEventArgs e)
{
try
//if(Methods.Player.State != PlayerDisplayState.Compact)
mainContent.Margin = new Thickness(mainContent.Margin.Left, e.NewSize.Height, mainContent.Margin.Right, mainContent.Margin.Bottom);
}
public void Maximize()
{
if (SecretsVault.UserChannel == null)
}
public void Minimize()
{
addTo.Visibility = Visibility.Collapsed;
return;
}
if (SecretsVault.WatchLater.Contains(item.Id))
(addList.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)
private void ResetControl()
{
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;
addList.Items.Add(menuItem);
}
}
catch
{
addTo.Visibility = Visibility.Collapsed;
}
mainScroll.ChangeView(0, 0, null);
upcoming.Visibility = Visibility.Collapsed;
schedule.Visibility = Visibility.Collapsed;
start.Visibility = Visibility.Collapsed;
end.Visibility = Visibility.Collapsed;
countdownPanel.Visibility = Visibility.Collapsed;
download.Visibility = Visibility.Visible;
addTo.Visibility = Visibility.Visible;
comments = new Comments();
}
private async void Item_Click(object sender, RoutedEventArgs e)
private void Playlist_ItemChanged(string id)
{
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;
}
}
}
private async void NewPlaylist_Click(object sender, RoutedEventArgs e)
{
await playlistDialog.ShowAsync();
}
private async void Wl_Click(object sender, RoutedEventArgs e)
{
if (wl.IsChecked)
{
try
{
PlaylistItem playlist = new PlaylistItem
{
Snippet = new PlaylistItemSnippet
{
PlaylistId = "WL",
ResourceId = new ResourceId
{
VideoId = item.Id,
Kind = "youtube#video"
}
}
};
PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet");
await request.ExecuteAsync();
}
catch
{
wl.IsChecked = false;
}
}
else
wl.IsChecked = true;
}
private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
string privacy = "private";
switch(newListDisc.SelectedIndex)
{
case 0:
privacy = "public";
break;
case 1:
privacy = "private";
break;
case 2:
privacy = "unlisted";
break;
}
Playlist newItem = new Playlist
{
Snippet = new PlaylistSnippet
{
Title = newListName.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;
addList.Items.Add(menuItem);
Item_Click(menuItem, null);
}
private void Left_Click(object sender, RoutedEventArgs e)
{
Player.Player.Position = history.LeftOn;
}
private async void Report_Click(object sender, RoutedEventArgs e)
{
await new ReportVideo(item.Id).ShowAsync();
ResetControl();
Initialize((id, null, incognito, playlistItem), true);
}
}
}
+1 -1
View File
@@ -169,7 +169,7 @@
<value>Skip forward for 30 seconds</value>
</data>
<data name="generatedCaption" xml:space="preserve">
<value>(Auto-generated)</value>
<value>Auto-generated</value>
</data>
<data name="goLive.Text" xml:space="preserve">
<value>Go to live broadcast</value>
+1 -1
View File
@@ -169,7 +169,7 @@
<value>Вперед на 30 секунд</value>
</data>
<data name="generatedCaption" xml:space="preserve">
<value>(Авто-перевод)</value>
<value>Авто-перевод</value>
</data>
<data name="goLive.Text" xml:space="preserve">
<value>Перейти к прямому эфиру</value>
+2 -1
View File
@@ -1,7 +1,7 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube"
xmlns:local="using:FoxTube.Controls.Player"
xmlns:controls="using:FoxTube.Controls"
xmlns:adverts="using:FoxTube.Controls.Adverts">
@@ -294,6 +294,7 @@
<KeyboardAccelerator Key="Space"/>
</Button.KeyboardAccelerators>
</Button>
<Button x:Name="PreviousButton" Content="&#xE100;" Visibility="Collapsed"/>
<Button x:Name="NextButton" Content="&#xE101;"/>
<Button x:Name="VolumeMenuButton" Content="&#xE15D;">
<Button.Flyout>