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