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 FoxTube.Classes;
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System;
using System.Collections.Generic; 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.Activation;
using Windows.ApplicationModel.Background;
using Windows.Globalization; using Windows.Globalization;
using Windows.Storage;
using Windows.System.Power;
using Windows.UI.Notifications; using Windows.UI.Notifications;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FoxTube namespace FoxTube
{ {
sealed partial class App : Application sealed partial class App : Application
{ {
public static string[] AvailableLanguages => new[] { "en-US", "ru-RU" };
Stopwatch sw = new Stopwatch();
public App() public App()
{ {
SettingsStorage.LoadData(); SettingsStorage.LoadData();
@@ -40,113 +26,30 @@ namespace FoxTube
} }
ApplicationLanguages.PrimaryLanguageOverride = SettingsStorage.Language; ApplicationLanguages.PrimaryLanguageOverride = SettingsStorage.Language;
CheckVersion();
InitializeComponent(); InitializeComponent();
Suspending += OnSuspending; Suspending += (s, e) => Processes.SuspendApp();
UnhandledException += UnhandledError; UnhandledException += (s, e) => Analytics.TrackEvent("The app crashed", new Dictionary<string, string>()
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()
{ {
PackageVersion ver = Package.Current.Id.Version; { "Exception", e.Exception.GetType().ToString() },
if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}.{ver.Build}") { "Details", e.Message },
{ { "StackTrace", e.Exception.StackTrace }
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 }
}); });
}
} Processes.InitializeApp();
} }
protected override void OnLaunched(LaunchActivatedEventArgs e) protected override void OnLaunched(LaunchActivatedEventArgs e)
{ {
if (!(Window.Current.Content is Frame rootFrame)) if (!(Window.Current.Content is Frame))
{ Window.Current.Content = new Frame();
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false) if (e.PrelaunchActivated == false)
{ {
if (rootFrame.Content == null) if ((Window.Current.Content as Frame).Content == null)
rootFrame.Navigate(typeof(MainPage), e.Arguments); (Window.Current.Content as Frame).Navigate(typeof(MainPage), e.Arguments);
Window.Current.Activate(); 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) protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
@@ -154,50 +57,10 @@ namespace FoxTube
var deferral = args.TaskInstance.GetDeferral(); var deferral = args.TaskInstance.GetDeferral();
base.OnBackgroundActivated(args); base.OnBackgroundActivated(args);
if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" && args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details) if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" || !(args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details))
{ return;
string[] arguments = details.Argument.Split('|');
switch (arguments[0]) Processes.ProcessToast(details.Argument);
{
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;
}
}
deferral.Complete(); deferral.Complete();
} }
@@ -206,7 +69,8 @@ namespace FoxTube
{ {
base.OnActivated(e); 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 = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed; rootFrame.NavigationFailed += OnNavigationFailed;
@@ -217,88 +81,15 @@ namespace FoxTube
if (rootFrame.Content == null) if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage)); 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) if (SecretsVault.IsAuthorized)
ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); Processes.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
else else
SecretsVault.AuthorizationStateChanged += (s, arg) => ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); SecretsVault.AuthorizationStateChanged += (arg) => Processes.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 }
});
} }
} }
} }
+1 -3
View File
@@ -4,9 +4,7 @@ namespace FoxTube.Classes
{ {
class AdaptiveCommandBar : CommandBar class AdaptiveCommandBar : CommandBar
{ {
public AdaptiveCommandBar() public AdaptiveCommandBar() =>
{
ClosedDisplayMode = SettingsStorage.AppBarClosedMode; ClosedDisplayMode = SettingsStorage.AppBarClosedMode;
} }
} }
}
+5 -7
View File
@@ -3,26 +3,26 @@ using System.Collections.Generic;
using Windows.Storage; using Windows.Storage;
using Newtonsoft.Json; using Newtonsoft.Json;
using YoutubeExplode.Models.MediaStreams; using YoutubeExplode.Models.MediaStreams;
using Google.Apis.YouTube.v3.Data;
using FoxTube.Controls; using FoxTube.Controls;
using FoxTube.Pages; using FoxTube.Pages;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using YoutubeExplode.Models;
namespace FoxTube namespace FoxTube
{ {
public static class DownloadAgent public static class DownloadAgent
{ {
public static List<DownloadItem> Items { get; set; } = new List<DownloadItem>(); 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 Downloads Page { get; set; }
public static StorageFolder Downloads { get; set; } public static StorageFolder Downloads { get; set; }
public static async void Initialize() public static async void Initialize()
{ {
Downloads = await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
Items.Clear(); Items.Clear();
try try
{ {
Downloads = await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
List<DownloadItemContainer> containers = JsonConvert.DeserializeObject<List<DownloadItemContainer>>((string)settings.Values[$"downloads"]); List<DownloadItemContainer> containers = JsonConvert.DeserializeObject<List<DownloadItemContainer>>((string)settings.Values[$"downloads"]);
containers.ForEach(i => Items.Add(new DownloadItem(i))); containers.ForEach(i => Items.Add(new DownloadItem(i)));
} }
@@ -38,10 +38,8 @@ namespace FoxTube
} }
} }
public static void Add(MediaStreamInfo info, Video meta, string qualty) public static void Add((MediaStreamInfo info, Video meta, string qualty) e) =>
{ Items.Insert(0, new DownloadItem(e.info, e.meta, e.qualty));
Items.Insert(0, new DownloadItem(info, meta, qualty));
}
public static void Remove(DownloadItem item) 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 InboxItemType Type { get; set; } = InboxItemType.Default;
public DateTime TimeStamp { get; set; } public DateTime TimeStamp { get; set; }
public string TimeStampString public string TimeStampString => Type == InboxItemType.PatchNote ? TimeStamp.ToShortDateString() : TimeStamp.ToString();
{
get
{
if (Type == InboxItemType.PatchNote)
return TimeStamp.ToShortDateString();
else
return TimeStamp.ToString();
}
}
public string Subject { get; set; } public string Subject { get; set; }
public string Content { get; set; } public string Content { get; set; }
@@ -26,38 +17,10 @@ namespace FoxTube.Classes
private ResourceLoader resources = ResourceLoader.GetForCurrentView("Inbox"); private ResourceLoader resources = ResourceLoader.GetForCurrentView("Inbox");
public string Icon public string Icon => Type == InboxItemType.PatchNote ? "\xE728" : "\xE119";
{ public string Subtitle => Type == InboxItemType.PatchNote ? resources.GetString("changelog") : resources.GetString("dev");
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 Title => Type == InboxItemType.PatchNote ? $"{resources.GetString("whatsnew")}{Id}" : Subject;
public InboxItem(string version, string content, DateTime timeStamp) public InboxItem(string version, string content, DateTime timeStamp)
{ {
+6 -43
View File
@@ -16,7 +16,7 @@ using Windows.ApplicationModel.Resources;
using Windows.Storage; using Windows.Storage;
using YoutubeExplode.Models.MediaStreams; using YoutubeExplode.Models.MediaStreams;
namespace FoxTube.Controls.Player namespace FoxTube.Classes
{ {
public static class ManifestGenerator 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"); XmlElement representation = doc.CreateElement("Representation");
representation.SetAttribute("bandwidth", GetBandwidth(info.Label)); representation.SetAttribute("bandwidth", GetBandwidth(info.Label));
@@ -108,7 +108,7 @@ namespace FoxTube.Controls.Player
return representation; return representation;
} }
private static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info) static XmlElement GetAudioPresentation(XmlDocument doc, StreamInfo.AudioInfo info)
{ {
XmlElement audio = doc.CreateElement("Representation"); XmlElement audio = doc.CreateElement("Representation");
audio.SetAttribute("bandwidth", "200000"); audio.SetAttribute("bandwidth", "200000");
@@ -133,7 +133,7 @@ namespace FoxTube.Controls.Player
return audio; return audio;
} }
private static async Task<StreamInfo> GetInfoAsync(Video info, VideoStreamInfo requestedQuality) static async Task<StreamInfo> GetInfoAsync(Video info, VideoStreamInfo requestedQuality)
{ {
try 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); Dictionary<string, string> dic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string[] paramsEncoded = query.TrimStart('?').Split("&"); string[] paramsEncoded = query.TrimStart('?').Split("&");
@@ -221,7 +221,7 @@ namespace FoxTube.Controls.Player
return dic; return dic;
} }
private static string GetBandwidth(string quality) static string GetBandwidth(string quality)
{ {
string parsed = quality.Split('p')[0]; string parsed = quality.Split('p')[0];
switch (quality) switch (quality)
@@ -301,41 +301,4 @@ namespace FoxTube.Controls.Player
await f.DeleteAsync(StorageDeleteOption.PermanentDelete); 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 Microsoft.AppCenter.Analytics;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -9,7 +10,6 @@ using System.Net.Mail;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Windows.ApplicationModel.Core;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Windows.Storage.Streams; using Windows.Storage.Streams;
@@ -26,21 +26,47 @@ namespace FoxTube
public delegate void ObjectEventHandler(object sender = null, params object[] args); public delegate void ObjectEventHandler(object sender = null, params object[] args);
public interface INavigationPage
{
object Parameter { get; set; }
}
public static class Methods public static class Methods
{ {
private static readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("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 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) public static Uri ToUri(this string url)
@@ -49,10 +75,8 @@ namespace FoxTube
catch { return null; } catch { return null; }
} }
public static string GuardFromNull(string str) public static string GuardFromNull(string str) =>
{ str ?? string.Empty;
return str ?? string.Empty;
}
public static void SendMail(string content) public static void SendMail(string content)
{ {
@@ -68,22 +92,6 @@ namespace FoxTube
client.Send(msg); 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) public static List<object> ToReversedList(this Array array)
{ {
List<object> list = new List<object>(); List<object> list = new List<object>();
@@ -93,22 +101,15 @@ namespace FoxTube
return list; 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); array.ToList().ForEach(action);
}
public static T Find<T>(this IEnumerable<T> array, Predicate<T> match) public static T Find<T>(this IEnumerable<T> array, Predicate<T> match) =>
{ array.ToList().Find(match);
return array.ToList().Find(match);
}
public static List<T> FindAll<T>(this IEnumerable<T> array, Predicate<T> match) public static List<T> FindAll<T>(this IEnumerable<T> array, Predicate<T> match) =>
{ array.ToList().FindAll(match);
return array.ToList().FindAll(match);
}
[Obsolete]
public static string ReplaceInvalidChars(this string str, char newValue) public static string ReplaceInvalidChars(this string str, char newValue)
{ {
foreach (char i in Path.GetInvalidFileNameChars()) foreach (char i in Path.GetInvalidFileNameChars())
@@ -116,12 +117,7 @@ namespace FoxTube
return str; return str;
} }
[Obsolete] [Obsolete("Use *YoutubeExplode.Models.Video instead*")]
public static string Last(this string[] arr)
{
return arr[arr.Length - 1];
}
public static TimeSpan GetDuration(this string str) public static TimeSpan GetDuration(this string str)
{ {
try try
@@ -243,6 +239,8 @@ namespace FoxTube
} }
public async static void ProcessLink(string url) public async static void ProcessLink(string url)
{
try
{ {
string type; string type;
@@ -274,19 +272,21 @@ namespace FoxTube
switch (type) switch (type)
{ {
case "channel": case "channel":
MainPage.GoToChannel(output); Navigation.GoToChannel(output);
break; break;
case "video": case "video":
MainPage.GoToVideo(output); Navigation.GoToVideo(output);
break; break;
case "playlist": case "playlist":
MainPage.GoToPlaylist(output); Navigation.GoToPlaylist(output);
break; break;
case "user": case "user":
MainPage.GoToChannel(await new YoutubeClient().GetChannelIdAsync(output)); Navigation.GoToChannel(await new YoutubeClient().GetChannelIdAsync(output));
break; break;
} }
} }
catch { }
}
public static void Share(DataRequestedEventArgs args, string thumbnail, string title, string url, string type) 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())); request.Data.SetBitmap(RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri()));
} }
public static async Task<List<string>> GetHistory() public static async Task<YoutubeExplode.Models.Playlist> GetHistory()
{ {
SecretsVault.RefreshToken(); 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}"); return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("HL");
dynamic raw = JsonConvert.DeserializeObject(output);
foreach (dynamic i in raw.video)
list.Add(i.encrypted_id.ToString());
return list;
} }
public static async Task<List<string>> GetLater() public static async Task<YoutubeExplode.Models.Playlist> GetLater()
{ {
SecretsVault.RefreshToken(); 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}"); return await new YoutubeClient(SecretsVault.HttpClient).GetPlaylistAsync("WL");
dynamic raw = JsonConvert.DeserializeObject(output);
foreach (dynamic i in raw.video)
list.Add(i.encrypted_id.ToString());
return list;
} }
} }
} }
+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.Services;
using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data; using Google.Apis.YouTube.v3.Data;
using Newtonsoft.Json;
using Windows.Storage;
using Windows.Services.Store; using Windows.Services.Store;
using System.Net.Http; using System.Net.Http;
using Google.Apis.Oauth2.v2.Data; using Google.Apis.Oauth2.v2.Data;
@@ -15,6 +13,8 @@ using Google.Apis.Oauth2.v2;
using static Google.Apis.Auth.OAuth2.UwpCodeReceiver; using static Google.Apis.Auth.OAuth2.UwpCodeReceiver;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System.Net; using System.Net;
using FoxTube.Classes;
using System.Linq;
namespace FoxTube namespace FoxTube
{ {
@@ -22,12 +22,12 @@ namespace FoxTube
{ {
#region Properties #region Properties
//Events //Events
public static event ObjectEventHandler AuthorizationStateChanged; public static event AuthorizationChangedEventHandler AuthorizationStateChanged;
public static event ObjectEventHandler SubscriptionsChanged; public static event SubscriptionsChangedEventHandler SubscriptionsChanged;
public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version or after purchase
//Properties //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 private static ClientSecrets Secrets => new ClientSecrets
{ {
ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com",
@@ -38,7 +38,7 @@ namespace FoxTube
ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0",
ApplicationName = "FoxTube" ApplicationName = "FoxTube"
}); });
public static BaseClientService.Initializer Initializer => new BaseClientService.Initializer private static BaseClientService.Initializer Initializer => new BaseClientService.Initializer
{ {
HttpClientInitializer = Credential, HttpClientInitializer = Credential,
ApplicationName = "FoxTube" ApplicationName = "FoxTube"
@@ -46,7 +46,7 @@ namespace FoxTube
public static YouTubeService Service => IsAuthorized ? new YouTubeService(Initializer) : NoAuthService; public static YouTubeService Service => IsAuthorized ? new YouTubeService(Initializer) : NoAuthService;
public static HttpClient HttpClient { get; } = new HttpClient(); 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 AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh";
public static string AdUnitId => TestAds ? "test" : "1100037769"; public static string AdUnitId => TestAds ? "test" : "1100037769";
public static bool AdsDisabled { get; private set; } = true; public static bool AdsDisabled { get; private set; } = true;
@@ -60,15 +60,13 @@ namespace FoxTube
public static Userinfoplus UserInfo { get; private set; } public static Userinfoplus UserInfo { get; private set; }
public static List<Subscription> Subscriptions { get; } = new List<Subscription>(); public static List<Subscription> Subscriptions { get; } = new List<Subscription>();
public static List<string> History { get; set; } = new List<string>(); public static YoutubeExplode.Models.Playlist History { get; set; }
public static List<string> WatchLater { get; set; } = new List<string>(); public static YoutubeExplode.Models.Playlist WatchLater { get; set; }
#endregion #endregion
#region Methods #region Methods
public static void RefreshToken() public static void RefreshToken() =>
{
Credential?.RefreshTokenAsync(CancellationToken.None); Credential?.RefreshTokenAsync(CancellationToken.None);
}
/// <summary> /// <summary>
/// Subscribes or unsibscribes authorized user from the channel /// 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> /// <returns>Returns 'true' if channel is in subscriptions now; 'false' if it's not</returns>
public static async Task<bool> ChangeSubscriptionState(string id) 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(subscription.Id).ExecuteAsync(); }
try { await Service.Subscriptions.Delete(s.Id).ExecuteAsync(); }
catch { return true; } catch { return true; }
SubscriptionsChanged?.Invoke(null, "remove", s); SubscriptionsChanged?.Invoke("remove", subscription);
Subscriptions.Remove(s); Subscriptions.Remove(subscription);
SaveSubscriptions();
return false; return false;
} }
else 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, ChannelId = id,
Kind = "youtube#channel" Kind = "youtube#channel"
@@ -104,13 +99,12 @@ namespace FoxTube
} }
}, "snippet"); }, "snippet");
Subscription s = await request.ExecuteAsync(); if (!(await request.ExecuteAsync() is Subscription sub))
if (s == null)
return false; return false;
Subscriptions.Add(s);
SubscriptionsChanged?.Invoke(null, "add", s);
SaveSubscriptions(); Subscriptions.Add(sub);
SubscriptionsChanged?.Invoke("add", sub);
return true; return true;
} }
} }
@@ -140,23 +134,17 @@ namespace FoxTube
Oauth2Service.Scope.UserinfoProfile, Oauth2Service.Scope.UserinfoProfile,
Oauth2Service.Scope.UserinfoEmail, Oauth2Service.Scope.UserinfoEmail,
YouTubeService.Scope.YoutubeForceSsl, YouTubeService.Scope.YoutubeForceSsl,
YouTubeService.Scope.Youtube, YouTubeService.Scope.YoutubeUpload
YouTubeService.Scope.YoutubeUpload,
YouTubeService.Scope.YoutubeReadonly,
YouTubeService.Scope.Youtubepartner
}, },
"user", "user",
CancellationToken.None); CancellationToken.None);
await Credential.RefreshTokenAsync(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")) AuthorizationStateChanged?.Invoke(null);
return;
else
{
AuthorizationStateChanged?.Invoke(args: new bool?[] { null });
Analytics.TrackEvent("Failed to authorize", new Dictionary<string, string> Analytics.TrackEvent("Failed to authorize", new Dictionary<string, string>
{ {
{ "Exception", e.GetType().ToString() }, { "Exception", e.GetType().ToString() },
@@ -164,7 +152,6 @@ namespace FoxTube
{ "StackTrace", e.StackTrace } { "StackTrace", e.StackTrace }
}); });
} }
}
if (Credential == null || !retrieveSubs) if (Credential == null || !retrieveSubs)
return; return;
@@ -188,7 +175,6 @@ namespace FoxTube
subRequest.Order = SubscriptionsResource.ListRequest.OrderEnum.Relevance; subRequest.Order = SubscriptionsResource.ListRequest.OrderEnum.Relevance;
SubscriptionListResponse subResponse; SubscriptionListResponse subResponse;
string nextToken = null; string nextToken = null;
Subscriptions.Clear();
do do
{ {
@@ -200,22 +186,17 @@ namespace FoxTube
} while (!string.IsNullOrWhiteSpace(nextToken)); } while (!string.IsNullOrWhiteSpace(nextToken));
var request = Service.Channels.List("snippet,contentDetails"); ChannelsResource.ListRequest request = Service.Channels.List("snippet,contentDetails");
request.Mine = true; request.Mine = true;
UserChannel = (await request.ExecuteAsync()).Items[0]; UserChannel = (await request.ExecuteAsync()).Items.FirstOrDefault();
#endregion #endregion
//Saving user's subscriptions for background task AuthorizationStateChanged?.Invoke(true);
SaveSubscriptions();
AuthorizationStateChanged?.Invoke(args: true);
} }
catch (Exception e) catch (Exception e)
{ {
AuthorizationStateChanged?.Invoke(args: new bool?[] { null }); AuthorizationStateChanged?.Invoke(null);
Methods.SendMail($@"Exception: {e.GetType()} Methods.SendMail(e.ToString());
Message: {e.Message}
Stack trace: {e.StackTrace}");
Analytics.TrackEvent("Failed to retrieve user's info", new Dictionary<string, string> Analytics.TrackEvent("Failed to retrieve user's info", new Dictionary<string, string>
{ {
{ "Exception", e.GetType().ToString() }, { "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> /// <summary>
/// Deauthenticates current user /// Deauthenticates current user
/// </summary> /// </summary>
@@ -273,12 +217,13 @@ Stack trace: {e.StackTrace}");
Credential = null; Credential = null;
UserChannel = null; UserChannel = null;
UserInfo = null; UserInfo = null;
History.Clear(); History = null;
WatchLater.Clear(); WatchLater = null;
Subscriptions.Clear(); Subscriptions.Clear();
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
AuthorizationStateChanged?.Invoke(args: false); HttpClient.DefaultRequestHeaders.Authorization = null;
AuthorizationStateChanged?.Invoke(false);
SettingsStorage.HasAccount = false; SettingsStorage.HasAccount = false;
} }
@@ -291,10 +236,7 @@ Stack trace: {e.StackTrace}");
if (SettingsStorage.HasAccount) if (SettingsStorage.HasAccount)
Authorize(retrieveSubs); Authorize(retrieveSubs);
else else
{ AuthorizationStateChanged.Invoke(false);
AuthorizationStateChanged.Invoke(args: false);
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
}
} }
/// <summary> /// <summary>
+1 -1
View File
@@ -41,7 +41,7 @@ namespace FoxTube
private static string GetLanguage() 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; return CultureInfo.InstalledUICulture.Name;
else if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName)) else if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName))
return "ru-RU"; 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" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Stretch"
d:DesignHeight="290" d:DesignHeight="290"
d:DesignWidth="384" d:DesignWidth="384"
Visibility="Collapsed"
Opacity="0" Opacity="0"
Name="card"> Name="card">
@@ -17,19 +16,9 @@
<Storyboard x:Name="show"> <Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/> <DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard> </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> </UserControl.Resources>
<Button Padding="0" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}"> <Grid Name="grid" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid Name="grid">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition/> <RowDefinition/>
<RowDefinition Height="20"/> <RowDefinition Height="20"/>
@@ -61,5 +50,4 @@
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/> <TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
</Grid> </Grid>
</Button>
</UserControl> </UserControl>
+7 -12
View File
@@ -12,8 +12,8 @@ namespace FoxTube.Controls.Adverts
/// </summary> /// </summary>
public sealed partial class CardAdvert : UserControl public sealed partial class CardAdvert : UserControl
{ {
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
public NativeAdV2 advert; NativeAdV2 advert;
public CardAdvert() public CardAdvert()
{ {
InitializeComponent(); InitializeComponent();
@@ -22,22 +22,21 @@ namespace FoxTube.Controls.Adverts
manager.RequestAd(); manager.RequestAd();
} }
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) void ErrorOccurred(object sender, NativeAdErrorEventArgs e)
{ {
(Parent as AdaptiveGridView)?.Items.Remove(this); (Parent as AdaptiveGridView)?.Items.Remove(this);
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad"); 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; advert = e.NativeAd;
Initialize(); Initialize();
e.NativeAd.RegisterAdContainer(grid); e.NativeAd.RegisterAdContainer(grid);
} }
public void Initialize() void Initialize()
{ {
Visibility = Visibility.Visible;
title.Text = advert.Title; title.Text = advert.Title;
image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri()); image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri());
@@ -57,13 +56,9 @@ namespace FoxTube.Controls.Adverts
if (!string.IsNullOrWhiteSpace(advert.Rating)) if (!string.IsNullOrWhiteSpace(advert.Rating))
desc.Text += " " + advert.Rating; desc.Text += " " + advert.Rating;
}
void Image_ImageOpened(object sender, RoutedEventArgs e) =>
show.Begin(); 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> /// </summary>
public sealed partial class CommentAdvert : UserControl public sealed partial class CommentAdvert : UserControl
{ {
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId); readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
public NativeAdV2 advert; NativeAdV2 advert;
public CommentAdvert() public CommentAdvert()
{ {
InitializeComponent(); InitializeComponent();
manager.AdReady += AdReady; manager.AdReady += AdReady;
manager.ErrorOccurred += ErrorOccurred;
manager.RequestAd(); manager.RequestAd();
} }
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) void AdReady(object sender, NativeAdReadyEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad");
}
private void AdReady(object sender, NativeAdReadyEventArgs e)
{ {
advert = e.NativeAd; advert = e.NativeAd;
Initialize(); Initialize();
@@ -36,7 +30,7 @@ namespace FoxTube.Controls.Adverts
e.NativeAd.RegisterAdContainer(grid, new List<FrameworkElement> { cta }); e.NativeAd.RegisterAdContainer(grid, new List<FrameworkElement> { cta });
} }
private void Initialize() void Initialize()
{ {
title.Text = advert.Title; title.Text = advert.Title;
description.Text = Methods.GuardFromNull(advert.Description); description.Text = Methods.GuardFromNull(advert.Description);
+5 -14
View File
@@ -8,10 +8,9 @@ namespace FoxTube.Controls.Adverts
{ {
public sealed partial class PlayerAdvert : UserControl 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; NativeAdV2 advert;
readonly DispatcherTimer timer = new DispatcherTimer
DispatcherTimer timer = new DispatcherTimer()
{ {
Interval = TimeSpan.FromMilliseconds(10000) Interval = TimeSpan.FromMilliseconds(10000)
}; };
@@ -20,7 +19,6 @@ namespace FoxTube.Controls.Adverts
InitializeComponent(); InitializeComponent();
manager.AdReady += AdReady; manager.AdReady += AdReady;
manager.ErrorOccurred += ErrorOccurred;
timer.Tick += (s, e) => timer.Tick += (s, e) =>
{ {
@@ -29,12 +27,7 @@ namespace FoxTube.Controls.Adverts
}; };
} }
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e) void AdReady(object sender, NativeAdReadyEventArgs arg)
{
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad");
}
private void AdReady(object sender, NativeAdReadyEventArgs arg)
{ {
advert = arg.NativeAd; advert = arg.NativeAd;
@@ -68,12 +61,10 @@ namespace FoxTube.Controls.Adverts
show.Begin(); show.Begin();
} }
public void PushAdvert() public void PushAdvert() =>
{
manager.RequestAd(); manager.RequestAd();
}
private void Button_Click(object sender, RoutedEventArgs e) void Button_Click(object sender, RoutedEventArgs e)
{ {
timer.Stop(); timer.Stop();
hide.Begin(); hide.Begin();
+6 -10
View File
@@ -6,25 +6,21 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Stretch"
d:DesignHeight="290" d:DesignHeight="290"
d:DesignWidth="384" d:DesignWidth="384"
MaxWidth="700" MaxWidth="700"
MaxHeight="600"
Opacity="0" Opacity="0"
Name="card" Name="card">
SizeChanged="Card_SizeChanged">
<UserControl.Resources> <UserControl.Resources>
<Storyboard x:Name="show"> <Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/> <DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard> </Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="cover" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources> </UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click"> <Grid Name="grid" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid Name="grid">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto"/> <RowDefinition Height="auto"/>
<RowDefinition Height="50"/> <RowDefinition Height="50"/>
@@ -33,7 +29,7 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image Source="/Assets/ChannelCoverTemplate.png" Stretch="UniformToFill" Opacity=".0001"/> <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"> <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"/> <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"/> <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>
</Grid> </Grid>
</Button>
<UserControl.ContextFlyout> <UserControl.ContextFlyout>
<MenuFlyout> <MenuFlyout>
<MenuFlyoutItem x:Uid="/Cards/channel" Icon="Contact" Text="View channel" Click="Button_Click"/> <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 Google.Apis.YouTube.v3.Data;
using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.Toolkit.Uwp.UI.Controls;
using System; using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Windows.System; using Windows.System;
@@ -16,32 +18,33 @@ namespace FoxTube.Controls
/// <summary> /// <summary>
/// Channel item card /// Channel item card
/// </summary> /// </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; Channel item;
public ChannelCard(string id, string live = null) public ChannelCard(string id, string live = null)
{ {
InitializeComponent(); InitializeComponent();
Initialize(id, live); channelId = id;
this.live = live;
} }
public async void Initialize(string id, string live) public async Task Initialize()
{ {
try try
{ {
if (id == SecretsVault.AccountId) if (channelId == SecretsVault.AccountId)
grid.RowDefinitions[3].Height = new GridLength(0); grid.RowDefinitions[3].Height = new GridLength(0);
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings"); ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
request.Id = id; request.Id = channelId;
ChannelListResponse response = await request.ExecuteAsync(); ChannelListResponse response = await request.ExecuteAsync();
item = response.Items[0]; item = response.Items[0];
channelId = id;
title.Text = item.Snippet.Title; title.Text = item.Snippet.Title;
description.Text = item.Snippet.Description; description.Text = item.Snippet.Description;
@@ -54,7 +57,7 @@ namespace FoxTube.Controls
if (SecretsVault.IsAuthorized) 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.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray); subscribe.Foreground = new SolidColorBrush(Colors.Gray);
@@ -63,10 +66,9 @@ namespace FoxTube.Controls
subscriptionPane.Visibility = Visibility.Visible; subscriptionPane.Visibility = Visibility.Visible;
} }
try { avatar.ProfilePicture = new BitmapImage(new Uri(item.Snippet.Thumbnails.Medium.Url)) { DecodePixelWidth = 74, DecodePixelHeight = 74 }; }
catch { }
try try
{ {
avatar.ProfilePicture = new BitmapImage(new Uri(item.Snippet.Thumbnails.Medium.Url)) { DecodePixelWidth = 74, DecodePixelHeight = 74 };
if (!item.BrandingSettings.Image.BannerImageUrl.Contains("default")) if (!item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
cover.Source = new BitmapImage(item.BrandingSettings.Image.BannerMobileImageUrl.ToUri()); cover.Source = new BitmapImage(item.BrandingSettings.Image.BannerMobileImageUrl.ToUri());
} }
@@ -80,25 +82,22 @@ namespace FoxTube.Controls
{ {
{ "Exception", e.GetType().ToString() }, { "Exception", e.GetType().ToString() },
{ "Message", e.Message }, { "Message", e.Message },
{ "Video ID", id }, { "Video ID", channelId },
{ "StackTrace", e.StackTrace } { "StackTrace", e.StackTrace }
}); });
} }
show.Begin();
} }
public void Button_Click(object sender, RoutedEventArgs e) public void ItemClicked() =>
{ Navigation.GoToChannel(channelId);
Methods.MainPage.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(); SecretsVault.Authorize();
}
private async void subscribe_Click(object sender, RoutedEventArgs e) async void subscribe_Click(object sender, RoutedEventArgs e)
{ {
if (await SecretsVault.ChangeSubscriptionState(channelId)) 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(); DataPackage data = new DataPackage();
data.SetText($"https://www.youtube.com/channel/{item.Id}"); data.SetText($"https://www.youtube.com/channel/{item.Id}");
Clipboard.SetContent(data); 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()); await Launcher.LaunchUriAsync($"https://www.youtube.com/channel/{item.Id}".ToUri());
}
private void Cover_ImageOpened(object sender, RoutedEventArgs e) void Cover_ImageOpened(object sender, RoutedEventArgs e) =>
{ show.Begin();
showThumb.Begin();
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
} }
} }
+7 -15
View File
@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Input;
@@ -10,6 +9,7 @@ using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Popups; using Windows.UI.Popups;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using FoxTube.Classes;
namespace FoxTube.Controls namespace FoxTube.Controls
{ {
@@ -135,12 +135,10 @@ namespace FoxTube.Controls
catch { } catch { }
} }
private void replyBtn_Click(object sender, RoutedEventArgs e) void replyBtn_Click(object sender, RoutedEventArgs e) =>
{
replyEditor.Visibility = replyEditor.Visibility == Visibility.Visible? Visibility.Collapsed : Visibility.Visible; 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; replies.Visibility = replies.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
@@ -169,15 +167,11 @@ namespace FoxTube.Controls
processing.Visibility = Visibility.Collapsed; processing.Visibility = Visibility.Collapsed;
} }
private void avatar_Tapped(object sender, TappedRoutedEventArgs e) void avatar_Tapped(object sender, TappedRoutedEventArgs e) =>
{ Navigation.GoToChannel(item.Snippet.AuthorChannelId.ToString().Split('"')[3]);
Methods.MainPage.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; send.IsEnabled = reply.Text.Length == 0 ? false : true;
}
private async void send_Click(object sender, RoutedEventArgs args) private async void send_Click(object sender, RoutedEventArgs args)
{ {
@@ -283,10 +277,8 @@ namespace FoxTube.Controls
processing.Visibility = Visibility.Collapsed; 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; editorSend.IsEnabled = editorText.Text.Length == 0 ? false : true;
}
private void editBtn_Click(object sender, RoutedEventArgs e) 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.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
@@ -10,26 +12,27 @@ namespace FoxTube.Controls
public LoadingPage LoadingPage => loading; public LoadingPage LoadingPage => loading;
public event NavigatedEventHandler Navigated; public event NavigatedEventHandler Navigated;
public new object Content => content.Content;
public ContentFrame() public ContentFrame()
{ {
InitializeComponent(); InitializeComponent();
content.Navigated += (s, e) => Navigated?.Invoke(s, e); content.Navigated += (s, e) => Navigated?.Invoke(s, e);
} }
private void Content_Navigating(object sender, NavigatingCancelEventArgs e) public void Navigate(Type sourcePageType, object parameter) =>
{ content.Navigate(sourcePageType, parameter);
loading.Refresh();
}
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.Navigate(content.CurrentSourcePageType, (content.Content as INavigationPage).Parameter);
content.BackStack.RemoveAt(content.BackStack.Count - 1); content.BackStack.RemoveAt(content.BackStack.Count - 1);
} }
public void Refresh() public void Refresh() =>
{
Loading_RefreshPage(this, null); Loading_RefreshPage(this, null);
} }
} }
}
-1
View File
@@ -9,7 +9,6 @@
d:DesignHeight="100" d:DesignHeight="100"
d:DesignWidth="1500"> 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 Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Height="100" HorizontalAlignment="Stretch" Margin="5">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/> <ColumnDefinition Width="auto"/>
+18 -23
View File
@@ -7,7 +7,6 @@ using Windows.System;
using YoutubeExplode.Models.MediaStreams; using YoutubeExplode.Models.MediaStreams;
using YoutubeExplode; using YoutubeExplode;
using Windows.Storage; using Windows.Storage;
using Google.Apis.YouTube.v3.Data;
using System.Threading; using System.Threading;
using Windows.UI.Popups; using Windows.UI.Popups;
using Windows.UI.Notifications; using Windows.UI.Notifications;
@@ -17,6 +16,8 @@ using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using YoutubeExplode.Models;
using FoxTube.Classes;
namespace FoxTube.Controls namespace FoxTube.Controls
{ {
@@ -52,14 +53,14 @@ namespace FoxTube.Controls
Container = new DownloadItemContainer() Container = new DownloadItemContainer()
{ {
Channel = meta.Snippet.ChannelTitle, Channel = meta.Author,
Duration = Methods.GetDuration(meta.ContentDetails.Duration), Duration = meta.Duration,
Extension = info.Container.GetFileExtension(), Extension = info.Container.GetFileExtension(),
Id = meta.Id, Id = meta.Id,
IsDownloaded = false, IsDownloaded = false,
Quality = q, Quality = q,
Thumbnail = meta.Snippet.Thumbnails.Medium.Url.ToUri(), Thumbnail = meta.Thumbnails.MediumResUrl.ToUri(),
Title = meta.Snippet.Title Title = meta.Title
}; };
Download(info); Download(info);
@@ -102,16 +103,16 @@ namespace FoxTube.Controls
timer.Tick += (s, e) => UpdateInfo(); timer.Tick += (s, e) => UpdateInfo();
#region Polling notification #region Polling notification
ToastContent toastContent = new ToastContent() ToastContent toastContent = new ToastContent
{ {
Visual = new ToastVisual() Visual = new ToastVisual
{ {
BindingGeneric = new ToastBindingGeneric() BindingGeneric = new ToastBindingGeneric
{ {
Children = Children =
{ {
new AdaptiveText() { Text = resources.GetString("/Downloads/toastStartHeader") }, new AdaptiveText { Text = resources.GetString("/Downloads/toastStartHeader") },
new AdaptiveProgressBar() new AdaptiveProgressBar
{ {
Title = Container.Title, Title = Container.Title,
Status = resources.GetString("/Downloads/downloading/Text"), Status = resources.GetString("/Downloads/downloading/Text"),
@@ -121,7 +122,7 @@ namespace FoxTube.Controls
} }
}, },
Actions = new ToastActionsCustom() Actions = new ToastActionsCustom
{ {
Buttons = Buttons =
{ {
@@ -197,7 +198,7 @@ namespace FoxTube.Controls
} }
} }
private void UpdateInfo() void UpdateInfo()
{ {
progressBar.Value = percentage; progressBar.Value = percentage;
progressText.Text = Math.Round(percentage * 100, 1) + "%"; progressText.Text = Math.Round(percentage * 100, 1) + "%";
@@ -206,7 +207,7 @@ namespace FoxTube.Controls
ToastNotificationManager.CreateToastNotifier().Update(data, $"download|{Container.Id}"); ToastNotificationManager.CreateToastNotifier().Update(data, $"download|{Container.Id}");
} }
private void DownloadCompleted() void DownloadCompleted()
{ {
Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument(); Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
template.LoadXml($@"<toast activationType='foreground' launch='download'> template.LoadXml($@"<toast activationType='foreground' launch='download'>
@@ -229,15 +230,11 @@ namespace FoxTube.Controls
progressPanel.Visibility = Visibility.Collapsed; 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); await Launcher.LaunchFileAsync(File);
}
private void gotoOriginal_Click(object sender, RoutedEventArgs e) void gotoOriginal_Click(object sender, RoutedEventArgs e) =>
{ Navigation.GoToVideo(Container.Id);
Methods.MainPage.GoToVideo(Container.Id);
}
public async void Cancel(bool prompt = true) 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(); 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.Adverts;
using FoxTube.Controls.Player; using FoxTube.Controls.Player;
using Google.Apis.YouTube.v3.Data; using Google.Apis.YouTube.v3.Data;
@@ -6,57 +7,29 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Windows.Foundation; using Windows.Foundation;
using Windows.Graphics.Display; using Windows.Graphics.Display;
using Windows.Media;
using Windows.Media.Core; using Windows.Media.Core;
using Windows.UI.Core;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
using YoutubeExplode; using YoutubeExplode;
using YoutubeExplode.Models.ClosedCaptions; using YoutubeExplode.Models.ClosedCaptions;
using YoutubeExplode.Models.MediaStreams; 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> /// <summary>
/// Custom controls for media player. MARKUP IS IN **Themes/Generic.xaml**!!! /// Custom controls for media player. MARKUP IS IN **Themes/Generic.xaml**!!!
/// </summary> /// </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 #region Controls variables
Button minimize; Button minimize;
@@ -64,6 +37,7 @@ namespace FoxTube
Button miniview; Button miniview;
Button play; Button play;
Button next; Button next;
Button prev;
Button volumeMenu; Button volumeMenu;
Button mute; Button mute;
Button live; Button live;
@@ -97,17 +71,12 @@ namespace FoxTube
Grid centerTrigger; Grid centerTrigger;
#endregion #endregion
PlayerDisplayState State { get; set; } = PlayerDisplayState.Normal;
public MediaElement Player { get; set; }
public PlayerAdvert Advert; public PlayerAdvert Advert;
public LiveCaptions Caption; public LiveCaptions Caption;
TimeSpan timecodeBackup; TimeSpan timecodeBackup;
bool needUpdateTimecode = false; bool needUpdateTimecode = false;
public Video Meta { get; set; }
public IReadOnlyList<ClosedCaptionTrackInfo> ClosedCaptions { get; set; } public IReadOnlyList<ClosedCaptionTrackInfo> ClosedCaptions { get; set; }
public MediaStreamInfoSet MediaStreams { get; set; } public MediaStreamInfoSet MediaStreams { get; set; }
@@ -125,11 +94,22 @@ namespace FoxTube
isReady = true; isReady = true;
Player.MediaOpened += Player_MediaOpened;
Player.MarkerReached += (s, e) => PushAdvert();
Player.CurrentStateChanged += Player_CurrentStateChanged;
Caption.Player = Player;
minimize.Click += Minimize_Click; minimize.Click += Minimize_Click;
close.Click += Close_Click; close.Click += Close_Click;
miniview.Click += Miniview_Click; miniview.Click += Miniview_Click;
mute.Click += async (s, e) =>
{
await Task.Delay(10);
Volume_ValueChanged(this, null);
};
next.Click += Next_Click; next.Click += Next_Click;
prev.Click += Prev_Click;
volume.ValueChanged += Volume_ValueChanged; volume.ValueChanged += Volume_ValueChanged;
playbackSpeed.ValueChanged += PlaybackSpeed_ValueChanged; playbackSpeed.ValueChanged += PlaybackSpeed_ValueChanged;
live.Click += Live_Click; live.Click += Live_Click;
@@ -139,8 +119,19 @@ namespace FoxTube
quality.SelectionChanged += Quality_SelectionChanged; quality.SelectionChanged += Quality_SelectionChanged;
seek.ValueChanged += Seek_ValueChanged; 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); Rect view = new Rect(0, 0, centerTrigger.ActualWidth, centerTrigger.ActualHeight);
Point p = e.GetPosition(centerTrigger); Point p = e.GetPosition(centerTrigger);
@@ -160,10 +151,20 @@ namespace FoxTube
base.OnApplyTemplate(); 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() void AssignControls()
{ {
@@ -172,6 +173,7 @@ namespace FoxTube
miniview = GetTemplateChild("CompactOverlayButton") as Button; miniview = GetTemplateChild("CompactOverlayButton") as Button;
play = GetTemplateChild("PlayPauseButton") as Button; play = GetTemplateChild("PlayPauseButton") as Button;
next = GetTemplateChild("NextButton") as Button; next = GetTemplateChild("NextButton") as Button;
prev = GetTemplateChild("PreviousButton") as Button;
volumeMenu = GetTemplateChild("VolumeMenuButton") as Button; volumeMenu = GetTemplateChild("VolumeMenuButton") as Button;
mute = GetTemplateChild("AudioMuteButton") as Button; mute = GetTemplateChild("AudioMuteButton") as Button;
live = GetTemplateChild("PlayLiveButton") as Button; live = GetTemplateChild("PlayLiveButton") as Button;
@@ -207,14 +209,14 @@ namespace FoxTube
centerTrigger = GetTemplateChild("centerTrigger") as Grid; 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; seekIndicator.Value = seek.Value;
}
private async void Quality_SelectionChanged(object sender, SelectionChangedEventArgs e) 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; goto SetQuality;
if(!needUpdateTimecode) if(!needUpdateTimecode)
timecodeBackup = Player.Position; timecodeBackup = Player.Position;
@@ -227,7 +229,7 @@ namespace FoxTube
if (info is MuxedStreamInfo) if (info is MuxedStreamInfo)
Player.SetPlaybackSource(MediaSource.CreateFromUri((info as MuxedStreamInfo).Url.ToUri())); Player.SetPlaybackSource(MediaSource.CreateFromUri((info as MuxedStreamInfo).Url.ToUri()));
else if (info is VideoStreamInfo || info == null) 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) else if (info is StreamQuality)
Player.SetPlaybackSource(MediaSource.CreateFromUri((info as StreamQuality).Url)); Player.SetPlaybackSource(MediaSource.CreateFromUri((info as StreamQuality).Url));
} }
@@ -256,9 +258,15 @@ namespace FoxTube
private void Next_Click(object sender, RoutedEventArgs e) 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) private void Miniview_Click(object sender, RoutedEventArgs e)
{ {
if (State == PlayerDisplayState.Compact) if (State == PlayerDisplayState.Compact)
@@ -267,11 +275,6 @@ namespace FoxTube
EnterMiniview(); EnterMiniview();
} }
public void UpdateVolumeIcon()
{
Volume_ValueChanged(this, null);
}
private void Volume_ValueChanged(object sender, RangeBaseValueChangedEventArgs e) private void Volume_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{ {
double v = volume.Value; double v = volume.Value;
@@ -289,6 +292,13 @@ namespace FoxTube
private void Player_MediaOpened(object sender, RoutedEventArgs args) 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) if (!needUpdateTimecode)
return; return;
@@ -298,7 +308,10 @@ namespace FoxTube
private void Close_Click(object sender, RoutedEventArgs e) 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) private void Minimize_Click(object sender, RoutedEventArgs e)
@@ -314,6 +327,8 @@ namespace FoxTube
if (State == PlayerDisplayState.Minimized) if (State == PlayerDisplayState.Minimized)
return; return;
//Methods.MainPage.MinimizeVideo(true);
header.Children.Remove(minimize); header.Children.Remove(minimize);
center.Children.Add(minimize); center.Children.Add(minimize);
rightHeader.Children.Remove(close); rightHeader.Children.Remove(close);
@@ -331,7 +346,7 @@ namespace FoxTube
minimize.Content = "\xE010"; minimize.Content = "\xE010";
MiniModeChanged.Invoke(this, true); MiniModeChanged?.Invoke(true);
Caption.Minimize(); Caption.Minimize();
State = PlayerDisplayState.Minimized; State = PlayerDisplayState.Minimized;
@@ -342,6 +357,8 @@ namespace FoxTube
if (State == PlayerDisplayState.Normal) if (State == PlayerDisplayState.Normal)
return; return;
//Methods.MainPage.MaximizeVideo(true);
if(State == PlayerDisplayState.Compact) if(State == PlayerDisplayState.Compact)
{ {
center.Children.Remove(miniview); center.Children.Remove(miniview);
@@ -370,7 +387,7 @@ namespace FoxTube
centerStack.Children.Remove(bwd); centerStack.Children.Remove(bwd);
rightFooter.Children.Insert(0, bwd); rightFooter.Children.Insert(0, bwd);
MiniModeChanged.Invoke(this, false); MiniModeChanged?.Invoke(false);
} }
drag.Visibility = Visibility.Collapsed; drag.Visibility = Visibility.Collapsed;
@@ -413,120 +430,27 @@ namespace FoxTube
State = PlayerDisplayState.Compact; State = PlayerDisplayState.Compact;
} }
public async void Load(Video meta) void PushAdvert()
{
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()
{ {
if(State == PlayerDisplayState.Normal) if(State == PlayerDisplayState.Normal)
Advert.PushAdvert(); 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:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:foxtube="using:FoxTube" xmlns:foxtube="using:FoxTube.Controls.Player"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="1080" d:DesignHeight="1080"
d:DesignWidth="1920" d:DesignWidth="1920"
+10 -9
View File
@@ -7,6 +7,7 @@ using Windows.UI.Xaml.Media.Imaging;
using Windows.Media; using Windows.Media;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
using FoxTube.Controls.Player;
namespace FoxTube namespace FoxTube
{ {
@@ -45,7 +46,7 @@ namespace FoxTube
if (item.Snippet.LiveBroadcastContent == "none") if (item.Snippet.LiveBroadcastContent == "none")
{ {
InitializeContols(); InitializeContols();
Controls.Load(item); //Controls.Load(item);
if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5) if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5)
videoSource.Markers.Add(new TimelineMarker { Time = Methods.GetDuration(item.ContentDetails.Duration) - TimeSpan.FromMinutes(1) }); 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") else if (item.Snippet.LiveBroadcastContent == "live")
{ {
InitializeContols(); InitializeContols();
Controls.Load(item); //Controls.Load(item);
} }
else else
videoSource.AreTransportControlsEnabled = false; videoSource.AreTransportControlsEnabled = false;
@@ -73,9 +74,9 @@ namespace FoxTube
videoSource.AutoPlay = SettingsStorage.Autoplay; videoSource.AutoPlay = SettingsStorage.Autoplay;
Controls.CloseRequested += Controls_CloseRequested; Controls.CloseRequested += Controls_CloseRequested;
Controls.NextRequested += (s, e) => NextClicked?.Invoke(); Controls.NextRequested += () => NextClicked?.Invoke();
Controls.MiniModeChanged += Controls_MiniModeChanged; Controls.MiniModeChanged += Controls_MiniModeChanged;
Controls.Player = videoSource; //Controls.Player = videoSource;
#region System Media Transport Controls #region System Media Transport Controls
systemControls = SystemMediaTransportControls.GetForCurrentView(); 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; videoSource.IsFullWindow = false;
@@ -125,7 +126,7 @@ namespace FoxTube
Controls.Minimize(); Controls.Minimize();
} }
public void Controls_CloseRequested(object sender, RoutedEventArgs e) public void Controls_CloseRequested()
{ {
videoSource.Pause(); videoSource.Pause();
systemControls.IsEnabled = false; systemControls.IsEnabled = false;
@@ -137,7 +138,7 @@ namespace FoxTube
HistorySet.Update(history); HistorySet.Update(history);
} }
Methods.MainPage.CloseVideo(); //Methods.MainPage.CloseVideo();
} }
public void Pause() public void Pause()
@@ -161,12 +162,12 @@ namespace FoxTube
{ {
if(videoSource.Volume != 0) if(videoSource.Volume != 0)
SettingsStorage.Volume = videoSource.Volume; SettingsStorage.Volume = videoSource.Volume;
Controls.UpdateVolumeIcon(); //Controls.UpdateVolumeIcon();
} }
private void VideoSource_MarkerReached(object sender, TimelineMarkerRoutedEventArgs e) 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" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Stretch"
d:DesignHeight="290" d:DesignHeight="290"
d:DesignWidth="384" d:DesignWidth="384"
MaxWidth="700" MaxWidth="700"
MaxHeight="600"
Opacity="0" Opacity="0"
Name="card" Name="card">
SizeChanged="Card_SizeChanged">
<UserControl.Resources> <UserControl.Resources>
<Storyboard x:Name="show"> <Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/> <DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard> </Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources> </UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click"> <Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition/> <RowDefinition/>
<RowDefinition Height="20"/> <RowDefinition Height="20"/>
@@ -32,7 +28,7 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image Source="/Assets/videoPlaceholder.png" Opacity=".0001" Stretch="Fill"/> <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 HorizontalAlignment="Right" Width="100">
<Grid.Background> <Grid.Background>
@@ -58,7 +54,7 @@
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" TextTrimming="CharacterEllipsis" Margin="5" MaxLines="2"/> <TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" TextTrimming="CharacterEllipsis" Margin="5" MaxLines="2"/>
</Grid> </Grid>
</Button>
<UserControl.ContextFlyout> <UserControl.ContextFlyout>
<MenuFlyout> <MenuFlyout>
<MenuFlyoutItem x:Uid="/Cards/playlist" Icon="List" Text="View playlist" Click="Button_Click"/> <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 Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.Toolkit.Uwp.UI.Controls;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.System; using Windows.System;
using Windows.UI.Xaml; using Windows.UI.Xaml;
@@ -16,24 +18,21 @@ namespace FoxTube.Controls
/// <summary> /// <summary>
/// Playlist card control /// Playlist card control
/// </summary> /// </summary>
public sealed partial class PlaylistCard : UserControl public sealed partial class PlaylistCard : UserControl, ICard
{ {
Playlist item; Playlist item;
public string playlistId; readonly string playlistId;
public bool NeedInitialize { get; set; } = true;
public PlaylistCard(string id) public PlaylistCard(string id)
{ {
InitializeComponent(); InitializeComponent();
Initialize(id); playlistId = id;
} }
public async void Initialize(string id) public async Task Initialize()
{ {
try try
{ {
playlistId = id;
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails"); PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails");
request.Id = playlistId; request.Id = playlistId;
request.Hl = SettingsStorage.RelevanceLanguage; request.Hl = SettingsStorage.RelevanceLanguage;
@@ -44,17 +43,12 @@ namespace FoxTube.Controls
counter.Text = item.ContentDetails.ItemCount.ToString(); counter.Text = item.ContentDetails.ItemCount.ToString();
date.Text = Methods.GetAgo(item.Snippet.PublishedAt.Value); date.Text = Methods.GetAgo(item.Snippet.PublishedAt.Value);
ChannelsResource.ListRequest r = SecretsVault.Service.Channels.List("snippet");
r.Id = item.Snippet.ChannelId;
try try
{ {
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); 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 { } catch { }
show.Begin();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -70,36 +64,26 @@ namespace FoxTube.Controls
} }
} }
public void Button_Click(object sender, RoutedEventArgs e) public void ItemClicked() =>
{ Navigation.GoToPlaylist(item.Id);
Methods.MainPage.GoToPlaylist(item.Id);
}
private void OpenChannel_Click(object sender, RoutedEventArgs e) public void Button_Click(object sender, RoutedEventArgs e) =>
{ ItemClicked();
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
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(); DataPackage data = new DataPackage();
data.SetText($"https://www.youtube.com/playlist?list={playlistId}"); data.SetText($"https://www.youtube.com/playlist?list={playlistId}");
Clipboard.SetContent(data); 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()); await Launcher.LaunchUriAsync($"https://www.youtube.com/playlist?list={playlistId}".ToUri());
}
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) =>
{ show.Begin();
showThumb.Begin();
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
} }
} }
+1 -14
View File
@@ -6,10 +6,8 @@ namespace FoxTube.Controls
public sealed partial class ShowMore : UserControl public sealed partial class ShowMore : UserControl
{ {
public event Event Clicked; public event Event Clicked;
public ShowMore() public ShowMore() =>
{
InitializeComponent(); InitializeComponent();
}
private void btn_Click(object sender, RoutedEventArgs e) private void btn_Click(object sender, RoutedEventArgs e)
{ {
@@ -18,17 +16,6 @@ namespace FoxTube.Controls
Clicked.Invoke(); Clicked.Invoke();
} }
public void Invoke()
{
btn_Click(this, null);
}
public void Show()
{
btn.Visibility = Visibility.Collapsed;
bar.Visibility = Visibility.Visible;
}
public void Complete() public void Complete()
{ {
bar.Visibility = Visibility.Collapsed; bar.Visibility = Visibility.Collapsed;
+12 -21
View File
@@ -6,25 +6,21 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Stretch"
d:DesignHeight="290" d:DesignHeight="290"
d:DesignWidth="384" d:DesignWidth="384"
MaxWidth="700" MaxWidth="700"
MaxHeight="600"
Opacity="0" Opacity="0"
Name="card" Name="card">
SizeChanged="Card_SizeChanged">
<UserControl.Resources> <UserControl.Resources>
<Storyboard x:Name="show"> <Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/> <DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard> </Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources> </UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click"> <Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition/> <RowDefinition/>
<RowDefinition Height="20"/> <RowDefinition Height="20"/>
@@ -32,20 +28,20 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image Source="/Assets/videoPlaceholder.png" Opacity=".0001" Stretch="Fill"/> <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"> <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"/> <TextBlock x:Uid="/Cards/watched" Text="Watched" Foreground="Gray" FontSize="12"/>
</StackPanel> </Border>
<ProgressBar VerticalAlignment="Bottom" Margin="54,0,0,0" Foreground="Red" Name="leftOn"/> <ProgressBar VerticalAlignment="Bottom" Margin="54,0,0,0" Name="leftOn"/>
</Grid> </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"/> <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 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"/> <TextBlock x:Uid="/Cards/live" Name="liveContent" Text="LIVE" VerticalAlignment="Center" Foreground="White" FontSize="12" FontWeight="Bold"/>
</StackPanel> </StackPanel>
@@ -64,7 +60,6 @@
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/> <TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
</Grid> </Grid>
</Button>
<UserControl.ContextFlyout> <UserControl.ContextFlyout>
<MenuFlyout> <MenuFlyout>
<MenuFlyoutItem x:Uid="/Cards/play" Icon="Play" Text="Play" Name="play" Click="Button_Click"/> <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"/> <MenuFlyoutItem x:Uid="/Cards/share" Icon="Share" Text="Share" Name="share" Click="share_Click"/>
<MenuFlyoutSeparator/> <MenuFlyoutSeparator/>
<MenuFlyoutSubItem x:Uid="/Cards/downloads" Icon="Download" Text="Download" Name="download"/> <MenuFlyoutSubItem x:Uid="/Cards/downloads" Icon="Download" Text="Download" Name="download"/>
<MenuFlyoutSubItem x:Uid="/Cards/addTo" Icon="Add" Text="Add to" Name="addTo"> <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>
<MenuFlyoutItem x:Uid="/Cards/delete" Text="Remove from playlist" Icon="Delete" Visibility="Collapsed" Name="delete" Click="Delete_Click"/> <MenuFlyoutItem x:Uid="/Cards/delete" Text="Remove from playlist" Icon="Delete" Visibility="Collapsed" Name="delete" Click="Delete_Click"/>
</MenuFlyout> </MenuFlyout>
</UserControl.ContextFlyout> </UserControl.ContextFlyout>
+83 -375
View File
@@ -2,7 +2,6 @@
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Windows.UI.Xaml.Media.Imaging; using Windows.UI.Xaml.Media.Imaging;
using Windows.System; using Windows.System;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
@@ -10,112 +9,108 @@ using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System.Collections.Generic; using System.Collections.Generic;
using YoutubeExplode; using YoutubeExplode;
using YoutubeExplode.Models;
using Windows.UI.Popups; using Windows.UI.Popups;
using YoutubeExplode.Models.MediaStreams;
using Windows.Foundation; using Windows.Foundation;
using FoxTube.Pages; using FoxTube.Pages;
using Windows.Networking.Connectivity; using Windows.Networking.Connectivity;
using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.Toolkit.Uwp.UI.Controls;
using FoxTube.Classes;
using FoxTube.Controls.Common;
using System.Threading.Tasks;
using System.Linq;
namespace FoxTube.Controls namespace FoxTube.Controls
{ {
/// <summary> /// <summary>
/// Video item card /// Video item card
/// </summary> /// </summary>
public sealed partial class VideoCard : UserControl public sealed partial class VideoCard : UserControl, ICard
{ {
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards"); ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
public string playlistId; readonly string playlistId;
readonly string id;
Video item; Video item;
HistoryItem history; HistoryItem history;
Google.Apis.YouTube.v3.Data.Video liveItem;
public VideoCard(string id, string playlist = null) public VideoCard(string id, string playlist = null)
{ {
InitializeComponent(); InitializeComponent();
Initialize(id, playlist); this.id = id;
}
public VideoCard(Video meta, string playlist = null)
{
InitializeComponent();
item = meta;
playlistId = playlist; playlistId = playlist;
LoadMeta();
} }
public async void Initialize(string id, string playlist = null) public async Task Initialize()
{ {
try try
{ {
playlistId = playlist; item = await new YoutubeClient(SecretsVault.HttpClient).GetVideoAsync(id);
delete.Visibility = string.IsNullOrWhiteSpace(playlistId) ? Visibility.Collapsed : Visibility.Visible;
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.Id = id;
request.Hl = SettingsStorage.RelevanceLanguage; liveItem = (await request.ExecuteAsync()).Items[0];
item = (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; if (liveItem.LiveStreamingDetails.ActualStartTime.HasValue)
show.Begin();
return;
}
if (item.Snippet.LiveBroadcastContent == "live")
{ {
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}"; views.Text = $"{liveItem.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue) if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue && liveItem.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}"; info.Text = $"{liveItem.LiveStreamingDetails.ScheduledEndTime - liveItem.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(liveItem.LiveStreamingDetails.ActualStartTime.Value)}";
else else
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value); info.Text = Methods.GetAgo(liveItem.LiveStreamingDetails.ActualStartTime.Value);
liveTag.Visibility = Visibility.Visible;
download.Visibility = Visibility.Collapsed;
} }
else if (item.Snippet.LiveBroadcastContent == "upcoming") else
{ {
views.Text = ""; views.Text = "";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue) if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue && liveItem.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}"; info.Text = $"{liveItem.LiveStreamingDetails.ScheduledEndTime - liveItem.LiveStreamingDetails.ScheduledStartTime} | {liveItem.LiveStreamingDetails.ScheduledStartTime}";
else else
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; info.Text = Methods.GetAgo(item.UploadDate.DateTime);
liveTag.Visibility = Visibility.Visible;
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue) if (liveItem.LiveStreamingDetails.ScheduledStartTime.HasValue)
liveContent.Text = resources.GetString("/Cards/goesLive") + (item.LiveStreamingDetails.ScheduledStartTime.Value > DateTime.Now ? " " : " -") + (item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss"); liveContent.Text = resources.GetString("/Cards/goesLive") + (liveItem.LiveStreamingDetails.ScheduledStartTime.Value > DateTime.Now ? " " : " -") + (liveItem.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss");
else liveContent.Text = resources.GetString("/Cards/upcoming"); else liveContent.Text = resources.GetString("/Cards/upcoming");
}
liveTag.Visibility = Visibility.Visible;
download.Visibility = Visibility.Collapsed; download.Visibility = Visibility.Collapsed;
} }
else else
{ {
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}"; views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; info.Text = $"{item.Duration} | {item.UploadDate}";
LoadDownloads();
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 try
{ {
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); thumbnail.Source = new BitmapImage(item.Thumbnails.MediumResUrl.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).GetVideoAuthorChannelAsync(id)).LogoUrl.ToUri()) { DecodePixelHeight = 46, DecodePixelWidth = 46 };
} }
catch { } catch { }
if (SecretsVault.History.Contains(item.Id)) if (SecretsVault.History.Videos.Any(i => i.Id == id))
watched.Visibility = Visibility.Visible; watched.Visibility = Visibility.Visible;
if (HistorySet.Items.Exists(i => i.Id == item.Id)) if (HistorySet.Items.Exists(i => i.Id == item.Id))
{ {
history = HistorySet.Items.Find(i => i.Id == item.Id); history = HistorySet.Items.Find(i => i.Id == item.Id);
watched.Visibility = Visibility.Visible; 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) catch (Exception e)
{ {
@@ -133,95 +128,30 @@ namespace FoxTube.Controls
} }
} }
async void LoadDownloads() async void ValidatePlaylist()
{ {
try if (string.IsNullOrWhiteSpace(playlistId) || !SecretsVault.IsAuthorized)
{
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();
return; return;
}
if (item.Snippet.LiveBroadcastContent == "live") if (playlistId == "WL" || playlistId == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
{ delete.Visibility = Visibility.Visible;
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}";
else
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
liveTag.Visibility = Visibility.Visible;
}
else if (item.Snippet.LiveBroadcastContent == "upcoming")
{
views.Text = "";
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {item.LiveStreamingDetails.ScheduledStartTime}";
else
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
liveTag.Visibility = Visibility.Visible;
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {item.LiveStreamingDetails.ScheduledStartTime - DateTime.Now}";
else liveContent.Text = resources.GetString("/Cards/upcoming");
}
else else
{ {
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}"; PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet");
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}"; 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()); public void ItemClicked() =>
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 }; ItemClicked(false);
if (SecretsVault.History.Contains(item.Id)) async void ItemClicked(bool incognito = false)
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)
{ {
if (item.ContentDetails.ContentRating != null) if (liveItem.ContentDetails.ContentRating != null)
{ {
if (SecretsVault.IsAuthorized) if (SecretsVault.IsAuthorized)
{ {
@@ -268,11 +198,11 @@ namespace FoxTube.Controls
{ {
try 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", Kind = "youtube#video",
VideoId = item.Id VideoId = item.Id
@@ -290,251 +220,37 @@ namespace FoxTube.Controls
return; 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, Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url, item.Thumbnails.MediumResUrl,
item.Snippet.Title, item.Title,
$"https://www.youtube.com/watch?v={item.Id}", item.GetShortUrl(),
resources.GetString("/Cards/videoShare")); 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.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
DataTransferManager.ShowShareUI(); DataTransferManager.ShowShareUI();
} }
private void ViewChannel_Click(object sender, RoutedEventArgs e) async void ViewChannel_Click(object sender, RoutedEventArgs e) =>
{ Navigation.GoToChannel((await new YoutubeClient(SecretsVault.HttpClient).GetVideoAuthorChannelAsync(id)).Id);
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
private void GetLink_Click(object sender, RoutedEventArgs e) void GetLink_Click(object sender, RoutedEventArgs e)
{ {
DataPackage data = new DataPackage(); DataPackage data = new DataPackage();
data.SetText($"https://www.youtube.com/watch?v={item.Id}"); data.SetText(item.GetShortUrl());
Clipboard.SetContent(data); Clipboard.SetContent(data);
} }
private async void InBrowser_Click(object sender, RoutedEventArgs e) async void InBrowser_Click(object sender, RoutedEventArgs e) =>
{ await Launcher.LaunchUriAsync(item.GetShortUrl().ToUri());
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={item.Id}".ToUri());
}
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) void Thumbnail_ImageOpened(object sender, RoutedEventArgs e) =>
{ show.Begin();
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;
}
}
}
private async void Delete_Click(object sender, RoutedEventArgs e) private async void Delete_Click(object sender, RoutedEventArgs e)
{ {
@@ -550,7 +266,7 @@ namespace FoxTube.Controls
else else
{ {
HistorySet.Delete(history); HistorySet.Delete(history);
(Methods.MainPage.PageContent.Frame.Content as History).Delete(this); (Navigation.Frame.Frame.Content as History).Delete(this);
} }
return; return;
} }
@@ -559,23 +275,15 @@ namespace FoxTube.Controls
PlaylistItemsResource.ListRequest request = SecretsVault.Service.PlaylistItems.List("snippet"); PlaylistItemsResource.ListRequest request = SecretsVault.Service.PlaylistItems.List("snippet");
request.PlaylistId = playlistId; request.PlaylistId = playlistId;
request.VideoId = item.Id; request.VideoId = item.Id;
PlaylistItemListResponse response = await request.ExecuteAsync();
PlaylistItem playlistItem = response.Items.Find(i => i.Snippet.PlaylistId == playlistId); await SecretsVault.Service.PlaylistItems.Delete((await request.ExecuteAsync()).Items[0].Id).ExecuteAsync();
await SecretsVault.Service.PlaylistItems.Delete(playlistItem.Id).ExecuteAsync();
(Methods.MainPage.PageContent.Frame.Content as PlaylistPage).DeleteItem(this);
}
catch
{
(Navigation.Frame.Frame.Content as PlaylistPage).DeleteItem(this);
} }
catch { }
} }
private void Card_SizeChanged(object sender, SizeChangedEventArgs e) private void Button_Click(object sender, RoutedEventArgs e) =>
{ ItemClicked(((FrameworkElement)sender).Name == "incognito" ? true : false);
Height = e.NewSize.Width * 0.75;
}
} }
} }
@@ -1,10 +1,10 @@
<UserControl <UserControl
x:Class="FoxTube.Controls.Chat" x:Class="FoxTube.Controls.VideoPage.Chat"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:FoxTube.Controls" xmlns:controls="using:FoxTube.Controls.VideoPage"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="600" d:DesignHeight="600"
d:DesignWidth="400"> d:DesignWidth="400">
@@ -10,10 +10,11 @@ using System.Collections.Generic;
using Windows.UI.Popups; using Windows.UI.Popups;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Microsoft.Advertising.WinRT.UI; using Microsoft.Advertising.WinRT.UI;
using Windows.UI.Xaml.Media.Imaging;
using System.Linq; using System.Linq;
using FoxTube.Classes;
using FoxTube.Controls.Adverts;
namespace FoxTube.Controls namespace FoxTube.Controls.VideoPage
{ {
public class ChatMessage public class ChatMessage
{ {
@@ -63,16 +64,12 @@ namespace FoxTube.Controls
private LiveChatMessage message; private LiveChatMessage message;
public ChatMessage(LiveChatMessage item) public ChatMessage(LiveChatMessage item) =>
{
message = item; message = item;
} }
}
public sealed partial class Chat : UserControl public sealed partial class Chat : UserControl
{ {
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
public NativeAdV2 advert;
string chatId; string chatId;
DateTime lastInsert; DateTime lastInsert;
@@ -81,7 +78,7 @@ namespace FoxTube.Controls
DispatcherTimer timer = new DispatcherTimer DispatcherTimer timer = new DispatcherTimer
{ {
Interval = TimeSpan.FromSeconds(1) Interval = TimeSpan.FromSeconds(3)
}; };
DispatcherTimer adTimer = new DispatcherTimer DispatcherTimer adTimer = new DispatcherTimer
@@ -104,130 +101,8 @@ namespace FoxTube.Controls
if (SecretsVault.AdsDisabled) if (SecretsVault.AdsDisabled)
return; return;
adTimer.Tick += (s, e) => manager.RequestAd(); adTimer.Tick += (s, e) => list.Items.Add(new ChatAdvert());
adTimer.Start(); 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) public async void Update(object sender, object e)
@@ -241,10 +116,8 @@ namespace FoxTube.Controls
timer.Start(); timer.Start();
} }
private void HyperlinkButton_Click(object sender, RoutedEventArgs e) private void HyperlinkButton_Click(object sender, RoutedEventArgs e) =>
{ Navigation.GoToChannel(((HyperlinkButton)sender).Tag as string);
Methods.MainPage.GoToChannel(((HyperlinkButton)sender).Tag as string);
}
private async void send_Click(object sender, RoutedEventArgs args) private async void send_Click(object sender, RoutedEventArgs args)
{ {
@@ -1,5 +1,5 @@
<Page <UserControl
x:Class="FoxTube.Pages.CommentsPage" x:Class="FoxTube.Controls.VideoPage.Comments"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -50,4 +50,4 @@
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
</Page> </UserControl>
@@ -9,12 +9,12 @@ using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System.Collections.Generic; using System.Collections.Generic;
namespace FoxTube.Pages namespace FoxTube.Controls.VideoPage
{ {
/// <summary> /// <summary>
/// Comments placeholder /// Comments placeholder
/// </summary> /// </summary>
public sealed partial class CommentsPage : Page public sealed partial class Comments : UserControl
{ {
ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage"); ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage");
@@ -24,7 +24,7 @@ namespace FoxTube.Pages
CommentThreadsResource.ListRequest.OrderEnum order = CommentThreadsResource.ListRequest.OrderEnum.Relevance; CommentThreadsResource.ListRequest.OrderEnum order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
CommentThreadsResource.ListRequest request; CommentThreadsResource.ListRequest request;
public CommentsPage() public Comments()
{ {
InitializeComponent(); 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"> <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')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<LangVersion>8.0</LangVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform> <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{2597B816-7316-4D20-BA6C-D78001E89C1A}</ProjectGuid> <ProjectGuid>{2597B816-7316-4D20-BA6C-D78001E89C1A}</ProjectGuid>
@@ -83,6 +84,7 @@
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath> <OutputPath>bin\x64\Release\</OutputPath>
@@ -104,15 +106,27 @@
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Classes\AdaptiveCommandBar.cs" /> <Compile Include="Classes\AdaptiveCommandBar.cs" />
<Compile Include="Classes\Extensions.cs" />
<Compile Include="Classes\HistorySet.cs" /> <Compile Include="Classes\HistorySet.cs" />
<Compile Include="Classes\InboxItem.cs" /> <Compile Include="Classes\InboxItem.cs" />
<Compile Include="Classes\Methods.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\SearchParameters.cs" />
<Compile Include="Classes\SettingsStorage.cs" /> <Compile Include="Classes\SettingsStorage.cs" />
<Compile Include="Classes\ManifestGenerator.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"> <Compile Include="Controls\Adverts\CardAdvert.xaml.cs">
<DependentUpon>CardAdvert.xaml</DependentUpon> <DependentUpon>CardAdvert.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\Adverts\ChatAdvert.xaml.cs">
<DependentUpon>ChatAdvert.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Adverts\CommentAdvert.xaml.cs"> <Compile Include="Controls\Adverts\CommentAdvert.xaml.cs">
<DependentUpon>CommentAdvert.xaml</DependentUpon> <DependentUpon>CommentAdvert.xaml</DependentUpon>
</Compile> </Compile>
@@ -122,7 +136,8 @@
<Compile Include="Controls\ChannelCard.xaml.cs"> <Compile Include="Controls\ChannelCard.xaml.cs">
<DependentUpon>ChannelCard.xaml</DependentUpon> <DependentUpon>ChannelCard.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\Chat.xaml.cs"> <Compile Include="Controls\Common\DownloadSelector.cs" />
<Compile Include="Controls\VideoPage\Chat.xaml.cs">
<DependentUpon>Chat.xaml</DependentUpon> <DependentUpon>Chat.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\CommentCard.xaml.cs"> <Compile Include="Controls\CommentCard.xaml.cs">
@@ -132,34 +147,44 @@
<Compile Include="Controls\ContentFrame.xaml.cs"> <Compile Include="Controls\ContentFrame.xaml.cs">
<DependentUpon>ContentFrame.xaml</DependentUpon> <DependentUpon>ContentFrame.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\Common\CreateAndAddPlaylist.xaml.cs">
<DependentUpon>CreateAndAddPlaylist.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\DownloadItem.xaml.cs"> <Compile Include="Controls\DownloadItem.xaml.cs">
<DependentUpon>DownloadItem.xaml</DependentUpon> <DependentUpon>DownloadItem.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\LiveCaptions.xaml.cs"> <Compile Include="Controls\Player\LiveCaptions.xaml.cs">
<DependentUpon>LiveCaptions.xaml</DependentUpon> <DependentUpon>LiveCaptions.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\Player\PlayerControls.cs" /> <Compile Include="Controls\Player\PlayerControls.cs" />
<Compile Include="Controls\Player\PlayerMain.cs" />
<Compile Include="Controls\PlaylistCard.xaml.cs"> <Compile Include="Controls\PlaylistCard.xaml.cs">
<DependentUpon>PlaylistCard.xaml</DependentUpon> <DependentUpon>PlaylistCard.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\ReportVideo.xaml.cs"> <Compile Include="Controls\VideoPage\ReportVideo.xaml.cs">
<DependentUpon>ReportVideo.xaml</DependentUpon> <DependentUpon>ReportVideo.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\ShowMore.xaml.cs"> <Compile Include="Controls\ShowMore.xaml.cs">
<DependentUpon>ShowMore.xaml</DependentUpon> <DependentUpon>ShowMore.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\Browser.xaml.cs"> <Compile Include="Controls\VideoPage\RelatedVideos.xaml.cs">
<DependentUpon>Browser.xaml</DependentUpon> <DependentUpon>RelatedVideos.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\VideoPage\VideoPlaylist.xaml.cs">
<DependentUpon>VideoPlaylist.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\ChannelPage.xaml.cs"> <Compile Include="Pages\ChannelPage.xaml.cs">
<DependentUpon>ChannelPage.xaml</DependentUpon> <DependentUpon>ChannelPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\CommentsPage.xaml.cs"> <Compile Include="Controls\VideoPage\Comments.xaml.cs">
<DependentUpon>CommentsPage.xaml</DependentUpon> <DependentUpon>Comments.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\Downloads.xaml.cs"> <Compile Include="Pages\Downloads.xaml.cs">
<DependentUpon>Downloads.xaml</DependentUpon> <DependentUpon>Downloads.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\MainFrame.xaml.cs">
<DependentUpon>MainFrame.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\SettingsPages\About.xaml.cs"> <Compile Include="Pages\SettingsPages\About.xaml.cs">
<DependentUpon>About.xaml</DependentUpon> <DependentUpon>About.xaml</DependentUpon>
</Compile> </Compile>
@@ -293,10 +318,18 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Page Include="Controls\Common\AccountManager.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Adverts\CardAdvert.xaml"> <Page Include="Controls\Adverts\CardAdvert.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\Adverts\ChatAdvert.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Adverts\CommentAdvert.xaml"> <Page Include="Controls\Adverts\CommentAdvert.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@@ -309,7 +342,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\Chat.xaml"> <Page Include="Controls\VideoPage\Chat.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
@@ -321,11 +354,15 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\Common\CreateAndAddPlaylist.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\DownloadItem.xaml"> <Page Include="Controls\DownloadItem.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\LiveCaptions.xaml"> <Page Include="Controls\Player\LiveCaptions.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
@@ -333,7 +370,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\ReportVideo.xaml"> <Page Include="Controls\VideoPage\ReportVideo.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
@@ -341,7 +378,11 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </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> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
@@ -349,7 +390,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Pages\CommentsPage.xaml"> <Page Include="Controls\VideoPage\Comments.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
@@ -357,6 +398,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Pages\MainFrame.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\SettingsPages\About.xaml"> <Page Include="Pages\SettingsPages\About.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <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 Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System.Collections.Generic; using System.Collections.Generic;
using FoxTube.Classes;
namespace FoxTube.Pages namespace FoxTube.Pages
{ {
@@ -105,21 +106,21 @@ namespace FoxTube.Pages
ScrollViewer_ViewChanged(this, null); ScrollViewer_ViewChanged(this, null);
Methods.MainPage.PageContent.LoadingPage.Close(); Navigation.Frame.Frame.LoadingPage.Close();
} }
catch (System.Net.Http.HttpRequestException) 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) catch (Exception e)
{ {
if(item == null) 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; 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>() Analytics.TrackEvent("Channel loading error", new Dictionary<string, string>()
{ {
{ "Exception", e.GetType().ToString() }, { "Exception", e.GetType().ToString() },
@@ -267,7 +268,7 @@ namespace FoxTube.Pages
private void Refresh_Click(object sender, RoutedEventArgs e) 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) 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 System;
using Windows.System; using Windows.System;
using Windows.UI.Xaml; using Windows.UI.Xaml;
@@ -24,7 +25,7 @@ namespace FoxTube.Pages
empty.Visibility = list.Children.Count == 0 ? Visibility.Visible : Visibility.Collapsed; 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) protected override void OnNavigatedFrom(NavigationEventArgs e)
@@ -34,10 +35,8 @@ namespace FoxTube.Pages
DownloadAgent.Page = null; 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); await Launcher.LaunchFolderAsync(DownloadAgent.Downloads);
}
public void Remove(DownloadItem item) public void Remove(DownloadItem item)
{ {
-4
View File
@@ -18,7 +18,6 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Pivot SelectionChanged="Pivot_SelectionChanged"> <Pivot SelectionChanged="Pivot_SelectionChanged">
<PivotItem x:Uid="/Playlist/appHistory" Header="Application"> <PivotItem x:Uid="/Playlist/appHistory" Header="Application">
<Grid>
<ScrollViewer> <ScrollViewer>
<StackPanel> <StackPanel>
<Grid Margin="0,10" BorderBrush="Red" BorderThickness="5" CornerRadius="10" Padding="10"> <Grid Margin="0,10" BorderBrush="Red" BorderThickness="5" CornerRadius="10" Padding="10">
@@ -39,9 +38,6 @@
<controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/> <controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
<foxtube:LoadingPage x:Name="loading" Visibility="Collapsed" RefreshPage="Refresh_Click"/>
</Grid>
</PivotItem> </PivotItem>
<PivotItem x:Uid="/Playlist/webHistory" Header="Website"> <PivotItem x:Uid="/Playlist/webHistory" Header="Website">
<Grid> <Grid>
+19 -36
View File
@@ -1,4 +1,5 @@
using FoxTube.Controls; using FoxTube.Classes;
using FoxTube.Controls;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -20,10 +21,8 @@ namespace FoxTube.Pages
int page = 1; int page = 1;
int webPage = 0; int webPage = 0;
public History() public History() =>
{
InitializeComponent(); InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e) protected override void OnNavigatedTo(NavigationEventArgs e)
{ {
@@ -31,15 +30,14 @@ namespace FoxTube.Pages
Parameter = e.Parameter; Parameter = e.Parameter;
Initialize(); Initialize();
Methods.MainPage.PageContent.LoadingPage.Close();
} }
public async void Initialize() public async void Initialize()
{ {
Methods.MainPage.VideoContent.LoadingPage.Close(); Navigation.Frame.Frame.LoadingPage.Close();
try try
{ {
loading.Refresh(); Navigation.Frame.Frame.LoadingPage.Refresh();
await Dispatcher.RunIdleAsync((command) => await Dispatcher.RunIdleAsync((command) =>
{ {
@@ -53,11 +51,11 @@ namespace FoxTube.Pages
clear.Visibility = Visibility.Collapsed; clear.Visibility = Visibility.Collapsed;
}); });
loading.Close(); Navigation.Frame.Frame.LoadingPage.Close();
} }
catch (Exception e) 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>() Analytics.TrackEvent("Local history loading error", new Dictionary<string, string>()
{ {
{ "Exception", e.GetType().ToString() }, { "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()); await Launcher.LaunchUriAsync("https://www.youtube.com/feed/history".ToUri());
}
private void Refresh_Click(object sender, RoutedEventArgs e) private void Refresh_Click(object sender, RoutedEventArgs e) =>
{ Navigation.Frame.Refresh();
Methods.MainPage.PageContent.Refresh();
}
private async void ShowMore_Clicked() private async void ShowMore_Clicked() => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{ {
for (int k = 25 * page++; k < 25 * page && k < HistorySet.Items.Count; k++) for (int k = 25 * page++; k < 25 * page && k < HistorySet.Items.Count; k++)
list.Add(new VideoCard(HistorySet.Items[k].Id, "HL")); list.Add(new VideoCard(HistorySet.Items[k].Id, "HL"));
@@ -89,7 +81,6 @@ namespace FoxTube.Pages
else else
more.Complete(); more.Complete();
}); });
}
private async void Clear_Click(object sender, RoutedEventArgs e) private async void Clear_Click(object sender, RoutedEventArgs e)
{ {
@@ -117,8 +108,6 @@ namespace FoxTube.Pages
} }
async void LoadWeb() async void LoadWeb()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{ {
try try
{ {
@@ -126,10 +115,10 @@ namespace FoxTube.Pages
SecretsVault.History = await Methods.GetHistory(); SecretsVault.History = await Methods.GetHistory();
for (int k = 0; k < 25 && k < SecretsVault.History.Count; k++) for (int k = 0; k < 25 && k < SecretsVault.History.Videos.Count; k++)
websiteList.Add(new VideoCard(SecretsVault.History[k], "HL")); 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; websiteMore.Visibility = Visibility.Collapsed;
websiteLoading.Close(); websiteLoading.Close();
@@ -144,26 +133,20 @@ namespace FoxTube.Pages
{ "StackTrace", e.StackTrace } { "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.Videos.Count; k++)
{ websiteList.Add(new VideoCard(SecretsVault.History.Videos[k].Id, "HL"));
for (int k = 25 * webPage++; k < 25 * webPage && k < SecretsVault.History.Count; k++)
websiteList.Add(new VideoCard(SecretsVault.History[k], "HL"));
if (websiteList.Count >= SecretsVault.History.Count) if (websiteList.Count >= SecretsVault.History.Videos.Count)
websiteMore.Visibility = Visibility.Collapsed; websiteMore.Visibility = Visibility.Collapsed;
else else
websiteMore.Complete(); websiteMore.Complete();
}); });
}
public void Delete (VideoCard item) public void Delete (VideoCard item) =>
{
list.DeleteItem(item); list.DeleteItem(item);
} }
} }
}
+7 -8
View File
@@ -8,6 +8,7 @@ using FoxTube.Controls;
using System.Net.Http; using System.Net.Http;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using FoxTube.Classes;
namespace FoxTube namespace FoxTube
{ {
@@ -42,7 +43,7 @@ namespace FoxTube
private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e) private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
Methods.MainPage.PageContent.LoadingPage.Close(); Navigation.Frame.Frame.LoadingPage.Close();
if (pivot.SelectedItem == recommended && !recLoaded) if (pivot.SelectedItem == recommended && !recLoaded)
LoadRecommendations(); LoadRecommendations();
@@ -96,7 +97,7 @@ namespace FoxTube
{ {
trendsLoading.Refresh(); trendsLoading.Refresh();
trendsRequest = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails"); trendsRequest = SecretsVault.Service.Videos.List("id");
trendsRequest.MaxResults = 25; trendsRequest.MaxResults = 25;
trendsRequest.Chart = VideosResource.ListRequest.ChartEnum.MostPopular; trendsRequest.Chart = VideosResource.ListRequest.ChartEnum.MostPopular;
@@ -108,7 +109,7 @@ namespace FoxTube
trendingMore.Visibility = string.IsNullOrWhiteSpace(response.NextPageToken) ? Visibility.Collapsed : Visibility.Visible; trendingMore.Visibility = string.IsNullOrWhiteSpace(response.NextPageToken) ? Visibility.Collapsed : Visibility.Visible;
foreach (Video i in response.Items) foreach (Video i in response.Items)
trendGrid.Add(new VideoCard(i)); trendGrid.Add(new VideoCard(i.Id));
trendsLoading.Close(); trendsLoading.Close();
trendLoaded = true; trendLoaded = true;
@@ -170,8 +171,7 @@ namespace FoxTube
#region More requests #region More requests
private void Recommended_More() private void Recommended_More()
{ {
int l = 25 + recGrid.Count; for (int k = recGrid.Count; k < 25 + recGrid.Count && k < homeList.Count; k++)
for (int k = recGrid.Count; k < l && k < homeList.Count; k++)
recGrid.Add(new VideoCard(homeList[k])); recGrid.Add(new VideoCard(homeList[k]));
recommendedMore.Visibility = recGrid.Count >= homeList.Count ? Visibility.Collapsed : Visibility.Visible; 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; trendingMore.Visibility = string.IsNullOrWhiteSpace(response.NextPageToken) ? Visibility.Collapsed : Visibility.Visible;
foreach (Video i in response.Items) foreach (Video i in response.Items)
trendGrid.Add(new VideoCard(i)); trendGrid.Add(new VideoCard(i.Id));
trendingMore.Complete(); trendingMore.Complete();
} }
private void Subscriptions_More() private void Subscriptions_More()
{ {
int l = 25 + subsGrid.Count; for (int k = subsGrid.Count; k < 25 + subsGrid.Count && k < subsList.Count; k++)
for (int k = subsGrid.Count; k < l && k < subsList.Count; k++)
subsGrid.Add(new VideoCard(subsList[k])); subsGrid.Add(new VideoCard(subsList[k]));
subscriptionsMore.Visibility = subsGrid.Count >= subsList.Count ? Visibility.Collapsed : Visibility.Visible; subscriptionsMore.Visibility = subsGrid.Count >= subsList.Count ? Visibility.Collapsed : Visibility.Visible;
+30 -37
View File
@@ -1,5 +1,6 @@
using System; using System;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Windows.System;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
@@ -12,15 +13,13 @@ namespace FoxTube
/// </summary> /// </summary>
public sealed partial class LoadingPage : Page public sealed partial class LoadingPage : Page
{ {
ResourceLoader resources = ResourceLoader.GetForCurrentView("LoadingPage"); readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("LoadingPage");
public event RoutedEventHandler RefreshPage; public event RoutedEventHandler RefreshPage;
public LoadingState State { get; private set; } = LoadingState.Loadnig; public LoadingState State { get; private set; } = LoadingState.Loadnig;
public LoadingPage() public LoadingPage() =>
{
InitializeComponent(); InitializeComponent();
}
public void Error(string exceptionId = "Unknown", string details = "N/A", bool isWifiTrouble = false) public void Error(string exceptionId = "Unknown", string details = "N/A", bool isWifiTrouble = false)
{ {
@@ -47,39 +46,6 @@ namespace FoxTube
State = LoadingState.Error; 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() public void Refresh()
{ {
Visibility = Visibility.Visible; Visibility = Visibility.Visible;
@@ -97,5 +63,32 @@ namespace FoxTube
State = LoadingState.Loaded; 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" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:ui="using:Microsoft.UI.Xaml.Controls" 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"> <Grid Name="grid">
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>
@@ -35,43 +39,23 @@
Style="{StaticResource CaptionTextBlockStyle}" /> Style="{StaticResource CaptionTextBlockStyle}" />
</Border> </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> <ui:NavigationView.Header>
<Grid> <Grid>
<TextBlock Name="Title" Style="{StaticResource TitleTextBlockStyle}"/> <TextBlock Name="Title" Style="{StaticResource TitleTextBlockStyle}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <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/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"/> <common:AccountManager x:Name="manager"/>
<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>
</StackPanel> </StackPanel>
</Grid> </Grid>
</ui:NavigationView.Header> </ui:NavigationView.Header>
<ui:NavigationView.MenuItemTemplate> <ui:NavigationView.MenuItemTemplate>
<DataTemplate> <DataTemplate x:DataType="data:Subscription">
<StackPanel Orientation="Horizontal" Padding="5" Margin="-5,0,0,0" Tag="{Binding Snippet.ResourceId.ChannelId}"> <StackPanel Orientation="Horizontal" Padding="5" Margin="-5,0,0,0">
<PersonPicture Height="20" Margin="-5,0,15,0"> <PersonPicture Height="20" Margin="-5,0,15,0">
<PersonPicture.ProfilePicture> <PersonPicture.ProfilePicture>
<BitmapImage UriSource="{Binding Snippet.Thumbnails.Medium.Url}" DecodePixelHeight="20" DecodePixelWidth="20"/> <BitmapImage UriSource="{Binding Snippet.Thumbnails.Medium.Url}" DecodePixelHeight="20" DecodePixelWidth="20"/>
@@ -99,21 +83,17 @@
</ui:NavigationView.MenuItems> </ui:NavigationView.MenuItems>
<ui:NavigationView.PaneFooter> <ui:NavigationView.PaneFooter>
<ui:NavigationViewItem Content="Remove ads" Visibility="Collapsed" Tapped="RemoveAds_Tapped" Name="removeAds"> <ui:NavigationViewList>
<ui:NavigationViewItem.Icon> <ui:NavigationViewItemSeparator/>
<FontIcon Glyph="&#xE14D;"/> <ui:NavigationViewItem Content="Remove ads" Visibility="Collapsed" Tapped="RemoveAds_Tapped" Name="removeAds" Icon="Shop"/>
</ui:NavigationViewItem.Icon> </ui:NavigationViewList>
</ui:NavigationViewItem>
</ui:NavigationView.PaneFooter> </ui:NavigationView.PaneFooter>
<ui:NavigationView.AutoSuggestBox> <ui:NavigationView.AutoSuggestBox>
<AutoSuggestBox x:Name="search" QueryIcon="Find" QuerySubmitted="Search_QuerySubmitted" TextChanged="Search_TextChanged" x:Uid="/Main/searchPlaceholder" PlaceholderText="Search"/> <AutoSuggestBox x:Name="search" QueryIcon="Find" QuerySubmitted="Search_QuerySubmitted" TextChanged="Search_TextChanged" x:Uid="/Main/searchPlaceholder" PlaceholderText="Search"/>
</ui:NavigationView.AutoSuggestBox> </ui:NavigationView.AutoSuggestBox>
<Grid> <pages:MainFrame x:Name="content" Navigated="Content_Navigated"/>
<controls:ContentFrame x:Name="content" Navigated="Content_Navigated"/>
<controls:ContentFrame x:Name="videoPlaceholder"/>
</Grid>
</ui:NavigationView> </ui:NavigationView>
</Grid> </Grid>
+132 -450
View File
@@ -1,27 +1,19 @@
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using Windows.UI; using Windows.UI;
using Windows.UI.ViewManagement; using Windows.UI.ViewManagement;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Media.Imaging;
using Google.Apis.YouTube.v3.Data; using Google.Apis.YouTube.v3.Data;
using Windows.ApplicationModel.Core; using Windows.ApplicationModel.Core;
using Windows.System;
using FoxTube.Pages; using FoxTube.Pages;
using Windows.UI.Popups; using Windows.UI.Popups;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Microsoft.Services.Store.Engagement; 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.Xml;
using System.Linq;
using FoxTube.Classes;
namespace FoxTube namespace FoxTube
{ {
@@ -30,12 +22,10 @@ namespace FoxTube
/// </summary> /// </summary>
public sealed partial class MainPage : Page public sealed partial class MainPage : Page
{ {
bool wasInvoked = false;
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main"); readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
Dictionary<Type, string> headers; readonly Dictionary<Type, string> headers;
public ContentFrame PageContent => content; public new MainFrame Content => content;
public ContentFrame VideoContent => videoPlaceholder;
public MainPage() public MainPage()
{ {
@@ -70,129 +60,21 @@ namespace FoxTube
} }
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/purchaseSuccess")); 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.Commands.Add(new UICommand(resources.GetString("/Main/delay")));
dialog.CancelCommandIndex = 1; dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0; dialog.DefaultCommandIndex = 0;
await dialog.ShowAsync(); await dialog.ShowAsync();
}; };
SecretsVault.Initialize();
if (SecretsVault.IsAuthorized)
AuthorizationStateChanged(true);
if (!SecretsVault.AdsDisabled)
SecretsVault.CheckAddons();
if(StoreServicesFeedbackLauncher.IsSupported()) if(StoreServicesFeedbackLauncher.IsSupported())
feedback.Visibility = Visibility.Visible; 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) public void SetTitleBar(CoreApplicationViewTitleBar coreTitleBar = null)
@@ -212,49 +94,38 @@ namespace FoxTube
titleBar.ButtonPressedBackgroundColor = Colors.DarkRed; titleBar.ButtonPressedBackgroundColor = Colors.DarkRed;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveForegroundColor = Colors.Gray; titleBar.ButtonInactiveForegroundColor = Colors.Gray;
titleBar.ButtonForegroundColor = Application.Current.RequestedTheme == ApplicationTheme.Dark ? Colors.Black : Colors.White;
if(RequestedTheme == ElementTheme.Dark || (RequestedTheme == ElementTheme.Default && Application.Current.RequestedTheme == ApplicationTheme.Dark))
titleBar.ButtonForegroundColor = Colors.White;
else
titleBar.ButtonForegroundColor = Colors.Black;
} }
private void SecretsVault_SubscriptionsChanged(object sender, params object[] args) void SecretsVault_SubscriptionsChanged(string action, Subscription subscription)
{ {
switch((string)args[0]) switch(action)
{ {
case "add": case "add":
subsHeader.Visibility = Visibility.Visible;
if (nav.MenuItems.Count < 19) if (nav.MenuItems.Count < 19)
nav.MenuItems.Add(args[1] as Subscription); nav.MenuItems.Add(subscription);
break; break;
case "remove": 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) if (SecretsVault.Subscriptions.Count >= 10)
nav.MenuItems.Add(SecretsVault.Subscriptions[9]); nav.MenuItems.Add(SecretsVault.Subscriptions[9]);
else if (SecretsVault.Subscriptions.Count == 0)
subsHeader.Visibility = Visibility.Collapsed;
} }
break; break;
} }
} }
private async void AuthorizationStateChanged(object sender, params object[] e) async void AuthorizationStateChanged(bool? isAuthozied)
{ {
wasInvoked = false; switch (isAuthozied)
switch (e[0] as bool?)
{ {
case true: case true:
account.Visibility = Visibility.Collapsed; manager.Logged();
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;
toChannel.Visibility = Visibility.Visible; toChannel.Visibility = Visibility.Visible;
toSubscriptions.Visibility = Visibility.Visible; toSubscriptions.Visibility = Visibility.Visible;
@@ -270,21 +141,10 @@ namespace FoxTube
nav.MenuItems.Add(SecretsVault.Subscriptions[k]); nav.MenuItems.Add(SecretsVault.Subscriptions[k]);
} }
HistorySet.Load(); HistorySet.Load();
if (content.Frame.Content != null)
content.Refresh();
else
content.Frame.Navigate(typeof(Home));
break; break;
case false: case false:
content.Frame.Navigate(typeof(Home)); manager.Quit();
for (int k = nav.MenuItems.Count - 1; k > 8; k--)
nav.MenuItems.RemoveAt(k);
account.Visibility = Visibility.Visible;
avatar.Visibility = Visibility.Collapsed;
toChannel.Visibility = Visibility.Collapsed; toChannel.Visibility = Visibility.Collapsed;
toSubscriptions.Visibility = Visibility.Collapsed; toSubscriptions.Visibility = Visibility.Collapsed;
@@ -296,8 +156,9 @@ namespace FoxTube
subsHeader.Visibility = Visibility.Collapsed; subsHeader.Visibility = Visibility.Collapsed;
content.Frame.BackStack.Clear(); for (int k = nav.MenuItems.Count - 1; k > 8; k--)
content.Frame.ForwardStack.Clear(); nav.MenuItems.RemoveAt(k);
HistorySet.Items.Clear();
break; break;
default: default:
@@ -305,14 +166,8 @@ namespace FoxTube
if (StoreServicesFeedbackLauncher.IsSupported()) 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("/About/feedback/Content"), async (command) => await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync()));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/tryAgain"), (command) => dialog.Commands.Add(new UICommand(resources.GetString("/Main/tryAgain"), (command) => SecretsVault.Authorize()));
{ dialog.Commands.Add(new UICommand(resources.GetString("/Main/quit"), (command) => CoreApplication.Exit()));
SecretsVault.Authorize();
}));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/quit"), (command) =>
{
Methods.CloseApp();
}));
dialog.CancelCommandIndex = 1; dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0; dialog.DefaultCommandIndex = 0;
@@ -321,114 +176,18 @@ namespace FoxTube
break; break;
} }
if (videoPlaceholder.Frame.Content != null) content.AuthorizationChanged(isAuthozied);
{
MaximizeVideo();
videoPlaceholder.Refresh();
} }
DownloadAgent.Initialize(); public void UpdateView()
}
private async void Feedback_Click(object sender, RoutedEventArgs e)
{ {
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync(); if(content.Video.Content != null && !content.VideoMinimized)
}
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 });
Title.Text = resources.GetString("/Main/video"); 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 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]; if (new[] { typeof(Home), typeof(Settings), typeof(Subscriptions) }.Contains(content.Frame.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))
{ {
nav.ExpandedModeThresholdWidth = 1008; nav.ExpandedModeThresholdWidth = 1008;
nav.IsPaneOpen = nav.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded ? true : false; 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; if (!SecretsVault.IsAuthorized)
videoPlaceholder.Height = double.NaN; return null;
videoPlaceholder.VerticalAlignment = VerticalAlignment.Stretch;
videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Stretch;
videoPlaceholder.Margin = new Thickness(0);
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; 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 try
{ {
XmlDocument doc = new XmlDocument(); XmlDocument doc = new XmlDocument();
doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}&hl={SettingsStorage.RelevanceLanguage}"); 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++) for (int i = 0; i < doc["toplevel"].ChildNodes.Count && i < 5; i++)
suggestions.Add(doc["toplevel"].ChildNodes[i]["suggestion"].GetAttribute("data")); search.Items.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;
} }
catch { } catch { }
} });
public void Content_Navigated(object sender, NavigationEventArgs e) #region Simple UI interaction events
{ async void Feedback_Click(object sender, RoutedEventArgs e) =>
Title.Text = headers[content.Frame.SourcePageType]; await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
if (!wasInvoked) void RemoveAds_Tapped(object sender, TappedRoutedEventArgs e) =>
{
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)
{
SecretsVault.GetAdblock(); SecretsVault.GetAdblock();
}
private void Nav_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args) void Nav_PaneClosing(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewPaneClosingEventArgs 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)
{
AppTitle.Visibility = Visibility.Collapsed; 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; 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 Height="auto"/>
<RowDefinition/> <RowDefinition/>
</Grid.RowDefinitions> </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"/> <Image Source="/Assets/videoThumbSample.png" Name="thumbnail"/>
<TextBlock FontSize="20" Text="[Title]" TextWrapping="WrapWholeWords" Name="title"/> <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="# videos | # views | Updated at: ##-##-## ##:##:##" TextWrapping="WrapWholeWords" Name="info"/>
<TextBlock Foreground="Gray" Text="description" TextWrapping="WrapWholeWords" Name="description"/> <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>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
@@ -79,13 +79,6 @@
<classes:AdaptiveCommandBar Grid.Row="2"> <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/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/refresh" Icon="Refresh" Label="Refresh" Name="refresh" Click="refresh_Click"/>
<AppBarButton x:Uid="/Playlist/share" Icon="Share" Label="Share" Name="share" Click="share_Click"/> <AppBarButton x:Uid="/Playlist/share" Icon="Share" Label="Share" Name="share" Click="share_Click"/>
</classes:AdaptiveCommandBar> </classes:AdaptiveCommandBar>
+52 -128
View File
@@ -1,10 +1,8 @@
using FoxTube.Controls; using FoxTube.Classes;
using Google.Apis.YouTube.v3; using FoxTube.Controls;
using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources; using Windows.ApplicationModel.Resources;
using Windows.Foundation; using Windows.Foundation;
@@ -13,6 +11,8 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging; using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using YoutubeExplode.Models;
using YoutubeExplode;
namespace FoxTube.Pages namespace FoxTube.Pages
{ {
@@ -22,196 +22,120 @@ namespace FoxTube.Pages
public sealed partial class PlaylistPage : Page, INavigationPage public sealed partial class PlaylistPage : Page, INavigationPage
{ {
public object Parameter { get; set; } = null; public object Parameter { get; set; } = null;
public string playlistId;
string authorId;
Playlist item; Playlist item;
PlaylistItemsResource.ListRequest request;
string token;
int page = 1; int page = 1;
public PlaylistPage() public PlaylistPage() =>
{
InitializeComponent(); InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e) protected override void OnNavigatedTo(NavigationEventArgs e)
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
Parameter = e.Parameter; Parameter = e.Parameter;
if (e.Parameter as string == "WL")
LoadWL();
else
Initialize(e.Parameter as string); Initialize(e.Parameter as string);
} }
public async void Initialize(string id) async void Initialize(string id)
{ {
try 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.Id = id;
infoRequest.Hl = SettingsStorage.RelevanceLanguage; authorId = (await infoRequest.ExecuteAsync()).Items[0].Snippet.ChannelId;
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);
try try
{ {
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); thumbnail.Source = new BitmapImage((item.Videos.Count > 0 ? item.Videos[0].Thumbnails.MediumResUrl : "/Assets/videoThumbSample.png").ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; avatar.ProfilePicture = new BitmapImage((await new YoutubeClient(SecretsVault.HttpClient).GetChannelAsync(authorId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 };
} }
catch { } catch { }
request = SecretsVault.Service.PlaylistItems.List("contentDetails"); for (int k = 0; k < 25 && k < item.Videos.Count; k++)
request.PlaylistId = id; list.Add(new VideoCard(item.Videos[k].Id, "WL"));
request.MaxResults = 25;
PlaylistItemListResponse response = await request.ExecuteAsync(); if (list.Count >= item.Videos.Count)
token = response.NextPageToken;
if (string.IsNullOrWhiteSpace(token))
more.Visibility = Visibility.Collapsed; more.Visibility = Visibility.Collapsed;
foreach (PlaylistItem i in response.Items) Navigation.Frame.Frame.LoadingPage.Close();
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
Methods.MainPage.PageContent.LoadingPage.Close();
} }
catch (System.Net.Http.HttpRequestException) 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)
{
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();
} }
catch (Exception e) catch (Exception e)
{ {
if (item == null) 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; return;
} }
Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message); Navigation.Frame.Frame.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("WL playlist loading error", new Dictionary<string, string>() Analytics.TrackEvent("Playlist loading error", new Dictionary<string, string>()
{ {
{ "Exception", e.GetType().ToString() }, { "Exception", e.GetType().ToString() },
{ "Message", e.Message }, { "Message", e.Message },
{ "Playlist ID", item.Id },
{ "StackTrace", e.StackTrace } { "StackTrace", e.StackTrace }
}); });
} }
} }
private void toChannel_Click(object sender, RoutedEventArgs e) void toChannel_Click(object sender, RoutedEventArgs e) =>
{ Navigation.GoToChannel(authorId);
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
}
private async void inBrowser_Click(object sender, RoutedEventArgs e) async void inBrowser_Click(object sender, RoutedEventArgs e) =>
{ await Launcher.LaunchUriAsync(item.GetUrl().ToUri());
await Launcher.LaunchUriAsync(new Uri($"https://www.youtube.com/playlist?list={item.Id}"));
}
private void refresh_Click(object sender, RoutedEventArgs e) void refresh_Click(object sender, RoutedEventArgs e) =>
{ Navigation.Frame.Refresh();
Methods.MainPage.PageContent.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.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
DataTransferManager.ShowShareUI(); DataTransferManager.ShowShareUI();
} }
private void Share(DataTransferManager sender, DataRequestedEventArgs args) void Share(DataTransferManager sender, DataRequestedEventArgs args)
{ {
Methods.Share(args, Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url, (thumbnail.Source as BitmapImage).UriSource.AbsoluteUri,
item.Snippet.Title, item.Title,
$"https://www.youtube.com/playlist?list={item.Id}", item.GetUrl(),
ResourceLoader.GetForCurrentView("Cards").GetString("/Cards/playlistShare")); ResourceLoader.GetForCurrentView("Cards").GetString("/Cards/playlistShare"));
} }
private async void ShowMore_Clicked() void ShowMore_Clicked()
{ {
if(playlistId == "WL") for (int k = 25 * page++; k < 25 * page && k < item.Videos.Count; k++)
{ list.Add(new VideoCard(item.Videos[k].Id, "WL"));
MoreWL();
return;
}
request.PageToken = token; if (list.Count >= item.Videos.Count)
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)
more.Visibility = Visibility.Collapsed; more.Visibility = Visibility.Collapsed;
else else
more.Complete(); more.Complete();
} }
public void DeleteItem(FrameworkElement card) public void DeleteItem(FrameworkElement card) =>
{
list.DeleteItem(card); list.DeleteItem(card);
} }
} }
}
-1
View File
@@ -2,7 +2,6 @@
x:Class="FoxTube.Search" x:Class="FoxTube.Search"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube"
xmlns:pages="using:FoxTube.Pages" xmlns:pages="using:FoxTube.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 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 Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
using System.Collections.Generic; using System.Collections.Generic;
using FoxTube.Classes;
namespace FoxTube namespace FoxTube
{ {
@@ -26,41 +27,9 @@ namespace FoxTube
SearchResource.ListRequest request; SearchResource.ListRequest request;
string nextToken; string nextToken;
public Search() public Search() =>
{
InitializeComponent(); 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) protected override void OnNavigatedTo(NavigationEventArgs e)
{ {
@@ -128,7 +97,7 @@ namespace FoxTube
features.SelectedItems.Add(features.Items[4]); features.SelectedItems.Add(features.Items[4]);
SearchListResponse response = await request.ExecuteAsync(); 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)) if (!string.IsNullOrWhiteSpace(response.NextPageToken))
nextToken = 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) 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(); frame.Refresh();
}
private async void More_Clicked() async void More_Clicked()
{ {
request.PageToken = nextToken; request.PageToken = nextToken;
SearchListResponse response = await request.ExecuteAsync(); SearchListResponse response = await request.ExecuteAsync();
nextToken = response.NextPageToken;
foreach (SearchResult item in response.Items) foreach (SearchResult item in response.Items)
AddItem(item); AddItem(item);
if (response.NextPageToken != null) if (!string.IsNullOrWhiteSpace(nextToken))
{
nextToken = response.NextPageToken;
more.Complete(); more.Complete();
}
else else
more.Visibility = Visibility.Collapsed; more.Visibility = Visibility.Collapsed;
} }
private void Type_SelectionChanged(object sender, SelectionChangedEventArgs e) void Type_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
try try
{ {
@@ -213,12 +210,7 @@ namespace FoxTube
catch (NullReferenceException) { } catch (NullReferenceException) { }
} }
private async void InBrowser_Click(object sender, RoutedEventArgs e) void Apply_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)
{ {
Parameters.Filter.HD = features.SelectedItems.Contains(features.Items[0]); Parameters.Filter.HD = features.SelectedItems.Contains(features.Items[0]);
Parameters.Filter.Is3D = features.SelectedItems.Contains(features.Items[1]); 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.Date = (SearchParameters.Filters.Enumerations.Date)date.SelectedIndex;
Parameters.Filter.Duration = (SearchParameters.Filters.Enumerations.Duration)duration.SelectedIndex; Parameters.Filter.Duration = (SearchParameters.Filters.Enumerations.Duration)duration.SelectedIndex;
frame.LoadingPage.Refresh();
Initialize(Parameters); 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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:settingspages="using:FoxTube.Pages.SettingsPages" 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"> <Pivot SelectedIndex="0" Name="pivot" IsHeaderItemsCarouselEnabled="False" SelectionChanged="Pivot_SelectionChanged">
<PivotItem Name="generalTab" Header="General" x:Uid="/Settings/general"> <PivotItem Name="generalTab" Header="General" x:Uid="/Settings/general">
<ScrollViewer> <ScrollViewer>
@@ -30,5 +30,4 @@
</ScrollViewer> </ScrollViewer>
</PivotItem> </PivotItem>
</Pivot> </Pivot>
</Grid>
</Page> </Page>
+4 -5
View File
@@ -1,6 +1,7 @@
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using FoxTube.Pages.SettingsPages; using FoxTube.Pages.SettingsPages;
using FoxTube.Classes;
namespace FoxTube namespace FoxTube
{ {
@@ -13,10 +14,8 @@ namespace FoxTube
bool inboxLoaded = false; bool inboxLoaded = false;
string inboxId = null; string inboxId = null;
public Settings() public Settings() =>
{
InitializeComponent(); InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e) protected override void OnNavigatedTo(NavigationEventArgs e)
{ {
@@ -38,10 +37,10 @@ namespace FoxTube
break; 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) if (pivot.SelectedItem == inboxTab && !inboxLoaded)
{ {
+1 -3
View File
@@ -24,9 +24,7 @@ namespace FoxTube.Pages.SettingsPages
feedback.Visibility = Visibility.Visible; 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(); 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); 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) if ((language.SelectedItem as ComboBoxItem).Tag.ToString() == SettingsStorage.Language)
return; return;
@@ -88,47 +88,31 @@ namespace FoxTube.Pages.SettingsPages
restartNote.Visibility = Visibility.Visible; 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; 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; SettingsStorage.CheckConnection = mobileWarning.IsOn;
}
private void autoplay_Toggled(object sender, RoutedEventArgs e) void autoplay_Toggled(object sender, RoutedEventArgs e) =>
{
SettingsStorage.Autoplay = autoplay.IsOn; SettingsStorage.Autoplay = autoplay.IsOn;
}
private void notification_IsEnabledChanged(object sender, RoutedEventArgs e) void notification_IsEnabledChanged(object sender, RoutedEventArgs e) =>
{
SettingsStorage.VideoNotifications = newVideo.IsOn; SettingsStorage.VideoNotifications = newVideo.IsOn;
}
private void devNews_Toggled(object sender, RoutedEventArgs e) void devNews_Toggled(object sender, RoutedEventArgs e) =>
{
SettingsStorage.DevNotifications = devNews.IsOn; 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(); 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(); 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; SettingsStorage.SafeSearch = safeSearch.SelectedIndex;
}
private void RadioButton_Checked(object sender, RoutedEventArgs e) void RadioButton_Checked(object sender, RoutedEventArgs e)
{ {
if (sender == light) if (sender == light)
{ {
@@ -151,19 +135,13 @@ namespace FoxTube.Pages.SettingsPages
Methods.MainPage.SetTitleBar(); Methods.MainPage.SetTitleBar();
} }
private void Button_Click(object sender, RoutedEventArgs e) void Button_Click(object sender, RoutedEventArgs e) =>
{
CoreApplication.Exit(); 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; 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; SettingsStorage.ProcessClipboard = clipboardProcessing.IsOn;
} }
} }
}
+5 -9
View File
@@ -72,7 +72,7 @@ namespace FoxTube.Pages.SettingsPages
list.SelectedItem = items.Find(i => i.Id == id); 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) if (list == null)
return; 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)) if (!(list.SelectedItem is InboxItem item))
return; 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(); CloseView();
}
private void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e) void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{ {
if (e.NewState == null) if (e.NewState == null)
CloseView(); 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); Methods.ProcessLink(e.Link);
} }
} }
}
@@ -20,7 +20,6 @@ namespace FoxTube.Pages.SettingsPages
{ {
public sealed partial class Translate : Page public sealed partial class Translate : Page
{ {
//TODO: Localize page
ResourceLoader resources = ResourceLoader.GetForCurrentView("Translate"); ResourceLoader resources = ResourceLoader.GetForCurrentView("Translate");
StorageFile submission; StorageFile submission;
public Translate() public Translate()
@@ -31,7 +30,7 @@ namespace FoxTube.Pages.SettingsPages
LangList.Items.Add(culture); 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(); FileSavePicker picker = new FileSavePicker();
picker.CommitButtonText = resources.GetString("/Translate/exportPicker"); picker.CommitButtonText = resources.GetString("/Translate/exportPicker");
@@ -49,7 +48,7 @@ namespace FoxTube.Pages.SettingsPages
await Launcher.LaunchFileAsync(file); await Launcher.LaunchFileAsync(file);
} }
private void LangList_SelectionChanged(object sender, SelectionChangedEventArgs e) void LangList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
greenResult.Visibility = Visibility.Collapsed; greenResult.Visibility = Visibility.Collapsed;
certification.Visibility = Visibility.Collapsed; certification.Visibility = Visibility.Collapsed;
@@ -59,7 +58,7 @@ namespace FoxTube.Pages.SettingsPages
export.IsEnabled = true; export.IsEnabled = true;
} }
private async void upload_Click(object sender, RoutedEventArgs e) async void upload_Click(object sender, RoutedEventArgs e)
{ {
FileOpenPicker picker = new FileOpenPicker() FileOpenPicker picker = new FileOpenPicker()
{ {
@@ -156,12 +155,10 @@ namespace FoxTube.Pages.SettingsPages
return report; 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); 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; uploadingProgress.Visibility = Visibility.Visible;
submit.IsEnabled = false; submit.IsEnabled = false;
+2 -6
View File
@@ -8,12 +8,10 @@
mc:Ignorable="d" mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer> <controls:AdaptiveGridView ItemsSource="{x:Bind list}" DesiredWidth="250" ItemClick="AdaptiveGridView_ItemClick">
<controls:AdaptiveGridView ItemsSource="{x:Bind list}" DesiredWidth="250">
<controls:AdaptiveGridView.ItemTemplate> <controls:AdaptiveGridView.ItemTemplate>
<DataTemplate> <DataTemplate>
<Button HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Padding="5" Tag="{Binding Path=Snippet.ResourceId.ChannelId}" Click="Button_Click"> <Grid HorizontalAlignment="Stretch" Padding="5" Background="{StaticResource ButtonBackgroundThemeBrush}">
<Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="55"/> <ColumnDefinition Width="55"/>
<ColumnDefinition/> <ColumnDefinition/>
@@ -25,9 +23,7 @@
</PersonPicture> </PersonPicture>
<TextBlock Grid.Column="1" TextWrapping="Wrap" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Text="{Binding Path=Snippet.Title}"/> <TextBlock Grid.Column="1" TextWrapping="Wrap" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Text="{Binding Path=Snippet.Title}"/>
</Grid> </Grid>
</Button>
</DataTemplate> </DataTemplate>
</controls:AdaptiveGridView.ItemTemplate> </controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView> </controls:AdaptiveGridView>
</ScrollViewer>
</Page> </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 System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
namespace FoxTube.Pages namespace FoxTube.Pages
@@ -16,12 +16,10 @@ namespace FoxTube.Pages
{ {
InitializeComponent(); InitializeComponent();
Methods.MainPage.PageContent.LoadingPage.Close(); Navigation.Frame.Frame.LoadingPage.Close();
} }
private void Button_Click(object sender, RoutedEventArgs e) void AdaptiveGridView_ItemClick(object sender, ItemClickEventArgs e) =>
{ Navigation.GoToChannel(((Subscription)e.ClickedItem).Snippet.ResourceId.ChannelId);
Methods.MainPage.GoToChannel(((Button)sender).Tag.ToString());
}
} }
} }
+1 -1
View File
@@ -8,7 +8,7 @@
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <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> <ui:AdaptiveGridView.ItemContainerTransitions>
<TransitionCollection> <TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/> <EntranceThemeTransition IsStaggeringEnabled="True"/>
+32 -9
View File
@@ -1,5 +1,7 @@
using FoxTube.Controls; using FoxTube.Classes;
using FoxTube.Controls.Adverts; using FoxTube.Controls.Adverts;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
@@ -12,17 +14,27 @@ namespace FoxTube.Pages
{ {
public int Count => list.Items.Count; public int Count => list.Items.Count;
public ItemCollection Children => list.Items; 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(); 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); list.Items.Add(item);
if ((list.Items.Count - 5) % 25 == 0) if ((ItemsCount - 5) % 25 == 0)
list.Items.Add(new CardAdvert()); list.Items.Add(new CardAdvert());
empty.Visibility = Visibility.Collapsed; empty.Visibility = Visibility.Collapsed;
} }
@@ -33,9 +45,20 @@ namespace FoxTube.Pages
empty.Visibility = Visibility.Visible; empty.Visibility = Visibility.Visible;
} }
public void DeleteItem(FrameworkElement item) public void DeleteItem(FrameworkElement item) =>
{
Children.Remove(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" x:Class="FoxTube.Pages.VideoPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:FoxTube.Pages"
xmlns:classes="using:FoxTube.Classes" xmlns:classes="using:FoxTube.Classes"
xmlns:videopage="using:FoxTube.Controls.VideoPage"
xmlns:controls="using:FoxTube.Controls"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" SizeChanged="grid_SizeChanged"> <Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" SizeChanged="grid_SizeChanged">
@@ -25,6 +25,7 @@
</VisualState> </VisualState>
</VisualStateGroup> </VisualStateGroup>
</VisualStateManager.VisualStateGroups> </VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="400"/> <ColumnDefinition Width="400"/>
@@ -34,7 +35,7 @@
<RowDefinition/> <RowDefinition/>
<RowDefinition Height="auto"/> <RowDefinition Height="auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ScrollViewer Name="mainScroll" VerticalScrollBarVisibility="Hidden"> <ScrollViewer Name="mainScroll" VerticalScrollBarVisibility="Hidden" ViewChanged="MainScroll_ViewChanged">
<StackPanel Name="mainContent"> <StackPanel Name="mainContent">
<Border BorderBrush="Red" BorderThickness="5" CornerRadius="10" Margin="10" Name="upcoming" Visibility="Collapsed"> <Border BorderBrush="Red" BorderThickness="5" CornerRadius="10" Margin="10" Name="upcoming" Visibility="Collapsed">
<Grid Margin="10"> <Grid Margin="10">
@@ -61,10 +62,8 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
</Border> </Border>
<local:VideoPlayer x:Name="player" NextClicked="Player_NextClicked" MiniMode="Player_Minimize"/>
<PivotItem Header="Description" Name="descriptionPanel"> <PivotItem Header="Description" Name="descriptionPanel">
<StackPanel Margin="0,10"> <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 IsTextSelectionEnabled="True" Name="title" Text="[Video title]" FontSize="25" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Start"/>
<TextBlock Text="Published at: " Name="meta"/> <TextBlock Text="Published at: " Name="meta"/>
<Grid> <Grid>
@@ -72,13 +71,13 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </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"> <StackPanel Orientation="Horizontal">
<PersonPicture Name="channelAvatar" Width="90"/> <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="channelName" Text="[Channel name]" FontSize="18"/>
<TextBlock Name="subscribers" Text="[subscribers]" Foreground="Gray" Margin="0,0,0,5"/> <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>
</StackPanel> </StackPanel>
</Button> </Button>
@@ -89,7 +88,7 @@
<TextBlock Foreground="Gray" Text="[dislikes]" Name="dislikes"/> <TextBlock Foreground="Gray" Text="[dislikes]" Name="dislikes"/>
<TextBlock HorizontalAlignment="Right" Foreground="Gray" Text="[likes]" Name="likes"/> <TextBlock HorizontalAlignment="Right" Foreground="Gray" Text="[likes]" Name="likes"/>
</Grid> </Grid>
<Grid BorderBrush="{StaticResource SystemControlBackgroundListMediumRevealBorderBrush}"> <Grid>
<FontIcon Foreground="Gray" <FontIcon Foreground="Gray"
HorizontalAlignment="Left" HorizontalAlignment="Left"
FontSize="40" FontSize="40"
@@ -113,16 +112,12 @@
<classes:AdaptiveCommandBar Grid.Row="1" VerticalAlignment="Bottom" x:Name="commandbar"> <classes:AdaptiveCommandBar Grid.Row="1" VerticalAlignment="Bottom" x:Name="commandbar">
<AppBarButton x:Uid="/VideoPage/download" Icon="Download" Label="Download video" Name="download"> <AppBarButton x:Uid="/VideoPage/download" Icon="Download" Label="Download video" Name="download">
<AppBarButton.Flyout> <AppBarButton.Flyout>
<MenuFlyout x:Name="downloadSelector"/> <Flyout x:Name="downloadSelector"/>
</AppBarButton.Flyout> </AppBarButton.Flyout>
</AppBarButton> </AppBarButton>
<AppBarButton x:Uid="/VideoPage/addTo" Name="addTo" Label="Add to" Icon="Add" Visibility="Visible"> <AppBarButton x:Uid="/VideoPage/addTo" Name="addTo" Label="Add to" Icon="Add" Visibility="Visible">
<AppBarButton.Flyout> <AppBarButton.Flyout>
<MenuFlyout x:Name="addList"> <Flyout x:Name="addToSelector"/>
<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>
</AppBarButton.Flyout> </AppBarButton.Flyout>
</AppBarButton> </AppBarButton>
<AppBarButton x:Uid="/VideoPage/refresh" Name="refresh" Click="refresh_Click" Icon="Refresh" Label="Refresh page"/> <AppBarButton x:Uid="/VideoPage/refresh" Name="refresh" Click="refresh_Click" Icon="Refresh" Label="Refresh page"/>
@@ -138,52 +133,15 @@
<Grid Grid.Column="1" Name="tabsPlaceholder"> <Grid Grid.Column="1" Name="tabsPlaceholder">
<Pivot Name="pivot" SelectedIndex="0" IsHeaderItemsCarouselEnabled="False"> <Pivot Name="pivot" SelectedIndex="0" IsHeaderItemsCarouselEnabled="False">
<PivotItem x:Uid="/VideoPage/related" Header="Suggestions"> <PivotItem x:Uid="/VideoPage/related" Header="Suggestions">
<ScrollViewer> <videopage:RelatedVideos x:Name="suggestions"/>
<pages:VideoGrid x:Name="relatedVideos"/>
</ScrollViewer>
</PivotItem> </PivotItem>
<PivotItem x:Uid="/VideoPage/comments" Header="Comments" Name="commentsPlaceholder"> <PivotItem x:Uid="/VideoPage/comments" Header="Comments" Name="commentsTab">
<pages:CommentsPage x:Name="comments"/> <videopage:Comments x:Name="comments"/>
</PivotItem> </PivotItem>
<PivotItem x:Uid="/VideoPage/playlist" Header="Playlist" Name="playlist"> <PivotItem x:Uid="/VideoPage/playlist" Header="Playlist" Name="playlistTab">
<ScrollViewer Name="playlistScroll"> <videopage:VideoPlaylist x:Name="playlist" ItemChanged="Playlist_ItemChanged"/>
<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> </PivotItem>
</Pivot> </Pivot>
</Grid> </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> </Grid>
</Page> </Page>
+131 -471
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;
using Google.Apis.YouTube.v3.Data; using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Analytics;
@@ -25,21 +27,6 @@ namespace FoxTube.Pages
{ {
public enum Rating { None, Like, Dislike } 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> /// <summary>
/// Video page /// Video page
/// </summary> /// </summary>
@@ -49,27 +36,25 @@ namespace FoxTube.Pages
public object Parameter { get; set; } = null; public object Parameter { get; set; } = null;
public string playlistId = null; string playlistItem = null;
public Video item; Video item;
public HistoryItem history; Channel channelItem;
public bool incognito = false; bool incognito = false;
bool isExtended = false;
Rating userRating = Rating.None; Rating userRating = Rating.None;
DispatcherTimer liveTimer; DispatcherTimer liveTimer;
DispatcherTimer countdownTimer; DispatcherTimer countdownTimer;
public VideoPlayer Player => player;
public VideoPage() public VideoPage()
{ {
InitializeComponent(); InitializeComponent();
if (Window.Current.Bounds.Width <= 1000) //Methods.Player.SizeChanged += Player_SizeChanged;
{
Debug.WriteLine("Correcting layout..."); if (Window.Current.Bounds.Width > 1000)
return;
mainContent.Children.Remove(descriptionPanel); mainContent.Children.Remove(descriptionPanel);
pivot.Items.Insert(0, descriptionPanel); pivot.Items.Insert(0, descriptionPanel);
@@ -78,65 +63,87 @@ namespace FoxTube.Pages
grid.ColumnDefinitions[1].Width = new GridLength(0); 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) protected override void OnNavigatedTo(NavigationEventArgs e)
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
Parameter = e.Parameter; Parameter = e.Parameter;
Initialize(e.Parameter as object[]); Initialize(((object, Channel, bool, string))e.Parameter);
} }
protected override void OnNavigatedFrom(NavigationEventArgs e) async void Initialize((object video, Channel channel, bool incognito, string playlist) parameter, bool playlistItemSwitch = false)
{
base.OnNavigatedFrom(e);
Player.Player.Stop();
}
public async void Initialize(object[] ids)
{ {
try try
{ {
incognito = (bool)ids[2]; incognito = parameter.incognito;
channelItem = parameter.channel;
if (ids[1] != null) if (parameter.video is Video)
LoadPlaylist(ids[1] as string); item = parameter.video as Video;
else else
pivot.Items.Remove(playlist); {
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,statistics,status,contentDetails,liveStreamingDetails"); 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; request.Hl = SettingsStorage.RelevanceLanguage;
item = (await request.ExecuteAsync()).Items[0]; 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") 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 else
LoadStream(); LoadStream();
if (item.Snippet.LiveBroadcastContent == "upcoming") if (item.Snippet.LiveBroadcastContent == "upcoming")
SetSchedule(); SetSchedule();
LoadInfo(); suggestions.Initialize(item.Id);
LoadAddTo();
Methods.MainPage.VideoContent.LoadingPage.Close(); //Methods.MainPage.VideoContent.LoadingPage.Close();
} }
catch (System.Net.Http.HttpRequestException) 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) catch (Exception e)
{ {
/*Methods.Player.Player.Visibility = Visibility.Collapsed;
if (item == null) if (item == null)
{ {
Methods.MainPage.PageContent.LoadingPage.Error("VideoNotFound", "Such video doesn't exist"); Methods.MainPage.PageContent.LoadingPage.Error("VideoNotFound", "Such video doesn't exist");
@@ -150,7 +157,7 @@ namespace FoxTube.Pages
{ "Message", e.Message }, { "Message", e.Message },
{ "Video ID", item.Id }, { "Video ID", item.Id },
{ "StackTrace", e.StackTrace } { "StackTrace", e.StackTrace }
}); });*/
} }
} }
@@ -183,82 +190,23 @@ namespace FoxTube.Pages
} }
} }
async void LoadPlaylist(string id) async void LoadDescription()
{
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()
{ {
//Setting meta //Setting meta
title.Text = item.Snippet.Localized.Title; 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.Text = "";
meta.Inlines.Add(new Run meta.Inlines.Add(new Run
{ {
@@ -270,8 +218,7 @@ namespace FoxTube.Pages
request.Id = item.Snippet.CategoryId; request.Id = item.Snippet.CategoryId;
request.Hl = SettingsStorage.RelevanceLanguage; request.Hl = SettingsStorage.RelevanceLanguage;
VideoCategoryListResponse response = await request.ExecuteAsync(); VideoCategoryListResponse response = await request.ExecuteAsync();
if(response.Items.Count != 0)
{
meta.Inlines.Add(new Run meta.Inlines.Add(new Run
{ {
Text = $" {resources.GetString("/VideoPage/inCat")} " Text = $" {resources.GetString("/VideoPage/inCat")} "
@@ -281,26 +228,9 @@ namespace FoxTube.Pages
{ {
Text = response.Items[0].Snippet.Title 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); 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 //Setting User's rate
if (SecretsVault.IsAuthorized) if (SecretsVault.IsAuthorized)
@@ -323,8 +253,11 @@ namespace FoxTube.Pages
subscribe.Foreground = new SolidColorBrush(Colors.Gray); subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe"); subscribe.Content = resources.GetString("/Cards/unsubscribe");
} }
if (item.Snippet.ChannelId == SecretsVault.AccountId) else if (item.Snippet.ChannelId == SecretsVault.AccountId)
subscribe.Visibility = Visibility.Collapsed; subscribe.Visibility = Visibility.Collapsed;
/*try { addToSelector.Initialize(item); }
catch { addTo.Visibility = Visibility.Collapsed; }*/
} }
else else
{ {
@@ -332,34 +265,22 @@ namespace FoxTube.Pages
addTo.Visibility = Visibility.Collapsed; addTo.Visibility = Visibility.Collapsed;
subscribe.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() void LoadStream()
{ {
liveTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(10) }; liveTimer = new DispatcherTimer() { Interval = TimeSpan.FromMinutes(1) };
liveTimer.Tick += LiveStatsUpdate; liveTimer.Tick += LiveStatsUpdate;
liveTimer.Start(); liveTimer.Start();
LiveStatsUpdate(); views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
if (string.IsNullOrWhiteSpace(item.LiveStreamingDetails.ActiveLiveChatId)) if (string.IsNullOrWhiteSpace(item.LiveStreamingDetails.ActiveLiveChatId))
pivot.Items.Remove(commentsPlaceholder); comments.Initialize(item);
else else
{ {
commentsPlaceholder.Header = resources.GetString("/VideoPage/chat"); commentsTab.Header = resources.GetString("/VideoPage/chat");
commentsPlaceholder.Content = new Chat(item.LiveStreamingDetails.ActiveLiveChatId); commentsTab.Content = new Chat(item.LiveStreamingDetails.ActiveLiveChatId);
pivot.SelectedItem = commentsPlaceholder; pivot.SelectedItem = commentsTab;
} }
download.Visibility = Visibility.Collapsed; download.Visibility = Visibility.Collapsed;
} }
@@ -372,117 +293,14 @@ namespace FoxTube.Pages
views.Text = $"{(await request.ExecuteAsync()).Items[0].LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}"; views.Text = $"{(await request.ExecuteAsync()).Items[0].LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
} }
void LoadStats() private void gotoChannel_Click(object sender, RoutedEventArgs e) =>
{ Navigation.GoToChannel(channelItem.Id);
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
comments.Initialize(item); private void openBrowser_Click(object sender, RoutedEventArgs e) => Methods.GuardFromNull("");
LoadDownloads(); //Methods.Player.OpenBrowser();
}
async void LoadDownloads() void refresh_Click(object sender, RoutedEventArgs e) => Methods.GuardFromNull("");
{ //Methods.MainPage.VideoContent.Refresh();
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);
}
private void grid_SizeChanged(object sender, SizeChangedEventArgs e) private void grid_SizeChanged(object sender, SizeChangedEventArgs e)
{ {
@@ -516,7 +334,7 @@ namespace FoxTube.Pages
private void Share(DataTransferManager sender, DataRequestedEventArgs args) private void Share(DataTransferManager sender, DataRequestedEventArgs args)
{ {
player.Pause(); //Methods.Player.Pause();
Methods.Share(args, Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url, item.Snippet.Thumbnails.Medium.Url,
@@ -531,6 +349,9 @@ namespace FoxTube.Pages
DataTransferManager.ShowShareUI(); 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) private async void dislike_Click(object sender, RoutedEventArgs e)
{ {
if (SecretsVault.IsAuthorized) 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) private async void subscribe_Click(object sender, RoutedEventArgs e)
{ {
if (await SecretsVault.ChangeSubscriptionState(item.Snippet.ChannelId)) 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);
try
{
if (SecretsVault.UserChannel == null)
{
addTo.Visibility = Visibility.Collapsed;
return;
}
if (SecretsVault.WatchLater.Contains(item.Id)) private void Player_SizeChanged(object sender, SizeChangedEventArgs e)
(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)
{ {
itemRequest.PlaylistId = i.Id; //if(Methods.Player.State != PlayerDisplayState.Compact)
ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem mainContent.Margin = new Thickness(mainContent.Margin.Left, e.NewSize.Height, mainContent.Margin.Right, mainContent.Margin.Bottom);
{
Text = i.Snippet.Title,
IsChecked = (await itemRequest.ExecuteAsync()).Items.Count > 0,
Tag = i,
Icon = new FontIcon
{
Glyph = "\xE728"
} }
}; public void Maximize()
menuItem.Click += Item_Click;
addList.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;
}
}
}
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();
} }
public void Minimize()
{
}
private void ResetControl()
{
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 void Playlist_ItemChanged(string id)
{
ResetControl();
Initialize((id, null, incognito, playlistItem), true);
}
} }
} }
+1 -1
View File
@@ -169,7 +169,7 @@
<value>Skip forward for 30 seconds</value> <value>Skip forward for 30 seconds</value>
</data> </data>
<data name="generatedCaption" xml:space="preserve"> <data name="generatedCaption" xml:space="preserve">
<value>(Auto-generated)</value> <value>Auto-generated</value>
</data> </data>
<data name="goLive.Text" xml:space="preserve"> <data name="goLive.Text" xml:space="preserve">
<value>Go to live broadcast</value> <value>Go to live broadcast</value>
+1 -1
View File
@@ -169,7 +169,7 @@
<value>Вперед на 30 секунд</value> <value>Вперед на 30 секунд</value>
</data> </data>
<data name="generatedCaption" xml:space="preserve"> <data name="generatedCaption" xml:space="preserve">
<value>(Авто-перевод)</value> <value>Авто-перевод</value>
</data> </data>
<data name="goLive.Text" xml:space="preserve"> <data name="goLive.Text" xml:space="preserve">
<value>Перейти к прямому эфиру</value> <value>Перейти к прямому эфиру</value>
+2 -1
View File
@@ -1,7 +1,7 @@
<ResourceDictionary <ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 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:controls="using:FoxTube.Controls"
xmlns:adverts="using:FoxTube.Controls.Adverts"> xmlns:adverts="using:FoxTube.Controls.Adverts">
@@ -294,6 +294,7 @@
<KeyboardAccelerator Key="Space"/> <KeyboardAccelerator Key="Space"/>
</Button.KeyboardAccelerators> </Button.KeyboardAccelerators>
</Button> </Button>
<Button x:Name="PreviousButton" Content="&#xE100;" Visibility="Collapsed"/>
<Button x:Name="NextButton" Content="&#xE101;"/> <Button x:Name="NextButton" Content="&#xE101;"/>
<Button x:Name="VolumeMenuButton" Content="&#xE15D;"> <Button x:Name="VolumeMenuButton" Content="&#xE15D;">
<Button.Flyout> <Button.Flyout>