Optimization, refactoring, debugging
Related Work Items: #251, #252, #261
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
using Google.Apis.Services;
|
using Google.Apis.Services;
|
||||||
using Google.Apis.YouTube.v3;
|
using Google.Apis.YouTube.v3;
|
||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
using Microsoft.AppCenter.Analytics;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Windows.ApplicationModel.Background;
|
using Windows.ApplicationModel.Background;
|
||||||
@@ -17,8 +17,9 @@ namespace FoxTube.Background
|
|||||||
public sealed class BackgroundProcessor : IBackgroundTask
|
public sealed class BackgroundProcessor : IBackgroundTask
|
||||||
{
|
{
|
||||||
private DateTime lastCheck = DateTime.Now;
|
private DateTime lastCheck = DateTime.Now;
|
||||||
private readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
|
private readonly ApplicationDataContainer settings = ApplicationData.Current.RoamingSettings;
|
||||||
private YouTubeService Service => new YouTubeService(new BaseClientService.Initializer()
|
dynamic prefs;
|
||||||
|
private readonly YouTubeService Service = new YouTubeService(new BaseClientService.Initializer()
|
||||||
{
|
{
|
||||||
ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0",
|
ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0",
|
||||||
ApplicationName = "FoxTube"
|
ApplicationName = "FoxTube"
|
||||||
@@ -30,21 +31,20 @@ namespace FoxTube.Background
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
def = taskInstance.GetDeferral();
|
def = taskInstance.GetDeferral();
|
||||||
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
|
|
||||||
|
|
||||||
if (settings.Values["lastCheck"] == null)
|
if (settings.Values["lastCheck"] == null)
|
||||||
{
|
{
|
||||||
settings.Values.Add("lastCheck", DateTime.Now.ToString());
|
settings.Values["lastCheck"] = DateTime.Now.ToString();
|
||||||
def.Complete();
|
def.Complete();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
lastCheck = DateTime.Parse(settings.Values["lastCheck"] as string);
|
lastCheck = DateTime.Parse(settings.Values["lastCheck"] as string);
|
||||||
|
|
||||||
bool[] notificationsSettings = JsonConvert.DeserializeObject<bool[]>(await FileIO.ReadTextAsync(await ApplicationData.Current.RoamingFolder.GetFileAsync("notifications.json")));
|
prefs = JsonConvert.DeserializeObject<dynamic>(settings.Values["settings"] as string);
|
||||||
if (notificationsSettings[0])
|
if ((bool)prefs.devNotifications)
|
||||||
CheckAnnouncements();
|
CheckAnnouncements();
|
||||||
if (notificationsSettings[1])
|
if ((bool)prefs.videoNotifications)
|
||||||
await CheckAccount();
|
await CheckAccount();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -54,20 +54,11 @@ namespace FoxTube.Background
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
|
||||||
{
|
|
||||||
Analytics.TrackEvent("Background task caneled", new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{ "Reason", reason.ToString() }
|
|
||||||
});
|
|
||||||
settings.Values["lastCheck"] = DateTime.Now.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task CheckAccount()
|
async Task CheckAccount()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Dictionary<string, string> subscriptions = JsonConvert.DeserializeObject<Dictionary<string, string>>(await FileIO.ReadTextAsync(await ApplicationData.Current.RoamingFolder.GetFileAsync("background.json")));
|
Dictionary<string, string> subscriptions = JsonConvert.DeserializeObject<Dictionary<string, string>>(settings.Values["subscriptions"] as string);
|
||||||
|
|
||||||
List<SearchResult> results = new List<SearchResult>();
|
List<SearchResult> results = new List<SearchResult>();
|
||||||
|
|
||||||
@@ -94,25 +85,28 @@ namespace FoxTube.Background
|
|||||||
TileUpdater updater = TileUpdateManager.CreateTileUpdaterForApplication();
|
TileUpdater updater = TileUpdateManager.CreateTileUpdaterForApplication();
|
||||||
updater.EnableNotificationQueue(true);
|
updater.EnableNotificationQueue(true);
|
||||||
updater.Clear();
|
updater.Clear();
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5 && i < results.Count; i++)
|
||||||
updater.Update(Tiles.GetTileLayout(results[i].Snippet.Title, results[i].Snippet.ChannelTitle, results[i].Snippet.Thumbnails.Medium.Url, subscriptions[results[i].Snippet.ChannelId]));
|
updater.Update(Tiles.GetTileLayout(System.Security.SecurityElement.Escape(results[i].Snippet.Title), System.Security.SecurityElement.Escape(results[i].Snippet.ChannelTitle), results[i].Snippet.Thumbnails.Medium.Url.Replace("&", "%26"), subscriptions[results[i].Snippet.ChannelId]));
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckAnnouncements()
|
async void CheckAnnouncements()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
XmlDocument doc = new XmlDocument();
|
XmlDocument doc = new XmlDocument();
|
||||||
doc.Load(XmlReader.Create("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml"));
|
doc.LoadXml(await new HttpClient().GetStringAsync("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml"));
|
||||||
if ((DateTime.Parse((doc["posts"].FirstChild as XmlElement).GetAttribute("time")) - lastCheck).TotalSeconds > 0)
|
XmlElement item = doc["posts"].FirstChild as XmlElement;
|
||||||
|
|
||||||
|
DateTime date = DateTime.Parse(item.GetAttribute("time"));
|
||||||
|
if (date > lastCheck && date < DateTime.Now)
|
||||||
ToastNotificationManager.CreateToastNotifier().Show(
|
ToastNotificationManager.CreateToastNotifier().Show(
|
||||||
Notification.GetInternalToast(doc["posts"].FirstChild["id"].InnerText,
|
Notification.GetInternalToast(item["id"].InnerText,
|
||||||
doc["posts"].FirstChild["header"].InnerText,
|
item["header"][(string)prefs.language].InnerText,
|
||||||
doc["posts"].FirstChild["content"].InnerText,
|
item["content"][(string)prefs.language].InnerText,
|
||||||
doc["posts"].FirstChild["thumbnail"].InnerText,
|
item["thumbnail"].InnerText,
|
||||||
doc["posts"].FirstChild["avatar"].InnerText));
|
item["avatar"].InnerText));
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ namespace FoxTube.Background
|
|||||||
{
|
{
|
||||||
public static class Notification
|
public static class Notification
|
||||||
{
|
{
|
||||||
private static Dictionary<string, string> languagePack = LoadPack();
|
private static readonly Dictionary<string, string> languagePack = LoadPack();
|
||||||
|
|
||||||
private static Dictionary<string, string> LoadPack()
|
private static Dictionary<string, string> LoadPack()
|
||||||
{
|
{
|
||||||
object[] saved = JsonConvert.DeserializeObject<object[]>(ApplicationData.Current.RoamingSettings.Values["settings"] as string);
|
dynamic saved = JsonConvert.DeserializeObject<dynamic>(ApplicationData.Current.RoamingSettings.Values["settings"] as string);
|
||||||
if (saved[7] as string == "ru-RU")
|
if (saved.language as string == "ru-RU")
|
||||||
return new Dictionary<string, string>()
|
return new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
{ "addLater", "Посмотреть позже" },
|
{ "addLater", "Посмотреть позже" },
|
||||||
@@ -60,8 +60,8 @@ namespace FoxTube.Background
|
|||||||
<binding template='ToastGeneric'>
|
<binding template='ToastGeneric'>
|
||||||
<image placement='hero' src='{thumbnail.Replace("&", "%26")}'/>
|
<image placement='hero' src='{thumbnail.Replace("&", "%26")}'/>
|
||||||
<image placement='appLogoOverride' hint-crop='circle' src='{avatar.Replace("&", "%26") ?? "http://foxgame-studio.000webhostapp.com/FoxTubeAssets/LogoAvatar.png"}'/>
|
<image placement='appLogoOverride' hint-crop='circle' src='{avatar.Replace("&", "%26") ?? "http://foxgame-studio.000webhostapp.com/FoxTubeAssets/LogoAvatar.png"}'/>
|
||||||
<text>{title}</text>
|
<text>{System.Security.SecurityElement.Escape(title)}</text>
|
||||||
<text>{channel} {languagePack["videoContent"]}</text>
|
<text>{System.Security.SecurityElement.Escape(channel)} {languagePack["videoContent"]}</text>
|
||||||
</binding>
|
</binding>
|
||||||
</visual>
|
</visual>
|
||||||
|
|
||||||
|
|||||||
+32
-26
@@ -12,7 +12,6 @@ using Windows.ApplicationModel.Activation;
|
|||||||
using Windows.ApplicationModel.Background;
|
using Windows.ApplicationModel.Background;
|
||||||
using Windows.Globalization;
|
using Windows.Globalization;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using Windows.System;
|
|
||||||
using Windows.System.Power;
|
using Windows.System.Power;
|
||||||
using Windows.UI.Notifications;
|
using Windows.UI.Notifications;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
@@ -63,7 +62,7 @@ namespace FoxTube
|
|||||||
changelog.Load(await file.OpenStreamForReadAsync());
|
changelog.Load(await file.OpenStreamForReadAsync());
|
||||||
XmlElement e = changelog["items"].ChildNodes[0] as XmlElement;
|
XmlElement e = changelog["items"].ChildNodes[0] as XmlElement;
|
||||||
|
|
||||||
ToastNotificationManager.CreateToastNotifier().Show(FoxTube.Background.Notification.GetChangelogToast(e.GetAttribute("version")));
|
ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(e.GetAttribute("version")));
|
||||||
|
|
||||||
SettingsStorage.Version = $"{ver.Major}.{ver.Minor}";
|
SettingsStorage.Version = $"{ver.Major}.{ver.Minor}";
|
||||||
}
|
}
|
||||||
@@ -189,9 +188,6 @@ namespace FoxTube
|
|||||||
Debug.WriteLine(e.Message);
|
Debug.WriteLine(e.Message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "download":
|
|
||||||
await Launcher.LaunchFileAsync(await StorageFile.GetFileFromPathAsync(arguments[1]));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,33 +213,42 @@ namespace FoxTube
|
|||||||
|
|
||||||
Window.Current.Activate();
|
Window.Current.Activate();
|
||||||
|
|
||||||
if (e is ToastNotificationActivatedEventArgs)
|
switch (e.Kind)
|
||||||
{
|
{
|
||||||
string[] args = (e as ToastNotificationActivatedEventArgs).Argument.Split('|');
|
case ActivationKind.Protocol:
|
||||||
switch (args[0])
|
break;
|
||||||
{
|
case ActivationKind.ToastNotification:
|
||||||
case "changelog":
|
string[] args = (e as ToastNotificationActivatedEventArgs).Argument.Split('|');
|
||||||
case "inbox":
|
switch (args[0])
|
||||||
Methods.MainPage.GoToDeveloper(args[1]);
|
{
|
||||||
break;
|
case "changelog":
|
||||||
|
case "inbox":
|
||||||
|
Methods.MainPage.GoToDeveloper(args[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
case "video":
|
case "video":
|
||||||
Methods.MainPage.GoToVideo(args[1]);
|
Methods.MainPage.GoToVideo(args[1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "channel":
|
case "channel":
|
||||||
Methods.MainPage.GoToChannel(args[1]);
|
Methods.MainPage.GoToChannel(args[1]);
|
||||||
break;
|
break;
|
||||||
case "download":
|
case "download":
|
||||||
Methods.MainPage.GoToDownloads();
|
Methods.MainPage.GoToDownloads();
|
||||||
break;
|
break;
|
||||||
case "dcancel":
|
case "dcancel":
|
||||||
DownloadAgent.Remove(args[1]);
|
DownloadAgent.Cancel(args[1]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Launch(string e = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||||
{
|
{
|
||||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||||
@@ -255,6 +260,7 @@ namespace FoxTube
|
|||||||
SettingsStorage.SaveData();
|
SettingsStorage.SaveData();
|
||||||
DownloadAgent.QuitPrompt();
|
DownloadAgent.QuitPrompt();
|
||||||
deferral.Complete();
|
deferral.Complete();
|
||||||
|
Analytics.TrackEvent("Session terminated");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UnhandledError(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
|
private void UnhandledError(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||||
|
|||||||
@@ -1,5 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<items>
|
<items>
|
||||||
|
<item time="2019-02-12" version="0.4">
|
||||||
|
<content>
|
||||||
|
<en-US>### What's new:
|
||||||
|
- Improved stability and speed of the app
|
||||||
|
- Fixed a lot of bugs
|
||||||
|
- Fixed player
|
||||||
|
- Added animations and acrylic
|
||||||
|
|
||||||
|
### Following features awaits their implementation:
|
||||||
|
- 'History' and 'Watch later' playlists (Progress has been made but it still incomplete)
|
||||||
|
- The same problem with 'Recommended' and 'Subscriptions' tabs on home page
|
||||||
|
- Playlists management will be introduced in the next version but it may miss some playlists
|
||||||
|
- Even though I've done ads delivering system it's not introduced because of some problems with ads' lookup. I just want to make your ads experience the best one. So it won't be implemented until I make sure that they are ready
|
||||||
|
</en-US>
|
||||||
|
<ru-RU>### Что нового:
|
||||||
|
- Улучшена стабильность и скорость приложения
|
||||||
|
- Исправлена куча багов
|
||||||
|
- Исправлен плеер
|
||||||
|
- Добавлены анимации и акрил
|
||||||
|
|
||||||
|
### Следующие функции ждут своего внедрения:
|
||||||
|
- История и плейлист 'Посмотреть позже' (прогресс есть, но нужно еще работать)
|
||||||
|
- Та же фигня и со вкладками 'Рекомендованные' и 'Подписки' на домашней странице
|
||||||
|
- Управление плейлистами будет добавлено в следующей версии, но некоторые плейлисты могут отсутствовать
|
||||||
|
- Хотя я сделал систему доставки рекламы, она не введена из-за некоторых проблем с видом банеров. Я хочу сделать ваш опыт взаимодействия с рекламой в приложении лучше, так что она не будет введена до тех пор, пока я не буду в этом уверен
|
||||||
|
</ru-RU>
|
||||||
|
</content>
|
||||||
|
</item>
|
||||||
<item time="2019-02-02" version="0.3">
|
<item time="2019-02-02" version="0.3">
|
||||||
<content>
|
<content>
|
||||||
<en-US>### What's new:
|
<en-US>### What's new:
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@@ -1,19 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using FoxTube.Classes;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using YoutubeExplode.Models.MediaStreams;
|
using YoutubeExplode.Models.MediaStreams;
|
||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
using FoxTube.Controls;
|
using FoxTube.Controls;
|
||||||
|
using FoxTube.Pages;
|
||||||
|
|
||||||
namespace FoxTube
|
namespace FoxTube
|
||||||
{
|
{
|
||||||
// TODO: Refactor DownloadAgent
|
|
||||||
public static class DownloadAgent
|
public static class DownloadAgent
|
||||||
{
|
{
|
||||||
public static List<DownloadItem> items = new List<DownloadItem>();
|
public static List<DownloadItem> items = new List<DownloadItem>();
|
||||||
private static ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
|
private static ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
|
||||||
|
public static Downloads Page { get; set; }
|
||||||
public static StorageFolder Downloads { get; set; }
|
public static StorageFolder Downloads { get; set; }
|
||||||
|
|
||||||
public static async void Initialize()
|
public static async void Initialize()
|
||||||
@@ -32,45 +32,34 @@ namespace FoxTube
|
|||||||
items.Insert(0, new DownloadItem(info, meta, qualty));
|
items.Insert(0, new DownloadItem(info, meta, qualty));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CancelItem(string id)
|
public static void Remove(DownloadItem item)
|
||||||
{
|
{
|
||||||
DownloadItem item = items.Find(x => x.Container.Id == id);
|
try { Page.Remove(item); }
|
||||||
if (item == null || !item.InProgress)
|
catch { }
|
||||||
return;
|
items.Remove(item);
|
||||||
|
|
||||||
item.CancelPrompt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Remove(string id)
|
public static void Cancel(string id)
|
||||||
{
|
{
|
||||||
DownloadItem item = items.Find(x => x.Container.Id == id);
|
DownloadItem item = items.Find(i => i.Container.Id == id);
|
||||||
if (item == null)
|
if (item != null)
|
||||||
return;
|
|
||||||
|
|
||||||
if (item.InProgress)
|
|
||||||
item.Cancel();
|
item.Cancel();
|
||||||
else
|
|
||||||
items.Remove(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void QuitPrompt()
|
public static void QuitPrompt()
|
||||||
{
|
{
|
||||||
foreach (DownloadItem i in items.FindAll(i => i.InProgress))
|
foreach (DownloadItem i in items.FindAll(i => !i.Container.IsDownloaded))
|
||||||
|
{
|
||||||
i.Cancel();
|
i.Cancel();
|
||||||
|
items.Remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
List<DownloadItemContainer> containers = new List<DownloadItemContainer>();
|
List<DownloadItemContainer> containers = new List<DownloadItemContainer>();
|
||||||
items.ForEach(i => containers.Add(i.Container));
|
items.ForEach(i => containers.Add(i.Container));
|
||||||
|
|
||||||
string data = JsonConvert.SerializeObject(containers);
|
string data = JsonConvert.SerializeObject(containers);
|
||||||
|
|
||||||
try
|
settings.Values["downloads"] = data;
|
||||||
{
|
|
||||||
settings.Values["downloads"] = data;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
settings.Values.Add("downloads", data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace FoxTube.Classes
|
|
||||||
{
|
|
||||||
public class DownloadItemContainer
|
|
||||||
{
|
|
||||||
public string Title { get; set; }
|
|
||||||
public string Channel { get; set; }
|
|
||||||
public string Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string Extension { get; set; }
|
|
||||||
public Uri Thumbnail { get; set; }
|
|
||||||
public string Quality { get; set; }
|
|
||||||
public TimeSpan Duration { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,16 @@ namespace FoxTube.Classes
|
|||||||
{
|
{
|
||||||
public InboxItemType Type { get; set; } = InboxItemType.Default;
|
public InboxItemType Type { get; set; } = InboxItemType.Default;
|
||||||
public DateTime TimeStamp { get; set; }
|
public DateTime TimeStamp { get; set; }
|
||||||
|
public string TimeStampString
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Type == InboxItemType.PatchNote)
|
||||||
|
return TimeStamp.ToShortDateString();
|
||||||
|
else
|
||||||
|
return TimeStamp.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string Subject { get; set; }
|
public string Subject { get; set; }
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|||||||
+54
-95
@@ -21,6 +21,7 @@ using Windows.UI.Xaml;
|
|||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Windows.UI.Xaml.Documents;
|
using Windows.UI.Xaml.Documents;
|
||||||
using Windows.UI.Xaml.Media;
|
using Windows.UI.Xaml.Media;
|
||||||
|
using YoutubeExplode;
|
||||||
using YoutubeExplode.Models.MediaStreams;
|
using YoutubeExplode.Models.MediaStreams;
|
||||||
|
|
||||||
namespace FoxTube
|
namespace FoxTube
|
||||||
@@ -45,15 +46,6 @@ namespace FoxTube
|
|||||||
return new Uri(url);
|
return new Uri(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Try(Action action)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetChars(this string str, int count)
|
public static string GetChars(this string str, int count)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -160,41 +152,25 @@ namespace FoxTube
|
|||||||
public static void FormatText(ref TextBlock block, string text)
|
public static void FormatText(ref TextBlock block, string text)
|
||||||
{
|
{
|
||||||
block.Inlines.Clear();
|
block.Inlines.Clear();
|
||||||
|
Regex filter = new Regex(@"\b((?:https?://|www\.)\S+)|(\S+@\S+)\b", RegexOptions.IgnoreCase);
|
||||||
|
Regex link = new Regex(@"\b(?:https?://|www\.)\S+\b", RegexOptions.IgnoreCase);
|
||||||
|
Regex mail = new Regex(@"\b\S+@\S+\b", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
Regex regx = new Regex(@"(http(s)?://[\S]+|www.[\S]+|[\S]+@[\S]+)", RegexOptions.IgnoreCase);
|
foreach (string item in filter.Split(text))
|
||||||
Regex isWWW = new Regex(@"(http[s]?://[\S]+|www.[\S]+)");
|
|
||||||
Regex isEmail = new Regex(@"[\S]+@[\S]+");
|
|
||||||
foreach (string item in regx.Split(text))
|
|
||||||
{
|
{
|
||||||
if (isWWW.IsMatch(item))
|
if (link.IsMatch(item))
|
||||||
{
|
{
|
||||||
try
|
Hyperlink hl = new Hyperlink();
|
||||||
{
|
hl.Click += (s, arg) => ProcessLink(item);
|
||||||
Hyperlink link = new Hyperlink();
|
hl.Inlines.Add(new Run { Text = item });
|
||||||
link.Click += (s, arg) => { ProcessLink(item); };
|
block.Inlines.Add(hl);
|
||||||
link.Inlines.Add(new Run { Text = item });
|
|
||||||
block.Inlines.Add(link);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
block.Inlines.Add(new Run { Text = item });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (isEmail.IsMatch(item))
|
else if (mail.IsMatch(item))
|
||||||
{
|
{
|
||||||
try
|
Hyperlink hl = new Hyperlink { NavigateUri = $"mailto:{item}".ToUri() };
|
||||||
{
|
hl.Inlines.Add(new Run { Text = item });
|
||||||
Hyperlink link = new Hyperlink { NavigateUri = new Uri($"mailto:{item}"), Foreground = new SolidColorBrush(Colors.Red) };
|
block.Inlines.Add(hl);
|
||||||
link.Inlines.Add(new Run { Text = item });
|
|
||||||
block.Inlines.Add(link);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
block.Inlines.Add(new Run { Text = item });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (item == "s")
|
|
||||||
continue;
|
|
||||||
else
|
else
|
||||||
block.Inlines.Add(new Run { Text = item });
|
block.Inlines.Add(new Run { Text = item });
|
||||||
}
|
}
|
||||||
@@ -233,65 +209,48 @@ namespace FoxTube
|
|||||||
|
|
||||||
public async static void ProcessLink(string url)
|
public async static void ProcessLink(string url)
|
||||||
{
|
{
|
||||||
try
|
string output;
|
||||||
{
|
string type;
|
||||||
Debug.WriteLine($"Processing link: {url}");
|
|
||||||
if (url.Contains("youtube.com/") || url.Contains("youtu.be/"))
|
|
||||||
{
|
|
||||||
Debug.WriteLine("This is an internal youtube link");
|
|
||||||
url = url.Replace("https://", "").Replace("http://", "").Replace("wwww.", "").Replace("//", "");
|
|
||||||
Debug.WriteLine($"Prepared link: {url}");
|
|
||||||
|
|
||||||
if (url.Contains("/playlist"))
|
if (YoutubeClient.TryParseChannelId(url, out output))
|
||||||
{
|
|
||||||
Debug.WriteLine($"This is a playlist link. ID: {HttpUtility.ParseQueryString(url).Get("list")}");
|
|
||||||
MainPage.GoToPlaylist(HttpUtility.ParseQueryString(url).Get("list"));
|
|
||||||
}
|
|
||||||
else if (url.Contains("youtu.be/"))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"This is obfuscated video link. Video ID: {url.Split('/')[1]}");
|
|
||||||
MainPage.GoToVideo(url.Split('/')[1]);
|
|
||||||
}
|
|
||||||
else if (url.Contains("/watch"))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"This is regular video link. Video ID: {HttpUtility.ParseQueryString(url).Get("v")}");
|
|
||||||
MainPage.GoToVideo(HttpUtility.ParseQueryString(url).Get(0), HttpUtility.ParseQueryString(url).Get("list"));
|
|
||||||
}
|
|
||||||
else if (url.Contains("/v/"))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"This is video link. ID: {url.Split('/')[2].Split('?')[0]}");
|
|
||||||
MainPage.GoToVideo(url.Split('/')[2].Split('?')[0]);
|
|
||||||
}
|
|
||||||
else if (url.Contains("/channel/"))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"This is channel link. ID: {url.Split('/')[2]}");
|
|
||||||
MainPage.GoToChannel(url.Split('/')[2]);
|
|
||||||
}
|
|
||||||
else if (url.Contains("/user/"))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"This is channel link with username. Username: {url.Split('/')[2]}");
|
|
||||||
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("id");
|
|
||||||
Debug.WriteLine(request.ForUsername = url.Split('/')[2]);
|
|
||||||
request.MaxResults = 1;
|
|
||||||
MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id);
|
|
||||||
}
|
|
||||||
else if (url.Contains("/c/"))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"This is channel link with custom url. Custom name: {url.Split('/')[2]}");
|
|
||||||
SearchResource.ListRequest request = SecretsVault.Service.Search.List("id");
|
|
||||||
Debug.WriteLine(request.Q = url.Split('/')[2]);
|
|
||||||
request.MaxResults = 1;
|
|
||||||
MainPage.GoToChannel((await request.ExecuteAsync()).Items[0].Id.ChannelId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
{
|
||||||
await Launcher.LaunchUriAsync(new Uri(url));
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ namespace FoxTube
|
|||||||
//Saving user's subscriptions for background task
|
//Saving user's subscriptions for background task
|
||||||
SaveSubscriptions();
|
SaveSubscriptions();
|
||||||
|
|
||||||
InvokeEvent:
|
InvokeEvent:
|
||||||
AuthorizationStateChanged?.Invoke(args: IsAuthorized);
|
AuthorizationStateChanged?.Invoke(args: IsAuthorized);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -186,13 +186,11 @@ namespace FoxTube
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves user's subscriptions keypairs (channel ID: avatar URL) into "background.json" file for concurrent background processing
|
/// Saves user's subscriptions keypairs (channel ID: avatar URL) into "background.json" file for concurrent background processing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async void SaveSubscriptions()
|
public static void SaveSubscriptions()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> subs = new Dictionary<string, string>();
|
Dictionary<string, string> subs = new Dictionary<string, string>();
|
||||||
Subscriptions.ForEach(x => subs.Add(x.Snippet.ResourceId.ChannelId, x.Snippet.Thumbnails.Medium.Url));
|
Subscriptions.ForEach(x => subs.Add(x.Snippet.ResourceId.ChannelId, x.Snippet.Thumbnails.Medium.Url));
|
||||||
await FileIO.WriteTextAsync(
|
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = JsonConvert.SerializeObject(subs);
|
||||||
await ApplicationData.Current.RoamingFolder.CreateFileAsync("background.json", CreationCollisionOption.ReplaceExisting),
|
|
||||||
JsonConvert.SerializeObject(subs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -206,10 +204,7 @@ namespace FoxTube
|
|||||||
AuthorizationStateChanged?.Invoke(args: false);
|
AuthorizationStateChanged?.Invoke(args: false);
|
||||||
SettingsStorage.HasAccount = false;
|
SettingsStorage.HasAccount = false;
|
||||||
|
|
||||||
await FileIO.WriteTextAsync(
|
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
|
||||||
await ApplicationData.Current.RoamingFolder.CreateFileAsync(
|
|
||||||
"background.json", CreationCollisionOption.ReplaceExisting)
|
|
||||||
, "");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,17 +212,14 @@ namespace FoxTube
|
|||||||
/// Checks if any user has already been logged in. If has, calls *Authorize()* to retrieve his info
|
/// Checks if any user has already been logged in. If has, calls *Authorize()* to retrieve his info
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="retrieveSubs">Loads user's subscriptions if true</param>
|
/// <param name="retrieveSubs">Loads user's subscriptions if true</param>
|
||||||
public static async void CheckAuthorization(bool retrieveSubs = true)
|
public static void CheckAuthorization(bool retrieveSubs = true)
|
||||||
{
|
{
|
||||||
if (SettingsStorage.HasAccount)
|
if (SettingsStorage.HasAccount)
|
||||||
Authorize(retrieveSubs);
|
Authorize(retrieveSubs);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AuthorizationStateChanged.Invoke(args: false);
|
AuthorizationStateChanged.Invoke(args: false);
|
||||||
await FileIO.WriteTextAsync(
|
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
|
||||||
await ApplicationData.Current.RoamingFolder.CreateFileAsync(
|
|
||||||
"background.json", CreationCollisionOption.ReplaceExisting),
|
|
||||||
"");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using Windows.Storage;
|
|||||||
|
|
||||||
namespace FoxTube
|
namespace FoxTube
|
||||||
{
|
{
|
||||||
public enum MatureState { Blocked, Allowed, AllowedOnce }
|
public enum MatureState { Blocked, Allowed }
|
||||||
|
|
||||||
public class SettingsContainer
|
public class SettingsContainer
|
||||||
{
|
{
|
||||||
@@ -207,19 +207,6 @@ namespace FoxTube
|
|||||||
public static void SaveData()
|
public static void SaveData()
|
||||||
{
|
{
|
||||||
storage.Values["settings"] = JsonConvert.SerializeObject(Container);
|
storage.Values["settings"] = JsonConvert.SerializeObject(Container);
|
||||||
ExportSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async void ExportSettings()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool[] notificationsSettings = new[] { VideoNotifications, DevNotifications };
|
|
||||||
await FileIO.WriteTextAsync(
|
|
||||||
await ApplicationData.Current.RoamingFolder.CreateFileAsync("notifications.json", CreationCollisionOption.ReplaceExisting),
|
|
||||||
JsonConvert.SerializeObject(notificationsSettings));
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,38 +7,38 @@
|
|||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
SizeChanged="UserControl_SizeChanged"
|
|
||||||
d:DesignHeight="290"
|
d:DesignHeight="290"
|
||||||
d:DesignWidth="384">
|
d:DesignWidth="384"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
|
||||||
<Button Padding="0" Background="Transparent" Name="btn">
|
<Button Padding="0" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
|
||||||
<Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="75"/>
|
<RowDefinition Height="20"/>
|
||||||
|
<RowDefinition Height="55"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Image Source="/Assets/videoPlaceholder.png" Opacity=".0001" Stretch="Fill"/>
|
||||||
<Image Name="image" Source="/Assets/videoThumbSample.png" Stretch="Fill"/>
|
<Image Name="image" Source="/Assets/videoThumbSample.png" Stretch="Fill"/>
|
||||||
|
|
||||||
<StackPanel Margin="0,0,5,5" Background="Orange" VerticalAlignment="Bottom" BorderBrush="OrangeRed" BorderThickness="1" HorizontalAlignment="Right" Padding="5,2,5,3">
|
<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="SPONSORED CONTENT" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Gray" FontSize="12"/>
|
<TextBlock Name="info" Text="SPONSORED CONTENT" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Gray" FontSize="12"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Grid Grid.Row="1">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="20"/>
|
|
||||||
<RowDefinition Height="55"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid 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 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"/>
|
<Grid Grid.Row="1" Name="contentGrid">
|
||||||
<TextBlock Grid.Column="1" Name="desc" Text="[Description]" HorizontalAlignment="Right" Foreground="Gray" Margin="0,2,2,0" FontSize="12"/>
|
<Grid.ColumnDefinitions>
|
||||||
</Grid>
|
<ColumnDefinition Width="60"/>
|
||||||
<TextBlock Grid.Row="1" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
|
<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>
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -31,10 +31,8 @@ namespace FoxTube.Controls.Adverts
|
|||||||
{
|
{
|
||||||
title.Text = advert.Title;
|
title.Text = advert.Title;
|
||||||
image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri());
|
image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri());
|
||||||
if (advert.AdIcon == null)
|
|
||||||
contentGrid.ColumnDefinitions[0].Width = new GridLength(0);
|
icon.ProfilePicture = advert.AdIcon.Source;
|
||||||
else
|
|
||||||
icon.ProfilePicture = advert.AdIcon.Source;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(advert.SponsoredBy))
|
if (string.IsNullOrWhiteSpace(advert.SponsoredBy))
|
||||||
sponsor.Visibility = Visibility.Collapsed;
|
sponsor.Visibility = Visibility.Collapsed;
|
||||||
@@ -53,10 +51,5 @@ namespace FoxTube.Controls.Adverts
|
|||||||
|
|
||||||
Visibility = Visibility.Visible;
|
Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
|
|
||||||
{
|
|
||||||
Height = e.NewSize.Width * 0.75;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,51 +4,65 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
SizeChanged="UserControl_SizeChanged"
|
|
||||||
d:DesignHeight="290"
|
d:DesignHeight="290"
|
||||||
d:DesignWidth="384">
|
d:DesignWidth="384"
|
||||||
|
MaxWidth="500">
|
||||||
|
|
||||||
<Button Padding="0" Background="Transparent" Click="Button_Click" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch">
|
<Windows10version1809:UserControl.OpacityTransition>
|
||||||
<Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
|
<ScalarTransition Duration="0:0:0.5"/>
|
||||||
|
</Windows10version1809:UserControl.OpacityTransition>
|
||||||
|
|
||||||
|
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
|
||||||
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
<RowDefinition Height="50"/>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Image Name="cover" Source="/Assets/ChannelCoverTemplate.png" Stretch="UniformToFill" VerticalAlignment="Center"/>
|
|
||||||
|
<Image Source="/Assets/ChannelCoverTemplate.png" Stretch="UniformToFill" Opacity=".0001"/>
|
||||||
|
<Image Name="cover" Source="/Assets/ChannelCoverTemplate.png" Stretch="UniformToFill" ImageOpened="Cover_ImageOpened" Opacity="0">
|
||||||
|
<Windows10version1809:Image.OpacityTransition>
|
||||||
|
<ScalarTransition Duration="0:0:0.5"/>
|
||||||
|
</Windows10version1809:Image.OpacityTransition>
|
||||||
|
</Image>
|
||||||
|
|
||||||
<StackPanel Name="liveTag" Margin="5" Background="Red" BorderBrush="White" BorderThickness="1" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3" Orientation="Horizontal" Visibility="Collapsed">
|
<StackPanel Name="liveTag" Margin="5" Background="Red" BorderBrush="White" BorderThickness="1" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3" Orientation="Horizontal" Visibility="Collapsed">
|
||||||
<TextBlock Text=" " VerticalAlignment="Center" Foreground="White" FontSize="12" FontFamily="Segoe MDL2 Assets" FontWeight="Black"/>
|
<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"/>
|
<TextBlock x:Uid="/Cards/live" Text="LIVE" VerticalAlignment="Center" Foreground="White" FontSize="12" FontWeight="Bold"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Grid Grid.Row="1">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="50"/>
|
|
||||||
<RowDefinition/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid>
|
|
||||||
<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"/>
|
<Grid Grid.Row="1">
|
||||||
<StackPanel Grid.Column="2" Margin="5">
|
<Grid.ColumnDefinitions>
|
||||||
<TextBlock Name="subs" Text="[Subscribers counter]" Foreground="Gray"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
<TextBlock Name="uploads" Text="[Uploads counter]" Foreground="Gray"/>
|
<ColumnDefinition/>
|
||||||
</StackPanel>
|
<ColumnDefinition Width="auto"/>
|
||||||
</Grid>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="50" Margin="10" TextAlignment="Center" Padding="0,16,0,0" Foreground="Gray">
|
<Ellipse Height="80" Width="80" Margin="0,-45,0,0" VerticalAlignment="Bottom" Fill="{ThemeResource SystemControlBackgroundChromeMediumBrush}"/>
|
||||||
<Hyperlink Click="Hyperlink_Click"><Run x:Uid="/Cards/login">Log in</Run></Hyperlink> <Run x:Uid="/Cards/tomanage">to manage your subscriptions</Run>
|
<PersonPicture Name="avatar" Grid.Column="0" Height="74" Margin="3,-45,3,3" VerticalAlignment="Bottom" BorderBrush="White" BorderThickness="10"/>
|
||||||
</TextBlock>
|
|
||||||
<Grid Visibility="Collapsed" Grid.Row="1" VerticalAlignment="Bottom" Margin="10" Name="subscriptionPane" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
|
<TextBlock Name="title" Grid.Column="1" Text="[Channel name]" Margin="5" TextWrapping="WrapWholeWords" MaxLines="2" TextTrimming="CharacterEllipsis"/>
|
||||||
<Button x:Uid="/Cards/subscribe" 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"/>
|
<StackPanel Grid.Column="2" Margin="5">
|
||||||
</Grid>
|
<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" Margin="10" 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="10" 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="10" 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>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -15,24 +15,19 @@ namespace FoxTube.Controls
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel item card
|
/// Channel item card
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class ChannelCard : UserControl, IItemCard
|
public sealed partial class ChannelCard : UserControl
|
||||||
{
|
{
|
||||||
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
|
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
|
||||||
|
|
||||||
string channelId;
|
string channelId;
|
||||||
Channel item;
|
Channel item;
|
||||||
|
|
||||||
public ChannelCard(string id, string live = "null")
|
public ChannelCard(string id, string live = null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Initialize(id, live);
|
Initialize(id, live);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
|
|
||||||
{
|
|
||||||
Height = e.NewSize.Width * 0.75;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Initialize(string id, string live)
|
public async void Initialize(string id, string live)
|
||||||
{
|
{
|
||||||
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
|
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
|
||||||
@@ -43,6 +38,7 @@ namespace FoxTube.Controls
|
|||||||
channelId = id;
|
channelId = id;
|
||||||
|
|
||||||
title.Text = item.Snippet.Title;
|
title.Text = item.Snippet.Title;
|
||||||
|
description.Text = item.Snippet.Description;
|
||||||
|
|
||||||
subs.Text = $"{item.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
|
subs.Text = $"{item.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
|
||||||
uploads.Text = $"{item.Statistics.VideoCount:0,0} {resources.GetString("/Cards/videos")}";
|
uploads.Text = $"{item.Statistics.VideoCount:0,0} {resources.GetString("/Cards/videos")}";
|
||||||
@@ -52,14 +48,11 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
if(SecretsVault.IsAuthorized)
|
if(SecretsVault.IsAuthorized)
|
||||||
{
|
{
|
||||||
foreach(Subscription s in SecretsVault.Subscriptions)
|
if(SecretsVault.Subscriptions.Exists(i => i.Snippet.ResourceId.ChannelId == id))
|
||||||
{
|
{
|
||||||
if(s.Snippet.ResourceId.ChannelId == id)
|
subscribe.Background = new SolidColorBrush(Colors.Transparent);
|
||||||
{
|
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
|
||||||
subscribe.Background = new SolidColorBrush(Colors.Transparent);
|
subscribe.Content = resources.GetString("/Cards/unsubscribe");
|
||||||
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
|
|
||||||
subscribe.Content = resources.GetString("/Cards/unsubscribe");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
subscriptionPane.Visibility = Visibility.Visible;
|
subscriptionPane.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
@@ -68,14 +61,15 @@ namespace FoxTube.Controls
|
|||||||
catch { }
|
catch { }
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
|
if (!item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
|
||||||
throw new Exception("Default channel cover detected");
|
cover.Source = new BitmapImage(item.BrandingSettings.Image.BannerImageUrl.ToUri());
|
||||||
cover.Source = new BitmapImage((item.BrandingSettings.Image.BannerTvHighImageUrl ?? item.BrandingSettings.Image.BannerTvImageUrl).ToUri());
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
|
Opacity = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Button_Click(object sender, RoutedEventArgs e)
|
public void Button_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Methods.MainPage.GoToChannel(channelId);
|
Methods.MainPage.GoToChannel(channelId);
|
||||||
}
|
}
|
||||||
@@ -104,13 +98,18 @@ namespace FoxTube.Controls
|
|||||||
private void GetLink_Click(object sender, RoutedEventArgs e)
|
private void GetLink_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
DataPackage data = new DataPackage();
|
DataPackage data = new DataPackage();
|
||||||
data.SetText(string.IsNullOrWhiteSpace(item.Snippet.CustomUrl) ? $"https://www.youtube.com/channel/{item.Id}" : $"https://www.youtube.com/user/{item.Snippet.CustomUrl}");
|
data.SetText($"https://www.youtube.com/channel/{item.Id}");
|
||||||
Clipboard.SetContent(data);
|
Clipboard.SetContent(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void InBrowser_Click(object sender, RoutedEventArgs e)
|
private async void InBrowser_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await Launcher.LaunchUriAsync((string.IsNullOrWhiteSpace(item.Snippet.CustomUrl) ? $"https://www.youtube.com/channel/{item.Id}" : $"https://www.youtube.com/user/{item.Snippet.CustomUrl}").ToUri());
|
await Launcher.LaunchUriAsync($"https://www.youtube.com/channel/{item.Id}".ToUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cover_ImageOpened(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
cover.Opacity = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-48
@@ -24,53 +24,51 @@
|
|||||||
Content="" FontSize="30"/>
|
Content="" FontSize="30"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1">
|
<ListView Name="list" Grid.Row="1" SelectionMode="None">
|
||||||
<ListView Name="list" SelectionMode="None">
|
<ListView.ItemTemplate>
|
||||||
<ListView.ItemTemplate>
|
<DataTemplate>
|
||||||
<DataTemplate>
|
<Border BorderBrush="Red" BorderThickness="{Binding Path=BorderThickness}" CornerRadius="5" HorizontalAlignment="Stretch" Background="{Binding Path=Background}" Margin="0,2">
|
||||||
<Border BorderBrush="Red" BorderThickness="{Binding Path=BorderThickness}" CornerRadius="5" HorizontalAlignment="Stretch" Background="{Binding Path=Background}" Margin="0,2">
|
<Grid Margin="0,5,5,0">
|
||||||
<Grid Margin="0,5,5,0">
|
<Grid.ColumnDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition/>
|
||||||
<ColumnDefinition/>
|
</Grid.ColumnDefinitions>
|
||||||
</Grid.ColumnDefinitions>
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="5,0">
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="5,0">
|
<PersonPicture Height="20" ProfilePicture="{Binding Path=Avatar}"/>
|
||||||
<PersonPicture Height="20" ProfilePicture="{Binding Path=Avatar}"/>
|
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsVerified}">
|
||||||
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsVerified}">
|
<ToolTipService.ToolTip>
|
||||||
<ToolTipService.ToolTip>
|
<TextBlock x:Uid="/Chat/verified"/>
|
||||||
<TextBlock x:Uid="/Chat/verified"/>
|
</ToolTipService.ToolTip>
|
||||||
</ToolTipService.ToolTip>
|
</FontIcon>
|
||||||
</FontIcon>
|
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsModerator}">
|
||||||
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsModerator}">
|
<ToolTipService.ToolTip>
|
||||||
<ToolTipService.ToolTip>
|
<TextBlock x:Uid="/Chat/moder"/>
|
||||||
<TextBlock x:Uid="/Chat/moder"/>
|
</ToolTipService.ToolTip>
|
||||||
</ToolTipService.ToolTip>
|
</FontIcon>
|
||||||
</FontIcon>
|
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsOwner}">
|
||||||
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsOwner}">
|
<ToolTipService.ToolTip>
|
||||||
<ToolTipService.ToolTip>
|
<TextBlock x:Uid="/Chat/owner"/>
|
||||||
<TextBlock x:Uid="/Chat/owner"/>
|
</ToolTipService.ToolTip>
|
||||||
</ToolTipService.ToolTip>
|
</FontIcon>
|
||||||
</FontIcon>
|
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsSponsor}">
|
||||||
<FontIcon Glyph="" Margin="2,0" Visibility="{Binding Path=IsSponsor}">
|
<ToolTipService.ToolTip>
|
||||||
<ToolTipService.ToolTip>
|
<TextBlock x:Uid="/Chat/sponsor"/>
|
||||||
<TextBlock x:Uid="/Chat/sponsor"/>
|
</ToolTipService.ToolTip>
|
||||||
</ToolTipService.ToolTip>
|
</FontIcon>
|
||||||
</FontIcon>
|
</StackPanel>
|
||||||
</StackPanel>
|
<StackPanel Orientation="Horizontal" Grid.Column="1" VerticalAlignment="Top" Margin="0,0,5,0">
|
||||||
<StackPanel Orientation="Horizontal" Grid.Column="1" VerticalAlignment="Top" Margin="0,0,5,0">
|
<HyperlinkButton Content="{Binding Path=Author}" Tag="{Binding Path=ChannelId}" Grid.Column="1" Margin="0,-6,0,0" FontWeight="Bold" Click="HyperlinkButton_Click"/>
|
||||||
<HyperlinkButton Content="{Binding Path=Author}" Tag="{Binding Path=ChannelId}" Grid.Column="1" Margin="0,-6,0,0" FontWeight="Bold" Click="HyperlinkButton_Click"/>
|
<TextBlock Text=":"/>
|
||||||
<TextBlock Text=":"/>
|
</StackPanel>
|
||||||
</StackPanel>
|
<GroupItem Content="{Binding Path=Message}" Grid.Column="2"/>
|
||||||
<GroupItem Content="{Binding Path=Message}" Grid.Column="2"/>
|
</Grid>
|
||||||
</Grid>
|
</Border>
|
||||||
</Border>
|
</DataTemplate>
|
||||||
</DataTemplate>
|
</ListView.ItemTemplate>
|
||||||
</ListView.ItemTemplate>
|
<ListViewItem>
|
||||||
<ListViewItem>
|
<TextBlock x:Uid="/Chat/welcome" Text="Welcome to the chat room" Foreground="Gray"/>
|
||||||
<TextBlock x:Uid="/Chat/welcome" Text="Welcome to the chat room" Foreground="Gray"/>
|
</ListViewItem>
|
||||||
</ListViewItem>
|
</ListView>
|
||||||
</ListView>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ using Windows.UI.Xaml.Media;
|
|||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
using Google.Apis.YouTube.v3;
|
using Google.Apis.YouTube.v3;
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
|
using Microsoft.AppCenter.Analytics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Windows.UI.Popups;
|
||||||
|
using Windows.ApplicationModel.Resources;
|
||||||
|
|
||||||
namespace FoxTube.Controls
|
namespace FoxTube.Controls
|
||||||
{
|
{
|
||||||
@@ -66,6 +70,10 @@ namespace FoxTube.Controls
|
|||||||
{
|
{
|
||||||
string chatId;
|
string chatId;
|
||||||
DateTime lastInsert;
|
DateTime lastInsert;
|
||||||
|
|
||||||
|
LiveChatMessagesResource.ListRequest request;
|
||||||
|
LiveChatMessageListResponse response;
|
||||||
|
|
||||||
DispatcherTimer timer = new DispatcherTimer()
|
DispatcherTimer timer = new DispatcherTimer()
|
||||||
{
|
{
|
||||||
Interval = TimeSpan.FromSeconds(1)
|
Interval = TimeSpan.FromSeconds(1)
|
||||||
@@ -76,19 +84,20 @@ namespace FoxTube.Controls
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
if (!SecretsVault.IsAuthorized)
|
if (!SecretsVault.IsAuthorized)
|
||||||
inputField.Visibility = Visibility.Collapsed;
|
inputField.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
chatId = id;
|
chatId = id;
|
||||||
|
|
||||||
|
request = SecretsVault.Service.LiveChatMessages.List(chatId, "snippet,authorDetails");
|
||||||
|
|
||||||
timer.Tick += Update;
|
timer.Tick += Update;
|
||||||
timer.Start();
|
timer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Update(object sender, object e)
|
public async void Update(object sender, object e)
|
||||||
{
|
{
|
||||||
LiveChatMessagesResource.ListRequest request = SecretsVault.Service.LiveChatMessages.List(chatId, "snippet,authorDetails");
|
response = await request.ExecuteAsync();
|
||||||
LiveChatMessageListResponse response = await request.ExecuteAsync();
|
foreach (LiveChatMessage i in response.Items.FindAll(i => i.Snippet.PublishedAt >= lastInsert))
|
||||||
foreach (LiveChatMessage i in response.Items)
|
list.Items.Insert(0, new ChatMessage(i));
|
||||||
if(i.Snippet.PublishedAt >= lastInsert)
|
|
||||||
list.Items.Insert(0, new ChatMessage(i));
|
|
||||||
lastInsert = DateTime.Now;
|
lastInsert = DateTime.Now;
|
||||||
timer.Interval = TimeSpan.FromMilliseconds(response.PollingIntervalMillis.Value);
|
timer.Interval = TimeSpan.FromMilliseconds(response.PollingIntervalMillis.Value);
|
||||||
timer.Start();
|
timer.Start();
|
||||||
@@ -99,16 +108,16 @@ namespace FoxTube.Controls
|
|||||||
Methods.MainPage.GoToChannel(((HyperlinkButton)sender).Tag as string);
|
Methods.MainPage.GoToChannel(((HyperlinkButton)sender).Tag as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void send_Click(object sender, RoutedEventArgs e)
|
private async void send_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
newMessage.IsEnabled = false;
|
|
||||||
send.IsEnabled = false;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(newMessage.Text))
|
if (string.IsNullOrWhiteSpace(newMessage.Text))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
newMessage.IsEnabled = false;
|
||||||
|
send.IsEnabled = false;
|
||||||
|
|
||||||
LiveChatMessage message = new LiveChatMessage()
|
LiveChatMessage message = new LiveChatMessage()
|
||||||
{
|
{
|
||||||
Snippet = new LiveChatMessageSnippet()
|
Snippet = new LiveChatMessageSnippet()
|
||||||
@@ -131,11 +140,18 @@ namespace FoxTube.Controls
|
|||||||
list.Items.Add(new ChatMessage(response));
|
list.Items.Add(new ChatMessage(response));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
newMessage.IsEnabled = true;
|
await new MessageDialog(ResourceLoader.GetForCurrentView("Chat").GetString("/Chat/failed")).ShowAsync();
|
||||||
send.IsEnabled = true;
|
Analytics.TrackEvent("Failed to send a chat message", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newMessage.IsEnabled = true;
|
||||||
|
send.IsEnabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,95 +7,83 @@
|
|||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="400">
|
d:DesignWidth="400">
|
||||||
|
|
||||||
<Grid Background="{ThemeResource AppBarBackgroundThemeBrush}" Margin="2" Name="grid">
|
<Grid Margin="2">
|
||||||
<Grid.RowDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<RowDefinition Height="auto"/>
|
<ColumnDefinition Width="60"/>
|
||||||
<RowDefinition Height="0"/>
|
<ColumnDefinition/>
|
||||||
<RowDefinition Height="0"/>
|
</Grid.ColumnDefinitions>
|
||||||
</Grid.RowDefinitions>
|
<PersonPicture Tapped="avatar_Tapped" Name="avatar" Height="50" Margin="5" VerticalAlignment="Top"/>
|
||||||
<Grid>
|
<StackPanel Grid.Column="1" Margin="5">
|
||||||
<Grid.ColumnDefinitions>
|
<TextBlock Text="Channel name" Name="author" TextWrapping="WrapWholeWords" Foreground="Gray" FontSize="13"/>
|
||||||
<ColumnDefinition Width="60"/>
|
<Border Visibility="Collapsed" Name="specialAuthor" HorizontalAlignment="Left" Background="Red" CornerRadius="5">
|
||||||
<ColumnDefinition/>
|
<TextBlock Text="Channelname" Foreground="White" TextWrapping="WrapWholeWords" FontSize="13" Padding="2,0,2,2"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Border>
|
||||||
<PersonPicture Tapped="avatar_Tapped" Name="avatar" Height="50" Margin="5" VerticalAlignment="Top"/>
|
<TextBlock Text="[Publish date] (edited)" Name="meta" TextWrapping="WrapWholeWords" Foreground="Gray" FontSize="13"/>
|
||||||
<Grid Grid.Column="1" Margin="5">
|
<TextBlock Name="text" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords" Text="Content"/>
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock Name="author" TextWrapping="WrapWholeWords" Foreground="Gray" FontSize="13" Text="[Author's name]"/>
|
|
||||||
<Border Name="authorBorder" HorizontalAlignment="Left" Background="Red" CornerRadius="5" Visibility="Collapsed">
|
|
||||||
<TextBlock Name="specialAuthor" TextWrapping="WrapWholeWords" Foreground="White" FontSize="13" Text="[Author's name]" Padding="2,0,2,2"/>
|
|
||||||
</Border>
|
|
||||||
<TextBlock Name="meta" Grid.Row="1" TextWrapping="WrapWholeWords" Foreground="Gray" FontSize="13" Text="[Published time span] [?](edited)"/>
|
|
||||||
<TextBlock Name="text" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords" Grid.Row="2" Text="[Content]"/>
|
|
||||||
|
|
||||||
<StackPanel Grid.Row="2" Name="editor" Visibility="Collapsed">
|
<StackPanel Name="editor" Visibility="Collapsed">
|
||||||
<TextBox Name="editorText" Text="[Content]" AcceptsReturn="True" TextWrapping="Wrap" TextChanged="editorText_TextChanged"/>
|
<TextBox Name="editorText" Text="[Content]" AcceptsReturn="True" TextWrapping="Wrap" TextChanged="editorText_TextChanged"/>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,5,0,5">
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,5,0,5">
|
||||||
<Button x:Uid="/CommentsPage/editorDelete" Content="Delete comment" Background="Red" Name="deleteComment" Click="DeleteComment_Click"/>
|
<Button x:Uid="/CommentsPage/editorDelete" FontFamily="Segoe MDL2 Assets" Content="" Background="Transparent" FontSize="20" Foreground="Red" Name="deleteComment" Click="DeleteComment_Click"/>
|
||||||
<Button x:Uid="/CommentsPage/editorCancel" Name="editorClose" Content="Cancel" Click="editorClose_Click" Margin="5, 0"/>
|
<Button x:Uid="/CommentsPage/editorCancel" Name="editorClose" Content="Cancel" Click="editorClose_Click" Margin="5, 0"/>
|
||||||
<Button x:Uid="/CommentsPage/editorSubmit" Name="editorSend" Content="Submit" Click="editorSend_Click"/>
|
<Button x:Uid="/CommentsPage/editorSubmit" Name="editorSend" Content="Submit" Click="editorSend_Click"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Name="toolbar">
|
||||||
|
<TextBlock Name="upvote" Foreground="Gray" Padding="0"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,5,0"
|
||||||
|
FontFamily="Segoe MDL2 Assets" Text="" FontSize="20"/>
|
||||||
|
<TextBlock Name="rating" Foreground="Gray" VerticalAlignment="Center" Text="123"/>
|
||||||
|
|
||||||
|
<Button Click="showReplies_Click" Name="showReplies" Background="Transparent" Foreground="Gray" Padding="0" Margin="10,0,0,0"
|
||||||
|
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
||||||
|
Height="35">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" Margin="0,0,5,0" FontSize="20"/>
|
||||||
|
<TextBlock Text="123"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ProgressBar Name="editorSending" Foreground="Red" IsIndeterminate="True" Visibility="Collapsed"/>
|
</Button>
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<StackPanel Grid.Row="3" Orientation="Horizontal">
|
<Button Click="replyBtn_Click" Name="replyBtn" Background="Transparent" Foreground="Gray" Padding="0" Margin="10,0,0,0"
|
||||||
<TextBlock Name="upvote" Foreground="Gray" Padding="0"
|
|
||||||
VerticalAlignment="Center" Margin="0,0,5,0"
|
|
||||||
FontFamily="Segoe MDL2 Assets" Text="" FontSize="20"/>
|
|
||||||
<TextBlock Name="rating" Foreground="Gray" VerticalAlignment="Center" Text="123"/>
|
|
||||||
|
|
||||||
<Button Visibility="Collapsed" Name="downvote" Background="Transparent" Foreground="Gray" Padding="0"
|
|
||||||
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
|
||||||
Height="35" Width="35"
|
|
||||||
FontFamily="Segoe MDL2 Assets" Content="" FontSize="20"/>
|
|
||||||
|
|
||||||
<Button Click="showReplies_Click" Name="showReplies" Background="Transparent" Foreground="Gray" Padding="0" Margin="10,0,0,0"
|
|
||||||
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
||||||
Height="35">
|
Height="35">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" Margin="0,0,5,0" FontSize="20"/>
|
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" FontSize="20"/>
|
||||||
<TextBlock Text="123"/>
|
<TextBlock x:Uid="/CommentsPage/reply" Text="Reply"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button Click="replyBtn_Click" Name="replyBtn" Background="Transparent" Foreground="Gray" Padding="0" Margin="10,0,0,0"
|
<Button Click="editBtn_Click" Visibility="Collapsed" Name="editBtn" Background="Transparent" Foreground="Gray" Padding="0" Margin="10,0,0,0"
|
||||||
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
||||||
Height="35">
|
Height="35">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" FontSize="20"/>
|
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" FontSize="20"/>
|
||||||
<TextBlock x:Uid="/CommentsPage/reply" Text="Reply"/>
|
<TextBlock x:Uid="/CommentsPage/edit" Text="Edit"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<Button Click="editBtn_Click" Visibility="Collapsed" Name="editBtn" Background="Transparent" Foreground="Gray" Padding="0" Margin="10,0,0,0"
|
<Grid Name="replyEditor" Visibility="Collapsed">
|
||||||
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
<TextBox x:Uid="/CommentsPage/replyBox" Name="reply" TextChanged="reply_TextChanged" TextWrapping="Wrap" AcceptsReturn="True" MaxLength="500"
|
||||||
Height="35">
|
Margin="2,0,34,0"
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" FontSize="20"/>
|
|
||||||
<TextBlock x:Uid="/CommentsPage/edit" Text="Edit"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
<ProgressBar Grid.Row="3" VerticalAlignment="Bottom" IsIndeterminate="True" Visibility="Collapsed" Name="commentsLoading"/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<TextBox x:Uid="/CommentsPage/replyBox" Grid.Row="1" Name="reply" TextChanged="reply_TextChanged" TextWrapping="Wrap" BorderThickness="0" Background="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" AcceptsReturn="True" MaxLength="500"
|
|
||||||
Padding="5" Margin="0,0,32,0"
|
|
||||||
PlaceholderText="Enter your reply..."/>
|
PlaceholderText="Enter your reply..."/>
|
||||||
<Button Grid.Row="1" Name="send" Click="send_Click" IsEnabled="False" HorizontalAlignment="Right" VerticalAlignment="Top"
|
<Button Name="send" Click="send_Click" IsEnabled="True" HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||||
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
|
|
||||||
Width="32" Height="32" Padding="0"
|
Width="32" Height="32" Padding="0"
|
||||||
Background="Red" Foreground="White"
|
Background="Transparent"
|
||||||
FontFamily="Segoe MDL2 Assets"
|
FontFamily="Segoe MDL2 Assets"
|
||||||
Content="" />
|
FontSize="30"
|
||||||
<ProgressBar Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" IsIndeterminate="True" Foreground="Red" Name="sending" Visibility="Collapsed"/>
|
Content=""/>
|
||||||
|
</Grid>
|
||||||
|
<ProgressBar Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" IsIndeterminate="True" Name="processing" Visibility="Collapsed"/>
|
||||||
|
|
||||||
<StackPanel Grid.Row="2" Margin="60,0,0,0" Name="replies"/>
|
<StackPanel Name="replies" Visibility="Collapsed">
|
||||||
|
<StackPanel.ChildrenTransitions>
|
||||||
|
<TransitionCollection>
|
||||||
|
<EntranceThemeTransition IsStaggeringEnabled="True"/>
|
||||||
|
</TransitionCollection>
|
||||||
|
</StackPanel.ChildrenTransitions>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ using System.Linq;
|
|||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Windows.UI.Xaml.Input;
|
using Windows.UI.Xaml.Input;
|
||||||
|
|
||||||
using Google.Apis.YouTube.v3;
|
using Google.Apis.YouTube.v3;
|
||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
using Windows.UI.Xaml.Media.Imaging;
|
using Windows.UI.Xaml.Media.Imaging;
|
||||||
using Windows.UI.Popups;
|
using Windows.UI.Popups;
|
||||||
using Windows.ApplicationModel.Resources;
|
using Windows.ApplicationModel.Resources;
|
||||||
|
using Microsoft.AppCenter.Analytics;
|
||||||
|
|
||||||
namespace FoxTube.Controls
|
namespace FoxTube.Controls
|
||||||
{
|
{
|
||||||
@@ -21,10 +21,11 @@ namespace FoxTube.Controls
|
|||||||
{
|
{
|
||||||
ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage");
|
ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage");
|
||||||
|
|
||||||
Comment item;
|
public Comment item;
|
||||||
CommentThread thread;
|
public CommentThread thread;
|
||||||
|
CommentType type;
|
||||||
|
|
||||||
bool repliesLoaded = false;
|
bool repliesLoaded = false;
|
||||||
CommentType type = CommentType.TopLevel;
|
|
||||||
|
|
||||||
public CommentCard(CommentThread comment)
|
public CommentCard(CommentThread comment)
|
||||||
{
|
{
|
||||||
@@ -37,15 +38,15 @@ namespace FoxTube.Controls
|
|||||||
item = comment.Snippet.TopLevelComment;
|
item = comment.Snippet.TopLevelComment;
|
||||||
thread = comment;
|
thread = comment;
|
||||||
|
|
||||||
replyBtn.Visibility = !comment.Snippet.CanReply.Value || !SecretsVault.IsAuthorized ? Visibility.Collapsed : Visibility.Visible;
|
replyBtn.Visibility = comment.Snippet.CanReply.Value && SecretsVault.IsAuthorized ? Visibility.Visible : Visibility.Collapsed;
|
||||||
if (!comment.Snippet.TotalReplyCount.HasValue || comment.Snippet.TotalReplyCount.Value == 0)
|
if (!comment.Snippet.TotalReplyCount.HasValue || comment.Snippet.TotalReplyCount.Value == 0)
|
||||||
showReplies.Visibility = Visibility.Collapsed;
|
showReplies.Visibility = Visibility.Collapsed;
|
||||||
else
|
else
|
||||||
((showReplies.Content as StackPanel).Children[1] as TextBlock).Text = comment.Snippet.TotalReplyCount.ToString();
|
((showReplies.Content as StackPanel).Children[1] as TextBlock).Text = comment.Snippet.TotalReplyCount.ToString();
|
||||||
|
|
||||||
if (comment.Snippet.TopLevelComment.Snippet.CanRate == false)
|
if (comment.Snippet.TopLevelComment.Snippet.CanRate == false)
|
||||||
{
|
{
|
||||||
upvote.Visibility = Visibility.Collapsed;
|
upvote.Visibility = Visibility.Collapsed;
|
||||||
downvote.Visibility = Visibility.Collapsed;
|
|
||||||
rating.Visibility = Visibility.Collapsed;
|
rating.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -53,15 +54,15 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
|
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
|
||||||
{
|
{
|
||||||
specialAuthor.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
|
(specialAuthor.Child as TextBlock).Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
|
||||||
authorBorder.Visibility = Visibility.Visible;
|
specialAuthor.Visibility = Visibility.Visible;
|
||||||
author.Visibility = Visibility.Collapsed;
|
author.Visibility = Visibility.Collapsed;
|
||||||
editBtn.Visibility = Visibility.Visible;
|
editBtn.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == Methods.MainPage.GetCurrentItem().Snippet.ChannelId)
|
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == item.Snippet.ChannelId)
|
||||||
{
|
{
|
||||||
specialAuthor.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
|
(specialAuthor.Child as TextBlock).Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
|
||||||
authorBorder.Visibility = Visibility.Visible;
|
specialAuthor.Visibility = Visibility.Visible;
|
||||||
author.Visibility = Visibility.Collapsed;
|
author.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -74,9 +75,20 @@ namespace FoxTube.Controls
|
|||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DeleteComment(CommentCard comment)
|
||||||
|
{
|
||||||
|
replies.Children.Remove(comment);
|
||||||
|
|
||||||
|
((showReplies.Content as StackPanel).Children[1] as TextBlock).Text = (--thread.Snippet.TotalReplyCount).ToString();
|
||||||
|
|
||||||
|
if(thread.Snippet.TotalReplyCount == 0)
|
||||||
|
showReplies.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public CommentCard(Comment comment)
|
public CommentCard(Comment comment)
|
||||||
{
|
{
|
||||||
this.InitializeComponent();
|
InitializeComponent();
|
||||||
Initialize(comment);
|
Initialize(comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +99,6 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
replyBtn.Visibility = Visibility.Collapsed;
|
replyBtn.Visibility = Visibility.Collapsed;
|
||||||
showReplies.Visibility = Visibility.Collapsed;
|
showReplies.Visibility = Visibility.Collapsed;
|
||||||
downvote.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
if (comment.Snippet.CanRate == false)
|
if (comment.Snippet.CanRate == false)
|
||||||
{
|
{
|
||||||
@@ -99,15 +110,15 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
|
if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == SecretsVault.AccountId)
|
||||||
{
|
{
|
||||||
specialAuthor.Text = comment.Snippet.AuthorDisplayName;
|
(specialAuthor.Child as TextBlock).Text = comment.Snippet.AuthorDisplayName;
|
||||||
authorBorder.Visibility = Visibility.Visible;
|
specialAuthor.Visibility = Visibility.Visible;
|
||||||
author.Visibility = Visibility.Collapsed;
|
author.Visibility = Visibility.Collapsed;
|
||||||
editBtn.Visibility = Visibility.Visible;
|
editBtn.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == Methods.MainPage.GetCurrentItem().Snippet.ChannelId)
|
else if (item.Snippet.AuthorChannelId.ToString().Split('"')[3] == item.Snippet.ChannelId)
|
||||||
{
|
{
|
||||||
specialAuthor.Text = comment.Snippet.AuthorDisplayName;
|
(specialAuthor.Child as TextBlock).Text = comment.Snippet.AuthorDisplayName;
|
||||||
authorBorder.Visibility = Visibility.Visible;
|
specialAuthor.Visibility = Visibility.Visible;
|
||||||
author.Visibility = Visibility.Collapsed;
|
author.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -122,53 +133,36 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
private void replyBtn_Click(object sender, RoutedEventArgs e)
|
private void replyBtn_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (grid.RowDefinitions[1].Height == new GridLength(0))
|
replyEditor.Visibility = replyEditor.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
|
||||||
grid.RowDefinitions[1].Height = GridLength.Auto;
|
|
||||||
else
|
|
||||||
grid.RowDefinitions[1].Height = new GridLength(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void showReplies_Click(object sender, RoutedEventArgs e)
|
private async void showReplies_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (grid.RowDefinitions[2].Height == new GridLength(0))
|
replies.Visibility = replies.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
|
||||||
|
if (repliesLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
processing.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
CommentsResource.ListRequest request = SecretsVault.Service.Comments.List("snippet");
|
||||||
|
request.ParentId = item.Id;
|
||||||
|
request.TextFormat = CommentsResource.ListRequest.TextFormatEnum.PlainText;
|
||||||
|
request.MaxResults = 50;
|
||||||
|
|
||||||
|
CommentListResponse response;
|
||||||
|
|
||||||
|
do
|
||||||
{
|
{
|
||||||
if(type == CommentType.TopLevel && !repliesLoaded)
|
response = await request.ExecuteAsync();
|
||||||
{
|
request.PageToken = response.NextPageToken;
|
||||||
commentsLoading.Visibility = Visibility.Visible;
|
|
||||||
var request = SecretsVault.Service.Comments.List("snippet");
|
|
||||||
request.ParentId = item.Id;
|
|
||||||
request.TextFormat = CommentsResource.ListRequest.TextFormatEnum.PlainText;
|
|
||||||
request.MaxResults = 50;
|
|
||||||
|
|
||||||
string token;
|
response.Items.ForEach(i => replies.Children.Add(new CommentCard(i)));
|
||||||
IList<Comment> list = new List<Comment>();
|
|
||||||
|
|
||||||
var response = await request.ExecuteAsync();
|
|
||||||
token = response.NextPageToken;
|
|
||||||
|
|
||||||
foreach (Comment i in response.Items)
|
|
||||||
list.Add(i);
|
|
||||||
|
|
||||||
while(token != null)
|
|
||||||
{
|
|
||||||
request.PageToken = token;
|
|
||||||
response = await request.ExecuteAsync();
|
|
||||||
token = response.NextPageToken;
|
|
||||||
|
|
||||||
foreach (Comment i in response.Items)
|
|
||||||
list.Add(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Comment c in list.Reverse())
|
|
||||||
replies.Children.Add(new CommentCard(c));
|
|
||||||
|
|
||||||
repliesLoaded = true;
|
|
||||||
commentsLoading.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
grid.RowDefinitions[2].Height = GridLength.Auto;
|
|
||||||
}
|
}
|
||||||
else
|
while (!string.IsNullOrWhiteSpace(request.PageToken));
|
||||||
grid.RowDefinitions[2].Height = new GridLength(0);
|
|
||||||
|
repliesLoaded = true;
|
||||||
|
processing.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void avatar_Tapped(object sender, TappedRoutedEventArgs e)
|
private void avatar_Tapped(object sender, TappedRoutedEventArgs e)
|
||||||
@@ -178,99 +172,126 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
private void reply_TextChanged(object sender, TextChangedEventArgs e)
|
private void reply_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (reply.Text.Length == 0)
|
send.IsEnabled = reply.Text.Length == 0 ? false : true;
|
||||||
send.IsEnabled = false;
|
|
||||||
else
|
|
||||||
send.IsEnabled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void send_Click(object sender, RoutedEventArgs e)
|
private async void send_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(reply.Text))
|
||||||
|
return;
|
||||||
|
|
||||||
send.IsEnabled = false;
|
send.IsEnabled = false;
|
||||||
reply.IsEnabled = false;
|
reply.IsEnabled = false;
|
||||||
sending.Visibility = Visibility.Visible;
|
processing.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
Comment comment = new Comment();
|
Comment comment = new Comment()
|
||||||
comment.Snippet = new CommentSnippet();
|
{
|
||||||
comment.Snippet.TextOriginal = reply.Text;
|
Snippet = new CommentSnippet()
|
||||||
comment.Snippet.ParentId = item.Id;
|
{
|
||||||
|
TextOriginal = reply.Text,
|
||||||
|
ParentId = item.Id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Comment response = await SecretsVault.Service.Comments.Insert(comment, "snippet").ExecuteAsync();
|
Comment response = await SecretsVault.Service.Comments.Insert(comment, "snippet").ExecuteAsync();
|
||||||
reply.Text = "";
|
reply.Text = "";
|
||||||
grid.RowDefinitions[1].Height = new GridLength(0);
|
replyEditor.Visibility = Visibility.Collapsed;
|
||||||
replies.Children.Add(new CommentCard(response));
|
if (repliesLoaded)
|
||||||
|
replies.Children.Insert(0, new CommentCard(response));
|
||||||
|
else
|
||||||
|
showReplies_Click(this, null);
|
||||||
|
|
||||||
|
showReplies.Visibility = Visibility.Visible;
|
||||||
|
((showReplies.Content as StackPanel).Children[1] as TextBlock).Text = (++thread.Snippet.TotalReplyCount).ToString();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await new MessageDialog(resources.GetString("/CommentsPage/failedReply")).ShowAsync();
|
await new MessageDialog(resources.GetString("/CommentsPage/failedReply")).ShowAsync();
|
||||||
|
Analytics.TrackEvent("Failed to send comment reply", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message },
|
||||||
|
{ "Top comment ID", item.Id }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
send.IsEnabled = true;
|
send.IsEnabled = true;
|
||||||
reply.IsEnabled = true;
|
reply.IsEnabled = true;
|
||||||
sending.Visibility = Visibility.Collapsed;
|
processing.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void editorClose_Click(object sender, RoutedEventArgs e)
|
private void editorClose_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
((grid.Children[0] as Grid).Children[1] as Grid).RowDefinitions[3].Height = GridLength.Auto;
|
|
||||||
text.Visibility = Visibility.Visible;
|
|
||||||
editor.Visibility = Visibility.Collapsed;
|
editor.Visibility = Visibility.Collapsed;
|
||||||
|
text.Visibility = Visibility.Visible;
|
||||||
|
toolbar.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void editorSend_Click(object sender, RoutedEventArgs e)
|
private async void editorSend_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(editorText.Text))
|
||||||
|
return;
|
||||||
|
|
||||||
editorText.IsEnabled = false;
|
editorText.IsEnabled = false;
|
||||||
editorSend.IsEnabled = false;
|
editorSend.IsEnabled = false;
|
||||||
editorClose.IsEnabled = false;
|
editorClose.IsEnabled = false;
|
||||||
editorSending.Visibility = Visibility.Visible;
|
deleteComment.IsEnabled = false;
|
||||||
|
processing.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
item.Snippet.TextDisplay = editorText.Text;
|
item.Snippet.TextOriginal = editorText.Text;
|
||||||
item.Snippet.UpdatedAt = DateTime.Now;
|
item.Snippet.UpdatedAt = DateTime.Now;
|
||||||
|
|
||||||
if (type == CommentType.Reply)
|
if (type == CommentType.Reply)
|
||||||
{
|
|
||||||
await SecretsVault.Service.Comments.Update(item, "snippet").ExecuteAsync();
|
await SecretsVault.Service.Comments.Update(item, "snippet").ExecuteAsync();
|
||||||
Initialize(item);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
thread.Snippet.TopLevelComment = item;
|
thread.Snippet.TopLevelComment = item;
|
||||||
await SecretsVault.Service.CommentThreads.Update(thread, "snippet").ExecuteAsync();
|
await SecretsVault.Service.CommentThreads.Update(thread, "snippet").ExecuteAsync();
|
||||||
Initialize(thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editorClose_Click(this, null);
|
text.Text = editorText.Text;
|
||||||
|
meta.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)} {resources.GetString("/CommentsPage/edited")}";
|
||||||
|
|
||||||
|
editor.Visibility = Visibility.Collapsed;
|
||||||
|
text.Visibility = Visibility.Visible;
|
||||||
|
toolbar.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await new MessageDialog(resources.GetString("/CommentsPage/failedEdit")).ShowAsync();
|
await new MessageDialog(resources.GetString("/CommentsPage/failedEdit")).ShowAsync();
|
||||||
|
Analytics.TrackEvent("Failed to edit comment", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
editorText.IsEnabled = true;
|
editorText.IsEnabled = true;
|
||||||
editorSend.IsEnabled = true;
|
editorSend.IsEnabled = true;
|
||||||
editorClose.IsEnabled = true;
|
editorClose.IsEnabled = true;
|
||||||
editorSending.Visibility = Visibility.Collapsed;
|
deleteComment.IsEnabled = true;
|
||||||
|
processing.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void editorText_TextChanged(object sender, TextChangedEventArgs e)
|
private void editorText_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (editorText.Text.Length == 0)
|
editorSend.IsEnabled = editorText.Text.Length == 0 ? false : true;
|
||||||
editorSend.IsEnabled = false;
|
|
||||||
else editorSend.IsEnabled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void editBtn_Click(object sender, RoutedEventArgs e)
|
private void editBtn_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
((grid.Children[0] as Grid).Children[1] as Grid).RowDefinitions[3].Height = new GridLength(0);
|
editor.Visibility = Visibility.Visible;
|
||||||
|
toolbar.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
text.Visibility = Visibility.Collapsed;
|
text.Visibility = Visibility.Collapsed;
|
||||||
editorText.Text = text.Text;
|
editorText.Text = text.Text;
|
||||||
editor.Visibility = Visibility.Visible;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void DeleteComment_Click(object sender, RoutedEventArgs e)
|
private async void DeleteComment_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
MessageDialog dialog = new MessageDialog(resources.GetString("/CommentsPage/deleteContent"), resources.GetString("/CommentsPage/deleteHeader"));
|
MessageDialog dialog = new MessageDialog(resources.GetString("/CommentsPage/deleteContent"), resources.GetString("/CommentsPage/deleteHeader"));
|
||||||
|
|
||||||
@@ -279,9 +300,20 @@ namespace FoxTube.Controls
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SecretsVault.Service.Comments.Delete(item.Id).ExecuteAsync();
|
await SecretsVault.Service.Comments.Delete(item.Id).ExecuteAsync();
|
||||||
Methods.CommentsPage.RemoveComment(this);
|
if (type == CommentType.Reply)
|
||||||
|
Methods.CommentsPage.RemoveComment(this, item.Snippet.ParentId);
|
||||||
|
else
|
||||||
|
Methods.CommentsPage.RemoveComment(this);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await new MessageDialog(resources.GetString("/CommentsPage/failedDelete")).ShowAsync();
|
||||||
|
Analytics.TrackEvent("Failed delete comment", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
}));
|
}));
|
||||||
dialog.Commands.Add(new UICommand(resources.GetString("/CommentsPage/no")));
|
dialog.Commands.Add(new UICommand(resources.GetString("/CommentsPage/no")));
|
||||||
|
|
||||||
|
|||||||
@@ -22,12 +22,7 @@
|
|||||||
<TextBlock Name="title" Grid.Column="1" Margin="5" TextWrapping="WrapWholeWords" Text="[Title]" FontSize="20" MaxLines="2"/>
|
<TextBlock Name="title" Grid.Column="1" Margin="5" TextWrapping="WrapWholeWords" Text="[Title]" FontSize="20" MaxLines="2"/>
|
||||||
<TextBlock Margin="5" Name="path" Grid.Column="1" VerticalAlignment="Bottom" Text="C://Users/Michael Gordeev/Downloads/[Title].mp4" Foreground="LightGray" MaxLines="1" TextWrapping="WrapWholeWords"/>
|
<TextBlock Margin="5" Name="path" Grid.Column="1" VerticalAlignment="Bottom" Text="C://Users/Michael Gordeev/Downloads/[Title].mp4" Foreground="LightGray" MaxLines="1" TextWrapping="WrapWholeWords"/>
|
||||||
|
|
||||||
<StackPanel Grid.Column="2" Margin="5">
|
<TextBlock Grid.Column="2" Margin="5" Name="meta"/>
|
||||||
<TextBlock Text="Extension:" Foreground="Gray" Name="ext"/>
|
|
||||||
<TextBlock Text="Quality:" Foreground="Gray" Name="quality"/>
|
|
||||||
<TextBlock Text="Duration:" Foreground="Gray" Name="duration"/>
|
|
||||||
<TextBlock Text="Author:" Foreground="Gray" Name="channel"/>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<StackPanel Name="donePanel" Grid.Column="3" Orientation="Horizontal" Visibility="Visible">
|
<StackPanel Name="donePanel" Grid.Column="3" Orientation="Horizontal" Visibility="Visible">
|
||||||
<Button Name="open" Click="open_Click" Width="80" Height="80" Padding="0" Background="Transparent">
|
<Button Name="open" Click="open_Click" Width="80" Height="80" Padding="0" Background="Transparent">
|
||||||
@@ -44,10 +39,10 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Name="progressPanel" Grid.Column="4" Margin="10">
|
<StackPanel Name="progressPanel" Grid.Column="4" Margin="10">
|
||||||
<TextBlock Name="status" Text="Downloading..." HorizontalAlignment="Left"/>
|
<TextBlock x:Uid="/Downloads/downloading" Name="status" Text="Downloading..." HorizontalAlignment="Left"/>
|
||||||
<ProgressBar Name="progressBar" Width="200" Maximum="1" IsIndeterminate="True" Foreground="Red"/>
|
<ProgressBar Name="progressBar" Width="200" Maximum="1"/>
|
||||||
<TextBlock Name="perc" Text="--%"/>
|
<TextBlock Name="progressText" Text="--%"/>
|
||||||
<Button x:Uid="/Downloads/cancel" Content="Cancel" Name="cancel" Click="CancelPrompt" HorizontalAlignment="Right"/>
|
<Button x:Uid="/Downloads/cancel" Content="Cancel" Name="cancel" Click="Cancel_Click" HorizontalAlignment="Right"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Windows.UI.Xaml;
|
|||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Windows.UI.Xaml.Media.Imaging;
|
using Windows.UI.Xaml.Media.Imaging;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
using FoxTube.Classes;
|
|
||||||
using YoutubeExplode.Models.MediaStreams;
|
using YoutubeExplode.Models.MediaStreams;
|
||||||
using YoutubeExplode;
|
using YoutubeExplode;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
@@ -16,30 +15,55 @@ using Windows.UI.Notifications;
|
|||||||
using Microsoft.Toolkit.Uwp.Notifications;
|
using Microsoft.Toolkit.Uwp.Notifications;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.ApplicationModel.Resources;
|
using Windows.ApplicationModel.Resources;
|
||||||
|
using Microsoft.AppCenter.Analytics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace FoxTube.Controls
|
namespace FoxTube.Controls
|
||||||
{
|
{
|
||||||
|
public class DownloadItemContainer
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Channel { get; set; }
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Extension { get; set; }
|
||||||
|
public Uri Thumbnail { get; set; }
|
||||||
|
public string Quality { get; set; }
|
||||||
|
public TimeSpan Duration { get; set; }
|
||||||
|
public bool IsDownloaded { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public sealed partial class DownloadItem : UserControl
|
public sealed partial class DownloadItem : UserControl
|
||||||
{
|
{
|
||||||
ResourceLoader resources = ResourceLoader.GetForCurrentView("Downloads");
|
ResourceLoader resources = ResourceLoader.GetForCurrentView("Downloads");
|
||||||
|
|
||||||
public DownloadItemContainer Container { get; private set; }
|
public DownloadItemContainer Container { get; private set; }
|
||||||
public StorageFile file;
|
public StorageFile File { get; private set; }
|
||||||
public bool InProgress { get; set; } = false;
|
|
||||||
|
|
||||||
readonly YoutubeClient client = new YoutubeClient();
|
CancellationTokenSource cts;
|
||||||
readonly CancellationTokenSource cts = new CancellationTokenSource();
|
Progress<double> progress;
|
||||||
CancellationToken token;
|
|
||||||
readonly Progress<double> progress = new Progress<double>();
|
|
||||||
private readonly NotificationData data = new NotificationData();
|
|
||||||
|
|
||||||
DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
|
|
||||||
double percentage;
|
double percentage;
|
||||||
|
DispatcherTimer timer;
|
||||||
|
NotificationData data;
|
||||||
|
|
||||||
public DownloadItem(MediaStreamInfo info, Video meta, string q)
|
public DownloadItem(MediaStreamInfo info, Video meta, string q)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Download(info, meta, q);
|
|
||||||
|
Container = new DownloadItemContainer()
|
||||||
|
{
|
||||||
|
Channel = meta.Snippet.ChannelTitle,
|
||||||
|
Duration = Methods.GetDuration(meta.ContentDetails.Duration),
|
||||||
|
Extension = info.Container.GetFileExtension(),
|
||||||
|
Id = meta.Id,
|
||||||
|
IsDownloaded = false,
|
||||||
|
Quality = q,
|
||||||
|
Thumbnail = meta.Snippet.Thumbnails.Medium.Url.ToUri(),
|
||||||
|
Title = meta.Snippet.Title
|
||||||
|
};
|
||||||
|
|
||||||
|
Download(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadItem(DownloadItemContainer container)
|
public DownloadItem(DownloadItemContainer container)
|
||||||
@@ -47,130 +71,145 @@ namespace FoxTube.Controls
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Container = container;
|
Container = container;
|
||||||
Initialize();
|
Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Initialize()
|
async void Load()
|
||||||
{
|
{
|
||||||
try
|
File = await DownloadAgent.Downloads.TryGetItemAsync(Container.Name) as StorageFile;
|
||||||
{
|
if (File == null)
|
||||||
file = await DownloadAgent.Downloads.GetFileAsync(Container.Name);
|
DownloadAgent.Remove(this);
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
DownloadAgent.Remove(Container.Id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
title.Text = Container.Title;
|
|
||||||
path.Text = file.Name;
|
|
||||||
thumbnail.Source = new BitmapImage(Container.Thumbnail);
|
|
||||||
quality.Text = $"{resources.GetString("/Downloads/quality")}: {Container.Quality}";
|
|
||||||
duration.Text = $"{resources.GetString("/Downloads/duration")}: {Container.Duration}";
|
|
||||||
channel.Text = $"{resources.GetString("/Downloads/author")}: {Container.Channel}";
|
|
||||||
ext.Text = $"{resources.GetString("/Downloads/ext")}: {Container.Extension}";
|
|
||||||
|
|
||||||
progressPanel.Visibility = Visibility.Collapsed;
|
|
||||||
donePanel.Visibility = Visibility.Visible;
|
donePanel.Visibility = Visibility.Visible;
|
||||||
|
progressPanel.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
SetMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
async void Download(MediaStreamInfo info, Video meta, string q)
|
async void Download(MediaStreamInfo info)
|
||||||
{
|
{
|
||||||
try
|
File = await DownloadAgent.Downloads.CreateFileAsync($"{Container.Title.ReplaceInvalidChars('_')}.{Container.Extension}", CreationCollisionOption.GenerateUniqueName);
|
||||||
|
Container.Name = File.Name;
|
||||||
|
|
||||||
|
donePanel.Visibility = Visibility.Collapsed;
|
||||||
|
progressPanel.Visibility = Visibility.Visible;
|
||||||
|
SetMeta();
|
||||||
|
|
||||||
|
cts = new CancellationTokenSource();
|
||||||
|
progress = new Progress<double>();
|
||||||
|
progress.ProgressChanged += (s, e) => percentage = e;
|
||||||
|
|
||||||
|
timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
|
||||||
|
timer.Tick += (s, e) => UpdateInfo();
|
||||||
|
|
||||||
|
#region Polling notification
|
||||||
|
ToastContent toastContent = new ToastContent()
|
||||||
{
|
{
|
||||||
InProgress = true;
|
Visual = new ToastVisual()
|
||||||
Container = new DownloadItemContainer();
|
|
||||||
|
|
||||||
token = cts.Token;
|
|
||||||
progress.ProgressChanged += (s, e) => percentage = e;
|
|
||||||
timer.Tick += (s, e) => UpdateInfo(percentage);
|
|
||||||
|
|
||||||
file = await DownloadAgent.Downloads.CreateFileAsync($"{meta.Snippet.Title.ReplaceInvalidChars('_')}.{info.Container.GetFileExtension()}", CreationCollisionOption.GenerateUniqueName);
|
|
||||||
Container.Name = file.Name;
|
|
||||||
|
|
||||||
ToastContent toastContent = new ToastContent()
|
|
||||||
{
|
{
|
||||||
Visual = new ToastVisual()
|
BindingGeneric = new ToastBindingGeneric()
|
||||||
{
|
{
|
||||||
BindingGeneric = new ToastBindingGeneric()
|
Children =
|
||||||
{
|
|
||||||
Children =
|
|
||||||
{
|
{
|
||||||
new AdaptiveText() { Text = resources.GetString("/Downloads/toastStartHeader") },
|
new AdaptiveText() { Text = resources.GetString("/Downloads/toastStartHeader") },
|
||||||
new AdaptiveProgressBar()
|
new AdaptiveProgressBar()
|
||||||
{
|
{
|
||||||
Title = meta.Snippet.Title,
|
Title = Container.Title,
|
||||||
Status = resources.GetString("/Downloads/downloading/Text"),
|
Status = resources.GetString("/Downloads/downloading/Text"),
|
||||||
Value = new BindableProgressBarValue("value")
|
Value = new BindableProgressBarValue("value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Actions = new ToastActionsCustom()
|
Actions = new ToastActionsCustom()
|
||||||
{
|
{
|
||||||
Buttons =
|
Buttons =
|
||||||
{
|
{
|
||||||
new ToastButton(resources.GetString("/Downloads/cancel/Content"), $"dcancel|{Container.Id}")
|
new ToastButton(resources.GetString("/Downloads/cancel/Content"), $"dcancel|{Container.Id}")
|
||||||
{
|
|
||||||
ActivationType = ToastActivationType.Background
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Launch = "download",
|
Launch = "download",
|
||||||
ActivationType = ToastActivationType.Foreground
|
ActivationType = ToastActivationType.Foreground
|
||||||
};
|
};
|
||||||
|
|
||||||
data.Values["value"] = "0";
|
data = new NotificationData();
|
||||||
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastContent.GetXml()) { Tag = $"download|{meta.Id}", Data = data });
|
data.Values["value"] = "0";
|
||||||
|
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastContent.GetXml()) { Tag = $"download|{Container.Id}", Data = data });
|
||||||
|
#endregion
|
||||||
|
|
||||||
Container.Channel = meta.Snippet.ChannelTitle;
|
timer.Start();
|
||||||
Container.Duration = XmlConvert.ToTimeSpan(meta.ContentDetails.Duration);
|
|
||||||
Container.Id = meta.Id;
|
|
||||||
Container.Quality = q;
|
|
||||||
Container.Thumbnail = meta.Snippet.Thumbnails.Medium.Url.ToUri();
|
|
||||||
Container.Title = meta.Snippet.Title;
|
|
||||||
Container.Extension = info.Container.GetFileExtension();
|
|
||||||
|
|
||||||
thumbnail.Source = new BitmapImage(new Uri(meta.Snippet.Thumbnails.Medium.Url));
|
try
|
||||||
title.Text = meta.Snippet.Title;
|
{
|
||||||
ext.Text = $"{resources.GetString("/Downloads/ext")}: {info.Container.GetFileExtension()}";
|
await new YoutubeClient().DownloadMediaStreamAsync(info, await File.OpenStreamForWriteAsync(), progress, cts.Token);
|
||||||
quality.Text = $"{resources.GetString("/Downloads/quality")}: {q}";
|
|
||||||
duration.Text = $"{resources.GetString("/Downloads/duration")}: {XmlConvert.ToTimeSpan(meta.ContentDetails.Duration)}";
|
|
||||||
channel.Text = $"{resources.GetString("/Downloads/author")}: {meta.Snippet.ChannelTitle}";
|
|
||||||
path.Text = file.Name;
|
|
||||||
|
|
||||||
progressPanel.Visibility = Visibility.Visible;
|
Container.IsDownloaded = true;
|
||||||
donePanel.Visibility = Visibility.Collapsed;
|
timer.Stop();
|
||||||
|
|
||||||
timer.Start();
|
if (cts.IsCancellationRequested)
|
||||||
|
throw new TaskCanceledException();
|
||||||
await client.DownloadMediaStreamAsync(info, await file.OpenStreamForWriteAsync(), progress, token);
|
else
|
||||||
|
|
||||||
progressPanel.Visibility = Visibility.Collapsed;
|
|
||||||
donePanel.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
InProgress = false;
|
|
||||||
|
|
||||||
if (!cts.IsCancellationRequested)
|
|
||||||
DownloadCompleted();
|
DownloadCompleted();
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException) { }
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
|
||||||
|
template.LoadXml($@"<toast>
|
||||||
|
<visual>
|
||||||
|
<binding template='ToastGeneric'>
|
||||||
|
<text>{resources.GetString("/Downloads/toastCanceledHeader")}</text>
|
||||||
|
<text>{Container.Title}</text>
|
||||||
|
</binding>
|
||||||
|
</visual>
|
||||||
|
</toast>");
|
||||||
|
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });
|
||||||
|
|
||||||
|
try { await File.DeleteAsync(StorageDeleteOption.PermanentDelete); }
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
DownloadAgent.Remove(this);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
|
||||||
|
template.LoadXml($@"<toast>
|
||||||
|
<visual>
|
||||||
|
<binding template='ToastGeneric'>
|
||||||
|
<text>{resources.GetString("/Downloads/failedHead")}</text>
|
||||||
|
<text>{resources.GetString("/Downloads/failedBody")}</text>
|
||||||
|
</binding>
|
||||||
|
</visual>
|
||||||
|
</toast>");
|
||||||
|
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });
|
||||||
|
|
||||||
|
Analytics.TrackEvent("Failed to download video", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message },
|
||||||
|
{ "Container", JsonConvert.SerializeObject(Container) }
|
||||||
|
});
|
||||||
|
|
||||||
|
try { await File.DeleteAsync(StorageDeleteOption.PermanentDelete); }
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
DownloadAgent.Remove(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateInfo(double e)
|
private void UpdateInfo()
|
||||||
{
|
{
|
||||||
progressBar.Value = e;
|
progressBar.Value = percentage;
|
||||||
perc.Text = $"{(int)e * 100}%";
|
progressText.Text = Math.Round(percentage * 100, 1) + "%";
|
||||||
|
|
||||||
data.Values["value"] = e.ToString();
|
data.Values["value"] = percentage.ToString();
|
||||||
ToastNotificationManager.CreateToastNotifier().Update(data, $"download|{Container.Id}");
|
ToastNotificationManager.CreateToastNotifier().Update(data, $"download|{Container.Id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DownloadCompleted()
|
private void DownloadCompleted()
|
||||||
{
|
{
|
||||||
Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
|
Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
|
||||||
template.LoadXml($@"<toast activationType='background' launch='download|{file.Path}'>
|
template.LoadXml($@"<toast activationType='foreground' launch='download'>
|
||||||
<visual>
|
<visual>
|
||||||
<binding template='ToastGeneric'>
|
<binding template='ToastGeneric'>
|
||||||
<text>{resources.GetString("/Downloads/toastCompleteHeader")}</text>
|
<text>{resources.GetString("/Downloads/toastCompleteHeader")}</text>
|
||||||
@@ -185,11 +224,14 @@ namespace FoxTube.Controls
|
|||||||
</actions>
|
</actions>
|
||||||
</toast>");
|
</toast>");
|
||||||
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });
|
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });
|
||||||
|
|
||||||
|
donePanel.Visibility = Visibility.Visible;
|
||||||
|
progressPanel.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void open_Click(object sender, RoutedEventArgs e)
|
private async void open_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await Launcher.LaunchFileAsync(file);
|
await Launcher.LaunchFileAsync(File);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gotoOriginal_Click(object sender, RoutedEventArgs e)
|
private void gotoOriginal_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -197,44 +239,45 @@ namespace FoxTube.Controls
|
|||||||
Methods.MainPage.GoToVideo(Container.Id);
|
Methods.MainPage.GoToVideo(Container.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Cancel()
|
public async void Cancel(bool prompt = true)
|
||||||
{
|
{
|
||||||
status.Text = resources.GetString("/Downloads/cancelling");
|
|
||||||
progressBar.IsIndeterminate = true;
|
|
||||||
cancel.IsEnabled = false;
|
cancel.IsEnabled = false;
|
||||||
cts.Cancel();
|
if(prompt)
|
||||||
try
|
|
||||||
{
|
|
||||||
await file.DeleteAsync();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
|
|
||||||
template.LoadXml($@"<toast activationType='foreground' launch='download'>
|
|
||||||
<visual>
|
|
||||||
<binding template='ToastGeneric'>
|
|
||||||
<text>{resources.GetString("/Downloads/toastCanceledHeader")}</text>
|
|
||||||
<text>{Container.Title}</text>
|
|
||||||
</binding>
|
|
||||||
</visual>
|
|
||||||
</toast>");
|
|
||||||
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });
|
|
||||||
|
|
||||||
DownloadAgent.Remove(Container.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void CancelPrompt(object sender = null, RoutedEventArgs e = null)
|
|
||||||
{
|
|
||||||
if(InProgress)
|
|
||||||
{
|
{
|
||||||
MessageDialog dialog = new MessageDialog(resources.GetString("/Downloads/prompt"), resources.GetString("/Downloads/cancellingHeader"));
|
MessageDialog dialog = new MessageDialog(resources.GetString("/Downloads/prompt"), resources.GetString("/Downloads/cancellingHeader"));
|
||||||
|
|
||||||
dialog.Commands.Add(new UICommand(resources.GetString("/Downloads/yes"), (command) => Cancel()));
|
dialog.Commands.Add(new UICommand(resources.GetString("/Downloads/yes"), null, false));
|
||||||
dialog.Commands.Add(new UICommand(resources.GetString("/Downloads/no")));
|
dialog.Commands.Add(new UICommand(resources.GetString("/Downloads/no"), null, true));
|
||||||
|
|
||||||
dialog.DefaultCommandIndex = 1;
|
dialog.DefaultCommandIndex = 1;
|
||||||
await dialog.ShowAsync();
|
if ((bool)(await dialog.ShowAsync()).Id)
|
||||||
|
{
|
||||||
|
cancel.IsEnabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
status.Text = resources.GetString("/Downloads/cancelling");
|
||||||
|
progressBar.IsIndeterminate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMeta()
|
||||||
|
{
|
||||||
|
thumbnail.Source = new BitmapImage(Container.Thumbnail);
|
||||||
|
|
||||||
|
title.Text = Container.Title;
|
||||||
|
path.Text = File.Path;
|
||||||
|
|
||||||
|
meta.Text = $@"{resources.GetString("/Downloads/ext")}: {Container.Extension}
|
||||||
|
{resources.GetString("/Downloads/quality")}: {Container.Quality}
|
||||||
|
{resources.GetString("/Downloads/duration")}: {Container.Duration}
|
||||||
|
{resources.GetString("/Downloads/author")}: {Container.Channel}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
x:Class="FoxTube.Controls.LiveCaptions"
|
x:Class="FoxTube.Controls.LiveCaptions"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:FoxTube.Controls"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
@@ -10,7 +9,10 @@
|
|||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Margin="0,55">
|
Margin="0,55">
|
||||||
|
|
||||||
<Grid Background="#99000000">
|
<Grid>
|
||||||
|
<Grid.Background>
|
||||||
|
<AcrylicBrush TintColor="#BF000000" TintOpacity="0.75"/>
|
||||||
|
</Grid.Background>
|
||||||
<TextBlock Text="Hello, World!" Name="text" Margin="5" FontSize="24"/>
|
<TextBlock Text="Hello, World!" Name="text" Margin="5" FontSize="24"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Windows.Media;
|
|
||||||
using YoutubeExplode.Models.ClosedCaptions;
|
using YoutubeExplode.Models.ClosedCaptions;
|
||||||
|
|
||||||
namespace FoxTube.Controls
|
namespace FoxTube.Controls
|
||||||
@@ -17,8 +16,8 @@ namespace FoxTube.Controls
|
|||||||
set => text.FontSize = value;
|
set => text.FontSize = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaTimelineController Player { get; set; }
|
public MediaElement Player { get; set; }
|
||||||
private bool isClosed = false;
|
|
||||||
DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
|
DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
|
||||||
ClosedCaption currentCaption = null;
|
ClosedCaption currentCaption = null;
|
||||||
|
|
||||||
@@ -32,41 +31,19 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
private void UpdateCaption(object sender, object e)
|
private void UpdateCaption(object sender, object e)
|
||||||
{
|
{
|
||||||
TimeSpan currentPosition = Player.Position;
|
currentCaption = track.Captions.Find(i => i.Offset <= Player.Position && i.Offset + i.Duration > Player.Position);
|
||||||
|
|
||||||
bool found = false;
|
if (currentCaption != null)
|
||||||
if(!isClosed)
|
text.Text = currentCaption.Text;
|
||||||
track.Captions.ForEach(i =>
|
|
||||||
{
|
|
||||||
if (Player.Position >= i.Offset && Player.Position <= i.Offset + i.Duration)
|
|
||||||
{
|
|
||||||
currentCaption = i;
|
|
||||||
text.Text = currentCaption.Text;
|
|
||||||
Visibility = Visibility.Visible;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!found)
|
Visibility = currentCaption == null ? Visibility.Collapsed : Visibility.Visible;
|
||||||
{
|
|
||||||
currentCaption = null;
|
|
||||||
Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Initialize(ClosedCaptionTrackInfo info)
|
public async void Initialize(ClosedCaptionTrackInfo info)
|
||||||
{
|
{
|
||||||
track = await new YoutubeExplode.YoutubeClient().GetClosedCaptionTrackAsync(info);
|
track = await new YoutubeExplode.YoutubeClient().GetClosedCaptionTrackAsync(info);
|
||||||
|
|
||||||
track.Captions.ForEach(i =>
|
UpdateCaption(this, null);
|
||||||
{
|
|
||||||
if (Player.Position > i.Offset && Player.Position < i.Offset + i.Duration)
|
|
||||||
{
|
|
||||||
currentCaption = i;
|
|
||||||
text.Text = currentCaption.Text;
|
|
||||||
Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
timer.Start();
|
timer.Start();
|
||||||
}
|
}
|
||||||
@@ -78,15 +55,5 @@ namespace FoxTube.Controls
|
|||||||
Visibility = Visibility.Collapsed;
|
Visibility = Visibility.Collapsed;
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Hide()
|
|
||||||
{
|
|
||||||
isClosed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Show()
|
|
||||||
{
|
|
||||||
isClosed = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,318 @@
|
|||||||
|
using FoxTube.Controls;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using Windows.ApplicationModel.Resources;
|
||||||
|
using Windows.UI.Xaml;
|
||||||
|
using Windows.UI.Xaml.Controls;
|
||||||
|
using Windows.UI.Xaml.Controls.Primitives;
|
||||||
|
using YoutubeExplode.Models.ClosedCaptions;
|
||||||
|
using YoutubeExplode.Models.MediaStreams;
|
||||||
|
|
||||||
|
namespace FoxTube
|
||||||
|
{
|
||||||
|
public delegate void QualityChangedEventHandler(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list);
|
||||||
|
public delegate void MinimodeChangedEventHandler(object sender, bool isOn);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom controls for media player. MARKUP IS IN **Themes/Generic.xaml**!!!
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PlayerControls : MediaTransportControls
|
||||||
|
{
|
||||||
|
public event RoutedEventHandler CloseRequested;
|
||||||
|
public event MinimodeChangedEventHandler MiniModeChanged;
|
||||||
|
public event RoutedEventHandler NextRequested;
|
||||||
|
|
||||||
|
public event QualityChangedEventHandler QualityChanged;
|
||||||
|
|
||||||
|
public MediaElement Player;
|
||||||
|
|
||||||
|
public IReadOnlyList<ClosedCaptionTrackInfo> ClosedCaptions { get; set; }
|
||||||
|
public MediaStreamInfoSet MediaStreams { get; set; }
|
||||||
|
|
||||||
|
Queue<Action> queue = new Queue<Action>();
|
||||||
|
bool isReady = false;
|
||||||
|
|
||||||
|
public PlayerControls()
|
||||||
|
{
|
||||||
|
DefaultStyleKey = typeof(PlayerControls);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApplyTemplate()
|
||||||
|
{
|
||||||
|
isReady = true;
|
||||||
|
|
||||||
|
(GetTemplateChild("close") as Button).Click += Close_Click;
|
||||||
|
(GetTemplateChild("compactClose") as Button).Click += Close_Click;
|
||||||
|
|
||||||
|
(GetTemplateChild("minimize") as Button).Click += Minimize_Click;
|
||||||
|
(GetTemplateChild("maximize") as Button).Click += Minimize_Click;
|
||||||
|
|
||||||
|
(GetTemplateChild("CompactOverlayButton") as Button).Click += CompactOverlay_Click;
|
||||||
|
|
||||||
|
(GetTemplateChild("qualitySelector") as ComboBox).SelectionChanged += QualitySelector_SelectionChanged;
|
||||||
|
|
||||||
|
(GetTemplateChild("ccSwitch") as ToggleSwitch).Toggled += CcSwitch_Toggled;
|
||||||
|
(GetTemplateChild("ccSelector") as ComboBox).SelectionChanged += CcSelector_SelectionChanged;
|
||||||
|
|
||||||
|
(GetTemplateChild("next") as Button).Click += (s, e) => NextRequested.Invoke(s, e);
|
||||||
|
|
||||||
|
(GetTemplateChild("AudioMuteButton") as Button).Click += Mute_Click;
|
||||||
|
(GetTemplateChild("VolumeSlider") as Slider).ValueChanged += Volume_ValueChanged;
|
||||||
|
|
||||||
|
(GetTemplateChild("ProgressSlider") as Slider).ValueChanged += ProgressSlider_ValueChanged;
|
||||||
|
|
||||||
|
if (queue.Count > 0)
|
||||||
|
foreach (Action i in queue)
|
||||||
|
i();
|
||||||
|
|
||||||
|
base.OnApplyTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Minimize_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender == (GetTemplateChild("minimize") as Button))
|
||||||
|
{
|
||||||
|
MiniModeChanged.Invoke(this, true);
|
||||||
|
SetMinimized();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MiniModeChanged.Invoke(this, false);
|
||||||
|
SetNormal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProgressSlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
|
||||||
|
{
|
||||||
|
(GetTemplateChild("compactSeek") as ProgressBar).Value = e.NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Mute_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph == "\xE74F")
|
||||||
|
Volume_ValueChanged(this, null);
|
||||||
|
else
|
||||||
|
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE74F";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Volume_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
|
||||||
|
{
|
||||||
|
double v = (GetTemplateChild("VolumeSlider") as Slider).Value;
|
||||||
|
if (v == 0)
|
||||||
|
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE74F";
|
||||||
|
else if (v <= 25 && v > 0)
|
||||||
|
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE992";
|
||||||
|
else if (v <= 50 && v > 25)
|
||||||
|
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE993";
|
||||||
|
else if (v <= 75 && v > 50)
|
||||||
|
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE994";
|
||||||
|
else if (v > 75)
|
||||||
|
((GetTemplateChild("AudioMuteButton") as Button).Content as FontIcon).Glyph = ((GetTemplateChild("volume") as Button).Content as FontIcon).Glyph = "\xE995";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CcSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CcSwitch_Toggled((GetTemplateChild("ccSwitch") as ToggleSwitch), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CcSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if((GetTemplateChild("captions") as LiveCaptions).Player == null)
|
||||||
|
(GetTemplateChild("captions") as LiveCaptions).Player = Player;
|
||||||
|
|
||||||
|
if ((sender as ToggleSwitch).IsOn)
|
||||||
|
(GetTemplateChild("captions") as LiveCaptions).Initialize(((GetTemplateChild("ccSelector") as ComboBox).SelectedItem as ComboBoxItem).Tag as ClosedCaptionTrackInfo);
|
||||||
|
else
|
||||||
|
(GetTemplateChild("captions") as LiveCaptions).Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QualitySelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
SettingsStorage.RememberedQuality = e.AddedItems[0] as string;
|
||||||
|
MediaStreamInfo item = MediaStreams.Muxed.Find(i => i.VideoQualityLabel == e.AddedItems[0] as string);
|
||||||
|
if (item == null)
|
||||||
|
item = MediaStreams.Video.Find(i => i.VideoQualityLabel == e.AddedItems[0] as string);
|
||||||
|
|
||||||
|
QualityChanged?.Invoke(sender, item, MediaStreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CompactOverlay_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if((sender as Button).Margin.Top > 0)
|
||||||
|
{
|
||||||
|
Button btnCompact = sender as Button;
|
||||||
|
(GetTemplateChild("center") as Grid).Children.Remove(btnCompact);
|
||||||
|
(GetTemplateChild("headerToolbar") as StackPanel).Children.Add(btnCompact);
|
||||||
|
|
||||||
|
btnCompact.Margin = new Thickness(0);
|
||||||
|
(btnCompact.Content as FontIcon).Glyph = "\xE2B3";
|
||||||
|
|
||||||
|
SetNormal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SetCompactView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Close_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
CloseRequested?.Invoke(sender, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCompactView()
|
||||||
|
{
|
||||||
|
Button btn = GetTemplateChild("CompactOverlayButton") as Button;
|
||||||
|
(GetTemplateChild("headerToolbar") as StackPanel).Children.Remove(btn);
|
||||||
|
(GetTemplateChild("center") as Grid).Children.Add(btn);
|
||||||
|
|
||||||
|
btn.Margin = new Thickness(0, 32, 0, 0);
|
||||||
|
(btn.Content as FontIcon).Glyph = "\xE2B4";
|
||||||
|
|
||||||
|
Button btnPlay = GetTemplateChild("PlayPauseButton") as Button;
|
||||||
|
(GetTemplateChild("leftStack") as StackPanel).Children.Remove(btnPlay);
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Add(btnPlay);
|
||||||
|
|
||||||
|
Button btnFwd = GetTemplateChild("SkipForwardButton") as Button;
|
||||||
|
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnFwd);
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(1, btnFwd);
|
||||||
|
|
||||||
|
Button btnBwd = GetTemplateChild("SkipBackwardButton") as Button;
|
||||||
|
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnBwd);
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(0, btnBwd);
|
||||||
|
|
||||||
|
(GetTemplateChild("header") as Grid).Visibility = Visibility.Collapsed;
|
||||||
|
(GetTemplateChild("centerBgr") as Grid).Visibility = Visibility.Visible;
|
||||||
|
(GetTemplateChild("center") as Grid).Visibility = Visibility.Visible;
|
||||||
|
(GetTemplateChild("footer") as Grid).Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
(GetTemplateChild("maximize") as Button).Visibility = Visibility.Collapsed;
|
||||||
|
(GetTemplateChild("compactClose") as Button).Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
(GetTemplateChild("captions") as LiveCaptions).Size = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMinimized()
|
||||||
|
{
|
||||||
|
Button btnPlay = GetTemplateChild("PlayPauseButton") as Button;
|
||||||
|
(GetTemplateChild("leftStack") as StackPanel).Children.Remove(btnPlay);
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Add(btnPlay);
|
||||||
|
|
||||||
|
Button btnFwd = GetTemplateChild("SkipForwardButton") as Button;
|
||||||
|
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnFwd);
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(1, btnFwd);
|
||||||
|
|
||||||
|
Button btnBwd = GetTemplateChild("SkipBackwardButton") as Button;
|
||||||
|
(GetTemplateChild("rightStack") as StackPanel).Children.Remove(btnBwd);
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Insert(0, btnBwd);
|
||||||
|
|
||||||
|
(GetTemplateChild("header") as Grid).Visibility = Visibility.Collapsed;
|
||||||
|
(GetTemplateChild("centerBgr") as Grid).Visibility = Visibility.Visible;
|
||||||
|
(GetTemplateChild("center") as Grid).Visibility = Visibility.Visible;
|
||||||
|
(GetTemplateChild("footer") as Grid).Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
(GetTemplateChild("compactClose") as Button).Visibility = Visibility.Visible;
|
||||||
|
(GetTemplateChild("maximize") as Button).Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
(GetTemplateChild("captions") as LiveCaptions).Size = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetNormal()
|
||||||
|
{
|
||||||
|
Button btnPlay = GetTemplateChild("PlayPauseButton") as Button;
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Remove(btnPlay);
|
||||||
|
(GetTemplateChild("leftStack") as StackPanel).Children.Insert(0, btnPlay);
|
||||||
|
|
||||||
|
Button btnFwd = GetTemplateChild("SkipForwardButton") as Button;
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Remove(btnFwd);
|
||||||
|
(GetTemplateChild("rightStack") as StackPanel).Children.Insert(0, btnFwd);
|
||||||
|
|
||||||
|
Button btnBwd = GetTemplateChild("SkipBackwardButton") as Button;
|
||||||
|
(GetTemplateChild("centralStack") as StackPanel).Children.Remove(btnBwd);
|
||||||
|
(GetTemplateChild("rightStack") as StackPanel).Children.Insert(0, btnBwd);
|
||||||
|
|
||||||
|
(GetTemplateChild("header") as Grid).Visibility = Visibility.Visible;
|
||||||
|
(GetTemplateChild("centerBgr") as Grid).Visibility = Visibility.Collapsed;
|
||||||
|
(GetTemplateChild("center") as Grid).Visibility = Visibility.Collapsed;
|
||||||
|
(GetTemplateChild("footer") as Grid).Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
(GetTemplateChild("captions") as LiveCaptions).Size = 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMeta(string title, string channel)
|
||||||
|
{
|
||||||
|
if (!isReady)
|
||||||
|
{
|
||||||
|
queue.Enqueue(() => SetMeta(title, channel));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(GetTemplateChild("title") as TextBlock).Text = title;
|
||||||
|
(GetTemplateChild("author") as TextBlock).Text = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCaptions(IReadOnlyList<ClosedCaptionTrackInfo> list)
|
||||||
|
{
|
||||||
|
if (!isReady)
|
||||||
|
{
|
||||||
|
queue.Enqueue(() => SetCaptions(list));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClosedCaptions = list;
|
||||||
|
|
||||||
|
if (list.Count > 0)
|
||||||
|
{
|
||||||
|
foreach(ClosedCaptionTrackInfo i in list)
|
||||||
|
(GetTemplateChild("ccSelector") as ComboBox).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 = list.Find(i => SettingsStorage.RelevanceLanguage.Contains(i.Language.Code)) ?? list.Find(i => "en-US".Contains(i.Language.Code));
|
||||||
|
if (item == null)
|
||||||
|
item = list.First();
|
||||||
|
|
||||||
|
(GetTemplateChild("ccSelector") as ComboBox).SelectedItem = (GetTemplateChild("ccSelector") as ComboBox).Items.Find(i => (i as ComboBoxItem).Tag == item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(GetTemplateChild("cc") as Button).Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetQualities(MediaStreamInfoSet list)
|
||||||
|
{
|
||||||
|
if (!isReady)
|
||||||
|
{
|
||||||
|
queue.Enqueue(() => SetQualities(list));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaStreams = list;
|
||||||
|
|
||||||
|
List<VideoQuality> q = list.GetAllVideoQualities().ToList();
|
||||||
|
q.Sort();
|
||||||
|
q.Reverse();
|
||||||
|
List<string> labels = new List<string>();
|
||||||
|
q.ForEach(i => labels.Add(i.GetVideoQualityLabel()));
|
||||||
|
(GetTemplateChild("qualitySelector") as ComboBox).ItemsSource = labels;
|
||||||
|
|
||||||
|
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
|
||||||
|
|
||||||
|
if (labels.Contains(s))
|
||||||
|
(GetTemplateChild("qualitySelector") as ComboBox).SelectedItem = s;
|
||||||
|
else
|
||||||
|
(GetTemplateChild("qualitySelector") as ComboBox).SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetStream(string url)
|
||||||
|
{
|
||||||
|
if (!isReady)
|
||||||
|
{
|
||||||
|
queue.Enqueue(() => SetStream(url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Set up stream UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="FoxTube.VideoPlayer"
|
||||||
|
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:controls1="using:FoxTube.Controls"
|
||||||
|
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
||||||
|
xmlns:foxtube="using:FoxTube"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="1080"
|
||||||
|
d:DesignWidth="1920"
|
||||||
|
RequestedTheme="Dark">
|
||||||
|
|
||||||
|
<Grid Background="White" Name="grid">
|
||||||
|
<MediaElement Name="videoSource" AreTransportControlsEnabled="True" PosterSource="ms-appx:///Assets/videoThumbSample.png" CurrentStateChanged="VideoSource_CurrentStateChanged" MediaOpened="VideoSource_MediaOpened" VolumeChanged="VideoSource_VolumeChanged">
|
||||||
|
<MediaElement.TransportControls>
|
||||||
|
<foxtube:PlayerControls IsCompactOverlayButtonVisible="True" IsCompactOverlayEnabled="True"
|
||||||
|
IsFullWindowButtonVisible="True" IsFullWindowEnabled="True"
|
||||||
|
IsSkipBackwardButtonVisible="True" IsSkipBackwardEnabled="True"
|
||||||
|
IsSkipForwardButtonVisible="True" IsSkipForwardEnabled="True"/>
|
||||||
|
</MediaElement.TransportControls>
|
||||||
|
</MediaElement>
|
||||||
|
<MediaElement Name="audioSource" Width="0" AreTransportControlsEnabled="False" Visibility="Collapsed" Height="0" VerticalAlignment="Top" HorizontalAlignment="Left" CurrentStateChanged="AudioSource_CurrentStateChanged" MediaOpened="AudioSource_MediaOpened"/>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Windows.Foundation;
|
||||||
|
using Windows.UI.Core;
|
||||||
|
using Windows.UI.Xaml;
|
||||||
|
using Windows.UI.Xaml.Controls;
|
||||||
|
using Windows.UI.Xaml.Controls.Primitives;
|
||||||
|
using Windows.UI.Xaml.Input;
|
||||||
|
using Google.Apis.YouTube.v3.Data;
|
||||||
|
using Windows.UI.Xaml.Media.Imaging;
|
||||||
|
using Windows.Media;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
using Windows.UI.ViewManagement;
|
||||||
|
using System.Xml;
|
||||||
|
using Windows.ApplicationModel.Core;
|
||||||
|
using Windows.UI;
|
||||||
|
using Windows.Media.Casting;
|
||||||
|
using YoutubeExplode.Models.MediaStreams;
|
||||||
|
using YoutubeExplode;
|
||||||
|
using YoutubeExplode.Models.ClosedCaptions;
|
||||||
|
using System.Globalization;
|
||||||
|
using FoxTube.Controls;
|
||||||
|
using Windows.System;
|
||||||
|
using Windows.Media.Core;
|
||||||
|
using Windows.Media.Playback;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Windows.UI.Popups;
|
||||||
|
|
||||||
|
namespace FoxTube
|
||||||
|
{
|
||||||
|
public sealed partial class VideoPlayer : UserControl
|
||||||
|
{
|
||||||
|
public Video item;
|
||||||
|
public string avatar;
|
||||||
|
|
||||||
|
public event Event NextClicked;
|
||||||
|
public event ObjectEventHandler MiniMode;
|
||||||
|
public PlayerControls Controls => videoSource.TransportControls as PlayerControls;
|
||||||
|
public TimeSpan Position => videoSource.Position;
|
||||||
|
|
||||||
|
bool audioLoaded = false;
|
||||||
|
bool videoLoaded = false;
|
||||||
|
bool isMuxed = false;
|
||||||
|
DispatcherTimer muxedTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
|
||||||
|
|
||||||
|
TimeSpan timecodeBackup;
|
||||||
|
bool needUpdateTimecode = false;
|
||||||
|
|
||||||
|
SystemMediaTransportControls systemControls;
|
||||||
|
|
||||||
|
public VideoPlayer()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Initialize(Video meta, string channelAvatar, bool privateMode = false)
|
||||||
|
{
|
||||||
|
item = meta;
|
||||||
|
avatar = channelAvatar;
|
||||||
|
|
||||||
|
videoSource.PosterSource = new BitmapImage((meta.Snippet.Thumbnails.Maxres ?? meta.Snippet.Thumbnails.Medium).Url.ToUri());
|
||||||
|
Controls.SetMeta(meta.Snippet.Title, meta.Snippet.ChannelTitle);
|
||||||
|
|
||||||
|
if (item.Snippet.LiveBroadcastContent == "none")
|
||||||
|
{
|
||||||
|
InitializeContols();
|
||||||
|
Controls.SetQualities(await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id));
|
||||||
|
Controls.SetCaptions(await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(item.Id));
|
||||||
|
}
|
||||||
|
else if (item.Snippet.LiveBroadcastContent == "live")
|
||||||
|
{
|
||||||
|
InitializeContols();
|
||||||
|
Controls.SetStream((await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id)).HlsLiveStreamUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
videoSource.AreTransportControlsEnabled = false;
|
||||||
|
|
||||||
|
if (!privateMode)
|
||||||
|
Debug.WriteLine("TODO: history entry creation");
|
||||||
|
// TODO: Create history entry
|
||||||
|
|
||||||
|
Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeContols()
|
||||||
|
{
|
||||||
|
videoSource.Volume = SettingsStorage.Volume;
|
||||||
|
|
||||||
|
muxedTimer.Tick += (s, e) =>
|
||||||
|
{
|
||||||
|
if (!Enumerable.Range(-100, 100).Contains((int)(videoSource.Position - audioSource.Position).TotalMilliseconds))
|
||||||
|
audioSource.Position = videoSource.Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
Controls.CloseRequested += Controls_CloseRequested;
|
||||||
|
Controls.NextRequested += (s, e) => NextClicked?.Invoke();
|
||||||
|
Controls.QualityChanged += Controls_QualityChanged;
|
||||||
|
Controls.MiniModeChanged += Controls_MiniModeChanged;
|
||||||
|
Controls.Player = videoSource;
|
||||||
|
|
||||||
|
#region System Media Transport Controls
|
||||||
|
systemControls = SystemMediaTransportControls.GetForCurrentView();
|
||||||
|
systemControls.IsNextEnabled = true;
|
||||||
|
systemControls.IsPauseEnabled = true;
|
||||||
|
systemControls.IsPlayEnabled = 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Controls_MiniModeChanged(object sender, bool e)
|
||||||
|
{
|
||||||
|
videoSource.IsFullWindow = false;
|
||||||
|
|
||||||
|
MiniMode?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Controls_QualityChanged(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list)
|
||||||
|
{
|
||||||
|
videoSource.Pause();
|
||||||
|
|
||||||
|
timecodeBackup = videoSource.Position;
|
||||||
|
needUpdateTimecode = true;
|
||||||
|
|
||||||
|
audioLoaded = false;
|
||||||
|
videoLoaded = false;
|
||||||
|
muxedTimer.Stop();
|
||||||
|
|
||||||
|
videoSource.Source = requestedQuality.Url.ToUri();
|
||||||
|
if(requestedQuality is MuxedStreamInfo)
|
||||||
|
{
|
||||||
|
isMuxed = true;
|
||||||
|
audioSource.Source = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
audioSource.Source = list.Audio.First().Url.ToUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Controls_CloseRequested(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
systemControls.IsEnabled = false;
|
||||||
|
|
||||||
|
videoSource.Stop();
|
||||||
|
audioSource.Stop();
|
||||||
|
|
||||||
|
Methods.MainPage.CloseVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
|
||||||
|
{
|
||||||
|
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||||
|
{
|
||||||
|
if (args.Button == SystemMediaTransportControlsButton.Next)
|
||||||
|
NextClicked?.Invoke();
|
||||||
|
|
||||||
|
switch (args.Button)
|
||||||
|
{
|
||||||
|
case SystemMediaTransportControlsButton.Play:
|
||||||
|
videoSource.Play();
|
||||||
|
break;
|
||||||
|
case SystemMediaTransportControlsButton.Pause:
|
||||||
|
videoSource.Pause();
|
||||||
|
break;
|
||||||
|
case SystemMediaTransportControlsButton.Next:
|
||||||
|
NextClicked.Invoke();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Pause()
|
||||||
|
{
|
||||||
|
videoSource.Pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void VideoSource_CurrentStateChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (audioSource.Source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(videoSource.CurrentState)
|
||||||
|
{
|
||||||
|
case Windows.UI.Xaml.Media.MediaElementState.Buffering:
|
||||||
|
case Windows.UI.Xaml.Media.MediaElementState.Paused:
|
||||||
|
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
|
||||||
|
// TODO: Create history entry
|
||||||
|
audioSource.Pause();
|
||||||
|
break;
|
||||||
|
case Windows.UI.Xaml.Media.MediaElementState.Playing:
|
||||||
|
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
|
||||||
|
audioSource.Play();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AudioSource_CurrentStateChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if(audioSource.CurrentState == Windows.UI.Xaml.Media.MediaElementState.Playing)
|
||||||
|
muxedTimer.Start();
|
||||||
|
else
|
||||||
|
muxedTimer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void VideoSource_MediaOpened(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
videoLoaded = true;
|
||||||
|
if (needUpdateTimecode)
|
||||||
|
{
|
||||||
|
videoSource.Position = timecodeBackup;
|
||||||
|
needUpdateTimecode = false;
|
||||||
|
}
|
||||||
|
if (audioLoaded || isMuxed)
|
||||||
|
videoSource.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AudioSource_MediaOpened(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
audioLoaded = true;
|
||||||
|
if (needUpdateTimecode)
|
||||||
|
audioSource.Position = timecodeBackup;
|
||||||
|
if (videoLoaded)
|
||||||
|
videoSource.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void VideoSource_VolumeChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
audioSource.Volume = videoSource.Volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,23 +2,36 @@
|
|||||||
x:Class="FoxTube.Controls.PlaylistCard"
|
x:Class="FoxTube.Controls.PlaylistCard"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:FoxTube.Controls"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
SizeChanged="UserControl_SizeChanged"
|
|
||||||
d:DesignHeight="290"
|
d:DesignHeight="290"
|
||||||
d:DesignWidth="384">
|
d:DesignWidth="384"
|
||||||
|
MaxWidth="500"
|
||||||
|
Opacity="0">
|
||||||
|
|
||||||
<Button Padding="0" Background="Transparent" Click="Button_Click">
|
<Windows10version1809:Page.OpacityTransition>
|
||||||
<Grid Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
|
<ScalarTransition Duration="0:0:0.5"/>
|
||||||
|
</Windows10version1809:Page.OpacityTransition>
|
||||||
|
|
||||||
|
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
|
||||||
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="75"/>
|
<RowDefinition Height="20"/>
|
||||||
|
<RowDefinition Height="55"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill"/>
|
|
||||||
|
<Image Source="/Assets/videoPlaceholder.png" Opacity=".0001" Stretch="Fill"/>
|
||||||
|
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill" Opacity="0" ImageOpened="Thumbnail_ImageOpened">
|
||||||
|
<Windows10version1809:Image.OpacityTransition>
|
||||||
|
<ScalarTransition Duration="0:0:0.5"/>
|
||||||
|
</Windows10version1809:Image.OpacityTransition>
|
||||||
|
</Image>
|
||||||
|
|
||||||
<Grid HorizontalAlignment="Right" Width="100">
|
<Grid HorizontalAlignment="Right" Width="100">
|
||||||
<Grid.Background>
|
<Grid.Background>
|
||||||
<AcrylicBrush TintColor="#7F000000" BackgroundSource="Backdrop" AlwaysUseFallback="False" TintOpacity="1" Opacity="0.97"/>
|
<AcrylicBrush TintColor="#7F000000" BackgroundSource="Backdrop" AlwaysUseFallback="False" TintOpacity="1" Opacity="0.97"/>
|
||||||
@@ -28,24 +41,20 @@
|
|||||||
<TextBlock Foreground="White" Text="[N/A]" HorizontalAlignment="Center" FontSize="20" Name="counter"/>
|
<TextBlock Foreground="White" Text="[N/A]" HorizontalAlignment="Center" FontSize="20" Name="counter"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Grid.Row="1">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="20"/>
|
|
||||||
<RowDefinition Height="55"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid>
|
|
||||||
<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"/>
|
<Grid Grid.Row="1">
|
||||||
<TextBlock Grid.Column="1" Name="date" Text="[Published at]" HorizontalAlignment="Right" Foreground="Gray" Margin="0,2,2,0" FontSize="12"/>
|
<Grid.ColumnDefinitions>
|
||||||
</Grid>
|
<ColumnDefinition Width="60"/>
|
||||||
<TextBlock Grid.Row="1" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" TextTrimming="CharacterEllipsis" Margin="5" MaxLines="2"/>
|
<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>
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" TextTrimming="CharacterEllipsis" Margin="5" MaxLines="2"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
<UserControl.ContextFlyout>
|
<UserControl.ContextFlyout>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Google.Apis.YouTube.v3;
|
using Google.Apis.YouTube.v3;
|
||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
|
using Microsoft.AppCenter.Analytics;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Windows.ApplicationModel.DataTransfer;
|
using Windows.ApplicationModel.DataTransfer;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
@@ -12,11 +14,13 @@ namespace FoxTube.Controls
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Playlist card control
|
/// Playlist card control
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class PlaylistCard : Page, IItemCard
|
public sealed partial class PlaylistCard : Page
|
||||||
{
|
{
|
||||||
Playlist item;
|
Playlist item;
|
||||||
public string playlistId;
|
public string playlistId;
|
||||||
|
|
||||||
|
public bool NeedInitialize { get; set; } = true;
|
||||||
|
|
||||||
public PlaylistCard(string id)
|
public PlaylistCard(string id)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -25,34 +29,41 @@ namespace FoxTube.Controls
|
|||||||
|
|
||||||
public async void Initialize(string id)
|
public async void Initialize(string id)
|
||||||
{
|
{
|
||||||
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails");
|
|
||||||
request.Id = id;
|
|
||||||
PlaylistListResponse response = await request.ExecuteAsync();
|
|
||||||
|
|
||||||
item = response.Items[0];
|
|
||||||
playlistId = id;
|
|
||||||
|
|
||||||
title.Text = item.Snippet.Title;
|
|
||||||
channelName.Text = item.Snippet.ChannelTitle;
|
|
||||||
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
|
try
|
||||||
{
|
{
|
||||||
thumbnail.Source = new BitmapImage((item.Snippet.Thumbnails.Maxres ?? item.Snippet.Thumbnails.Medium).Url.ToUri());
|
playlistId = id;
|
||||||
avatar.ProfilePicture = new BitmapImage(new Uri((await r.ExecuteAsync()).Items[0].Snippet.Thumbnails.Medium.Url));
|
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails");
|
||||||
} catch { }
|
request.Id = playlistId;
|
||||||
|
item = (await request.ExecuteAsync()).Items[0];
|
||||||
|
|
||||||
|
title.Text = item.Snippet.Title;
|
||||||
|
channelName.Text = item.Snippet.ChannelTitle;
|
||||||
|
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()); }
|
||||||
|
catch { }
|
||||||
|
try { avatar.ProfilePicture = new BitmapImage(new Uri((await r.ExecuteAsync()).Items[0].Snippet.Thumbnails.Medium.Url)); }
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
Opacity = 1;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Visibility = Visibility.Collapsed;
|
||||||
|
Analytics.TrackEvent("PlaylistCard loading failed", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message },
|
||||||
|
{ "Playlist ID", playlistId }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
|
public void Button_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
|
||||||
Height = e.NewSize.Width * 0.75;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Button_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
{
|
||||||
Methods.MainPage.GoToPlaylist(item.Id);
|
Methods.MainPage.GoToPlaylist(item.Id);
|
||||||
}
|
}
|
||||||
@@ -73,5 +84,10 @@ namespace FoxTube.Controls
|
|||||||
{
|
{
|
||||||
await Launcher.LaunchUriAsync($"https://www.youtube.com/playlist?list={playlistId}".ToUri());
|
await Launcher.LaunchUriAsync($"https://www.youtube.com/playlist?list={playlistId}".ToUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
thumbnail.Opacity = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
x:Class="FoxTube.Controls.ShowMore"
|
x:Class="FoxTube.Controls.ShowMore"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:FoxTube.Controls"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -1,19 +1,5 @@
|
|||||||
using System;
|
using Windows.UI.Xaml;
|
||||||
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;
|
||||||
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
|
namespace FoxTube.Controls
|
||||||
{
|
{
|
||||||
@@ -22,7 +8,7 @@ namespace FoxTube.Controls
|
|||||||
public event Event Clicked;
|
public event Event Clicked;
|
||||||
public ShowMore()
|
public ShowMore()
|
||||||
{
|
{
|
||||||
this.InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btn_Click(object sender, RoutedEventArgs e)
|
private void btn_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -32,11 +18,21 @@ namespace FoxTube.Controls
|
|||||||
Clicked.Invoke();
|
Clicked.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Complete(bool close = false)
|
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;
|
bar.Visibility = Visibility.Collapsed;
|
||||||
if (!close)
|
btn.Visibility = Visibility.Visible;
|
||||||
btn.Visibility = Visibility.Visible;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,51 +4,63 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
SizeChanged="UserControl_SizeChanged"
|
|
||||||
d:DesignHeight="290"
|
d:DesignHeight="290"
|
||||||
d:DesignWidth="384">
|
d:DesignWidth="384"
|
||||||
|
MaxWidth="500"
|
||||||
|
Opacity="0">
|
||||||
|
|
||||||
<Button Padding="0" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
|
<Windows10version1809:UserControl.OpacityTransition>
|
||||||
|
<ScalarTransition Duration="0:0:0.5"/>
|
||||||
|
</Windows10version1809:UserControl.OpacityTransition>
|
||||||
|
|
||||||
|
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="75"/>
|
<RowDefinition Height="20"/>
|
||||||
|
<RowDefinition Height="55"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill"/>
|
|
||||||
|
<Image Source="/Assets/videoPlaceholder.png" Opacity=".0001" Stretch="Fill"/>
|
||||||
|
<Image Name="thumbnail" Source="/Assets/videoThumbSample.png" Stretch="Fill" ImageOpened="Thumbnail_ImageOpened" Opacity="0">
|
||||||
|
<Windows10version1809:Image.OpacityTransition>
|
||||||
|
<ScalarTransition Duration="0:0:0.5"/>
|
||||||
|
</Windows10version1809:Image.OpacityTransition>
|
||||||
|
</Image>
|
||||||
|
|
||||||
<Grid Background="#7F000000" Name="watched" Visibility="Collapsed">
|
<Grid Background="#7F000000" Name="watched" Visibility="Collapsed">
|
||||||
<StackPanel Margin="5" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" VerticalAlignment="Top" HorizontalAlignment="Left" Padding="5,2,5,2" BorderBrush="Gray" BorderThickness="1">
|
<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"/>
|
<TextBlock x:Uid="/Cards/watched" Text="Watched" Foreground="Gray" FontSize="12"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ProgressBar VerticalAlignment="Bottom" Margin="54,0,0,0" Foreground="Red" Name="leftOn"/>
|
<ProgressBar VerticalAlignment="Bottom" Margin="54,0,0,0" Foreground="Red" Name="leftOn"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Margin="0,0,5,5" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="5,2,5,3">
|
<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"/>
|
<TextBlock Name="info" Text="[Duration] | [Published at]" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Gray" FontSize="12"/>
|
||||||
</StackPanel>
|
</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">
|
<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 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"/>
|
<TextBlock x:Uid="/Cards/live" Name="liveContent" Text="LIVE" VerticalAlignment="Center" Foreground="White" FontSize="12" FontWeight="Bold"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Grid Grid.Row="1">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="20"/>
|
|
||||||
<RowDefinition Height="55"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid>
|
|
||||||
<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 MaxWidth="200" Name="channelName" HorizontalAlignment="Left" Grid.Column="1" Text="[Channel name]" TextTrimming="CharacterEllipsis" Foreground="Gray" Margin="0,2,0,0" FontSize="12"/>
|
<Grid Grid.Row="1">
|
||||||
<TextBlock Grid.Column="1" Name="views" Text="[Views]" HorizontalAlignment="Right" Foreground="Gray" Margin="0,2,2,0" FontSize="12"/>
|
<Grid.ColumnDefinitions>
|
||||||
</Grid>
|
<ColumnDefinition Width="60"/>
|
||||||
<TextBlock Grid.Row="1" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
|
<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>
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Name="title" Text="[Title]" TextWrapping="WrapWholeWords" Margin="5" MaxLines="2" TextTrimming="CharacterEllipsis"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
<UserControl.ContextFlyout>
|
<UserControl.ContextFlyout>
|
||||||
|
|||||||
@@ -7,13 +7,17 @@ using Windows.UI.Xaml.Media.Imaging;
|
|||||||
using Windows.System;
|
using Windows.System;
|
||||||
using Windows.ApplicationModel.DataTransfer;
|
using Windows.ApplicationModel.DataTransfer;
|
||||||
using Windows.ApplicationModel.Resources;
|
using Windows.ApplicationModel.Resources;
|
||||||
|
using Microsoft.AppCenter.Analytics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using YoutubeExplode;
|
||||||
|
using Windows.UI.Popups;
|
||||||
|
|
||||||
namespace FoxTube.Controls
|
namespace FoxTube.Controls
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Video item card
|
/// Video item card
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class VideoCard : UserControl, IItemCard
|
public sealed partial class VideoCard : UserControl
|
||||||
{
|
{
|
||||||
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
|
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
|
||||||
|
|
||||||
@@ -27,31 +31,35 @@ namespace FoxTube.Controls
|
|||||||
Initialize(id, playlist);
|
Initialize(id, playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
|
public VideoCard(Video meta, string playlist = null)
|
||||||
{
|
{
|
||||||
Height = e.NewSize.Width * 0.75;
|
InitializeComponent();
|
||||||
|
item = meta;
|
||||||
|
playlistId = playlist;
|
||||||
|
LoadMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Initialize(string id, string playlist = null)
|
public async void Initialize(string id, string playlist = null)
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails");
|
{
|
||||||
request.Id = id;
|
|
||||||
VideoListResponse response = await request.ExecuteAsync();
|
|
||||||
|
|
||||||
item = response.Items[0];
|
|
||||||
videoId = id;
|
videoId = id;
|
||||||
playlistId = playlist;
|
playlistId = playlist;
|
||||||
|
|
||||||
|
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails");
|
||||||
|
request.Id = id;
|
||||||
|
item = (await request.ExecuteAsync()).Items[0];
|
||||||
|
|
||||||
title.Text = item.Snippet.Title;
|
title.Text = item.Snippet.Title;
|
||||||
channelName.Text = item.Snippet.ChannelTitle;
|
channelName.Text = item.Snippet.ChannelTitle;
|
||||||
|
|
||||||
if (item.Snippet.LiveBroadcastContent == "live")
|
if (item.Snippet.LiveBroadcastContent == "live")
|
||||||
{
|
{
|
||||||
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
|
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
|
||||||
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
||||||
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ScheduledStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}";
|
info.Text = $"{item.LiveStreamingDetails.ScheduledEndTime - item.LiveStreamingDetails.ActualStartTime} | {Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value)}";
|
||||||
else
|
else
|
||||||
info.Text = item.LiveStreamingDetails.ActualStartTime.Value.ToString();
|
info.Text = Methods.GetAgo(item.LiveStreamingDetails.ActualStartTime.Value);
|
||||||
liveTag.Visibility = Visibility.Visible;
|
liveTag.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
else if (item.Snippet.LiveBroadcastContent == "upcoming")
|
else if (item.Snippet.LiveBroadcastContent == "upcoming")
|
||||||
@@ -63,8 +71,8 @@ namespace FoxTube.Controls
|
|||||||
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
info.Text = $"{Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
||||||
liveTag.Visibility = Visibility.Visible;
|
liveTag.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue && (item.LiveStreamingDetails.ScheduledStartTime - DateTime.Now).Value.TotalMilliseconds > 0)
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
|
||||||
liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {item.LiveStreamingDetails.ScheduledStartTime}";
|
liveContent.Text = $"{resources.GetString("/Cards/goesLive")} {(item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss")}";
|
||||||
else liveContent.Text = resources.GetString("/Cards/upcoming");
|
else liveContent.Text = resources.GetString("/Cards/upcoming");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -73,31 +81,107 @@ namespace FoxTube.Controls
|
|||||||
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
var request1 = SecretsVault.Service.Channels.List("snippet");
|
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
|
||||||
request1.Id = item.Snippet.ChannelId;
|
catch { }
|
||||||
ChannelListResponse response1 = await request1.ExecuteAsync();
|
try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()); }
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
avatar.ProfilePicture = new BitmapImage(new Uri(response1.Items[0].Snippet.Thumbnails.Medium.Url));
|
|
||||||
thumbnail.Source = new BitmapImage(new Uri((item.Snippet.Thumbnails.Maxres ?? item.Snippet.Thumbnails.Medium).Url));
|
|
||||||
}
|
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
/*if(SecretsVault.UserHistory.Exists(x => x.Id == videoId))
|
if(SecretsVault.History.Contains(videoId))
|
||||||
{
|
|
||||||
watched.Visibility = Visibility.Visible;
|
watched.Visibility = Visibility.Visible;
|
||||||
leftOn.Value = SecretsVault.UserHistory.Find(x => x.Id == videoId).LeftOn;
|
|
||||||
}*/
|
Opacity = 1;
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Visibility = Visibility.Collapsed;
|
Visibility = Visibility.Collapsed;
|
||||||
|
Analytics.TrackEvent("VideoCard loading failed", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message },
|
||||||
|
{ "Video ID", videoId }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Button_Click(object sender, RoutedEventArgs e)
|
public async void LoadMeta()
|
||||||
{
|
{
|
||||||
|
videoId = item.Id;
|
||||||
|
title.Text = item.Snippet.Title;
|
||||||
|
channelName.Text = item.Snippet.ChannelTitle;
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
views.Text = $"{item.Statistics.ViewCount:0,0} {resources.GetString("/Cards/views")}";
|
||||||
|
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
|
||||||
|
catch { }
|
||||||
|
try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 }; }
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (SecretsVault.History.Contains(videoId))
|
||||||
|
watched.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
Opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Button_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (item.ContentDetails.ContentRating != null)
|
||||||
|
{
|
||||||
|
if (SecretsVault.IsAuthorized)
|
||||||
|
{
|
||||||
|
if (SettingsStorage.Mature == MatureState.Blocked)
|
||||||
|
{
|
||||||
|
MessageDialog dialog = new MessageDialog(resources.GetString("/VideoPage/matureText"), resources.GetString("/VideoPage/wantContinue"));
|
||||||
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/yes"), null, true));
|
||||||
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/always"), (command) => SettingsStorage.Mature = MatureState.Allowed, true));
|
||||||
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/no"), null, false));
|
||||||
|
|
||||||
|
dialog.CancelCommandIndex = 2;
|
||||||
|
dialog.DefaultCommandIndex = 0;
|
||||||
|
|
||||||
|
if (!(bool)(await dialog.ShowAsync()).Id)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageDialog dialog = new MessageDialog(resources.GetString("/VideoPage/matureText"), resources.GetString("/VideoPage/signRequired"));
|
||||||
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/signin"), (command) => SecretsVault.Authorize()));
|
||||||
|
dialog.Commands.Add(new UICommand(resources.GetString("/VideoPage/cancel")));
|
||||||
|
|
||||||
|
dialog.CancelCommandIndex = 1;
|
||||||
|
dialog.DefaultCommandIndex = 0;
|
||||||
|
|
||||||
|
await dialog.ShowAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Methods.MainPage.GoToVideo(videoId, playlistId);
|
Methods.MainPage.GoToVideo(videoId, playlistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,5 +201,10 @@ namespace FoxTube.Controls
|
|||||||
{
|
{
|
||||||
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}".ToUri());
|
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}".ToUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
thumbnail.Opacity = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
<UserControl
|
|
||||||
x:Class="FoxTube.VideoPlayer"
|
|
||||||
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:controls1="using:FoxTube.Controls"
|
|
||||||
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="1080"
|
|
||||||
d:DesignWidth="1920"
|
|
||||||
RequestedTheme="Dark"
|
|
||||||
PointerMoved="UserControl_PointerMoved"
|
|
||||||
PointerExited="UserControl_PointerExited"
|
|
||||||
PointerEntered="UserControl_PointerEntered">
|
|
||||||
|
|
||||||
<Grid Background="White" Name="grid" Tapped="UserControl_Tapped">
|
|
||||||
<MediaPlayerElement IsDoubleTapEnabled="False" Name="videoSource" AreTransportControlsEnabled="False" PosterSource="ms-appx:///Assets/videoThumbSample.png"/>
|
|
||||||
<MediaPlayerElement Name="audioSource" Width="0" Height="0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
|
||||||
<controls1:LiveCaptions Player="{x:Bind controller}" Visibility="Collapsed"/>
|
|
||||||
|
|
||||||
<Grid Name="controls" Visibility="Visible">
|
|
||||||
<Windows10version1809:Grid.OpacityTransition>
|
|
||||||
<Windows10version1809:ScalarTransition/>
|
|
||||||
</Windows10version1809:Grid.OpacityTransition>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid Name="header" Height="50">
|
|
||||||
<Grid.Background>
|
|
||||||
<AcrylicBrush TintColor="#CC000000" TintOpacity="0.6"/>
|
|
||||||
</Grid.Background>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition/>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Button Click="minimize_Click" Name="minimize" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/minimize"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<StackPanel Grid.Column="1" Margin="10,0,10,0" VerticalAlignment="Center">
|
|
||||||
<TextBlock Name="title" Text="[Title]" Foreground="White" VerticalAlignment="Center" TextWrapping="WrapWholeWords" FontSize="20" MaxLines="1"/>
|
|
||||||
<TextBlock Foreground="LightGray" Text="[Channel name]" Name="channelName" FontStyle="Italic"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
|
||||||
<Button Name="closeHeader" Click="close_Click" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/close"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Button Name="cast" Click="cast_Click" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/cast"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Button Name="miniViewBtn" Click="miniView_Click" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25" HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/miniview"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid Grid.Row="1" Name="playPauseArea" Tapped="playPauseArea_Tapped" DoubleTapped="playPauseArea_DoubleTapped" Background="Black" Opacity=".0001"/>
|
|
||||||
|
|
||||||
<Grid Name="touchCentral" Visibility="Collapsed" Grid.Row="1">
|
|
||||||
<Grid.Background>
|
|
||||||
<AcrylicBrush TintColor="#CC000000" TintOpacity="0.1"/>
|
|
||||||
</Grid.Background>
|
|
||||||
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
|
|
||||||
<Button VerticalAlignment="Center" Content="" FontFamily="Segoe MDL2 Assets" Background="Transparent" FontSize="40" Foreground="WhiteSmoke" Name="touchBack10" Click="back10_Click"/>
|
|
||||||
<Button VerticalAlignment="Center" Content="" FontFamily="Segoe MDL2 Assets" Background="Transparent" FontSize="100" Foreground="WhiteSmoke" Name="touchPlay" Click="play_Click"/>
|
|
||||||
<Button VerticalAlignment="Center" Content="" FontFamily="Segoe MDL2 Assets" Background="Transparent" FontSize="40" Foreground="WhiteSmoke" Name="touchFwd30" Click="fwd30_Click"/>
|
|
||||||
</StackPanel>
|
|
||||||
<Button Visibility="Collapsed" Margin="0,32,0,0" VerticalAlignment="Top" HorizontalAlignment="Right" Name="miniViewExit" Grid.Row="1" Click="miniView_Click" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="45" Height="45" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/exitminiview"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Button Visibility="Collapsed" VerticalAlignment="Top" HorizontalAlignment="Right" Name="close" Click="close_Click" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="45" Height="45" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/close"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Button Visibility="Collapsed"
|
|
||||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
|
||||||
Name="maximize" Click="maximize_Click"
|
|
||||||
Background="Transparent" FontFamily="Segoe MDL2 Assets" Foreground="White" FontSize="25"
|
|
||||||
Content=""
|
|
||||||
Width="45" Height="45">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/maximize"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<ProgressBar VerticalAlignment="Bottom" Foreground="Red" Name="seekIndicator" Visibility="Collapsed"/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<StackPanel Name="schedulePanel" Visibility="Collapsed" VerticalAlignment="Bottom" HorizontalAlignment="Right" Grid.Row="1" BorderBrush="Black" BorderThickness="2" Padding="10" Margin="0,25" Orientation="Horizontal">
|
|
||||||
<StackPanel.Background>
|
|
||||||
<AcrylicBrush TintColor="#CC000000" TintOpacity="0.6"/>
|
|
||||||
</StackPanel.Background>
|
|
||||||
<FontIcon Glyph="" FontSize="30" Margin="0,0,10,0" VerticalAlignment="Top"/>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontWeight="Bold" Text="Stream hasn't started yet"/>
|
|
||||||
<TextBlock Name="scheduleHeader" Visibility="Collapsed" Text="Stream schedule:"/>
|
|
||||||
<TextBlock Name="scheduleStart" Visibility="Collapsed" Text="Start time: "/>
|
|
||||||
<TextBlock Name="scheduleEnd" Visibility="Collapsed" Text="End time:"/>
|
|
||||||
|
|
||||||
<TextBlock Name="countdownHeader" Visibility="Collapsed" FontWeight="Bold" Text="Stream will be started in:" Margin="0,10,0,0"/>
|
|
||||||
<TextBlock Name="countdown" Visibility="Collapsed" FontWeight="SemiBold" FontSize="20" Text="1:00:00:00"/>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Grid Grid.Row="2" Height="50" Name="mainControls">
|
|
||||||
<Grid.Background>
|
|
||||||
<AcrylicBrush TintColor="#CC000000" TintOpacity="0.6"/>
|
|
||||||
</Grid.Background>
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition/>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<Button Click="play_Click" Name="play" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/play"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Button Name="next" Click="next_Click" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/next"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Button Name="openVolume" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/volume"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
<Button.Flyout>
|
|
||||||
<Flyout>
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="-10">
|
|
||||||
<Button Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25" Name="muteBtn" Click="muteBtn_Click">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/mute"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Slider Orientation="Horizontal" Width="150" Margin="10,5,10,0" VerticalAlignment="Center" Name="volume" ValueChanged="volume_ValueChanged"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Flyout>
|
|
||||||
</Button.Flyout>
|
|
||||||
</Button>
|
|
||||||
<Button Name="gotoLive" Visibility="Collapsed" Click="GotoLive_Click" Background="Transparent" Foreground="White" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/goLive"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<FontIcon Glyph="" Foreground="Red"/>
|
|
||||||
<TextBlock Text="Live" Margin="5,0,0,0" FontSize="15" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Grid Grid.Column="1" Name="seekPanel">
|
|
||||||
<TextBlock Name="elapsedTime" Foreground="White" Text="[Elapsed]" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
|
|
||||||
<TextBlock Name="remainingTime" Foreground="White" Text="[Remaining]" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
|
|
||||||
<Grid VerticalAlignment="Top" Margin="0,15,0,0" Height="2">
|
|
||||||
<ProgressBar Background="#66FFFFFF" Foreground="LightGray" Name="bufferingLevel"/>
|
|
||||||
</Grid>
|
|
||||||
<Slider PointerCaptureLost="seek_PointerCaptureLost" ManipulationStarted="Seek_PointerCaptured" ManipulationMode="TranslateRailsX" ValueChanged="seek_ValueChanged" Name="seek" VerticalAlignment="Top" IsThumbToolTipEnabled="False" Background="Transparent" HorizontalAlignment="Stretch"/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
|
||||||
<TextBlock Text="1:12:32" Name="liveElapsed" Visibility="Collapsed" Margin="10,0" FontSize="20" VerticalAlignment="Center">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/streamElapsed"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</TextBlock>
|
|
||||||
<StackPanel Orientation="Horizontal" Name="rewindPanel">
|
|
||||||
<Button Click="back10_Click" Name="back10" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/back"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
<Button Click="fwd30_Click" Name="fwd30" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/fwd"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Line Stroke="White" StrokeThickness="2" Y1="5" Y2="45"/>
|
|
||||||
|
|
||||||
<Button Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25" Name="captionsBtn">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/subs"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
<Button.Flyout>
|
|
||||||
<Flyout>
|
|
||||||
<StackPanel Width="225">
|
|
||||||
<ToggleSwitch x:Uid="/VideoPage/subsSwitch" Name="subsSwitch" Toggled="subsSwitch_Toggled" OnContent="Subtitles" OffContent="Subtitles"/>
|
|
||||||
<ComboBox x:Uid="/VideoPage/subsSelector" Name="subsLang" Header="Language" PlaceholderText="No subtitles are available" Visibility="Collapsed" HorizontalAlignment="Stretch" SelectionChanged="subsLang_SelectionChanged"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Flyout>
|
|
||||||
</Button.Flyout>
|
|
||||||
</Button>
|
|
||||||
<Button Name="qualityBtn" Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/quality"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
<Button.Flyout>
|
|
||||||
<Flyout>
|
|
||||||
<ComboBox x:Uid="/VideoPage/qualitySelector" Width="225" Header="Quality" Name="quality" SelectionChanged="quality_SelectionChanged"/>
|
|
||||||
</Flyout>
|
|
||||||
</Button.Flyout>
|
|
||||||
</Button>
|
|
||||||
<Button Background="Transparent" FontFamily="Segoe MDL2 Assets" Content="" Foreground="White" Width="50" Height="50" FontSize="25" Name="fullscreen" Click="fullscreen_Click">
|
|
||||||
<ToolTipService.ToolTip>
|
|
||||||
<TextBlock x:Uid="/VideoPage/fullscreen"/>
|
|
||||||
</ToolTipService.ToolTip>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid Name="matureBlock" Visibility="Collapsed" Background="#FF333333" Padding="25">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
<RowDefinition/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Warning! Mature content!" Foreground="White" FontSize="30"/>
|
|
||||||
<Line Stroke="White" StrokeThickness="2" X1="0" X2="350"/>
|
|
||||||
</StackPanel>
|
|
||||||
<Grid Name="proceedMature" Visibility="Collapsed" Grid.Row="1">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
<RowDefinition/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock TextWrapping="WrapWholeWords" Foreground="White" Text="This content isn't advised for children. It can represent violance, blood or sexual scenes." FontSize="20"/>
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="1" VerticalAlignment="Bottom">
|
|
||||||
<CheckBox Foreground="White" Name="matureDisable">
|
|
||||||
<TextBlock Foreground="White" Text="Don't show me it again"/>
|
|
||||||
</CheckBox>
|
|
||||||
<Button Content="Continue" Name="matureDismiss" Click="matureDismiss_Click" Margin="5,0,0,0" Foreground="White" Background="Gray"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
<Grid BorderBrush="OrangeRed" BorderThickness="5" Margin="0,10,0,0" Visibility="Collapsed" Name="signReq" Grid.Row="1" VerticalAlignment="Top">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" FontSize="40" Foreground="OrangeRed" Margin="5"/>
|
|
||||||
<StackPanel HorizontalAlignment="Stretch" Grid.Column="1">
|
|
||||||
<TextBlock Text="Sign in into your account to continue" Foreground="OrangeRed" FontWeight="Bold" FontSize="20"/>
|
|
||||||
<TextBlock Text="To watch this video you have to confirm your age by sign in into your account which contains your age" Foreground="OrangeRed"/>
|
|
||||||
</StackPanel>
|
|
||||||
<Button Name="signin" Click="signin_Click" Content="Sign in now" Foreground="White" Background="Gray" HorizontalAlignment="Right" Grid.Column="1" Margin="0,0,10,0"/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,824 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Windows.Foundation;
|
|
||||||
using Windows.UI.Core;
|
|
||||||
using Windows.UI.Xaml;
|
|
||||||
using Windows.UI.Xaml.Controls;
|
|
||||||
using Windows.UI.Xaml.Controls.Primitives;
|
|
||||||
using Windows.UI.Xaml.Input;
|
|
||||||
using Google.Apis.YouTube.v3.Data;
|
|
||||||
using Windows.UI.Xaml.Media.Imaging;
|
|
||||||
using Windows.Media;
|
|
||||||
using Windows.Storage.Streams;
|
|
||||||
using Windows.UI.ViewManagement;
|
|
||||||
using System.Xml;
|
|
||||||
using Windows.ApplicationModel.Core;
|
|
||||||
using Windows.UI;
|
|
||||||
using Windows.Media.Casting;
|
|
||||||
using YoutubeExplode.Models.MediaStreams;
|
|
||||||
using YoutubeExplode;
|
|
||||||
using YoutubeExplode.Models.ClosedCaptions;
|
|
||||||
using System.Globalization;
|
|
||||||
using FoxTube.Controls;
|
|
||||||
using Windows.System;
|
|
||||||
using Windows.Media.Core;
|
|
||||||
using Windows.Media.Playback;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace FoxTube
|
|
||||||
{
|
|
||||||
public enum PlayerLayout { Normal, Fullscreen, Minimized }
|
|
||||||
|
|
||||||
public sealed partial class VideoPlayer : UserControl
|
|
||||||
{
|
|
||||||
public string videoId;
|
|
||||||
public Video item;
|
|
||||||
public string avatar;
|
|
||||||
public bool incognito = false;
|
|
||||||
|
|
||||||
public PlayerLayout layout = PlayerLayout.Normal;
|
|
||||||
public bool pointerCaptured = false;
|
|
||||||
bool seekCaptured = false;
|
|
||||||
Point cursorBackup;
|
|
||||||
|
|
||||||
public event ObjectEventHandler SetFullSize;
|
|
||||||
public event Event NextClicked;
|
|
||||||
public Button Next => next;
|
|
||||||
|
|
||||||
public TimeSpan Elapsed { get; set; } = TimeSpan.FromMilliseconds(0);
|
|
||||||
public TimeSpan Remaining => Total.Subtract(Elapsed);
|
|
||||||
public TimeSpan Total { get; set; }
|
|
||||||
|
|
||||||
double timecodeBackup = 0;
|
|
||||||
bool needUpdateTimecode = false;
|
|
||||||
|
|
||||||
SystemMediaTransportControls systemControls;
|
|
||||||
|
|
||||||
IReadOnlyList<ClosedCaptionTrackInfo> ccInfo;
|
|
||||||
MediaStreamInfoSet streamInfo;
|
|
||||||
|
|
||||||
readonly DispatcherTimer timer = new DispatcherTimer()
|
|
||||||
{
|
|
||||||
Interval = TimeSpan.FromSeconds(1)
|
|
||||||
};
|
|
||||||
DispatcherTimer ctrlsFadeTimer = null;
|
|
||||||
|
|
||||||
LiveCaptions captions;
|
|
||||||
|
|
||||||
MediaPlayer videoPlayer;
|
|
||||||
MediaPlayer audioPlayer;
|
|
||||||
MediaTimelineController controller;
|
|
||||||
|
|
||||||
public VideoPlayer()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize(Video meta, string channelAvatar, bool privateMode = false)
|
|
||||||
{
|
|
||||||
Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
item = meta;
|
|
||||||
avatar = channelAvatar;
|
|
||||||
videoId = item.Id;
|
|
||||||
incognito = privateMode;
|
|
||||||
|
|
||||||
if (item.ContentDetails.ContentRating != null)
|
|
||||||
{
|
|
||||||
if (SecretsVault.IsAuthorized)
|
|
||||||
{
|
|
||||||
if (SettingsStorage.Mature == MatureState.AllowedOnce)
|
|
||||||
SettingsStorage.Mature = MatureState.Blocked;
|
|
||||||
else if (SettingsStorage.Mature == MatureState.Blocked)
|
|
||||||
{
|
|
||||||
Visibility = Visibility.Visible;
|
|
||||||
proceedMature.Visibility = Visibility.Visible;
|
|
||||||
matureBlock.Visibility = Visibility.Visible;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Visibility = Visibility.Visible;
|
|
||||||
signReq.Visibility = Visibility.Visible;
|
|
||||||
matureBlock.Visibility = Visibility.Visible;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.Snippet.LiveBroadcastContent == "none")
|
|
||||||
LoadVideo();
|
|
||||||
else if (item.Snippet.LiveBroadcastContent == "live")
|
|
||||||
LoadStream();
|
|
||||||
else
|
|
||||||
LoadUpcoming();
|
|
||||||
|
|
||||||
if (!incognito)
|
|
||||||
Debug.WriteLine("TODO: history entry creation");
|
|
||||||
// TODO: Create history entry
|
|
||||||
|
|
||||||
Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeContols()
|
|
||||||
{
|
|
||||||
videoPlayer = new MediaPlayer();
|
|
||||||
controller = new MediaTimelineController();
|
|
||||||
|
|
||||||
videoPlayer.TimelineController = controller;
|
|
||||||
videoSource.SetMediaPlayer(videoPlayer);
|
|
||||||
videoPlayer.MediaOpened += async (s, e) =>
|
|
||||||
{
|
|
||||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
|
||||||
{
|
|
||||||
if (controller.State == MediaTimelineControllerState.Running && needUpdateTimecode)
|
|
||||||
{
|
|
||||||
controller.Position = TimeSpan.FromSeconds(timecodeBackup);
|
|
||||||
needUpdateTimecode = false;
|
|
||||||
controller.Resume();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
seek.IsEnabled = true;
|
|
||||||
play.IsEnabled = true;
|
|
||||||
touchPlay.IsEnabled = true;
|
|
||||||
|
|
||||||
if (SettingsStorage.Autoplay)
|
|
||||||
controller.Resume();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
volume.Value = SettingsStorage.Volume;
|
|
||||||
|
|
||||||
if (!ApplicationView.GetForCurrentView().IsViewModeSupported(ApplicationViewMode.CompactOverlay))
|
|
||||||
miniViewBtn.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
controller.StateChanged += CurrentStateChanged;
|
|
||||||
controller.Ended += (s, arg) =>
|
|
||||||
{
|
|
||||||
seek.Value = seek.Maximum;
|
|
||||||
seekIndicator.Value = seekIndicator.Maximum;
|
|
||||||
};
|
|
||||||
|
|
||||||
#region System Media Transport Controls
|
|
||||||
systemControls = SystemMediaTransportControls.GetForCurrentView();
|
|
||||||
systemControls.IsNextEnabled = true;
|
|
||||||
systemControls.IsPauseEnabled = true;
|
|
||||||
systemControls.IsPlayEnabled = 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
|
|
||||||
|
|
||||||
videoSource.PosterSource = new BitmapImage((item.Snippet.Thumbnails.Maxres ?? item.Snippet.Thumbnails.Medium).Url.ToUri());
|
|
||||||
title.Text = item.Snippet.Title;
|
|
||||||
channelName.Text = item.Snippet.ChannelTitle;
|
|
||||||
|
|
||||||
ctrlsFadeTimer = new DispatcherTimer
|
|
||||||
{
|
|
||||||
Interval = TimeSpan.FromSeconds(5)
|
|
||||||
};
|
|
||||||
ctrlsFadeTimer.Tick += ControlsFade;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadUpcoming()
|
|
||||||
{
|
|
||||||
schedulePanel.Visibility = Visibility.Visible;
|
|
||||||
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
|
|
||||||
{
|
|
||||||
scheduleHeader.Visibility = Visibility.Visible;
|
|
||||||
scheduleStart.Visibility = Visibility.Visible;
|
|
||||||
countdownHeader.Visibility = Visibility.Visible;
|
|
||||||
countdown.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
scheduleStart.Text = $"Start time: {item.LiveStreamingDetails.ScheduledStartTime.Value}";
|
|
||||||
timer.Tick += UpdateCountdown;
|
|
||||||
timer.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
|
||||||
{
|
|
||||||
scheduleHeader.Visibility = Visibility.Visible;
|
|
||||||
scheduleEnd.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
scheduleEnd.Text = $"End time: {item.LiveStreamingDetails.ScheduledEndTime.Value}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void LoadStream()
|
|
||||||
{
|
|
||||||
InitializeContols();
|
|
||||||
seekPanel.Visibility = Visibility.Collapsed;
|
|
||||||
rewindPanel.Visibility = Visibility.Collapsed;
|
|
||||||
captionsBtn.Visibility = Visibility.Collapsed;
|
|
||||||
qualityBtn.Visibility = Visibility.Collapsed;
|
|
||||||
gotoLive.Visibility = Visibility.Visible;
|
|
||||||
liveElapsed.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
timer.Tick += UpdateLive;
|
|
||||||
timer.Start();
|
|
||||||
|
|
||||||
streamInfo = await new YoutubeClient().GetVideoMediaStreamInfosAsync(videoId);
|
|
||||||
videoPlayer.Source = MediaSource.CreateFromUri(streamInfo.HlsLiveStreamUrl.ToUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void LoadVideo()
|
|
||||||
{
|
|
||||||
InitializeContols();
|
|
||||||
captions = grid.Children[2] as LiveCaptions;
|
|
||||||
captions.Player = controller;
|
|
||||||
controller.PositionChanged += UpdateSeek;
|
|
||||||
|
|
||||||
Total = XmlConvert.ToTimeSpan(item.ContentDetails.Duration);
|
|
||||||
seek.Maximum = Total.TotalMilliseconds;
|
|
||||||
seekIndicator.Maximum = Total.TotalMilliseconds;
|
|
||||||
|
|
||||||
elapsedTime.Text = Elapsed.Hours > 0 ? $"{Elapsed:hh\\:mm\\:ss}" : $"{Elapsed:mm\\:ss}";
|
|
||||||
remainingTime.Text = Remaining.Hours > 0 ? $"{Remaining:hh\\:mm\\:ss}" : $"{Remaining:mm\\:ss}";
|
|
||||||
|
|
||||||
#region Retrieving info for CC and Media streams
|
|
||||||
//Loading streams
|
|
||||||
streamInfo = await new YoutubeClient().GetVideoMediaStreamInfosAsync(videoId);
|
|
||||||
|
|
||||||
List<VideoQuality> q = streamInfo.GetAllVideoQualities().ToList();
|
|
||||||
q.Sort();
|
|
||||||
q.Reverse();
|
|
||||||
foreach (VideoQuality i in q)
|
|
||||||
quality.Items.Add(new ComboBoxItem() { Content = i.GetVideoQualityLabel() });
|
|
||||||
|
|
||||||
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
|
|
||||||
|
|
||||||
quality.SelectedItem = quality.Items.ToList().Exists(x => (x as ComboBoxItem).Content as string == s) ? quality.Items.Find(x => (x as ComboBoxItem).Content as string == s) : quality.Items.First();
|
|
||||||
|
|
||||||
//Loading captions
|
|
||||||
ccInfo = await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(videoId);
|
|
||||||
if (ccInfo.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (ClosedCaptionTrackInfo cc in ccInfo)
|
|
||||||
{
|
|
||||||
subsLang.Items.Add(new ComboBoxItem()
|
|
||||||
{
|
|
||||||
Content = string.Format("{0}{1}", CultureInfo.GetCultureInfo(cc.Language.Code).DisplayName, cc.IsAutoGenerated ? " (Auto-generated)" : ""),
|
|
||||||
Tag = cc
|
|
||||||
});
|
|
||||||
|
|
||||||
if (SettingsStorage.RelevanceLanguage.Contains(cc.Language.Code))
|
|
||||||
subsLang.SelectedItem = subsLang.Items.Last();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subsLang.SelectedItem == null)
|
|
||||||
if(ccInfo.ToList().Exists(i => i.Language.Code == "en"))
|
|
||||||
subsLang.SelectedItem = subsLang.Items.Find(i => (((ComboBoxItem)i).Tag as ClosedCaptionTrackInfo).Language.Code == "en");
|
|
||||||
else
|
|
||||||
subsLang.SelectedIndex = 0;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
captionsBtn.Visibility = Visibility.Collapsed;
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateCountdown(object sender, object e)
|
|
||||||
{
|
|
||||||
countdown.Text = $"{item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now:hh\\:mm\\:ss}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateLive(object sender, object e)
|
|
||||||
{
|
|
||||||
liveElapsed.Text = $"{DateTime.Now - item.LiveStreamingDetails.ActualStartTime:hh\\:mm\\:ss}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void UpdateSeek(MediaTimelineController sender, object e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
|
||||||
{
|
|
||||||
bufferingLevel.Value = videoPlayer.PlaybackSession.DownloadProgress * 100;
|
|
||||||
if (seekCaptured)
|
|
||||||
return;
|
|
||||||
if(needUpdateTimecode)
|
|
||||||
if (controller.State == MediaTimelineControllerState.Running)
|
|
||||||
needUpdateTimecode = false;
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
seek.Value = controller.Position.TotalMilliseconds;
|
|
||||||
seekIndicator.Value = seek.Value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
|
|
||||||
{
|
|
||||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
|
||||||
{
|
|
||||||
switch (args.Button)
|
|
||||||
{
|
|
||||||
case SystemMediaTransportControlsButton.Pause:
|
|
||||||
controller.Pause();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SystemMediaTransportControlsButton.Play:
|
|
||||||
controller.Resume();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SystemMediaTransportControlsButton.Next:
|
|
||||||
NextClicked?.Invoke();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControlsFade(object sender, object e)
|
|
||||||
{
|
|
||||||
if (seekCaptured)
|
|
||||||
return;
|
|
||||||
|
|
||||||
controls.Opacity = 0;
|
|
||||||
//controls.Visibility = Visibility.Collapsed;
|
|
||||||
if (layout != PlayerLayout.Minimized)
|
|
||||||
touchCentral.Visibility = Visibility.Collapsed;
|
|
||||||
if (pointerCaptured)
|
|
||||||
Window.Current.CoreWindow.PointerCursor = null;
|
|
||||||
seekIndicator.Visibility = Visibility.Collapsed;
|
|
||||||
ctrlsFadeTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateSize()
|
|
||||||
{
|
|
||||||
if(layout != PlayerLayout.Normal)
|
|
||||||
Height = Window.Current.Bounds.Height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowControls()
|
|
||||||
{
|
|
||||||
if (ctrlsFadeTimer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
controls.Opacity = 1;
|
|
||||||
//controls.Visibility = Visibility.Visible;
|
|
||||||
Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0);
|
|
||||||
ctrlsFadeTimer.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void volume_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
|
|
||||||
{
|
|
||||||
double v = volume.Value;
|
|
||||||
if (v == 0)
|
|
||||||
muteBtn.Content = openVolume.Content = "\xE74F";
|
|
||||||
else if (v <= 25 && v > 0)
|
|
||||||
muteBtn.Content = openVolume.Content = "\xE992";
|
|
||||||
else if (v <= 50 && v > 25)
|
|
||||||
muteBtn.Content = openVolume.Content = "\xE993";
|
|
||||||
else if (v <= 75 && v > 50)
|
|
||||||
muteBtn.Content = openVolume.Content = "\xE994";
|
|
||||||
else if (v > 75)
|
|
||||||
muteBtn.Content = openVolume.Content = "\xE995";
|
|
||||||
|
|
||||||
SettingsStorage.Volume = (int) volume.Value;
|
|
||||||
|
|
||||||
if(audioPlayer != null)
|
|
||||||
audioPlayer.Volume = volume.Value * .01;
|
|
||||||
videoPlayer.Volume = volume.Value * .01;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void muteBtn_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (volume.Value != 0)
|
|
||||||
{
|
|
||||||
int v = SettingsStorage.Volume;
|
|
||||||
volume.Value = 0;
|
|
||||||
SettingsStorage.Volume = v;
|
|
||||||
}
|
|
||||||
else volume.Value = SettingsStorage.Volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserControl_PointerMoved(object sender, PointerRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (ctrlsFadeTimer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
else if (cursorBackup != Window.Current.CoreWindow.PointerPosition)
|
|
||||||
ShowControls();
|
|
||||||
|
|
||||||
cursorBackup = Window.Current.CoreWindow.PointerPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserControl_PointerExited(object sender, PointerRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse && ctrlsFadeTimer != null)
|
|
||||||
{
|
|
||||||
pointerCaptured = false;
|
|
||||||
ControlsFade(this, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserControl_PointerEntered(object sender, PointerRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse && ctrlsFadeTimer != null)
|
|
||||||
pointerCaptured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void playPauseArea_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
switch (layout)
|
|
||||||
{
|
|
||||||
case PlayerLayout.Minimized:
|
|
||||||
if (ApplicationView.GetForCurrentView().ViewMode == ApplicationViewMode.CompactOverlay)
|
|
||||||
miniView_Click(this, null);
|
|
||||||
else if (ApplicationView.GetForCurrentView().ViewMode == ApplicationViewMode.Default)
|
|
||||||
maximize_Click(this, null);
|
|
||||||
break;
|
|
||||||
case PlayerLayout.Fullscreen:
|
|
||||||
case PlayerLayout.Normal:
|
|
||||||
fullscreen_Click(this, null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void playPauseArea_Tapped(object sender, TappedRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse && layout != PlayerLayout.Minimized)
|
|
||||||
play_Click(this, null);
|
|
||||||
UserControl_Tapped(sender, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserControl_Tapped(object sender, TappedRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch && ctrlsFadeTimer != null)
|
|
||||||
{
|
|
||||||
touchCentral.Visibility = Visibility.Visible;
|
|
||||||
if (ctrlsFadeTimer.IsEnabled)
|
|
||||||
ControlsFade(this, null);
|
|
||||||
else
|
|
||||||
ShowControls();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void quality_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SettingsStorage.RememberedQuality = ((ComboBoxItem)quality.SelectedItem).Content.ToString();
|
|
||||||
|
|
||||||
controller.Pause();
|
|
||||||
|
|
||||||
timecodeBackup = controller.Position.TotalSeconds;
|
|
||||||
needUpdateTimecode = true;
|
|
||||||
|
|
||||||
if (streamInfo.Muxed.ToList().Exists(x => x.VideoQualityLabel == (quality.SelectedItem as ComboBoxItem).Content.ToString()))
|
|
||||||
{
|
|
||||||
videoPlayer.Source = MediaSource.CreateFromUri(streamInfo.Muxed.Find(x => x.VideoQualityLabel == (quality.SelectedItem as ComboBoxItem).Content as string).Url.ToUri());
|
|
||||||
audioPlayer = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (audioPlayer == null)
|
|
||||||
{
|
|
||||||
audioPlayer = new MediaPlayer
|
|
||||||
{
|
|
||||||
TimelineController = controller,
|
|
||||||
Volume = volume.Value * .01
|
|
||||||
};
|
|
||||||
audioSource.SetMediaPlayer(audioPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoStreamInfo videoInfo = streamInfo.Video.Find(i => i.VideoQualityLabel == (quality.SelectedItem as ComboBoxItem).Content.ToString());
|
|
||||||
AudioStreamInfo audioInfo = streamInfo.Audio.First();
|
|
||||||
|
|
||||||
videoPlayer.Source = MediaSource.CreateFromUri(videoInfo.Url.ToUri());
|
|
||||||
audioPlayer.Source = MediaSource.CreateFromUri(audioInfo.Url.ToUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.Resume();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void subsSwitch_Toggled(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (subsSwitch.IsOn)
|
|
||||||
subsLang.Visibility = Visibility.Visible;
|
|
||||||
else
|
|
||||||
subsLang.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
LoadTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadTrack()
|
|
||||||
{
|
|
||||||
if (subsSwitch.IsOn)
|
|
||||||
captions.Initialize((subsLang.SelectedItem as ComboBoxItem).Tag as ClosedCaptionTrackInfo);
|
|
||||||
else
|
|
||||||
captions?.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fullscreen_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
bool fullScreen = layout == PlayerLayout.Fullscreen ? false : true;
|
|
||||||
SetFullSize.Invoke(this, fullScreen);
|
|
||||||
Methods.MainPage.Fullscreen(fullScreen);
|
|
||||||
|
|
||||||
if(fullScreen)
|
|
||||||
{
|
|
||||||
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
|
|
||||||
fullscreen.Content = "\xE1D8";
|
|
||||||
layout = PlayerLayout.Fullscreen;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ApplicationView.GetForCurrentView().ExitFullScreenMode();
|
|
||||||
fullscreen.Content = "\xE1D9";
|
|
||||||
layout = PlayerLayout.Normal;
|
|
||||||
Height = double.NaN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void play_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (controller.State == MediaTimelineControllerState.Running)
|
|
||||||
controller.Pause();
|
|
||||||
else if (controller.State == MediaTimelineControllerState.Paused)
|
|
||||||
controller.Resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void CurrentStateChanged(MediaTimelineController sender, object e)
|
|
||||||
{
|
|
||||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
|
||||||
{
|
|
||||||
needUpdateTimecode = false;
|
|
||||||
switch (sender.State)
|
|
||||||
{
|
|
||||||
case MediaTimelineControllerState.Paused:
|
|
||||||
play.Content = "\xE102";
|
|
||||||
touchPlay.Content = "\xE102";
|
|
||||||
|
|
||||||
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MediaTimelineControllerState.Running:
|
|
||||||
play.Content = "\xE103";
|
|
||||||
touchPlay.Content = "\xE103";
|
|
||||||
|
|
||||||
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
systemControls.PlaybackStatus = MediaPlaybackStatus.Closed;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//SecretsVault.HistoryAdd(videoId, elapsed, total);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void miniView_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
|
||||||
bool MiniView = layout == PlayerLayout.Minimized ? false : true;
|
|
||||||
SetFullSize(this, MiniView);
|
|
||||||
|
|
||||||
if (MiniView)
|
|
||||||
{
|
|
||||||
if (layout == PlayerLayout.Fullscreen)
|
|
||||||
fullscreen.Content = "\xE740";
|
|
||||||
|
|
||||||
await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.CompactOverlay);
|
|
||||||
pointerCaptured = false;
|
|
||||||
|
|
||||||
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
|
||||||
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
|
||||||
|
|
||||||
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
|
|
||||||
|
|
||||||
mainControls.Visibility = Visibility.Collapsed;
|
|
||||||
header.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
touchCentral.Visibility = Visibility.Visible;
|
|
||||||
miniViewExit.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
touchBack10.FontSize = touchFwd30.FontSize = 20;
|
|
||||||
touchPlay.FontSize = 50;
|
|
||||||
Methods.MainPage.Fullscreen(true);
|
|
||||||
|
|
||||||
layout = PlayerLayout.Minimized;
|
|
||||||
|
|
||||||
if(captions != null)
|
|
||||||
captions.Size = 15;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.Default);
|
|
||||||
|
|
||||||
mainControls.Visibility = Visibility.Visible;
|
|
||||||
header.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
touchCentral.Visibility = Visibility.Collapsed;
|
|
||||||
miniViewExit.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
touchBack10.FontSize = touchFwd30.FontSize = 40;
|
|
||||||
touchPlay.FontSize = 100;
|
|
||||||
Methods.MainPage.Fullscreen(false);
|
|
||||||
|
|
||||||
Height = double.NaN;
|
|
||||||
|
|
||||||
layout = PlayerLayout.Normal;
|
|
||||||
|
|
||||||
if(captions != null)
|
|
||||||
captions.Size = 24;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void minimize_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (layout == PlayerLayout.Fullscreen)
|
|
||||||
{
|
|
||||||
ApplicationView.GetForCurrentView().ExitFullScreenMode();
|
|
||||||
fullscreen.Content = "\xE740";
|
|
||||||
layout = PlayerLayout.Normal;
|
|
||||||
Methods.MainPage.Fullscreen(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SetFullSize?.Invoke(this, true);
|
|
||||||
|
|
||||||
Width = 432;
|
|
||||||
Height = 243;
|
|
||||||
|
|
||||||
layout = PlayerLayout.Minimized;
|
|
||||||
Methods.MainPage.MinimizeVideo();
|
|
||||||
|
|
||||||
mainControls.Visibility = Visibility.Collapsed;
|
|
||||||
header.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
touchCentral.Visibility = Visibility.Visible;
|
|
||||||
maximize.Visibility = Visibility.Visible;
|
|
||||||
close.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
touchBack10.FontSize = touchFwd30.FontSize = 20;
|
|
||||||
touchPlay.FontSize = 50;
|
|
||||||
|
|
||||||
if (captions != null)
|
|
||||||
captions.Size = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maximize_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
SetFullSize?.Invoke(this, false);
|
|
||||||
|
|
||||||
Width = double.NaN;
|
|
||||||
Height = double.NaN;
|
|
||||||
|
|
||||||
layout = PlayerLayout.Normal;
|
|
||||||
Methods.MainPage.MaximizeVideo();
|
|
||||||
|
|
||||||
mainControls.Visibility = Visibility.Visible;
|
|
||||||
header.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
touchCentral.Visibility = Visibility.Collapsed;
|
|
||||||
maximize.Visibility = Visibility.Collapsed;
|
|
||||||
close.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
touchBack10.FontSize = touchFwd30.FontSize = 40;
|
|
||||||
touchPlay.FontSize = 100;
|
|
||||||
|
|
||||||
if (captions != null)
|
|
||||||
captions.Size = 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void seek_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
|
|
||||||
{
|
|
||||||
Elapsed = TimeSpan.FromMilliseconds(seek.Value);
|
|
||||||
|
|
||||||
elapsedTime.Text = Elapsed.Hours > 0 ? $"{Elapsed:hh\\:mm\\:ss}" : $"{Elapsed:mm\\:ss}";
|
|
||||||
remainingTime.Text = Remaining.Hours > 0 ? $"{Remaining:hh\\:mm\\:ss}" : $"{Remaining:mm\\:ss}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void seek_PointerCaptureLost(object sender, PointerRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
seekCaptured = false;
|
|
||||||
needUpdateTimecode = true;
|
|
||||||
controller.Position = Elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fwd30_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if(Remaining.TotalSeconds >= 30)
|
|
||||||
controller.Position = Elapsed.Add(TimeSpan.FromSeconds(30));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void back10_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (Elapsed.TotalSeconds >= 10)
|
|
||||||
controller.Position = Elapsed.Subtract(TimeSpan.FromSeconds(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void next_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
NextClicked?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void matureDismiss_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if ((bool)matureDisable.IsChecked)
|
|
||||||
SettingsStorage.Mature = MatureState.Allowed;
|
|
||||||
else
|
|
||||||
SettingsStorage.Mature = MatureState.AllowedOnce;
|
|
||||||
Methods.MainPage.GoToVideo(videoId, Methods.MainPage.GetPlaylist());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void signin_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
SecretsVault.Authorize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pause()
|
|
||||||
{
|
|
||||||
controller.Pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
systemControls.IsEnabled = false;
|
|
||||||
pointerCaptured = false;
|
|
||||||
|
|
||||||
controller.Pause();
|
|
||||||
|
|
||||||
ctrlsFadeTimer?.Stop();
|
|
||||||
timer?.Stop();
|
|
||||||
|
|
||||||
Methods.MainPage.CloseVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cast_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (videoPlayer.Source == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
controller.Pause();
|
|
||||||
CastingDevicePicker picker = new CastingDevicePicker();
|
|
||||||
picker.Filter.SupportsVideo = true;
|
|
||||||
picker.CastingDeviceSelected += async (s, args) =>
|
|
||||||
{
|
|
||||||
CastingConnection connection = args.SelectedCastingDevice.CreateCastingConnection();
|
|
||||||
await connection.RequestStartCastingAsync(videoPlayer.GetAsCastingSource());
|
|
||||||
};
|
|
||||||
|
|
||||||
Point positinon = cast.TransformToVisual(Window.Current.Content).TransformPoint(new Point(0, 0));
|
|
||||||
|
|
||||||
picker.Show(new Rect(positinon.X, positinon.Y, cast.ActualWidth, cast.ActualHeight), Windows.UI.Popups.Placement.Below);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void KeyUpPressed(object sender, KeyRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
switch(e.Key)
|
|
||||||
{
|
|
||||||
case VirtualKey.Escape:
|
|
||||||
if (layout == PlayerLayout.Fullscreen)
|
|
||||||
fullscreen_Click(this, null);
|
|
||||||
break;
|
|
||||||
case VirtualKey.F11:
|
|
||||||
fullscreen_Click(this, null);
|
|
||||||
break;
|
|
||||||
case VirtualKey.Space:
|
|
||||||
play_Click(this, null);
|
|
||||||
break;
|
|
||||||
case VirtualKey.Left:
|
|
||||||
back10_Click(this, null);
|
|
||||||
break;
|
|
||||||
case VirtualKey.Right:
|
|
||||||
fwd30_Click(this, null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void subsLang_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
LoadTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GotoLive_Click(object sender, RoutedEventArgs e) //TODO: Refactor
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MediaTimeRange range = videoPlayer.PlaybackSession.GetSeekableRanges().Last();
|
|
||||||
controller.Position = range.End.Subtract(TimeSpan.FromMilliseconds(100));
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Seek_PointerCaptured(object sender, ManipulationStartedRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
seekCaptured = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -103,7 +103,6 @@
|
|||||||
<Compile Include="App.xaml.cs">
|
<Compile Include="App.xaml.cs">
|
||||||
<DependentUpon>App.xaml</DependentUpon>
|
<DependentUpon>App.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Classes\DownloadItemContainer.cs" />
|
|
||||||
<Compile Include="Classes\InboxItem.cs" />
|
<Compile Include="Classes\InboxItem.cs" />
|
||||||
<Compile Include="Classes\Methods.cs" />
|
<Compile Include="Classes\Methods.cs" />
|
||||||
<Compile Include="Classes\SearchPaameters.cs" />
|
<Compile Include="Classes\SearchPaameters.cs" />
|
||||||
@@ -130,6 +129,7 @@
|
|||||||
<Compile Include="Controls\LiveCaptions.xaml.cs">
|
<Compile Include="Controls\LiveCaptions.xaml.cs">
|
||||||
<DependentUpon>LiveCaptions.xaml</DependentUpon>
|
<DependentUpon>LiveCaptions.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Controls\Player\PlayerControls.cs" />
|
||||||
<Compile Include="Controls\PlaylistCard.xaml.cs">
|
<Compile Include="Controls\PlaylistCard.xaml.cs">
|
||||||
<DependentUpon>PlaylistCard.xaml</DependentUpon>
|
<DependentUpon>PlaylistCard.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
<Compile Include="Pages\VideoGrid.xaml.cs">
|
<Compile Include="Pages\VideoGrid.xaml.cs">
|
||||||
<DependentUpon>VideoGrid.xaml</DependentUpon>
|
<DependentUpon>VideoGrid.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Controls\VideoPlayer.xaml.cs">
|
<Compile Include="Controls\Player\VideoPlayer.xaml.cs">
|
||||||
<DependentUpon>VideoPlayer.xaml</DependentUpon>
|
<DependentUpon>VideoPlayer.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -252,6 +252,7 @@
|
|||||||
<Content Include="Assets\StoreLogo.scale-150.png" />
|
<Content Include="Assets\StoreLogo.scale-150.png" />
|
||||||
<Content Include="Assets\StoreLogo.scale-200.png" />
|
<Content Include="Assets\StoreLogo.scale-200.png" />
|
||||||
<Content Include="Assets\StoreLogo.scale-400.png" />
|
<Content Include="Assets\StoreLogo.scale-400.png" />
|
||||||
|
<Content Include="Assets\videoPlaceholder.png" />
|
||||||
<Content Include="Assets\videoThumbSample.png" />
|
<Content Include="Assets\videoThumbSample.png" />
|
||||||
<Content Include="Assets\WhatsNewThumb.png" />
|
<Content Include="Assets\WhatsNewThumb.png" />
|
||||||
<Content Include="Assets\Wide310x150Logo.scale-100.png" />
|
<Content Include="Assets\Wide310x150Logo.scale-100.png" />
|
||||||
@@ -381,10 +382,14 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
<Page Include="Controls\VideoPlayer.xaml">
|
<Page Include="Controls\Player\VideoPlayer.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Themes\Generic.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AdaptiveCards.Rendering.Uwp">
|
<PackageReference Include="AdaptiveCards.Rendering.Uwp">
|
||||||
|
|||||||
@@ -27,6 +27,9 @@
|
|||||||
<uap:LockScreen Notification="badgeAndTileText" BadgeLogo="Assets\BadgeLogo.png"/>
|
<uap:LockScreen Notification="badgeAndTileText" BadgeLogo="Assets\BadgeLogo.png"/>
|
||||||
</uap:VisualElements>
|
</uap:VisualElements>
|
||||||
<Extensions>
|
<Extensions>
|
||||||
|
<uap:Extension Category="windows.protocol">
|
||||||
|
<uap:Protocol Name="foxtube"/>
|
||||||
|
</uap:Extension>
|
||||||
<Extension Category="windows.backgroundTasks" EntryPoint="FoxTube.Background.BackgroundProcessor">
|
<Extension Category="windows.backgroundTasks" EntryPoint="FoxTube.Background.BackgroundProcessor">
|
||||||
<BackgroundTasks>
|
<BackgroundTasks>
|
||||||
<Task Type="general" />
|
<Task Type="general" />
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<Page
|
<Page
|
||||||
x:Class="FoxTube.Pages.Browser"
|
x:Class="FoxTube.Pages.Home1"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:FoxTube.Pages"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
@@ -18,8 +16,7 @@
|
|||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<AutoSuggestBox Grid.ColumnSpan="2" Text="https://youtube.com/" QueryIcon="Forward" QuerySubmitted="Adress_QuerySubmitted" Name="adress" Margin="60,0,0,0"/>
|
<AutoSuggestBox Grid.ColumnSpan="2" Text="https://youtube.com/" QueryIcon="Forward" QuerySubmitted="Adress_QuerySubmitted" Name="adress"/>
|
||||||
<Button Content="POST" Click="Button_Click"/>
|
|
||||||
<ScrollViewer Grid.Row="1">
|
<ScrollViewer Grid.Row="1">
|
||||||
<TextBlock TextWrapping="Wrap" Name="code" IsTextSelectionEnabled="True"/>
|
<TextBlock TextWrapping="Wrap" Name="code" IsTextSelectionEnabled="True"/>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
@@ -1,57 +1,25 @@
|
|||||||
using Google.Apis.Http;
|
using Windows.UI.Xaml.Controls;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
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 Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
|
|
||||||
|
|
||||||
namespace FoxTube.Pages
|
namespace FoxTube.Pages
|
||||||
{
|
{
|
||||||
/// <summary>
|
public sealed partial class Home1 : Page
|
||||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
|
||||||
/// </summary>
|
|
||||||
public sealed partial class Browser : Page
|
|
||||||
{
|
{
|
||||||
public Browser()
|
public Home1()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async void Initialize()
|
public async void Initialize()
|
||||||
{
|
{
|
||||||
SecretsVault.HttpClient.DefaultRequestHeaders.Referrer = "https://youtube.com/".ToUri();
|
SecretsVault.HttpClient.DefaultRequestHeaders.Referrer = "https://youtube.com/".ToUri();
|
||||||
string response = await SecretsVault.Service.HttpClient.GetStringAsync(adress.Text);
|
string response = await SecretsVault.Service.HttpClient.GetStringAsync(adress.Text);
|
||||||
code.Text = response;
|
code.Text = response;
|
||||||
view.NavigateToString(response);
|
//view.NavigateToString(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Adress_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
private void Adress_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
||||||
{
|
{
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Button_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
/*HttpClient client = new HttpClient();
|
|
||||||
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", SecretsVault.Credential.Token.AccessToken);
|
|
||||||
//string response = await SecretsVault.Service.HttpClient.Po(adress.Text);
|
|
||||||
code.Text = response;
|
|
||||||
view.NavigateToString(response);*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
@@ -19,55 +19,59 @@
|
|||||||
|
|
||||||
<Pivot SelectedIndex="0" Name="content" IsHeaderItemsCarouselEnabled="False" SelectionChanged="Content_SelectionChanged">
|
<Pivot SelectedIndex="0" Name="content" IsHeaderItemsCarouselEnabled="False" SelectionChanged="Content_SelectionChanged">
|
||||||
<PivotItem x:Uid="/Channel/videos" Header="Videos">
|
<PivotItem x:Uid="/Channel/videos" Header="Videos">
|
||||||
<ScrollViewer>
|
<Grid>
|
||||||
<ScrollViewer.Background>
|
<ParallaxView Source="{x:Bind videoScroll}" VerticalShift="100">
|
||||||
<ImageBrush ImageSource="/Assets/ChannelCoverTemplate.png" x:Name="channelCover" Stretch="Uniform" AlignmentY="Top"/>
|
|
||||||
</ScrollViewer.Background>
|
|
||||||
<StackPanel Name="videos" Background="{ThemeResource AppBarBackgroundThemeBrush}" Margin="0,310,0,0" Visibility="Visible">
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Image Source="/Assets/ChannelCoverTemplate.png" Name="channelCover" Stretch="Uniform" HorizontalAlignment="Left" VerticalAlignment="Top" Opacity="0" ImageOpened="ChannelCover_ImageOpened">
|
||||||
<ColumnDefinition Width="120"/>
|
<Windows10version1809:Image.OpacityTransition>
|
||||||
<ColumnDefinition Width="*"/>
|
<ScalarTransition Duration="0:0:0.5"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
</Windows10version1809:Image.OpacityTransition>
|
||||||
</Grid.ColumnDefinitions>
|
</Image>
|
||||||
<Ellipse HorizontalAlignment="Left" Margin="10,-40,0,0" Fill="Black" Width="100" Height="100"/>
|
|
||||||
<PersonPicture Name="avatar" HorizontalAlignment="Left" Margin="10,-40,0,0"/>
|
|
||||||
<StackPanel Grid.Column="1" Orientation="Vertical" Margin="10,0,0,5">
|
|
||||||
<TextBlock Name="title" FontWeight="SemiBold" FontSize="22" Text="Channel name"/>
|
|
||||||
<TextBlock Name="subscribers" Foreground="Gray" Text="1,000,000 subscribers"/>
|
|
||||||
<TextBlock Name="videosCount" Foreground="Gray" Text="563,000 videos"/>
|
|
||||||
</StackPanel>
|
|
||||||
<TextBlock Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="50" Margin="10" 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.Column="2" VerticalAlignment="Bottom" Margin="10" Name="subscriptionPane" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
|
||||||
<Button x:Uid="/Cards/subscribe" Click="Subscribe_Click" Name="subscribe" Width="250" Height="50" Background="Red" Foreground="White" FontSize="18" FontWeight="SemiBold" Content="Subscirbe"/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<pages:VideoGrid/>
|
</ParallaxView>
|
||||||
<controls:ShowMore Clicked="VideoMore_Clicked"/>
|
<ScrollViewer ViewChanged="ScrollViewer_ViewChanged" Name="videoScroll">
|
||||||
</StackPanel>
|
<StackPanel Background="{ThemeResource AppBarBackgroundThemeBrush}" Margin="0,300,0,0" Visibility="Visible">
|
||||||
</ScrollViewer>
|
<Grid Name="infoPanel">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Ellipse HorizontalAlignment="Left" Margin="10,-40,0,0" Fill="Black" Width="100" Height="1"/>
|
||||||
|
<PersonPicture Name="avatar" HorizontalAlignment="Left" Margin="10,-40,0,0"/>
|
||||||
|
<StackPanel Grid.Column="1" Orientation="Vertical" Margin="10,0,0,5">
|
||||||
|
<TextBlock Name="title" FontWeight="SemiBold" FontSize="22" Text="Channel name"/>
|
||||||
|
<TextBlock Name="subscribers" Foreground="Gray" Text="1,000,000 subscribers"/>
|
||||||
|
<TextBlock Name="videosCount" Foreground="Gray" Text="563,000 videos"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="50" Margin="10" 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.Column="2" VerticalAlignment="Bottom" Margin="10" Name="subscriptionPane" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
<Button x:Uid="/Cards/subscribe" Click="Subscribe_Click" Name="subscribe" Width="250" Height="50" Background="Red" Foreground="White" FontSize="18" FontWeight="SemiBold" Content="Subscirbe"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<pages:VideoGrid x:Name="videoList"/>
|
||||||
|
<controls:ShowMore Clicked="VideoMore_Clicked" x:Name="videoMore"/>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem x:Uid="/Channel/playlists" Header="Playlists">
|
<PivotItem x:Uid="/Channel/playlists" Header="Playlists">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<Grid Name="playlists">
|
<Grid>
|
||||||
<StackPanel Margin="10" Visibility="Visible">
|
<StackPanel Margin="10" Visibility="Visible">
|
||||||
<TextBlock x:Uid="/Channel/playlistTitle" FontSize="28" Text="Playlists"/>
|
<pages:VideoGrid x:Name="playlistList"/>
|
||||||
<pages:VideoGrid/>
|
<controls:ShowMore Clicked="ShowMorePlaylists_Click" x:Name="playlistMore"/>
|
||||||
<controls:ShowMore Clicked="ShowMorePlaylists_Click"/>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<local:LoadingPage Visibility="Collapsed"/>
|
<local:LoadingPage Visibility="Collapsed" x:Name="playlistLoading"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem x:Uid="/Channel/about" Header="About channel">
|
<PivotItem x:Uid="/Channel/about" Header="About channel">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel Margin="10">
|
<TextBlock Name="description" TextWrapping="WrapWholeWords" Margin="10" IsTextSelectionEnabled="True" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum justo erat, dapibus sit amet maximus eget, volutpat non turpis. Suspendisse. "/>
|
||||||
<TextBlock x:Uid="/Channel/aboutTitle" FontSize="28" Text="About this channel"/>
|
|
||||||
<TextBlock Name="description" TextWrapping="WrapWholeWords" IsTextSelectionEnabled="True"/>
|
|
||||||
</StackPanel>
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
|
|
||||||
@@ -77,9 +81,9 @@
|
|||||||
<Windows10version1809:StackPanel.OpacityTransition>
|
<Windows10version1809:StackPanel.OpacityTransition>
|
||||||
<ScalarTransition/>
|
<ScalarTransition/>
|
||||||
</Windows10version1809:StackPanel.OpacityTransition>
|
</Windows10version1809:StackPanel.OpacityTransition>
|
||||||
<PersonPicture Height="32"/>
|
<PersonPicture Height="32" Name="collapsedAvatar"/>
|
||||||
<TextBlock Text="Channel name" VerticalAlignment="Center" Margin="10,0"/>
|
<TextBlock Text="Channel name" VerticalAlignment="Center" Margin="10,0" Name="collapsedTitle"/>
|
||||||
<Button Background="Red" Foreground="White" FontWeight="SemiBold" Content="Subscribe" Width="150" Padding="2"/>
|
<Button x:Uid="/Cards/subscribe" Background="Red" Foreground="White" FontWeight="SemiBold" Content="Subscribe" Width="150" Name="collapsedBtn" Click="Subscribe_Click" Padding="2"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<AutoSuggestBox x:Uid="/Channel/search" VerticalAlignment="Center" Width="250" Margin="8" PlaceholderText="Search on channel" QueryIcon="Find" Name="search" QuerySubmitted="AutoSuggestBox_QuerySubmitted"/>
|
<AutoSuggestBox x:Uid="/Channel/search" VerticalAlignment="Center" Width="250" Margin="8" PlaceholderText="Search on channel" QueryIcon="Find" Name="search" QuerySubmitted="AutoSuggestBox_QuerySubmitted"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -92,6 +96,6 @@
|
|||||||
<AppBarButton x:Uid="/Channel/share" Icon="Share" Label="Share" Name="share" Click="Share_Click"/>
|
<AppBarButton x:Uid="/Channel/share" Icon="Share" Label="Share" Name="share" Click="Share_Click"/>
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
|
|
||||||
<local:LoadingPage Grid.RowSpan="2" Visibility="Collapsed"/>
|
<local:LoadingPage Grid.RowSpan="2" Visibility="Collapsed" x:Name="loading" RefreshPage="Refresh_Click"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -28,29 +28,15 @@ namespace FoxTube.Pages
|
|||||||
public string channelId;
|
public string channelId;
|
||||||
public Channel item;
|
public Channel item;
|
||||||
|
|
||||||
readonly LoadingPage loading, playlistLoading;
|
SearchResource.ListRequest request;
|
||||||
readonly VideoGrid videoList, playlistList;
|
|
||||||
readonly ShowMore videoMore, playlistMore;
|
|
||||||
|
|
||||||
SearchResource.ListRequest videoRequest, playlistRequest;
|
private string videoToken, playlistToken;
|
||||||
|
|
||||||
private string videoToken;
|
|
||||||
private string playlistToken;
|
|
||||||
private bool playlistLoaded = false;
|
private bool playlistLoaded = false;
|
||||||
|
|
||||||
public ChannelPage()
|
public ChannelPage()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
loading = grid.Children[2] as LoadingPage;
|
|
||||||
playlistLoading = playlists.Children[1] as LoadingPage;
|
|
||||||
|
|
||||||
videoList = videos.Children[1] as VideoGrid;
|
|
||||||
playlistList = (playlists.Children[0] as StackPanel).Children[1] as VideoGrid;
|
|
||||||
|
|
||||||
videoMore = videos.Children[2] as ShowMore;
|
|
||||||
playlistMore = (playlists.Children[0] as StackPanel).Children[2] as ShowMore;
|
|
||||||
|
|
||||||
loading.RefreshPage += Refresh_Click;
|
|
||||||
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
|
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,56 +51,44 @@ namespace FoxTube.Pages
|
|||||||
|
|
||||||
public async void Initialize(string id)
|
public async void Initialize(string id)
|
||||||
{
|
{
|
||||||
content.SelectedIndex = 0;
|
|
||||||
loading.Refresh();
|
loading.Refresh();
|
||||||
playlistLoading.Refresh();
|
playlistLoading.Refresh();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
channelId = id;
|
ChannelsResource.ListRequest infoRequest = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
|
||||||
|
infoRequest.Id = channelId = id;
|
||||||
|
|
||||||
ChannelsResource.ListRequest request = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
|
item = (await infoRequest.ExecuteAsync()).Items[0];
|
||||||
request.Id = id;
|
|
||||||
if (content.Items.Count == 4)
|
|
||||||
content.Items.RemoveAt(3);
|
|
||||||
|
|
||||||
item = (await request.ExecuteAsync()).Items[0];
|
title.Text = collapsedTitle.Text = item.Snippet.Title;
|
||||||
|
|
||||||
title.Text = item.Snippet.Title;
|
|
||||||
subscribers.Text = $"{item.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
|
subscribers.Text = $"{item.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
|
||||||
videosCount.Text = $"{item.Statistics.VideoCount:0,0} {resources.GetString("/Cards/videos")}";
|
videosCount.Text = $"{item.Statistics.VideoCount:0,0} {resources.GetString("/Cards/videos")}";
|
||||||
|
|
||||||
try
|
if (!item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
|
||||||
{
|
try { channelCover.Source = new BitmapImage(new Uri(item.BrandingSettings.Image.BannerImageUrl)); }
|
||||||
if (item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
|
catch { }
|
||||||
throw new Exception("Default channel cover detected");
|
|
||||||
channelCover.ImageSource = new BitmapImage(new Uri(item.BrandingSettings.Image.BannerImageUrl));
|
try { avatar.ProfilePicture = collapsedAvatar.ProfilePicture = new BitmapImage(new Uri(item.Snippet.Thumbnails.Medium.Url)); }
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
try { avatar.ProfilePicture = new BitmapImage(new Uri(item.Snippet.Thumbnails.Medium.Url)); }
|
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
Methods.FormatText(ref description, item.Snippet.Description);
|
Methods.FormatText(ref description, item.Snippet.Description);
|
||||||
|
|
||||||
videoRequest = SecretsVault.Service.Search.List("id");
|
request = SecretsVault.Service.Search.List("id");
|
||||||
videoRequest.ChannelId = id;
|
request.ChannelId = id;
|
||||||
videoRequest.Type = "video";
|
request.Type = "video";
|
||||||
videoRequest.Order = SearchResource.ListRequest.OrderEnum.Date;
|
request.Order = SearchResource.ListRequest.OrderEnum.Date;
|
||||||
videoRequest.MaxResults = 25;
|
request.MaxResults = 25;
|
||||||
|
|
||||||
SearchListResponse response = await videoRequest.ExecuteAsync();
|
SearchListResponse response = await request.ExecuteAsync();
|
||||||
|
|
||||||
videoList.Clear();
|
|
||||||
foreach (SearchResult i in response.Items)
|
foreach (SearchResult i in response.Items)
|
||||||
{
|
videoList.Add(new VideoCard(i.Id.VideoId));
|
||||||
VideoCard card = new VideoCard(i.Id.VideoId);
|
|
||||||
videoList.Add(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
||||||
videoToken = response.NextPageToken;
|
videoToken = response.NextPageToken;
|
||||||
else
|
else
|
||||||
videoMore.Complete(true);
|
videoMore.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
if (SecretsVault.IsAuthorized)
|
if (SecretsVault.IsAuthorized)
|
||||||
{
|
{
|
||||||
@@ -123,6 +97,7 @@ namespace FoxTube.Pages
|
|||||||
subscribe.Background = new SolidColorBrush(Colors.Transparent);
|
subscribe.Background = new SolidColorBrush(Colors.Transparent);
|
||||||
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
|
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
|
||||||
subscribe.Content = resources.GetString("/Cards/unsubscribe");
|
subscribe.Content = resources.GetString("/Cards/unsubscribe");
|
||||||
|
collapsedBtn.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
subscriptionPane.Visibility = Visibility.Visible;
|
subscriptionPane.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
@@ -151,20 +126,12 @@ namespace FoxTube.Pages
|
|||||||
{
|
{
|
||||||
playlistLoading.Refresh();
|
playlistLoading.Refresh();
|
||||||
|
|
||||||
playlistRequest = SecretsVault.Service.Search.List("id");
|
request.Type = "playlist";
|
||||||
playlistRequest.ChannelId = channelId;
|
|
||||||
playlistRequest.Order = SearchResource.ListRequest.OrderEnum.Date;
|
|
||||||
playlistRequest.Type = "playlist";
|
|
||||||
playlistRequest.MaxResults = 25;
|
|
||||||
|
|
||||||
SearchListResponse response = await playlistRequest.ExecuteAsync();
|
SearchListResponse response = await request.ExecuteAsync();
|
||||||
|
|
||||||
playlistList.Clear();
|
|
||||||
foreach (SearchResult i in response.Items)
|
foreach (SearchResult i in response.Items)
|
||||||
{
|
playlistList.Add(new PlaylistCard(i.Id.PlaylistId));
|
||||||
PlaylistCard card = new PlaylistCard(i.Id.PlaylistId);
|
|
||||||
playlistList.Add(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
||||||
playlistToken = response.NextPageToken;
|
playlistToken = response.NextPageToken;
|
||||||
@@ -178,9 +145,15 @@ namespace FoxTube.Pages
|
|||||||
{
|
{
|
||||||
playlistLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
playlistLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
playlistLoading.Error();
|
playlistLoading.Error(e.GetType().ToString(), e.Message);
|
||||||
|
Analytics.TrackEvent("Channel playlists list loading error", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message },
|
||||||
|
{ "Channel ID", channelId }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,42 +165,38 @@ namespace FoxTube.Pages
|
|||||||
|
|
||||||
private async void ShowMorePlaylists_Click()
|
private async void ShowMorePlaylists_Click()
|
||||||
{
|
{
|
||||||
playlistRequest.PageToken = playlistToken;
|
request.Type = "playlist";
|
||||||
SearchListResponse response = await playlistRequest.ExecuteAsync();
|
request.PageToken = playlistToken;
|
||||||
|
SearchListResponse response = await request.ExecuteAsync();
|
||||||
|
|
||||||
foreach (SearchResult i in response.Items)
|
foreach (SearchResult i in response.Items)
|
||||||
{
|
playlistList.Add(new PlaylistCard(i.Id.PlaylistId));
|
||||||
PlaylistCard card = new PlaylistCard(i.Id.PlaylistId);
|
|
||||||
playlistList.Add(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
if (string.IsNullOrWhiteSpace(response.NextPageToken))
|
||||||
|
playlistMore.Visibility = Visibility.Collapsed;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
playlistToken = response.NextPageToken;
|
playlistToken = response.NextPageToken;
|
||||||
playlistMore.Complete();
|
playlistMore.Complete();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
playlistMore.Complete(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void VideoMore_Clicked()
|
private async void VideoMore_Clicked()
|
||||||
{
|
{
|
||||||
videoRequest.PageToken = videoToken;
|
request.Type = "video";
|
||||||
SearchListResponse response = await videoRequest.ExecuteAsync();
|
request.PageToken = videoToken;
|
||||||
|
SearchListResponse response = await request.ExecuteAsync();
|
||||||
|
|
||||||
foreach (SearchResult i in response.Items)
|
foreach (SearchResult i in response.Items)
|
||||||
{
|
videoList.Add(new VideoCard(i.Id.VideoId));
|
||||||
VideoCard card = new VideoCard(i.Id.VideoId);
|
|
||||||
videoList.Add(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
if (string.IsNullOrWhiteSpace(response.NextPageToken))
|
||||||
|
videoMore.Visibility = Visibility.Collapsed;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
videoToken = response.NextPageToken;
|
videoToken = response.NextPageToken;
|
||||||
videoMore.Complete();
|
videoMore.Complete();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
videoMore.Complete(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Subscribe_Click(object sender, RoutedEventArgs e)
|
private async void Subscribe_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -237,12 +206,14 @@ namespace FoxTube.Pages
|
|||||||
subscribe.Background = new SolidColorBrush(Colors.Transparent);
|
subscribe.Background = new SolidColorBrush(Colors.Transparent);
|
||||||
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
|
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
|
||||||
subscribe.Content = resources.GetString("/Cards/unsubscribe");
|
subscribe.Content = resources.GetString("/Cards/unsubscribe");
|
||||||
|
collapsedBtn.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
subscribe.Background = new SolidColorBrush(Colors.Red);
|
subscribe.Background = new SolidColorBrush(Colors.Red);
|
||||||
subscribe.Foreground = new SolidColorBrush(Colors.White);
|
subscribe.Foreground = new SolidColorBrush(Colors.White);
|
||||||
subscribe.Content = resources.GetString("/Cards/subscribe/Content");
|
subscribe.Content = resources.GetString("/Cards/subscribe/Content");
|
||||||
|
collapsedBtn.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,21 +226,29 @@ namespace FoxTube.Pages
|
|||||||
{
|
{
|
||||||
if(search.Text.Length > 2)
|
if(search.Text.Length > 2)
|
||||||
{
|
{
|
||||||
if (content.Items.Count == 4)
|
if(content.Items.Count < 4)
|
||||||
((content.Items[3] as PivotItem).Content as Frame).Navigate(typeof(Search), new SearchParameters(search.Text, item.Id));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
content.Items.Add(new PivotItem()
|
content.Items.Add(new PivotItem()
|
||||||
{
|
{
|
||||||
Content = new Frame()
|
Header = resources.GetString("/Channel/searchHeader"),
|
||||||
|
Content = new Search()
|
||||||
});
|
});
|
||||||
((content.Items[3] as PivotItem).Content as Frame).Navigate(typeof(Search), new SearchParameters(search.Text, item.Id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
((content.Items[3] as PivotItem).Content as Search).Initialize(new SearchParameters(search.Text, item.Id));
|
||||||
content.SelectedIndex = 3;
|
content.SelectedIndex = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
|
||||||
|
{
|
||||||
|
Rect panel = infoPanel.TransformToVisual(videoScroll).TransformBounds(new Rect(0.0, 0.0, infoPanel.ActualWidth, infoPanel.ActualHeight));
|
||||||
|
Rect view = new Rect(0.0, 0.0, videoScroll.ActualWidth, videoScroll.ActualHeight);
|
||||||
|
|
||||||
|
if (view.Contains(new Point(panel.Left, panel.Bottom)))
|
||||||
|
ColapsedHeader.Opacity = 0;
|
||||||
|
else
|
||||||
|
ColapsedHeader.Opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
private void Refresh_Click(object sender, RoutedEventArgs e)
|
private void Refresh_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Methods.MainPage.GoToChannel(channelId);
|
Methods.MainPage.GoToChannel(channelId);
|
||||||
@@ -277,10 +256,7 @@ namespace FoxTube.Pages
|
|||||||
|
|
||||||
private async void InBrowser_Click(object sender, RoutedEventArgs e)
|
private async void InBrowser_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(item.Snippet.CustomUrl))
|
await Launcher.LaunchUriAsync(new Uri($"https://www.youtube.com/channel/{item.Id}"));
|
||||||
await Launcher.LaunchUriAsync(new Uri($"https://www.youtube.com/user/{item.Snippet.CustomUrl}"));
|
|
||||||
else
|
|
||||||
await Launcher.LaunchUriAsync(new Uri($"https://www.youtube.com/channel/{item.Id}"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Share_Click(object sender, RoutedEventArgs e)
|
private void Share_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -288,6 +264,11 @@ namespace FoxTube.Pages
|
|||||||
DataTransferManager.ShowShareUI();
|
DataTransferManager.ShowShareUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ChannelCover_ImageOpened(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
channelCover.Opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
|
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
|
||||||
{
|
{
|
||||||
Methods.Share(args,
|
Methods.Share(args,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="using:FoxTube.Controls"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
@@ -43,9 +44,14 @@
|
|||||||
|
|
||||||
<ScrollViewer Grid.Row="1" Name="scroll">
|
<ScrollViewer Grid.Row="1" Name="scroll">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<StackPanel Name="placeholder"/>
|
<StackPanel Name="placeholder">
|
||||||
<HyperlinkButton x:Uid="/CommentsPage/more" Visibility="Collapsed" Name="more" Click="more_Click" HorizontalAlignment="Center" Foreground="Red" Content="Show more"/>
|
<StackPanel.ChildrenTransitions>
|
||||||
<ProgressBar Name="moreLoading" Visibility="Collapsed" IsIndeterminate="True" Foreground="Red"/>
|
<TransitionCollection>
|
||||||
|
<EntranceThemeTransition IsStaggeringEnabled="True"/>
|
||||||
|
</TransitionCollection>
|
||||||
|
</StackPanel.ChildrenTransitions>
|
||||||
|
</StackPanel>
|
||||||
|
<controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ namespace FoxTube.Pages
|
|||||||
ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage");
|
ResourceLoader resources = ResourceLoader.GetForCurrentView("CommentsPage");
|
||||||
|
|
||||||
string threadId;
|
string threadId;
|
||||||
string nextPageToken;
|
string token;
|
||||||
|
|
||||||
CommentThreadsResource.ListRequest.OrderEnum order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
|
CommentThreadsResource.ListRequest.OrderEnum order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
|
||||||
|
CommentThreadsResource.ListRequest request;
|
||||||
|
|
||||||
public CommentsPage()
|
public CommentsPage()
|
||||||
{
|
{
|
||||||
@@ -33,144 +34,134 @@ namespace FoxTube.Pages
|
|||||||
|
|
||||||
if (!SecretsVault.IsAuthorized)
|
if (!SecretsVault.IsAuthorized)
|
||||||
grid.RowDefinitions[0].Height = new GridLength(0);
|
grid.RowDefinitions[0].Height = new GridLength(0);
|
||||||
else
|
|
||||||
grid.RowDefinitions[0].Height = GridLength.Auto;
|
|
||||||
|
|
||||||
counter.Text = $"{video.Statistics.CommentCount:0,0} {resources.GetString("/CommentsPage/comments")}";
|
counter.Text = $"{video.Statistics.CommentCount:0,0} {resources.GetString("/CommentsPage/comments")}";
|
||||||
orderBtn.Content = resources.GetString("/CommentsPage/relevance/Text");
|
orderBtn.Content = resources.GetString("/CommentsPage/relevance/Text");
|
||||||
|
|
||||||
var request = SecretsVault.Service.CommentThreads.List("snippet,replies");
|
request = SecretsVault.Service.CommentThreads.List("snippet,replies");
|
||||||
|
request.MaxResults = 25;
|
||||||
request.Order = order;
|
request.Order = order;
|
||||||
request.VideoId = video.Id;
|
request.VideoId = video.Id;
|
||||||
request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
|
request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
|
||||||
|
|
||||||
var response = await request.ExecuteAsync();
|
var response = await request.ExecuteAsync();
|
||||||
|
|
||||||
if(response.NextPageToken != null)
|
token = response.NextPageToken;
|
||||||
{
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
nextPageToken = response.NextPageToken;
|
more.Visibility = Visibility.Collapsed;
|
||||||
more.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (CommentThread comment in response.Items)
|
foreach (CommentThread comment in response.Items)
|
||||||
placeholder.Children.Add(new CommentCard(comment));
|
placeholder.Children.Add(new CommentCard(comment));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveComment(CommentCard commentCard)
|
public void RemoveComment(CommentCard commentCard, string topCommentId = null)
|
||||||
{
|
{
|
||||||
placeholder.Children.Remove(commentCard);
|
if (string.IsNullOrWhiteSpace(topCommentId))
|
||||||
}
|
placeholder.Children.Remove(commentCard);
|
||||||
|
else
|
||||||
private async void more_Click(object sender, RoutedEventArgs e)
|
(placeholder.Children.Find(i => (i as CommentCard).thread.Id == topCommentId) as CommentCard).DeleteComment(commentCard);
|
||||||
{
|
|
||||||
more.Visibility = Visibility.Collapsed;
|
|
||||||
moreLoading.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
var request = SecretsVault.Service.CommentThreads.List("snippet,replies");
|
|
||||||
request.Order = order;
|
|
||||||
request.VideoId = threadId;
|
|
||||||
request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
|
|
||||||
request.PageToken = nextPageToken;
|
|
||||||
var response = await request.ExecuteAsync();
|
|
||||||
|
|
||||||
foreach (CommentThread comment in response.Items)
|
|
||||||
placeholder.Children.Add(new CommentCard(comment));
|
|
||||||
|
|
||||||
if(response.NextPageToken != null)
|
|
||||||
{
|
|
||||||
nextPageToken = response.NextPageToken;
|
|
||||||
more.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
moreLoading.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void toRelevance_Click(object sender, RoutedEventArgs e)
|
private async void toRelevance_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if(order != CommentThreadsResource.ListRequest.OrderEnum.Relevance)
|
if (order == CommentThreadsResource.ListRequest.OrderEnum.Relevance)
|
||||||
{
|
return;
|
||||||
|
|
||||||
|
more.Show();
|
||||||
|
|
||||||
|
order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
|
||||||
|
orderBtn.Content = resources.GetString("/CommentsPage/relevance/Text");
|
||||||
|
|
||||||
|
placeholder.Children.Clear();
|
||||||
|
|
||||||
|
request.Order = order;
|
||||||
|
var response = await request.ExecuteAsync();
|
||||||
|
|
||||||
|
token = response.NextPageToken;
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
more.Visibility = Visibility.Collapsed;
|
more.Visibility = Visibility.Collapsed;
|
||||||
moreLoading.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
|
foreach (CommentThread comment in response.Items)
|
||||||
orderBtn.Content = resources.GetString("/CommentsPage/relevance/Text");
|
placeholder.Children.Add(new CommentCard(comment));
|
||||||
|
|
||||||
placeholder.Children.Clear();
|
more.Complete();
|
||||||
|
|
||||||
var request = SecretsVault.Service.CommentThreads.List("snippet,replies");
|
|
||||||
request.Order = order;
|
|
||||||
request.VideoId = threadId;
|
|
||||||
request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
|
|
||||||
var response = await request.ExecuteAsync();
|
|
||||||
|
|
||||||
nextPageToken = response.NextPageToken;
|
|
||||||
foreach (CommentThread comment in response.Items)
|
|
||||||
placeholder.Children.Add(new CommentCard(comment));
|
|
||||||
|
|
||||||
more.Visibility = Visibility.Visible;
|
|
||||||
moreLoading.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void toDate_Click(object sender, RoutedEventArgs e)
|
private async void toDate_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (order != CommentThreadsResource.ListRequest.OrderEnum.Time)
|
if (order == CommentThreadsResource.ListRequest.OrderEnum.Time)
|
||||||
{
|
return;
|
||||||
|
|
||||||
|
more.Show();
|
||||||
|
|
||||||
|
order = CommentThreadsResource.ListRequest.OrderEnum.Time;
|
||||||
|
orderBtn.Content = resources.GetString("/CommentsPage/publish");
|
||||||
|
|
||||||
|
placeholder.Children.Clear();
|
||||||
|
|
||||||
|
request.Order = order;
|
||||||
|
var response = await request.ExecuteAsync();
|
||||||
|
|
||||||
|
token = response.NextPageToken;
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
more.Visibility = Visibility.Collapsed;
|
more.Visibility = Visibility.Collapsed;
|
||||||
moreLoading.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
order = CommentThreadsResource.ListRequest.OrderEnum.Time;
|
foreach (CommentThread comment in response.Items)
|
||||||
orderBtn.Content = resources.GetString("/CommentsPage/publish");
|
placeholder.Children.Add(new CommentCard(comment));
|
||||||
|
|
||||||
placeholder.Children.Clear();
|
more.Complete();
|
||||||
|
|
||||||
var request = SecretsVault.Service.CommentThreads.List("snippet,replies");
|
|
||||||
request.Order = order;
|
|
||||||
request.VideoId = threadId;
|
|
||||||
request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
|
|
||||||
var response = await request.ExecuteAsync();
|
|
||||||
|
|
||||||
nextPageToken = response.NextPageToken;
|
|
||||||
foreach (CommentThread comment in response.Items)
|
|
||||||
placeholder.Children.Add(new CommentCard(comment));
|
|
||||||
|
|
||||||
more.Visibility = Visibility.Visible;
|
|
||||||
moreLoading.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void send_Click(object sender, RoutedEventArgs e)
|
private async void send_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if(newComment.Text.Length > 0)
|
if (string.IsNullOrWhiteSpace(newComment.Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
newComment.IsEnabled = false;
|
||||||
|
send.IsEnabled = false;
|
||||||
|
sending.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
CommentThread thread = new CommentThread()
|
||||||
{
|
{
|
||||||
newComment.IsEnabled = false;
|
Snippet = new CommentThreadSnippet()
|
||||||
send.IsEnabled = false;
|
|
||||||
sending.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
CommentThread thread = new CommentThread();
|
|
||||||
thread.Snippet = new CommentThreadSnippet();
|
|
||||||
|
|
||||||
Comment comment = new Comment();
|
|
||||||
comment.Snippet = new CommentSnippet();
|
|
||||||
comment.Snippet.TextOriginal = newComment.Text;
|
|
||||||
|
|
||||||
thread.Snippet.VideoId = threadId;
|
|
||||||
thread.Snippet.TopLevelComment = comment;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
CommentThread response = await SecretsVault.Service.CommentThreads.Insert(thread, "snippet").ExecuteAsync();
|
TopLevelComment = new Comment()
|
||||||
newComment.Text = "";
|
{
|
||||||
placeholder.Children.Insert(0, new CommentCard(response));
|
Snippet = new CommentSnippet()
|
||||||
scroll.ChangeView(null, 0, null);
|
{
|
||||||
}
|
TextOriginal = newComment.Text
|
||||||
catch
|
}
|
||||||
{
|
},
|
||||||
await new MessageDialog("Failed to publish your comment. Please, try again later.", "Failed to publish your comment").ShowAsync();
|
VideoId = threadId
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
newComment.IsEnabled = true;
|
try
|
||||||
send.IsEnabled = true;
|
{
|
||||||
sending.Visibility = Visibility.Collapsed;
|
CommentThread response = await SecretsVault.Service.CommentThreads.Insert(thread, "snippet").ExecuteAsync();
|
||||||
|
placeholder.Children.Insert(0, new CommentCard(response));
|
||||||
|
newComment.Text = "";
|
||||||
|
scroll.ChangeView(null, 0, null);
|
||||||
}
|
}
|
||||||
|
catch { await new MessageDialog("Failed to publish your comment. Please, try again later.").ShowAsync(); }
|
||||||
|
|
||||||
|
newComment.IsEnabled = true;
|
||||||
|
send.IsEnabled = true;
|
||||||
|
sending.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ShowMore_Clicked()
|
||||||
|
{
|
||||||
|
request.PageToken = token;
|
||||||
|
var response = await request.ExecuteAsync();
|
||||||
|
|
||||||
|
foreach (CommentThread comment in response.Items)
|
||||||
|
placeholder.Children.Add(new CommentCard(comment));
|
||||||
|
|
||||||
|
token = response.NextPageToken;
|
||||||
|
more.Complete();
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
|
more.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,20 @@
|
|||||||
<Button Grid.Column="1" x:Uid="/Downloads/openFolder" Content="Open folder" Name="open" Click="Open_Click" VerticalAlignment="Center"/>
|
<Button Grid.Column="1" x:Uid="/Downloads/openFolder" Content="Open folder" Name="open" Click="Open_Click" VerticalAlignment="Center"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1">
|
|
||||||
<StackPanel Name="stack"/>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<TextBlock x:Uid="/Downloads/noItems" Grid.Row="1" Name="empty" HorizontalAlignment="Center" VerticalAlignment="Top" FontSize="28" Text="You haven't downloaded anything yet" Margin="10" TextWrapping="WrapWholeWords" Foreground="Gray" FontWeight="SemiBold"/>
|
<TextBlock x:Uid="/Downloads/noItems" Grid.Row="1" Name="empty" HorizontalAlignment="Center" VerticalAlignment="Top" FontSize="28" Text="You haven't downloaded anything yet" Margin="10" TextWrapping="WrapWholeWords" Foreground="Gray" FontWeight="SemiBold"/>
|
||||||
|
|
||||||
<CommandBar DefaultLabelPosition="Right" Grid.Row="2">
|
<ScrollViewer Grid.Row="1">
|
||||||
|
<StackPanel Name="list">
|
||||||
|
<StackPanel.ChildrenTransitions>
|
||||||
|
<TransitionCollection>
|
||||||
|
<EntranceThemeTransition IsStaggeringEnabled="True"/>
|
||||||
|
</TransitionCollection>
|
||||||
|
</StackPanel.ChildrenTransitions>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<CommandBar Grid.Row="2">
|
||||||
<AppBarButton x:Uid="/Downloads/refresh" Label="Refresh" Icon="Refresh" Click="Refresh"/>
|
<AppBarButton x:Uid="/Downloads/refresh" Label="Refresh" Icon="Refresh" Click="Refresh"/>
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using FoxTube.Controls;
|
||||||
|
using System;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Windows.UI.Xaml.Navigation;
|
|
||||||
|
|
||||||
namespace FoxTube.Pages
|
namespace FoxTube.Pages
|
||||||
{
|
{
|
||||||
@@ -13,47 +13,29 @@ namespace FoxTube.Pages
|
|||||||
{
|
{
|
||||||
public Downloads()
|
public Downloads()
|
||||||
{
|
{
|
||||||
this.InitializeComponent();
|
InitializeComponent();
|
||||||
SetPath();
|
DownloadAgent.Page = this;
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedFrom(e);
|
|
||||||
stack.Children.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedTo(e);
|
|
||||||
DownloadAgent.items.ForEach(i =>
|
|
||||||
{
|
|
||||||
stack.Children.Add(i);
|
|
||||||
i.Initialize();
|
|
||||||
});
|
|
||||||
empty.Visibility = stack.Children.Count > 0 ? Visibility.Collapsed : Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetPath()
|
|
||||||
{
|
|
||||||
path.Text = DownloadAgent.Downloads.Path;
|
path.Text = DownloadAgent.Downloads.Path;
|
||||||
}
|
Refresh(this, null);
|
||||||
|
|
||||||
void Refresh(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
stack.Children.Clear();
|
|
||||||
|
|
||||||
DownloadAgent.items.ForEach(i =>
|
|
||||||
{
|
|
||||||
stack.Children.Add(i);
|
|
||||||
i.Initialize();
|
|
||||||
});
|
|
||||||
empty.Visibility = stack.Children.Count > 0 ? Visibility.Collapsed : Visibility.Visible;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Open_Click(object sender, RoutedEventArgs e)
|
private async void Open_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await Launcher.LaunchFolderAsync(DownloadAgent.Downloads);
|
await Launcher.LaunchFolderAsync(DownloadAgent.Downloads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Refresh(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
list.Children.Clear();
|
||||||
|
DownloadAgent.items.ForEach(i => list.Children.Add(i));
|
||||||
|
|
||||||
|
empty.Visibility = list.Children.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(DownloadItem item)
|
||||||
|
{
|
||||||
|
list.Children.Remove(item);
|
||||||
|
empty.Visibility = list.Children.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,21 +10,21 @@
|
|||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
<Grid Name="grid">
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel Name="stack">
|
<StackPanel>
|
||||||
<local:VideoGrid/>
|
<local:VideoGrid x:Name="list"/>
|
||||||
<controls:ShowMore Clicked="ShowMore_Clicked"/>
|
<controls:ShowMore Clicked="ShowMore_Clicked" x:Name="more"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
<CommandBar Grid.Row="1" DefaultLabelPosition="Right">
|
<CommandBar Grid.Row="1" DefaultLabelPosition="Right">
|
||||||
<AppBarButton Icon="Refresh" Label="Refresh" Name="refresh" Click="Refresh_Click"/>
|
<AppBarButton Icon="Refresh" Label="Refresh" Name="refresh" Click="Refresh_Click"/>
|
||||||
<AppBarButton Label="Open in browser" Icon="Globe" Name="toBrowser" Click="toBrowser_Click"/>
|
<AppBarButton Label="Open in browser" Icon="Globe" Name="toBrowser" Click="toBrowser_Click"/>
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
<foxtube:LoadingPage Visibility="Collapsed" Grid.RowSpan="2"/>
|
<foxtube:LoadingPage Visibility="Collapsed" Grid.RowSpan="2" x:Name="loading"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ namespace FoxTube.Pages
|
|||||||
List<string> entries;
|
List<string> entries;
|
||||||
int page = 1;
|
int page = 1;
|
||||||
public string id = "HL";
|
public string id = "HL";
|
||||||
LoadingPage loading;
|
|
||||||
VideoGrid list;
|
|
||||||
ShowMore more;
|
|
||||||
|
|
||||||
public History()
|
public History()
|
||||||
{
|
{
|
||||||
@@ -30,10 +27,6 @@ namespace FoxTube.Pages
|
|||||||
{
|
{
|
||||||
base.OnNavigatedTo(e);
|
base.OnNavigatedTo(e);
|
||||||
|
|
||||||
loading = grid.Children[2] as LoadingPage;
|
|
||||||
list = stack.Children[0] as VideoGrid;
|
|
||||||
more = stack.Children[1] as ShowMore;
|
|
||||||
|
|
||||||
loading.RefreshPage += Refresh_Click;
|
loading.RefreshPage += Refresh_Click;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(e.Parameter.ToString()))
|
if (!string.IsNullOrWhiteSpace(e.Parameter.ToString()))
|
||||||
@@ -91,7 +84,9 @@ namespace FoxTube.Pages
|
|||||||
list.Add(new VideoCard(entries[k]));
|
list.Add(new VideoCard(entries[k]));
|
||||||
|
|
||||||
if (list.Count >= entries.Count)
|
if (list.Count >= entries.Count)
|
||||||
more.Complete(true);
|
more.Visibility = Visibility.Collapsed;
|
||||||
|
else
|
||||||
|
more.Complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-25
@@ -9,39 +9,69 @@
|
|||||||
xmlns:controls="using:FoxTube.Controls"
|
xmlns:controls="using:FoxTube.Controls"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Name="grid">
|
<Pivot SelectionChanged="pivot_SelectionChanged" Name="pivot">
|
||||||
<Grid.RowDefinitions>
|
<PivotItem Name="recommended" Header="Recommended" x:Uid="/Home/recommended">
|
||||||
<RowDefinition/>
|
<Grid>
|
||||||
<RowDefinition Height="auto"/>
|
<Grid.RowDefinitions>
|
||||||
</Grid.RowDefinitions>
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Pivot Name="pivot" SelectionChanged="pivot_SelectionChanged">
|
|
||||||
<PivotItem x:Uid="/Home/recommended" Header="Recommended" Name="recommended">
|
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<pages:VideoGrid/>
|
<pages:VideoGrid x:Name="recGrid"/>
|
||||||
<controls:ShowMore Clicked="RecMore_Clicked"/>
|
<controls:ShowMore Clicked="Recommended_More" x:Name="recommendedMore"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</PivotItem>
|
|
||||||
<PivotItem x:Uid="/Home/trending" Header="Trending" Name="trending">
|
<CommandBar Grid.Row="1">
|
||||||
|
<AppBarButton Icon="Refresh" Label="Refresh" x:Uid="/Home/refresh" Click="Recommended_Refresh"/>
|
||||||
|
</CommandBar>
|
||||||
|
|
||||||
|
<local:LoadingPage x:Name="recsLoading" Grid.RowSpan="2" RefreshPage="Recommended_Refresh"/>
|
||||||
|
</Grid>
|
||||||
|
</PivotItem>
|
||||||
|
<PivotItem Name="trending" Header="Trending" x:Uid="/Home/trending">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<pages:VideoGrid/>
|
<pages:VideoGrid x:Name="trendGrid"/>
|
||||||
<controls:ShowMore Clicked="TrendMore_Clicked"/>
|
<controls:ShowMore Clicked="Trending_More" x:Name="trendingMore"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</PivotItem>
|
|
||||||
<PivotItem x:Uid="/Home/subs" Header="Subscriptions" Name="subscriptions">
|
|
||||||
<ScrollViewer>
|
|
||||||
<StackPanel/>
|
|
||||||
</ScrollViewer>
|
|
||||||
</PivotItem>
|
|
||||||
</Pivot>
|
|
||||||
|
|
||||||
<CommandBar Grid.Row="1">
|
<CommandBar Grid.Row="1">
|
||||||
<AppBarButton x:Uid="/Home/refresh" Icon="Refresh" Label="Refresh page" Name="refresh" Click="refreshPage"/>
|
<AppBarButton Icon="Refresh" Label="Refresh" x:Uid="/Home/refresh" Click="Trends_Refresh"/>
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
<local:LoadingPage Grid.RowSpan="2" Margin="0,50,0,0" Visibility="Visible" RefreshPage="refreshPage"/>
|
|
||||||
</Grid>
|
<local:LoadingPage x:Name="trendsLoading" Grid.RowSpan="2" RefreshPage="Trends_Refresh"/>
|
||||||
|
</Grid>
|
||||||
|
</PivotItem>
|
||||||
|
<PivotItem Name="subscriptions" Header="Subscriptions" x:Uid="/Home/subs">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ScrollViewer>
|
||||||
|
<StackPanel>
|
||||||
|
<pages:VideoGrid x:Name="subsGrid"/>
|
||||||
|
<controls:ShowMore Clicked="Subscriptions_More" x:Name="subscriptionsMore"/>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<CommandBar Grid.Row="1">
|
||||||
|
<AppBarButton Icon="Refresh" Label="Refresh" x:Uid="/Home/refresh" Click="Subscriptions_Refresh"/>
|
||||||
|
</CommandBar>
|
||||||
|
|
||||||
|
<local:LoadingPage x:Name="subsLoading" Grid.RowSpan="2" RefreshPage="Subscriptions_Refresh"/>
|
||||||
|
</Grid>
|
||||||
|
</PivotItem>
|
||||||
|
</Pivot>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
+141
-112
@@ -5,10 +5,9 @@ using Windows.UI.Xaml.Controls;
|
|||||||
using Google.Apis.YouTube.v3;
|
using Google.Apis.YouTube.v3;
|
||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
using FoxTube.Controls;
|
using FoxTube.Controls;
|
||||||
using FoxTube.Pages;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Xml;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AppCenter.Analytics;
|
||||||
|
|
||||||
namespace FoxTube
|
namespace FoxTube
|
||||||
{
|
{
|
||||||
@@ -17,137 +16,46 @@ namespace FoxTube
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class Home : Page
|
public sealed partial class Home : Page
|
||||||
{
|
{
|
||||||
// TODO: Refactor home page
|
private bool trendLoaded = false, recLoaded = false, subsLoaded = false;
|
||||||
private bool trendLoaded = false, recLoaded = false;
|
|
||||||
|
|
||||||
VideoGrid trendGrid, recGrid;
|
|
||||||
ShowMore trendMore, recMore;
|
|
||||||
LoadingPage loading;
|
|
||||||
|
|
||||||
string trendToken;
|
|
||||||
List<string> homeList = new List<string>();
|
List<string> homeList = new List<string>();
|
||||||
|
List<string> subsList = new List<string>();
|
||||||
|
|
||||||
|
VideosResource.ListRequest trendsRequest;
|
||||||
|
|
||||||
public Home()
|
public Home()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
trendGrid = ((trending.Content as ScrollViewer).Content as StackPanel).Children[0] as VideoGrid;
|
|
||||||
trendMore = ((trending.Content as ScrollViewer).Content as StackPanel).Children[1] as ShowMore;
|
|
||||||
|
|
||||||
recGrid = ((recommended.Content as ScrollViewer).Content as StackPanel).Children[0] as VideoGrid;
|
|
||||||
recMore = ((recommended.Content as ScrollViewer).Content as StackPanel).Children[1] as ShowMore;
|
|
||||||
|
|
||||||
loading = grid.Children[2] as LoadingPage;
|
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshPage(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Methods.MainPage.GoToHome();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void TrendMore_Clicked()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("id");
|
|
||||||
request.MaxResults = 25;
|
|
||||||
request.PageToken = trendToken;
|
|
||||||
|
|
||||||
request.Chart = VideosResource.ListRequest.ChartEnum.MostPopular;
|
|
||||||
request.RegionCode = SettingsStorage.Region;
|
|
||||||
VideoListResponse response = await request.ExecuteAsync();
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
|
||||||
trendToken = response.NextPageToken;
|
|
||||||
else
|
|
||||||
trendMore.Complete(true);
|
|
||||||
|
|
||||||
foreach (Video vid in response.Items)
|
|
||||||
{
|
|
||||||
VideoCard vCard = new VideoCard(vid.Id);
|
|
||||||
trendGrid.Add(vCard);
|
|
||||||
}
|
|
||||||
trendMore.Complete();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
trendMore.Complete(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
if(SecretsVault.IsAuthorized)
|
if(!SecretsVault.IsAuthorized)
|
||||||
LoadRecommendations();
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
pivot.Items.Remove(recommended);
|
pivot.Items.Remove(recommended);
|
||||||
pivot.Items.Remove(subscriptions);
|
pivot.Items.Remove(subscriptions);
|
||||||
|
|
||||||
LoadTrending();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
loading.Close();
|
|
||||||
if (pivot.SelectedItem == recommended && !recLoaded)
|
if (pivot.SelectedItem == recommended && !recLoaded)
|
||||||
LoadRecommendations();
|
LoadRecommendations();
|
||||||
else if (pivot.SelectedItem == trending && !trendLoaded)
|
else if (pivot.SelectedItem == trending && !trendLoaded)
|
||||||
LoadTrending();
|
LoadTrending();
|
||||||
else if (pivot.SelectedItem == subscriptions)
|
else if (pivot.SelectedItem == subscriptions && !subsLoaded)
|
||||||
LoadSubscriptions();
|
LoadSubscriptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async void LoadTrending()
|
#region Initializing tabs
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
loading.Refresh();
|
|
||||||
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("id");
|
|
||||||
request.MaxResults = 25;
|
|
||||||
|
|
||||||
request.Chart = VideosResource.ListRequest.ChartEnum.MostPopular;
|
|
||||||
request.RegionCode = SettingsStorage.Region;
|
|
||||||
VideoListResponse response = await request.ExecuteAsync();
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
|
||||||
trendToken = response.NextPageToken;
|
|
||||||
else
|
|
||||||
trendMore.Complete(true);
|
|
||||||
|
|
||||||
foreach (Video vid in response.Items)
|
|
||||||
{
|
|
||||||
VideoCard vCard = new VideoCard(vid.Id);
|
|
||||||
trendGrid.Add(vCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.Close();
|
|
||||||
trendLoaded = true;
|
|
||||||
}
|
|
||||||
catch (HttpRequestException)
|
|
||||||
{
|
|
||||||
trendLoaded = false;
|
|
||||||
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
trendLoaded = false;
|
|
||||||
loading.Error(e.GetType().ToString(), e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async void LoadRecommendations()
|
async void LoadRecommendations()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
loading.Refresh();
|
recsLoading.Refresh();
|
||||||
//throw new NotImplementedException("This page has not implemented yet.");
|
|
||||||
|
|
||||||
HttpResponseMessage r = await SecretsVault.HttpClient.GetAsync("https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=WL");
|
string response = await SecretsVault.HttpClient.GetStringAsync("https://www.youtube.com/");
|
||||||
string response = await SecretsVault.HttpClient.GetStringAsync("https://youtube.com/");
|
|
||||||
|
|
||||||
foreach (Match match in Regex.Matches(response, @"\bdata-context-item-id=(\S*)\b", RegexOptions.IgnoreCase))
|
foreach (Match match in Regex.Matches(response, @"\bdata-context-item-id=(\S*)\b", RegexOptions.IgnoreCase))
|
||||||
homeList.Add(match.Value.Split('"')[1]);
|
homeList.Add(match.Value.Split('"')[1]);
|
||||||
@@ -155,39 +63,160 @@ namespace FoxTube
|
|||||||
for (int k = 0; k < 25 && k < homeList.Count; k++)
|
for (int k = 0; k < 25 && k < homeList.Count; k++)
|
||||||
recGrid.Add(new VideoCard(homeList[k]));
|
recGrid.Add(new VideoCard(homeList[k]));
|
||||||
|
|
||||||
loading.Close();
|
recommendedMore.Visibility = recGrid.Count >= homeList.Count ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
|
||||||
|
recsLoading.Close();
|
||||||
recLoaded = true;
|
recLoaded = true;
|
||||||
}
|
}
|
||||||
catch (HttpRequestException)
|
catch (HttpRequestException)
|
||||||
{
|
{
|
||||||
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
recLoaded = false;
|
||||||
|
recsLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
loading.Error(e.GetType().ToString(), e.Message);
|
recLoaded = false;
|
||||||
|
recsLoading.Error(e.GetType().ToString(), e.Message);
|
||||||
|
|
||||||
|
Analytics.TrackEvent("Failed to load recommendations", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async void LoadTrending()
|
||||||
void LoadSubscriptions()
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
loading.Refresh();
|
trendsLoading.Refresh();
|
||||||
throw new NotImplementedException("This page isn't not implemented yet.");
|
|
||||||
|
trendsRequest = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails");
|
||||||
|
trendsRequest.MaxResults = 25;
|
||||||
|
|
||||||
|
trendsRequest.Chart = VideosResource.ListRequest.ChartEnum.MostPopular;
|
||||||
|
trendsRequest.RegionCode = SettingsStorage.Region;
|
||||||
|
|
||||||
|
VideoListResponse response = await trendsRequest.ExecuteAsync();
|
||||||
|
|
||||||
|
trendsRequest.PageToken = response.NextPageToken;
|
||||||
|
trendingMore.Visibility = string.IsNullOrWhiteSpace(response.NextPageToken) ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
|
||||||
|
foreach (Video i in response.Items)
|
||||||
|
trendGrid.Add(new VideoCard(i));
|
||||||
|
|
||||||
|
trendsLoading.Close();
|
||||||
|
trendLoaded = true;
|
||||||
}
|
}
|
||||||
catch (HttpRequestException)
|
catch (HttpRequestException)
|
||||||
{
|
{
|
||||||
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
trendLoaded = false;
|
||||||
|
trendsLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
trendLoaded = false;
|
||||||
|
trendsLoading.Error(e.GetType().ToString(), e.Message);
|
||||||
|
|
||||||
|
Analytics.TrackEvent("Failed to load trendings", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async void LoadSubscriptions()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
subsLoading.Refresh();
|
||||||
|
|
||||||
|
await SecretsVault.HttpClient.GetStringAsync("https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=WL");
|
||||||
|
string response = await SecretsVault.HttpClient.GetStringAsync("https://www.youtube.com/feed/subscriptions");
|
||||||
|
|
||||||
|
foreach (Match match in Regex.Matches(response, @"\bdata-context-item-id=(\S*)\b", RegexOptions.IgnoreCase))
|
||||||
|
subsList.Add(match.Value.Split('"')[1]);
|
||||||
|
|
||||||
|
for (int k = 0; k < 25 && k < subsList.Count; k++)
|
||||||
|
subsGrid.Add(new VideoCard(subsList[k]));
|
||||||
|
|
||||||
|
subscriptionsMore.Visibility = subsGrid.Count >= subsList.Count ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
|
||||||
|
subsLoading.Close();
|
||||||
|
subsLoaded = true;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException)
|
||||||
|
{
|
||||||
|
subsLoaded = false;
|
||||||
|
subsLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
loading.Error(e.GetType().ToString(), e.Message);
|
subsLoaded = false;
|
||||||
|
subsLoading.Error(e.GetType().ToString(), e.Message);
|
||||||
|
|
||||||
|
Analytics.TrackEvent("Failed to load subscriptions", new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Exception", e.GetType().ToString() },
|
||||||
|
{ "Message", e.Message }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private void RecMore_Clicked()
|
#region More requests
|
||||||
|
private void Recommended_More()
|
||||||
{
|
{
|
||||||
|
int l = 25 + recGrid.Count;
|
||||||
|
for (int k = recGrid.Count; k < l && k < homeList.Count; k++)
|
||||||
|
recGrid.Add(new VideoCard(homeList[k]));
|
||||||
|
|
||||||
|
recommendedMore.Visibility = recGrid.Count >= homeList.Count ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
recommendedMore.Complete();
|
||||||
}
|
}
|
||||||
|
private async void Trending_More()
|
||||||
|
{
|
||||||
|
VideoListResponse response = await trendsRequest.ExecuteAsync();
|
||||||
|
|
||||||
|
trendsRequest.PageToken = response.NextPageToken;
|
||||||
|
trendingMore.Visibility = string.IsNullOrWhiteSpace(response.NextPageToken) ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
|
||||||
|
foreach (Video i in response.Items)
|
||||||
|
trendGrid.Add(new VideoCard(i));
|
||||||
|
|
||||||
|
trendingMore.Complete();
|
||||||
|
}
|
||||||
|
private void Subscriptions_More()
|
||||||
|
{
|
||||||
|
int l = 25 + subsGrid.Count;
|
||||||
|
for (int k = subsGrid.Count; k < l && k < subsList.Count; k++)
|
||||||
|
subsGrid.Add(new VideoCard(subsList[k]));
|
||||||
|
|
||||||
|
subscriptionsMore.Visibility = subsGrid.Count >= subsList.Count ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
subscriptionsMore.Complete();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Refreshing tabs
|
||||||
|
private void Recommended_Refresh(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
recGrid.Clear();
|
||||||
|
|
||||||
|
LoadRecommendations();
|
||||||
|
}
|
||||||
|
private void Trends_Refresh(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
trendsRequest = null;
|
||||||
|
trendGrid.Clear();
|
||||||
|
|
||||||
|
LoadTrending();
|
||||||
|
}
|
||||||
|
private void Subscriptions_Refresh(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
subsGrid.Clear();
|
||||||
|
|
||||||
|
LoadSubscriptions();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,10 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
|
||||||
xmlns:Windows10version1803="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 6)"
|
xmlns:Windows10version1803="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 6)"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d">
|
||||||
SizeChanged="Page_SizeChanged"
|
|
||||||
PreviewKeyUp="Page_PreviewKeyUp">
|
|
||||||
|
|
||||||
<Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
<Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
<Windows10version1809:Grid.BackgroundTransition>
|
<Windows10version1809:Grid.BackgroundTransition>
|
||||||
@@ -29,7 +26,7 @@
|
|||||||
Style="{StaticResource CaptionTextBlockStyle}" />
|
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<NavigationView Header="History" SelectedItem="toHome" Windows10version1803:BackRequested="Nav_BackRequested" Windows10version1803:PaneClosing="Nav_PaneChanged" Windows10version1803:PaneOpening="Nav_PaneChanged" Windows10version1803:PaneTitle="FoxTube" OpenPaneLength="300" Name="nav" SelectionChanged="Nav_SelectionChanged">
|
<NavigationView SelectedItem="toHome" Windows10version1803:BackRequested="Nav_BackRequested" Windows10version1803:PaneClosing="Nav_PaneChanged" Windows10version1803:PaneOpening="Nav_PaneChanged" Windows10version1803:PaneTitle="FoxTube" OpenPaneLength="300" Name="nav" SelectionChanged="Nav_SelectionChanged">
|
||||||
|
|
||||||
<NavigationView.MenuItemTemplate>
|
<NavigationView.MenuItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
@@ -58,7 +55,13 @@
|
|||||||
|
|
||||||
<NavigationView.PaneFooter>
|
<NavigationView.PaneFooter>
|
||||||
<NavigationViewList>
|
<NavigationViewList>
|
||||||
<NavigationViewItem Name="openWeb" Tapped="Web_Tapped" Icon="Globe" Content="Browser" Visibility="Visible"/>
|
<NavigationViewList.ItemContainerTransitions>
|
||||||
|
<TransitionCollection>
|
||||||
|
<EntranceThemeTransition IsStaggeringEnabled="True"/>
|
||||||
|
<ReorderThemeTransition/>
|
||||||
|
</TransitionCollection>
|
||||||
|
</NavigationViewList.ItemContainerTransitions>
|
||||||
|
<NavigationViewItem Name="openWeb" Tapped="Web_Tapped" Icon="Globe" Content="Browser" Visibility="Collapsed"/>
|
||||||
<NavigationViewItem x:Uid="/Main/feedback" Name="feedback" Content="Give a feedback" Tapped="Feedback_Click">
|
<NavigationViewItem x:Uid="/Main/feedback" Name="feedback" Content="Give a feedback" Tapped="Feedback_Click">
|
||||||
<NavigationViewItem.Icon>
|
<NavigationViewItem.Icon>
|
||||||
<FontIcon Glyph=""/>
|
<FontIcon Glyph=""/>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using Windows.UI.Xaml.Controls;
|
|||||||
using Windows.UI.Xaml.Input;
|
using Windows.UI.Xaml.Input;
|
||||||
using Windows.UI.Xaml.Navigation;
|
using Windows.UI.Xaml.Navigation;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Windows.UI.Notifications;
|
|
||||||
using Windows.UI.Xaml.Media.Imaging;
|
using Windows.UI.Xaml.Media.Imaging;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
@@ -16,14 +15,9 @@ using Windows.System;
|
|||||||
using Google.Apis.Oauth2.v2;
|
using Google.Apis.Oauth2.v2;
|
||||||
using Google.Apis.Oauth2.v2.Data;
|
using Google.Apis.Oauth2.v2.Data;
|
||||||
using FoxTube.Pages;
|
using FoxTube.Pages;
|
||||||
using Windows.ApplicationModel;
|
|
||||||
using System.Net;
|
|
||||||
using Windows.UI.Popups;
|
using Windows.UI.Popups;
|
||||||
using Windows.Networking.Connectivity;
|
using Windows.Networking.Connectivity;
|
||||||
using Windows.UI.Core;
|
|
||||||
using Windows.ApplicationModel.Resources;
|
using Windows.ApplicationModel.Resources;
|
||||||
using Windows.Storage;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace FoxTube
|
namespace FoxTube
|
||||||
{
|
{
|
||||||
@@ -34,7 +28,6 @@ namespace FoxTube
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class MainPage : Page
|
public sealed partial class MainPage : Page
|
||||||
{
|
{
|
||||||
// TODO: Refactor main page
|
|
||||||
Sender s = Sender.None;
|
Sender s = Sender.None;
|
||||||
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
|
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
|
||||||
Dictionary<Type, Action> headers;
|
Dictionary<Type, Action> headers;
|
||||||
@@ -71,12 +64,6 @@ namespace FoxTube
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Video GetCurrentItem()
|
|
||||||
{
|
|
||||||
try { return (videoPlaceholder.Content as VideoPage).item; }
|
|
||||||
catch { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetPlaylist()
|
public string GetPlaylist()
|
||||||
{
|
{
|
||||||
try { return (videoPlaceholder.Content as VideoPage).playlistId; }
|
try { return (videoPlaceholder.Content as VideoPage).playlistId; }
|
||||||
@@ -90,7 +77,8 @@ namespace FoxTube
|
|||||||
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||||
titleBar.ButtonHoverBackgroundColor = Colors.IndianRed;
|
titleBar.ButtonHoverBackgroundColor = Colors.IndianRed;
|
||||||
titleBar.ButtonPressedBackgroundColor = Colors.DarkRed;
|
titleBar.ButtonPressedBackgroundColor = Colors.DarkRed;
|
||||||
titleBar.ButtonInactiveBackgroundColor = Colors.Gray;
|
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||||
|
titleBar.ButtonInactiveForegroundColor = Colors.Gray;
|
||||||
|
|
||||||
if(Application.Current.RequestedTheme == ApplicationTheme.Dark)
|
if(Application.Current.RequestedTheme == ApplicationTheme.Dark)
|
||||||
titleBar.ForegroundColor = Colors.White;
|
titleBar.ForegroundColor = Colors.White;
|
||||||
@@ -197,7 +185,11 @@ namespace FoxTube
|
|||||||
if(e[0] as bool? != null)
|
if(e[0] as bool? != null)
|
||||||
DownloadAgent.Initialize();
|
DownloadAgent.Initialize();
|
||||||
|
|
||||||
content.Navigate(typeof(Home));
|
s = Sender.None;
|
||||||
|
if (content.Content != null)
|
||||||
|
content.Navigate(content.SourcePageType);
|
||||||
|
else
|
||||||
|
content.Navigate(typeof(Home));
|
||||||
|
|
||||||
if (videoPlaceholder.Content != null)
|
if (videoPlaceholder.Content != null)
|
||||||
GoToVideo((videoPlaceholder.Content as VideoPage).videoId, (videoPlaceholder.Content as VideoPage).playlistId);
|
GoToVideo((videoPlaceholder.Content as VideoPage).videoId, (videoPlaceholder.Content as VideoPage).playlistId);
|
||||||
@@ -291,8 +283,9 @@ namespace FoxTube
|
|||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
if (videoPlaceholder.Content != null)
|
if (videoPlaceholder.Content != null)
|
||||||
(videoPlaceholder.Content as VideoPage).player.close_Click(this, null);
|
(videoPlaceholder.Content as VideoPage).CloseVideo();
|
||||||
|
|
||||||
|
nav.IsBackEnabled = true;
|
||||||
nav.ExpandedModeThresholdWidth = short.MaxValue;
|
nav.ExpandedModeThresholdWidth = short.MaxValue;
|
||||||
nav.IsPaneOpen = false;
|
nav.IsPaneOpen = false;
|
||||||
|
|
||||||
@@ -301,7 +294,7 @@ namespace FoxTube
|
|||||||
|
|
||||||
public void GoToDeveloper(string id)
|
public void GoToDeveloper(string id)
|
||||||
{
|
{
|
||||||
content.Navigate(typeof(Settings), $"inbox&{id}");
|
content.Navigate(typeof(Settings), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GoToPlaylist(string id)
|
public void GoToPlaylist(string id)
|
||||||
@@ -314,22 +307,18 @@ namespace FoxTube
|
|||||||
content.Navigate(typeof(Downloads));
|
content.Navigate(typeof(Downloads));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Page_SizeChanged(object sender, SizeChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (videoPlaceholder.Content != null)
|
|
||||||
(videoPlaceholder.Content as VideoPage).player.UpdateSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MinimizeAsInitializer()
|
public void MinimizeAsInitializer()
|
||||||
{
|
{
|
||||||
if (videoPlaceholder.Content != null)
|
if (videoPlaceholder.Content != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((videoPlaceholder.Content as VideoPage).loading.State != LoadingState.Loaded)
|
if ((videoPlaceholder.Content as VideoPage).LoadingPage.State != LoadingState.Loaded)
|
||||||
CloseVideo();
|
CloseVideo();
|
||||||
else
|
else
|
||||||
try { headers[content.SourcePageType](); }
|
(videoPlaceholder.Content as VideoPage).Player.Controls_MiniModeChanged(this, true);
|
||||||
catch { }
|
|
||||||
|
try { headers[content.SourcePageType](); }
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MinimizeVideo()
|
public void MinimizeVideo()
|
||||||
@@ -356,8 +345,7 @@ namespace FoxTube
|
|||||||
if (content.SourcePageType == typeof(Home) || content.SourcePageType == typeof(Settings) || content.SourcePageType == typeof(Subscriptions))
|
if (content.SourcePageType == typeof(Home) || content.SourcePageType == typeof(Settings) || content.SourcePageType == typeof(Subscriptions))
|
||||||
{
|
{
|
||||||
nav.ExpandedModeThresholdWidth = 1008;
|
nav.ExpandedModeThresholdWidth = 1008;
|
||||||
if (nav.DisplayMode == NavigationViewDisplayMode.Expanded)
|
nav.IsPaneOpen = nav.DisplayMode == NavigationViewDisplayMode.Expanded ? true : false;
|
||||||
nav.IsPaneOpen = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
nav.ExpandedModeThresholdWidth = short.MaxValue;
|
nav.ExpandedModeThresholdWidth = short.MaxValue;
|
||||||
@@ -371,7 +359,7 @@ namespace FoxTube
|
|||||||
videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Stretch;
|
videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||||
videoPlaceholder.Margin = new Thickness(0);
|
videoPlaceholder.Margin = new Thickness(0);
|
||||||
|
|
||||||
if (videoPlaceholder.Content != null)
|
if (videoPlaceholder.Content == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nav.IsBackEnabled = true;
|
nav.IsBackEnabled = true;
|
||||||
@@ -380,41 +368,13 @@ namespace FoxTube
|
|||||||
nav.IsPaneOpen = false;
|
nav.IsPaneOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Fullscreen(bool on)
|
|
||||||
{
|
|
||||||
if (on)
|
|
||||||
{
|
|
||||||
nav.CompactModeThresholdWidth = short.MaxValue;
|
|
||||||
nav.ExpandedModeThresholdWidth = short.MaxValue;
|
|
||||||
try { nav.IsPaneVisible = false; }
|
|
||||||
catch { nav.CompactPaneLength = 0; }
|
|
||||||
nav.Margin = new Thickness(0, -45, 0, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nav.CompactModeThresholdWidth = 641;
|
|
||||||
if (videoPlaceholder.Content == null)
|
|
||||||
SetNavigationMenu();
|
|
||||||
|
|
||||||
try { nav.IsPaneVisible = true; }
|
|
||||||
catch { nav.CompactPaneLength = new NavigationView().CompactPaneLength; }
|
|
||||||
nav.Margin = new Thickness(0);
|
|
||||||
|
|
||||||
if (videoPlaceholder.Content != null && nav.IsPaneOpen)
|
|
||||||
nav.IsPaneOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CloseVideo()
|
public void CloseVideo()
|
||||||
{
|
{
|
||||||
if (ApplicationView.GetForCurrentView().IsFullScreenMode)
|
if (ApplicationView.GetForCurrentView().IsFullScreenMode)
|
||||||
{
|
|
||||||
ApplicationView.GetForCurrentView().ExitFullScreenMode();
|
ApplicationView.GetForCurrentView().ExitFullScreenMode();
|
||||||
Fullscreen(false);
|
|
||||||
}
|
|
||||||
videoPlaceholder.Content = null;
|
videoPlaceholder.Content = null;
|
||||||
GC.Collect();
|
GC.Collect();
|
||||||
Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0);
|
|
||||||
MaximizeVideo();
|
MaximizeVideo();
|
||||||
|
|
||||||
if (content.CanGoBack)
|
if (content.CanGoBack)
|
||||||
@@ -492,15 +452,9 @@ namespace FoxTube
|
|||||||
else if(e.SourcePageType == typeof(History))
|
else if(e.SourcePageType == typeof(History))
|
||||||
{
|
{
|
||||||
if (e.Parameter.ToString() == "HL")
|
if (e.Parameter.ToString() == "HL")
|
||||||
{
|
|
||||||
SetNavigationItem(toHistory);
|
SetNavigationItem(toHistory);
|
||||||
nav.Header = resources.GetString("/Main/history/Content");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
SetNavigationItem(toLater);
|
SetNavigationItem(toLater);
|
||||||
nav.Header = resources.GetString("/Main/later/Content");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(e.SourcePageType == typeof(PlaylistPage))
|
else if(e.SourcePageType == typeof(PlaylistPage))
|
||||||
{
|
{
|
||||||
@@ -511,6 +465,14 @@ namespace FoxTube
|
|||||||
else
|
else
|
||||||
s = Sender.None;
|
s = Sender.None;
|
||||||
|
|
||||||
|
if(e.SourcePageType == typeof(History))
|
||||||
|
{
|
||||||
|
if(e.Parameter.ToString() == "HL")
|
||||||
|
nav.Header = resources.GetString("/Main/history/Content");
|
||||||
|
else if(e.Parameter.ToString() == "WL")
|
||||||
|
nav.Header = resources.GetString("/Main/later/Content");
|
||||||
|
}
|
||||||
|
|
||||||
if (content.CanGoBack)
|
if (content.CanGoBack)
|
||||||
nav.IsBackEnabled = true;
|
nav.IsBackEnabled = true;
|
||||||
else
|
else
|
||||||
@@ -522,15 +484,6 @@ namespace FoxTube
|
|||||||
MinimizeAsInitializer();
|
MinimizeAsInitializer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Page_PreviewKeyUp(object sender, KeyRoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if(videoPlaceholder.Content != null && FocusManager.GetFocusedElement().GetType() != typeof(TextBox))
|
|
||||||
{
|
|
||||||
e.Handled = true;
|
|
||||||
(videoPlaceholder.Content as VideoPage).player.KeyUpPressed(sender, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenContext(object sender, TappedRoutedEventArgs e)
|
private void OpenContext(object sender, TappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
((NavigationViewItem)sender).ContextFlyout.ShowAt((NavigationViewItem)sender);
|
((NavigationViewItem)sender).ContextFlyout.ShowAt((NavigationViewItem)sender);
|
||||||
@@ -543,7 +496,7 @@ namespace FoxTube
|
|||||||
|
|
||||||
private void Web_Tapped(object sender, TappedRoutedEventArgs e)
|
private void Web_Tapped(object sender, TappedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
content.Navigate(typeof(Browser));
|
content.Navigate(typeof(Home1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Nav_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
private void Nav_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||||
@@ -585,7 +538,7 @@ namespace FoxTube
|
|||||||
{
|
{
|
||||||
if (videoPlaceholder.Content != null)
|
if (videoPlaceholder.Content != null)
|
||||||
{
|
{
|
||||||
if ((videoPlaceholder.Content as VideoPage).loading.State != LoadingState.Loaded)
|
if ((videoPlaceholder.Content as VideoPage).LoadingPage.State != LoadingState.Loaded)
|
||||||
CloseVideo();
|
CloseVideo();
|
||||||
else if (videoPlaceholder.HorizontalAlignment == HorizontalAlignment.Stretch)
|
else if (videoPlaceholder.HorizontalAlignment == HorizontalAlignment.Stretch)
|
||||||
MinimizeAsInitializer();
|
MinimizeAsInitializer();
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<Page
|
<Page
|
||||||
NavigationCacheMode="Enabled"
|
|
||||||
x:Class="FoxTube.Pages.PlaylistPage"
|
x:Class="FoxTube.Pages.PlaylistPage"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@@ -7,10 +6,11 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:foxtube="using:FoxTube"
|
xmlns:foxtube="using:FoxTube"
|
||||||
|
xmlns:controls="using:FoxTube.Controls"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
<Grid Name="grid">
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
@@ -57,7 +57,10 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<local:VideoGrid Grid.Column="1" Grid.Row="1"/>
|
<StackPanel Grid.Column="1" Grid.Row="1">
|
||||||
|
<local:VideoGrid x:Name="list"/>
|
||||||
|
<controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
@@ -74,6 +77,6 @@
|
|||||||
<AppBarButton x:Uid="/Playlist/share" Icon="Share" Label="Share" Name="share" Click="share_Click"/>
|
<AppBarButton x:Uid="/Playlist/share" Icon="Share" Label="Share" Name="share" Click="share_Click"/>
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
|
|
||||||
<foxtube:LoadingPage Grid.RowSpan="2" Visibility="Collapsed"/>
|
<foxtube:LoadingPage Grid.RowSpan="2" Visibility="Collapsed" RefreshPage="refresh_Click" x:Name="loading"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -23,15 +23,13 @@ namespace FoxTube.Pages
|
|||||||
public string playlistId;
|
public string playlistId;
|
||||||
Playlist item;
|
Playlist item;
|
||||||
|
|
||||||
LoadingPage loading;
|
PlaylistItemsResource.ListRequest request;
|
||||||
VideoGrid list;
|
string token;
|
||||||
|
|
||||||
public PlaylistPage()
|
public PlaylistPage()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
loading = grid.Children[2] as LoadingPage;
|
|
||||||
list = ((grid.Children[0] as ScrollViewer).Content as Grid).Children[1] as VideoGrid;
|
|
||||||
loading.RefreshPage += refresh_Click;
|
|
||||||
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
|
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,10 +50,10 @@ namespace FoxTube.Pages
|
|||||||
{
|
{
|
||||||
playlistId = id;
|
playlistId = id;
|
||||||
|
|
||||||
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet,contentDetails");
|
PlaylistsResource.ListRequest infoRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
|
||||||
request.Id = id;
|
infoRequest.Id = id;
|
||||||
|
|
||||||
item = (await request.ExecuteAsync()).Items[0];
|
item = (await infoRequest.ExecuteAsync()).Items[0];
|
||||||
|
|
||||||
title.Text = item.Snippet.Title;
|
title.Text = item.Snippet.Title;
|
||||||
info.Text = $"{item.ContentDetails.ItemCount} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}";
|
info.Text = $"{item.ContentDetails.ItemCount} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}";
|
||||||
@@ -63,31 +61,27 @@ namespace FoxTube.Pages
|
|||||||
|
|
||||||
channelName.Text = item.Snippet.ChannelTitle;
|
channelName.Text = item.Snippet.ChannelTitle;
|
||||||
|
|
||||||
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
|
|
||||||
ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet");
|
ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet");
|
||||||
channelRequest.Id = item.Snippet.ChannelId;
|
channelRequest.Id = item.Snippet.ChannelId;
|
||||||
Channel channel = (await channelRequest.ExecuteAsync()).Items[0];
|
Channel channel = (await channelRequest.ExecuteAsync()).Items[0];
|
||||||
avatar.ProfilePicture = new BitmapImage(channel.Snippet.Thumbnails.Medium.Url.ToUri());
|
|
||||||
|
|
||||||
PlaylistItemsResource.ListRequest listRequest = SecretsVault.Service.PlaylistItems.List("contentDetails");
|
try { avatar.ProfilePicture = new BitmapImage(channel.Snippet.Thumbnails.Medium.Url.ToUri()); }
|
||||||
listRequest.PlaylistId = id;
|
catch { }
|
||||||
listRequest.MaxResults = 50;
|
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
|
||||||
|
catch { }
|
||||||
|
|
||||||
PlaylistItemListResponse response = await listRequest.ExecuteAsync();
|
request = SecretsVault.Service.PlaylistItems.List("contentDetails");
|
||||||
|
request.PlaylistId = id;
|
||||||
|
request.MaxResults = 25;
|
||||||
|
|
||||||
|
PlaylistItemListResponse response = await request.ExecuteAsync();
|
||||||
|
token = response.NextPageToken;
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
|
more.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
list.Clear();
|
|
||||||
foreach (PlaylistItem i in response.Items)
|
foreach (PlaylistItem i in response.Items)
|
||||||
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
|
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
|
||||||
|
|
||||||
while (response.NextPageToken != null)
|
|
||||||
{
|
|
||||||
listRequest.PageToken = response.NextPageToken;
|
|
||||||
response = await listRequest.ExecuteAsync();
|
|
||||||
|
|
||||||
foreach (PlaylistItem i in response.Items)
|
|
||||||
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.Close();
|
loading.Close();
|
||||||
}
|
}
|
||||||
catch (System.Net.Http.HttpRequestException)
|
catch (System.Net.Http.HttpRequestException)
|
||||||
@@ -134,5 +128,22 @@ namespace FoxTube.Pages
|
|||||||
$"https://www.youtube.com/playlist?list={item.Id}",
|
$"https://www.youtube.com/playlist?list={item.Id}",
|
||||||
ResourceLoader.GetForCurrentView("Cards").GetString("/Cards/playlistShare"));
|
ResourceLoader.GetForCurrentView("Cards").GetString("/Cards/playlistShare"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void ShowMore_Clicked()
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,8 @@
|
|||||||
<Button x:Uid="/Search/apply" Content="Apply" Margin="10,0,0,10" Name="apply" Click="Apply_Click"/>
|
<Button x:Uid="/Search/apply" Content="Apply" Margin="10,0,0,10" Name="apply" Click="Apply_Click"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<pages:VideoGrid/>
|
<pages:VideoGrid x:Name="list"/>
|
||||||
<controls:ShowMore Clicked="More_Clicked"/>
|
<controls:ShowMore x:Name="more" Clicked="More_Clicked"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
@@ -77,6 +77,6 @@
|
|||||||
<AppBarButton Label="Refresh" Icon="Refresh" Click="AppBarButton_Click"/>
|
<AppBarButton Label="Refresh" Icon="Refresh" Click="AppBarButton_Click"/>
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
|
|
||||||
<local:LoadingPage Grid.RowSpan="2" Visibility="Collapsed" RefreshPage="AppBarButton_Click"/>
|
<local:LoadingPage x:Name="loading" Grid.RowSpan="2" Visibility="Collapsed" RefreshPage="AppBarButton_Click"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -24,16 +24,9 @@ namespace FoxTube
|
|||||||
SearchResource.ListRequest request;
|
SearchResource.ListRequest request;
|
||||||
string nextToken;
|
string nextToken;
|
||||||
|
|
||||||
readonly VideoGrid list;
|
|
||||||
readonly LoadingPage loading;
|
|
||||||
readonly ShowMore more;
|
|
||||||
|
|
||||||
public Search()
|
public Search()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
loading = grid.Children[2] as LoadingPage;
|
|
||||||
list = ((grid.Children[0] as ScrollViewer).Content as StackPanel).Children[4] as VideoGrid;
|
|
||||||
more = ((grid.Children[0] as ScrollViewer).Content as StackPanel).Children[5] as ShowMore;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SetResults(int? count)
|
public string SetResults(int? count)
|
||||||
@@ -105,10 +98,11 @@ namespace FoxTube
|
|||||||
|
|
||||||
request.Order = (SearchResource.ListRequest.OrderEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Order);
|
request.Order = (SearchResource.ListRequest.OrderEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Order);
|
||||||
request.Type = (string)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Type);
|
request.Type = (string)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Type);
|
||||||
request.PublishedAfter = (DateTime)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Date);
|
request.PublishedAfter = (DateTime?)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Date);
|
||||||
|
|
||||||
if (Parameters.Filter.Type == SearchParameters.Filters.Enumerations.Type.Video)
|
if (Parameters.Filter.Type == SearchParameters.Filters.Enumerations.Type.Video)
|
||||||
{
|
{
|
||||||
|
request.VideoDuration = (SearchResource.ListRequest.VideoDurationEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Duration);
|
||||||
request.VideoDefinition = (SearchResource.ListRequest.VideoDefinitionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.HD);
|
request.VideoDefinition = (SearchResource.ListRequest.VideoDefinitionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.HD);
|
||||||
request.VideoDimension = (SearchResource.ListRequest.VideoDimensionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.ThreeD);
|
request.VideoDimension = (SearchResource.ListRequest.VideoDimensionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.ThreeD);
|
||||||
request.VideoCaption = (SearchResource.ListRequest.VideoCaptionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Captions);
|
request.VideoCaption = (SearchResource.ListRequest.VideoCaptionEnum)arg.Filter.GetParameter(SearchParameters.Filters.Enumerations.ConversionType.Captions);
|
||||||
@@ -136,7 +130,7 @@ namespace FoxTube
|
|||||||
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
|
||||||
nextToken = response.NextPageToken;
|
nextToken = response.NextPageToken;
|
||||||
else
|
else
|
||||||
more.Complete(true);
|
more.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
list.Clear();
|
list.Clear();
|
||||||
foreach (SearchResult item in response.Items)
|
foreach (SearchResult item in response.Items)
|
||||||
@@ -192,7 +186,7 @@ namespace FoxTube
|
|||||||
more.Complete();
|
more.Complete();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
more.Complete(true);
|
more.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Type_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void Type_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
|||||||
@@ -21,14 +21,8 @@ namespace FoxTube
|
|||||||
base.OnNavigatedTo(e);
|
base.OnNavigatedTo(e);
|
||||||
if(!string.IsNullOrWhiteSpace(e.Parameter as string))
|
if(!string.IsNullOrWhiteSpace(e.Parameter as string))
|
||||||
{
|
{
|
||||||
if ((e.Parameter as string).Contains("inbox") || (e.Parameter as string).Contains("changelog"))
|
inboxId = e.Parameter as string;
|
||||||
{
|
pivot.SelectedIndex = 2;
|
||||||
if ((string)e.Parameter != "inbox" && (string)e.Parameter != "changelog")
|
|
||||||
inboxId = e.Parameter as string;
|
|
||||||
pivot.SelectedIndex = 2;
|
|
||||||
}
|
|
||||||
else if ((e.Parameter as string) == "about")
|
|
||||||
pivot.SelectedIndex = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,42 +52,41 @@
|
|||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
<ColumnDefinition Width="0"/>
|
<ColumnDefinition Width="0"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<ScrollViewer Background="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}">
|
|
||||||
<StackPanel VerticalAlignment="Stretch">
|
<StackPanel VerticalAlignment="Stretch" Background="{StaticResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||||
<ComboBox x:Uid="/Inbox/filter" Header="Filter" Margin="10" HorizontalAlignment="Stretch" SelectedIndex="0" Name="filter" SelectionChanged="filter_SelectionChanged">
|
<ComboBox x:Uid="/Inbox/filter" Header="Filter" Margin="10" HorizontalAlignment="Stretch" SelectedIndex="0" Name="filter" SelectionChanged="filter_SelectionChanged">
|
||||||
<ComboBoxItem x:Uid="/Inbox/all" Content="All"/>
|
<ComboBoxItem x:Uid="/Inbox/all" Content="All"/>
|
||||||
<ComboBoxItem x:Uid="/Inbox/messages" Content="Messages"/>
|
<ComboBoxItem x:Uid="/Inbox/messages" Content="Messages"/>
|
||||||
<ComboBoxItem x:Uid="/Inbox/changelogs" Content="Patch notes"/>
|
<ComboBoxItem x:Uid="/Inbox/changelogs" Content="Patch notes"/>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
<ListView Name="list" SelectionChanged="list_SelectionChanged" Background="Transparent">
|
<ListView Name="list" SelectionChanged="list_SelectionChanged" Background="Transparent">
|
||||||
<ListView.ItemContainerStyle>
|
<ListView.ItemContainerStyle>
|
||||||
<Style TargetType="ListViewItem">
|
<Style TargetType="ListViewItem">
|
||||||
<Setter Property="Padding" Value="10"/>
|
<Setter Property="Padding" Value="10"/>
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||||
</Style>
|
</Style>
|
||||||
</ListView.ItemContainerStyle>
|
</ListView.ItemContainerStyle>
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Margin="0, 10">
|
<Grid Margin="0, 10">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="auto"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="2*"/>
|
<ColumnDefinition Width="2*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid Margin="0,0,10,0">
|
<Grid Margin="0,0,10,0">
|
||||||
<Ellipse Fill="Red" Height="40" Width="40"/>
|
<Ellipse Fill="Red" Height="40" Width="40"/>
|
||||||
<TextBlock Foreground="White" FontFamily="Segoe MDL2 Assets" Text="{Binding Path=Icon}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Light" FontSize="17"/>
|
<TextBlock Foreground="White" FontFamily="Segoe MDL2 Assets" Text="{Binding Path=Icon}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Light" FontSize="17"/>
|
||||||
</Grid>
|
|
||||||
<StackPanel Grid.Column="1">
|
|
||||||
<TextBlock FontWeight="Bold" Text="{Binding Path=Title}" MaxLines="1" TextWrapping="Wrap"/>
|
|
||||||
<TextBlock Opacity=".5" Text="{Binding Path=Subtitle}"/>
|
|
||||||
<TextBlock Opacity=".5" FontSize="13" Text="{Binding Path=TimeStamp}" TextWrapping="WrapWholeWords"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
<StackPanel Grid.Column="1">
|
||||||
</ListView.ItemTemplate>
|
<TextBlock FontWeight="Bold" Text="{Binding Path=Title}" MaxLines="1" TextWrapping="Wrap"/>
|
||||||
</ListView>
|
<TextBlock Opacity=".5" Text="{Binding Path=Subtitle}"/>
|
||||||
</StackPanel>
|
<TextBlock Opacity=".5" FontSize="13" Text="{Binding Path=TimeStampString}" TextWrapping="WrapWholeWords"/>
|
||||||
</ScrollViewer>
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<ScrollViewer Grid.Column="1">
|
<ScrollViewer Grid.Column="1">
|
||||||
<StackPanel Margin="10">
|
<StackPanel Margin="10">
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ namespace FoxTube.Pages.SettingsPages
|
|||||||
public sealed partial class Inbox : Page
|
public sealed partial class Inbox : Page
|
||||||
{
|
{
|
||||||
List<InboxItem> items = new List<InboxItem>();
|
List<InboxItem> items = new List<InboxItem>();
|
||||||
|
|
||||||
|
string open;
|
||||||
public Inbox()
|
public Inbox()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -38,7 +40,7 @@ namespace FoxTube.Pages.SettingsPages
|
|||||||
doc.Load("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml");
|
doc.Load("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml");
|
||||||
foreach (XmlElement e in doc["posts"].ChildNodes)
|
foreach (XmlElement e in doc["posts"].ChildNodes)
|
||||||
items.Add(new InboxItem(
|
items.Add(new InboxItem(
|
||||||
e["header"].InnerText,
|
e["header"][SettingsStorage.Language].InnerText,
|
||||||
e["content"][SettingsStorage.Language].InnerText,
|
e["content"][SettingsStorage.Language].InnerText,
|
||||||
DateTime.Parse(e.GetAttribute("time")),
|
DateTime.Parse(e.GetAttribute("time")),
|
||||||
e["id"].InnerText
|
e["id"].InnerText
|
||||||
@@ -49,6 +51,11 @@ namespace FoxTube.Pages.SettingsPages
|
|||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
items.ForEach(i => list.Items.Add(i));
|
items.ForEach(i => list.Items.Add(i));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(open))
|
||||||
|
Open(open);
|
||||||
|
|
||||||
|
open = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
@@ -118,8 +125,13 @@ namespace FoxTube.Pages.SettingsPages
|
|||||||
|
|
||||||
public void Open(string arg)
|
public void Open(string arg)
|
||||||
{
|
{
|
||||||
string id = arg.Split('&')[1];
|
if(items.Count == 0)
|
||||||
InboxItem item = items.Find(x => x.Id == id);
|
{
|
||||||
|
open = arg;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InboxItem item = items.Find(i => i.Id == arg);
|
||||||
if(item != null)
|
if(item != null)
|
||||||
list.SelectedItem = item;
|
list.SelectedItem = item;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,11 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||||
xmlns:controls="using:FoxTube.Controls"
|
|
||||||
x:Name="root"
|
x:Name="root"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
<Grid.RowDefinitions>
|
<ui:AdaptiveGridView Name="list" DesiredWidth="400" SelectionMode="None" HorizontalContentAlignment="Left">
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
<RowDefinition/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<controls:Advert/>
|
|
||||||
<ui:AdaptiveGridView Name="list" OneRowModeEnabled="False" DesiredWidth="384" SelectionMode="None" Grid.Row="1">
|
|
||||||
<ui:AdaptiveGridView.ItemContainerTransitions>
|
<ui:AdaptiveGridView.ItemContainerTransitions>
|
||||||
<TransitionCollection>
|
<TransitionCollection>
|
||||||
<EntranceThemeTransition IsStaggeringEnabled="True"/>
|
<EntranceThemeTransition IsStaggeringEnabled="True"/>
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
using Windows.UI.Xaml;
|
using FoxTube.Controls;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
|
|
||||||
namespace FoxTube
|
|
||||||
{
|
|
||||||
public interface IItemCard
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FoxTube.Pages
|
namespace FoxTube.Pages
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Items cards container
|
/// Items cards container
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -24,7 +18,7 @@ namespace FoxTube.Pages
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(IItemCard card)
|
public void Add(UIElement card)
|
||||||
{
|
{
|
||||||
list.Items.Add(card);
|
list.Items.Add(card);
|
||||||
/*if (list.Items.Count % 10 == 0)
|
/*if (list.Items.Count % 10 == 0)
|
||||||
|
|||||||
@@ -30,12 +30,37 @@
|
|||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="400"/>
|
<ColumnDefinition Width="400"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<ScrollViewer Margin="0,0,0,50" Name="mainScroll">
|
<ScrollViewer Margin="0,0,0,50" Name="mainScroll" VerticalScrollBarVisibility="Hidden">
|
||||||
<StackPanel Orientation="Vertical" Name="mainContent">
|
<StackPanel Orientation="Vertical" Name="mainContent">
|
||||||
<local:VideoPlayer/>
|
<local:VideoPlayer x:Name="player" NextClicked="Player_NextClicked" MiniMode="Player_Minimize"/>
|
||||||
<PivotItem Header="Description" Name="descriptionPanel">
|
<PivotItem Header="Description" Name="descriptionPanel">
|
||||||
<StackPanel Margin="0,10">
|
<StackPanel Margin="0,10">
|
||||||
<TextBlock IsTextSelectionEnabled="True" Name="title" Text="[Video title]" FontSize="25" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Start"/>
|
<TextBlock IsTextSelectionEnabled="True" Name="title" Text="[Video title]" FontSize="25" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Start"/>
|
||||||
|
<Border BorderBrush="Red" BorderThickness="5" CornerRadius="10" Margin="0,10" Name="upcoming" Visibility="Collapsed">
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<FontIcon Glyph="" FontSize="50" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
|
<TextBlock x:Uid="/VideoPage/upcomingHeader" FontWeight="Bold" FontSize="25" Text="Stream hasn't started yet" Grid.Column="1" Margin="10,0" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="2" Margin="10,0" Name="schedule" Visibility="Collapsed">
|
||||||
|
<TextBlock x:Uid="/VideoPage/scheduleHeader" FontSize="20" FontWeight="SemiBold" Text="Stream schedule:"/>
|
||||||
|
<TextBlock Name="start" Text="Starts at: 2/15/2019 21:00:00" Visibility="Collapsed"/>
|
||||||
|
<TextBlock Name="end" Text="Ends at: 2/15/2019 23:00:00" Visibility="Collapsed"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="3" Margin="10,0" Name="countdownPanel" Visibility="Collapsed">
|
||||||
|
<TextBlock x:Uid="/VideoPage/countdown" FontSize="20" FontWeight="SemiBold" Text="Stream starts in:"/>
|
||||||
|
<TextBlock Name="countdown" FontWeight="Bold" FontSize="35" Text="00:12:12"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
@@ -74,7 +99,6 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock Name="description" Text="[Description]" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords"/>
|
<TextBlock Name="description" Text="[Description]" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords"/>
|
||||||
<TextBlock Margin="0,20" Name="category" Text="Category: "/>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -149,7 +173,7 @@
|
|||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem x:Uid="/VideoPage/comments" Header="Comments" Name="commentsPlaceholder">
|
<PivotItem x:Uid="/VideoPage/comments" Header="Comments" Name="commentsPlaceholder">
|
||||||
<pages:CommentsPage/>
|
<pages:CommentsPage x:Name="comments"/>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem x:Uid="/VideoPage/playlist" Header="Playlist" Name="playlist">
|
<PivotItem x:Uid="/VideoPage/playlist" Header="Playlist" Name="playlist">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
@@ -181,6 +205,6 @@
|
|||||||
</Pivot>
|
</Pivot>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<local:LoadingPage Grid.ColumnSpan="2" Visibility="Collapsed"/>
|
<local:LoadingPage Grid.ColumnSpan="2" Visibility="Collapsed" x:Name="loading" RefreshPage="refresh_Click"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using FoxTube.Controls;
|
using FoxTube.Controls;
|
||||||
|
using FoxTube.Controls.Adverts;
|
||||||
using Google.Apis.YouTube.v3;
|
using Google.Apis.YouTube.v3;
|
||||||
using Google.Apis.YouTube.v3.Data;
|
using Google.Apis.YouTube.v3.Data;
|
||||||
using Microsoft.AppCenter.Analytics;
|
using Microsoft.AppCenter.Analytics;
|
||||||
@@ -50,26 +51,19 @@ namespace FoxTube.Pages
|
|||||||
public string playlistId = null;
|
public string playlistId = null;
|
||||||
public Video item;
|
public Video item;
|
||||||
|
|
||||||
bool wide;
|
|
||||||
bool isExtended = false;
|
bool isExtended = false;
|
||||||
|
|
||||||
Rating userRating = Rating.None;
|
Rating userRating = Rating.None;
|
||||||
|
|
||||||
public VideoPlayer player;
|
|
||||||
public CommentsPage comments;
|
|
||||||
public LoadingPage loading;
|
|
||||||
|
|
||||||
DispatcherTimer liveTimer;
|
DispatcherTimer liveTimer;
|
||||||
|
DispatcherTimer countdownTimer;
|
||||||
|
|
||||||
|
public LoadingPage LoadingPage => loading;
|
||||||
|
public VideoPlayer Player => player;
|
||||||
|
|
||||||
public VideoPage()
|
public VideoPage()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
loading = grid.Children[3] as LoadingPage;
|
|
||||||
loading.RefreshPage += refresh_Click;
|
|
||||||
player = mainContent.Children[0] as VideoPlayer;
|
|
||||||
player.SetFullSize += Player_SetFullSize;
|
|
||||||
player.NextClicked += Player_NextClicked;
|
|
||||||
comments = commentsPlaceholder.Content as CommentsPage;
|
|
||||||
|
|
||||||
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
|
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
|
||||||
|
|
||||||
@@ -127,6 +121,9 @@ namespace FoxTube.Pages
|
|||||||
else
|
else
|
||||||
LoadStream();
|
LoadStream();
|
||||||
|
|
||||||
|
if (item.Snippet.LiveBroadcastContent == "upcoming")
|
||||||
|
SetSchedule();
|
||||||
|
|
||||||
LoadInfo();
|
LoadInfo();
|
||||||
|
|
||||||
loading.Close();
|
loading.Close();
|
||||||
@@ -147,6 +144,34 @@ namespace FoxTube.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetSchedule()
|
||||||
|
{
|
||||||
|
if (item.LiveStreamingDetails.ScheduledEndTime.HasValue || item.LiveStreamingDetails.ScheduledStartTime.HasValue)
|
||||||
|
schedule.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
if (item.LiveStreamingDetails.ScheduledEndTime.HasValue)
|
||||||
|
{
|
||||||
|
end.Text = $"{resources.GetString("/VideoPage/end")} {item.LiveStreamingDetails.ActualEndTime.Value}";
|
||||||
|
end.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
if (item.LiveStreamingDetails.ScheduledStartTime.HasValue)
|
||||||
|
{
|
||||||
|
start.Text = $"{resources.GetString("/VideoPage/start")} {item.LiveStreamingDetails.ActualStartTime.Value}";
|
||||||
|
start.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
countdownPanel.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
countdownTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||||||
|
countdownTimer.Tick += (s, e) =>
|
||||||
|
{
|
||||||
|
countdown.Text = (item.LiveStreamingDetails.ScheduledStartTime.Value - DateTime.Now).ToString(@"hh\:mm\:ss");
|
||||||
|
if (countdown.Text == "00:00:00")
|
||||||
|
refresh_Click(this, null);
|
||||||
|
};
|
||||||
|
countdownTimer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async void LoadPlaylist(string id)
|
async void LoadPlaylist(string id)
|
||||||
{
|
{
|
||||||
playlistId = id;
|
playlistId = id;
|
||||||
@@ -200,7 +225,7 @@ namespace FoxTube.Pages
|
|||||||
pivot.SelectedItem = playlist;
|
pivot.SelectedItem = playlist;
|
||||||
|
|
||||||
if (playlistList.SelectedIndex == playlistList.Items.Count - 1)
|
if (playlistList.SelectedIndex == playlistList.Items.Count - 1)
|
||||||
player.Next.Visibility = Visibility.Collapsed;
|
player.Controls.IsNextTrackButtonVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async void LoadInfo()
|
async void LoadInfo()
|
||||||
@@ -223,11 +248,6 @@ namespace FoxTube.Pages
|
|||||||
likes.Text = $"{item.Statistics.LikeCount:0,0}";
|
likes.Text = $"{item.Statistics.LikeCount:0,0}";
|
||||||
rating.Value = (double)item.Statistics.DislikeCount / (double)(item.Statistics.DislikeCount + item.Statistics.LikeCount) * 100;
|
rating.Value = (double)item.Statistics.DislikeCount / (double)(item.Statistics.DislikeCount + item.Statistics.LikeCount) * 100;
|
||||||
|
|
||||||
//Setting category
|
|
||||||
VideoCategoriesResource.ListRequest categoryRequest = SecretsVault.Service.VideoCategories.List("snippet");
|
|
||||||
categoryRequest.Id = item.Snippet.CategoryId;
|
|
||||||
category.Text = (await categoryRequest.ExecuteAsync()).Items[0].Snippet.Title;
|
|
||||||
|
|
||||||
//Setting User's rate
|
//Setting User's rate
|
||||||
if (SecretsVault.IsAuthorized)
|
if (SecretsVault.IsAuthorized)
|
||||||
{
|
{
|
||||||
@@ -289,9 +309,8 @@ namespace FoxTube.Pages
|
|||||||
{
|
{
|
||||||
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("liveStreamingDetails");
|
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("liveStreamingDetails");
|
||||||
request.Id = videoId;
|
request.Id = videoId;
|
||||||
Video video = (await request.ExecuteAsync()).Items[0];
|
|
||||||
|
|
||||||
views.Text = $"{video.LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
|
views.Text = $"{(await request.ExecuteAsync()).Items[0].LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadStats()
|
void LoadStats()
|
||||||
@@ -326,9 +345,9 @@ namespace FoxTube.Pages
|
|||||||
audioItem.Click += downloadItemSelected;
|
audioItem.Click += downloadItemSelected;
|
||||||
downloadSelector.Items.Add(audioItem);
|
downloadSelector.Items.Add(audioItem);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch
|
||||||
{
|
{
|
||||||
loading.Error(e.GetType().ToString(), e.Message);
|
download.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,29 +372,33 @@ namespace FoxTube.Pages
|
|||||||
relatedVideos.Children.Add(new VideoCard(video.Id.VideoId));
|
relatedVideos.Children.Add(new VideoCard(video.Id.VideoId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Player_SetFullSize(object sender, params object[] e)
|
private void Player_Minimize(object sender, params object[] e)
|
||||||
{
|
{
|
||||||
|
if (isExtended == (bool)e[0])
|
||||||
|
return;
|
||||||
|
|
||||||
isExtended = (bool)e[0];
|
isExtended = (bool)e[0];
|
||||||
if(isExtended)
|
if(isExtended)
|
||||||
{
|
{
|
||||||
grid.ColumnDefinitions[1].Width = new GridLength(0);
|
grid.ColumnDefinitions[1].Width = new GridLength(0);
|
||||||
commandbar.Visibility = Visibility.Collapsed;
|
commandbar.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
mainScroll.Margin = new Thickness(0);
|
|
||||||
mainScroll.ChangeView(0, 0, null);
|
mainScroll.ChangeView(0, 0, null);
|
||||||
mainScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
|
|
||||||
|
mainScroll.Margin = new Thickness(0);
|
||||||
mainScroll.VerticalScrollMode = ScrollMode.Disabled;
|
mainScroll.VerticalScrollMode = ScrollMode.Disabled;
|
||||||
|
|
||||||
|
Methods.MainPage.MinimizeVideo();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
grid.ColumnDefinitions[1].Width = new GridLength(400);
|
||||||
commandbar.Visibility = Visibility.Visible;
|
commandbar.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
mainScroll.Margin = new Thickness(0,0,0,50);
|
mainScroll.Margin = new Thickness(0,0,0,50);
|
||||||
mainScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
|
|
||||||
mainScroll.VerticalScrollMode = ScrollMode.Auto;
|
mainScroll.VerticalScrollMode = ScrollMode.Auto;
|
||||||
|
|
||||||
if (wide)
|
Methods.MainPage.MaximizeVideo();
|
||||||
grid.ColumnDefinitions[1].Width = new GridLength(400);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,8 +410,8 @@ namespace FoxTube.Pages
|
|||||||
private async void openBrowser_Click(object sender, RoutedEventArgs e)
|
private async void openBrowser_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
player.Pause();
|
player.Pause();
|
||||||
string timecode = player.Elapsed.TotalSeconds > 10 ?
|
string timecode = player.Position.TotalSeconds > 10 ?
|
||||||
"&t=" + (int)player.Elapsed.TotalSeconds + "s" : string.Empty;
|
"&t=" + (int)player.Position.TotalSeconds + "s" : string.Empty;
|
||||||
|
|
||||||
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}{timecode}".ToUri());
|
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}{timecode}".ToUri());
|
||||||
}
|
}
|
||||||
@@ -398,15 +421,14 @@ namespace FoxTube.Pages
|
|||||||
Methods.MainPage.GoToVideo(videoId, playlistId);
|
Methods.MainPage.GoToVideo(videoId, playlistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CloseVideo()
|
||||||
|
{
|
||||||
|
player.Controls_CloseRequested(this, null);
|
||||||
|
}
|
||||||
|
|
||||||
private void grid_SizeChanged(object sender, SizeChangedEventArgs e)
|
private void grid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
{
|
{
|
||||||
Debug.WriteLine(e.NewSize.Width);
|
if (e.NewSize.Width > 1000 && mainContent.Children.Contains(pivot))
|
||||||
if (e.NewSize.Width > 1000)
|
|
||||||
wide = true;
|
|
||||||
else
|
|
||||||
wide = false;
|
|
||||||
|
|
||||||
if (e.NewSize.Width > 1000 && mainContent.Children.Contains(pivot) && !isExtended)
|
|
||||||
{
|
{
|
||||||
mainContent.Children.Remove(pivot);
|
mainContent.Children.Remove(pivot);
|
||||||
tabsPlaceholder.Children.Add(pivot);
|
tabsPlaceholder.Children.Add(pivot);
|
||||||
|
|||||||
@@ -156,4 +156,7 @@
|
|||||||
<data name="views.Text" xml:space="preserve">
|
<data name="views.Text" xml:space="preserve">
|
||||||
<value>Views:</value>
|
<value>Views:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="searchHeader" xml:space="preserve">
|
||||||
|
<value>Search results</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -120,6 +120,9 @@
|
|||||||
<data name="box.PlaceholderText" xml:space="preserve">
|
<data name="box.PlaceholderText" xml:space="preserve">
|
||||||
<value>Send a message</value>
|
<value>Send a message</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="failed" xml:space="preserve">
|
||||||
|
<value>Failed to send your message. It may be caused by turned on chat slow mode. Please, try again later. P.S. Report has been sent to developers</value>
|
||||||
|
</data>
|
||||||
<data name="moder.Text" xml:space="preserve">
|
<data name="moder.Text" xml:space="preserve">
|
||||||
<value>Moderator</value>
|
<value>Moderator</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -138,12 +138,15 @@
|
|||||||
<data name="editorCancel.Content" xml:space="preserve">
|
<data name="editorCancel.Content" xml:space="preserve">
|
||||||
<value>Cancel</value>
|
<value>Cancel</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="editorDelete.Content" xml:space="preserve">
|
<data name="editorDelete.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||||
<value>Delete comment</value>
|
<value>Delete comment</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="editorSubmin.Content" xml:space="preserve">
|
<data name="editorSubmin.Content" xml:space="preserve">
|
||||||
<value>Submit</value>
|
<value>Submit</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="failedDelete" xml:space="preserve">
|
||||||
|
<value>Failed to delete your commentary. Please, try again later.</value>
|
||||||
|
</data>
|
||||||
<data name="failedEdit" xml:space="preserve">
|
<data name="failedEdit" xml:space="preserve">
|
||||||
<value>Failed to edit your commentary. Please, try again later.</value>
|
<value>Failed to edit your commentary. Please, try again later.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -138,6 +138,12 @@
|
|||||||
<data name="ext" xml:space="preserve">
|
<data name="ext" xml:space="preserve">
|
||||||
<value>Extension</value>
|
<value>Extension</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="failedBody" xml:space="preserve">
|
||||||
|
<value>Bug report has been sent</value>
|
||||||
|
</data>
|
||||||
|
<data name="failedHead" xml:space="preserve">
|
||||||
|
<value>Error occured while dowloading a video</value>
|
||||||
|
</data>
|
||||||
<data name="gotoOrign.Text" xml:space="preserve">
|
<data name="gotoOrign.Text" xml:space="preserve">
|
||||||
<value>Go to original</value>
|
<value>Go to original</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -120,6 +120,9 @@
|
|||||||
<data name="addTo.Label" xml:space="preserve">
|
<data name="addTo.Label" xml:space="preserve">
|
||||||
<value>Add to</value>
|
<value>Add to</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="always" xml:space="preserve">
|
||||||
|
<value>Don't ask me again</value>
|
||||||
|
</data>
|
||||||
<data name="audio" xml:space="preserve">
|
<data name="audio" xml:space="preserve">
|
||||||
<value>Audio track</value>
|
<value>Audio track</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -129,6 +132,9 @@
|
|||||||
<data name="back.Text" xml:space="preserve">
|
<data name="back.Text" xml:space="preserve">
|
||||||
<value>Go back for 10 seconds</value>
|
<value>Go back for 10 seconds</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="cancel" xml:space="preserve">
|
||||||
|
<value>Cancel</value>
|
||||||
|
</data>
|
||||||
<data name="cast.Text" xml:space="preserve">
|
<data name="cast.Text" xml:space="preserve">
|
||||||
<value>Cast to device</value>
|
<value>Cast to device</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -141,12 +147,18 @@
|
|||||||
<data name="comments.Header" xml:space="preserve">
|
<data name="comments.Header" xml:space="preserve">
|
||||||
<value>Comments</value>
|
<value>Comments</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="countdown.Text" xml:space="preserve">
|
||||||
|
<value>Stream starts in:</value>
|
||||||
|
</data>
|
||||||
<data name="desc" xml:space="preserve">
|
<data name="desc" xml:space="preserve">
|
||||||
<value>Description</value>
|
<value>Description</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="download.Label" xml:space="preserve">
|
<data name="download.Label" xml:space="preserve">
|
||||||
<value>Download video</value>
|
<value>Download video</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="end" xml:space="preserve">
|
||||||
|
<value>Ends at:</value>
|
||||||
|
</data>
|
||||||
<data name="exitminiview.Text" xml:space="preserve">
|
<data name="exitminiview.Text" xml:space="preserve">
|
||||||
<value>Exit compact view mode</value>
|
<value>Exit compact view mode</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -156,9 +168,15 @@
|
|||||||
<data name="fwd.Text" xml:space="preserve">
|
<data name="fwd.Text" xml:space="preserve">
|
||||||
<value>Skip forward for 30 seconds</value>
|
<value>Skip forward for 30 seconds</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="generatedCaption" xml:space="preserve">
|
||||||
|
<value>(Auto-generated)</value>
|
||||||
|
</data>
|
||||||
<data name="goLive.Text" xml:space="preserve">
|
<data name="goLive.Text" xml:space="preserve">
|
||||||
<value>Go to live broadcast</value>
|
<value>Go to live broadcast</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="matureText" xml:space="preserve">
|
||||||
|
<value>This content may be not apropriate for children under 18</value>
|
||||||
|
</data>
|
||||||
<data name="maximize.Text" xml:space="preserve">
|
<data name="maximize.Text" xml:space="preserve">
|
||||||
<value>Maximize</value>
|
<value>Maximize</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -174,6 +192,9 @@
|
|||||||
<data name="next.Text" xml:space="preserve">
|
<data name="next.Text" xml:space="preserve">
|
||||||
<value>Next video</value>
|
<value>Next video</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="no" xml:space="preserve">
|
||||||
|
<value>No</value>
|
||||||
|
</data>
|
||||||
<data name="openWeb.Label" xml:space="preserve">
|
<data name="openWeb.Label" xml:space="preserve">
|
||||||
<value>Open in browser</value>
|
<value>Open in browser</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -195,15 +216,30 @@
|
|||||||
<data name="related.Header" xml:space="preserve">
|
<data name="related.Header" xml:space="preserve">
|
||||||
<value>Suggestons</value>
|
<value>Suggestons</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="schedule.Text" xml:space="preserve">
|
||||||
|
<value>Stream schedule:</value>
|
||||||
|
</data>
|
||||||
<data name="share.Label" xml:space="preserve">
|
<data name="share.Label" xml:space="preserve">
|
||||||
<value>Share</value>
|
<value>Share</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="signin" xml:space="preserve">
|
||||||
|
<value>Sign in</value>
|
||||||
|
</data>
|
||||||
|
<data name="signRequired" xml:space="preserve">
|
||||||
|
<value>Please, sign into your account</value>
|
||||||
|
</data>
|
||||||
|
<data name="start" xml:space="preserve">
|
||||||
|
<value>Starts at:</value>
|
||||||
|
</data>
|
||||||
<data name="streamElapsed.Text" xml:space="preserve">
|
<data name="streamElapsed.Text" xml:space="preserve">
|
||||||
<value>Elapsed time since stream start</value>
|
<value>Elapsed time since stream start</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="subs.Text" xml:space="preserve">
|
<data name="subs.Text" xml:space="preserve">
|
||||||
<value>Captions</value>
|
<value>Captions</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="subsSelector.Header" xml:space="preserve">
|
||||||
|
<value>Language</value>
|
||||||
|
</data>
|
||||||
<data name="subsSelector.PlaceholderText" xml:space="preserve">
|
<data name="subsSelector.PlaceholderText" xml:space="preserve">
|
||||||
<value>No captions are available</value>
|
<value>No captions are available</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -213,7 +249,16 @@
|
|||||||
<data name="subsSwitch.OnContent" xml:space="preserve">
|
<data name="subsSwitch.OnContent" xml:space="preserve">
|
||||||
<value>Captions</value>
|
<value>Captions</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="upcomingHeader.Text" xml:space="preserve">
|
||||||
|
<value>Stream hasn't started yet</value>
|
||||||
|
</data>
|
||||||
<data name="volume.Text" xml:space="preserve">
|
<data name="volume.Text" xml:space="preserve">
|
||||||
<value>Volume</value>
|
<value>Volume</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="wantContinue" xml:space="preserve">
|
||||||
|
<value>Do you want to continue</value>
|
||||||
|
</data>
|
||||||
|
<data name="yes" xml:space="preserve">
|
||||||
|
<value>Yes</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -156,4 +156,7 @@
|
|||||||
<data name="views.Text" xml:space="preserve">
|
<data name="views.Text" xml:space="preserve">
|
||||||
<value>Просмотры: </value>
|
<value>Просмотры: </value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="searchHeader" xml:space="preserve">
|
||||||
|
<value>Результаты поиска</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -120,6 +120,9 @@
|
|||||||
<data name="box.PlaceholderText" xml:space="preserve">
|
<data name="box.PlaceholderText" xml:space="preserve">
|
||||||
<value>Отправить сообщение</value>
|
<value>Отправить сообщение</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="failed" xml:space="preserve">
|
||||||
|
<value>Не удалось отправить ваше сообщение. Это могло произойти из-за включенного в этом чате замедленного режима. Пожалуйста, попробуйте позже. P.S. Отчет об ошибке был отправлен разработчикам</value>
|
||||||
|
</data>
|
||||||
<data name="moder.Text" xml:space="preserve">
|
<data name="moder.Text" xml:space="preserve">
|
||||||
<value>Модератор</value>
|
<value>Модератор</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -138,12 +138,15 @@
|
|||||||
<data name="editorCancel.Content" xml:space="preserve">
|
<data name="editorCancel.Content" xml:space="preserve">
|
||||||
<value>Отмена</value>
|
<value>Отмена</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="editorDelete.Content" xml:space="preserve">
|
<data name="editorDelete.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||||
<value>Удалить комментарий</value>
|
<value>Удалить комментарий</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="editorSubmin.Content" xml:space="preserve">
|
<data name="editorSubmin.Content" xml:space="preserve">
|
||||||
<value>Отправить</value>
|
<value>Отправить</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="failedDelete" xml:space="preserve">
|
||||||
|
<value>Не удалось удалить комментарий. Пожалуйста, попробуйте позже</value>
|
||||||
|
</data>
|
||||||
<data name="failedEdit" xml:space="preserve">
|
<data name="failedEdit" xml:space="preserve">
|
||||||
<value>Не удалось изменить комментарий. Пожалуйста, попробуйте позже</value>
|
<value>Не удалось изменить комментарий. Пожалуйста, попробуйте позже</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -138,6 +138,12 @@
|
|||||||
<data name="ext" xml:space="preserve">
|
<data name="ext" xml:space="preserve">
|
||||||
<value>Расширение</value>
|
<value>Расширение</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="failedBody" xml:space="preserve">
|
||||||
|
<value>Отчет об ошибке был отправлен</value>
|
||||||
|
</data>
|
||||||
|
<data name="failedHead" xml:space="preserve">
|
||||||
|
<value>При загрузке видео произошла ошибка</value>
|
||||||
|
</data>
|
||||||
<data name="gotoOrign.Text" xml:space="preserve">
|
<data name="gotoOrign.Text" xml:space="preserve">
|
||||||
<value>Открыть оригинал</value>
|
<value>Открыть оригинал</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -120,6 +120,9 @@
|
|||||||
<data name="addTo.Label" xml:space="preserve">
|
<data name="addTo.Label" xml:space="preserve">
|
||||||
<value>Добавить в</value>
|
<value>Добавить в</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="always" xml:space="preserve">
|
||||||
|
<value>Больше не спрашивать</value>
|
||||||
|
</data>
|
||||||
<data name="audio" xml:space="preserve">
|
<data name="audio" xml:space="preserve">
|
||||||
<value>Аудио дорожка</value>
|
<value>Аудио дорожка</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -129,6 +132,9 @@
|
|||||||
<data name="back.Text" xml:space="preserve">
|
<data name="back.Text" xml:space="preserve">
|
||||||
<value>Назад на 10 секунд</value>
|
<value>Назад на 10 секунд</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="cancel" xml:space="preserve">
|
||||||
|
<value>Отмена</value>
|
||||||
|
</data>
|
||||||
<data name="cast.Text" xml:space="preserve">
|
<data name="cast.Text" xml:space="preserve">
|
||||||
<value>Отправить на устройство</value>
|
<value>Отправить на устройство</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -141,12 +147,18 @@
|
|||||||
<data name="comments.Header" xml:space="preserve">
|
<data name="comments.Header" xml:space="preserve">
|
||||||
<value>Комментарии</value>
|
<value>Комментарии</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="countdown.Text" xml:space="preserve">
|
||||||
|
<value>Стрим начнется через:</value>
|
||||||
|
</data>
|
||||||
<data name="desc" xml:space="preserve">
|
<data name="desc" xml:space="preserve">
|
||||||
<value>Описание</value>
|
<value>Описание</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="download.Label" xml:space="preserve">
|
<data name="download.Label" xml:space="preserve">
|
||||||
<value>Скачать видео</value>
|
<value>Скачать видео</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="end" xml:space="preserve">
|
||||||
|
<value>Конец:</value>
|
||||||
|
</data>
|
||||||
<data name="exitminiview.Text" xml:space="preserve">
|
<data name="exitminiview.Text" xml:space="preserve">
|
||||||
<value>Развернуть</value>
|
<value>Развернуть</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -156,9 +168,15 @@
|
|||||||
<data name="fwd.Text" xml:space="preserve">
|
<data name="fwd.Text" xml:space="preserve">
|
||||||
<value>Вперед на 30 секунд</value>
|
<value>Вперед на 30 секунд</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="generatedCaption" xml:space="preserve">
|
||||||
|
<value>(Авто-перевод)</value>
|
||||||
|
</data>
|
||||||
<data name="goLive.Text" xml:space="preserve">
|
<data name="goLive.Text" xml:space="preserve">
|
||||||
<value>Перейти к прямому эфиру</value>
|
<value>Перейти к прямому эфиру</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="matureText" xml:space="preserve">
|
||||||
|
<value>Этот контент может быть неподходящим для лиц младше 18 лет</value>
|
||||||
|
</data>
|
||||||
<data name="maximize.Text" xml:space="preserve">
|
<data name="maximize.Text" xml:space="preserve">
|
||||||
<value>Развернуть</value>
|
<value>Развернуть</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -174,6 +192,9 @@
|
|||||||
<data name="next.Text" xml:space="preserve">
|
<data name="next.Text" xml:space="preserve">
|
||||||
<value>Следующее видео</value>
|
<value>Следующее видео</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="no" xml:space="preserve">
|
||||||
|
<value>Нет</value>
|
||||||
|
</data>
|
||||||
<data name="openWeb.Label" xml:space="preserve">
|
<data name="openWeb.Label" xml:space="preserve">
|
||||||
<value>Открыть в браузере</value>
|
<value>Открыть в браузере</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -195,15 +216,30 @@
|
|||||||
<data name="related.Header" xml:space="preserve">
|
<data name="related.Header" xml:space="preserve">
|
||||||
<value>Похожее</value>
|
<value>Похожее</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="schedule.Text" xml:space="preserve">
|
||||||
|
<value>Расписание эфира</value>
|
||||||
|
</data>
|
||||||
<data name="share.Label" xml:space="preserve">
|
<data name="share.Label" xml:space="preserve">
|
||||||
<value>Поделиться</value>
|
<value>Поделиться</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="signin" xml:space="preserve">
|
||||||
|
<value>Войти</value>
|
||||||
|
</data>
|
||||||
|
<data name="signRequired" xml:space="preserve">
|
||||||
|
<value>Пожалуйста, войдите в аккаунт</value>
|
||||||
|
</data>
|
||||||
|
<data name="start" xml:space="preserve">
|
||||||
|
<value>Начало:</value>
|
||||||
|
</data>
|
||||||
<data name="streamElapsed.Text" xml:space="preserve">
|
<data name="streamElapsed.Text" xml:space="preserve">
|
||||||
<value>Время, прошедшее с начала стрима</value>
|
<value>Время, прошедшее с начала стрима</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="subs.Text" xml:space="preserve">
|
<data name="subs.Text" xml:space="preserve">
|
||||||
<value>Субтитры</value>
|
<value>Субтитры</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="subsSelector.Header" xml:space="preserve">
|
||||||
|
<value>Язык субтитров</value>
|
||||||
|
</data>
|
||||||
<data name="subsSelector.PlaceholderText" xml:space="preserve">
|
<data name="subsSelector.PlaceholderText" xml:space="preserve">
|
||||||
<value>Нет доступных субтитров</value>
|
<value>Нет доступных субтитров</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -213,7 +249,16 @@
|
|||||||
<data name="subsSwitch.OnContent" xml:space="preserve">
|
<data name="subsSwitch.OnContent" xml:space="preserve">
|
||||||
<value>Субтитры</value>
|
<value>Субтитры</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="upcomingHeader.Text" xml:space="preserve">
|
||||||
|
<value>Прямой эфир еще не начался</value>
|
||||||
|
</data>
|
||||||
<data name="volume.Text" xml:space="preserve">
|
<data name="volume.Text" xml:space="preserve">
|
||||||
<value>Громкость</value>
|
<value>Громкость</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="wantContinue" xml:space="preserve">
|
||||||
|
<value>Хотите продолжить?</value>
|
||||||
|
</data>
|
||||||
|
<data name="yes" xml:space="preserve">
|
||||||
|
<value>Да</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -0,0 +1,632 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:FoxTube"
|
||||||
|
xmlns:controls="using:FoxTube.Controls">
|
||||||
|
|
||||||
|
<Style TargetType="local:PlayerControls">
|
||||||
|
<Setter Property="IsTabStop" Value="False" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="FlowDirection" Value="LeftToRight" />
|
||||||
|
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||||
|
<Setter Property="IsTextScaleFactorEnabled" Value="False" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="local:PlayerControls">
|
||||||
|
<Grid Background="Transparent">
|
||||||
|
<Grid.Resources>
|
||||||
|
<Style TargetType="Button" BasedOn="{StaticResource ButtonRevealStyle}">
|
||||||
|
<Setter Property="Width" Value="50"/>
|
||||||
|
<Setter Property="Height" Value="50"/>
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="AppBarButtonStyle" TargetType="AppBarButton">
|
||||||
|
<Setter Property="Width" Value="{ThemeResource MTCMediaButtonWidth}" />
|
||||||
|
<Setter Property="Height" Value="{ThemeResource MTCMediaButtonHeight}" />
|
||||||
|
<Setter Property="AllowFocusOnInteraction" Value="True" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="CommandBarStyle" TargetType="CommandBar">
|
||||||
|
<Setter Property="Height" Value="{ThemeResource MTCMediaButtonHeight}" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="MediaTextBlockStyle" TargetType="TextBlock">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
|
||||||
|
<Setter Property="FontSize" Value="{ThemeResource MTCMediaFontSize}" />
|
||||||
|
<Setter Property="FontFamily" Value="{ThemeResource MTCMediaFontFamily}" />
|
||||||
|
<Setter Property="Style" Value="{ThemeResource CaptionTextBlockStyle }" />
|
||||||
|
<Setter Property="IsTextScaleFactorEnabled" Value="False" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="PlayerSeek" TargetType="Slider">
|
||||||
|
<Setter Property="Background" Value="{ThemeResource SliderTrackFill}" />
|
||||||
|
<Setter Property="BorderThickness" Value="{ThemeResource SliderBorderThemeThickness}" />
|
||||||
|
<Setter Property="Foreground" Value="{ThemeResource SliderTrackValueFill}" />
|
||||||
|
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||||
|
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||||
|
<Setter Property="ManipulationMode" Value="None" />
|
||||||
|
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||||
|
<Setter Property="IsFocusEngagementEnabled" Value="True" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Slider">
|
||||||
|
<Grid Margin="{TemplateBinding Padding}">
|
||||||
|
<Grid.Resources>
|
||||||
|
<Style TargetType="Thumb" x:Key="SliderThumbStyle">
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Background" Value="{ThemeResource SliderThumbBackground}" />
|
||||||
|
<Setter Property="Foreground" Value="{ThemeResource SystemControlBackgroundChromeMediumBrush}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Thumb">
|
||||||
|
<Ellipse x:Name="ellipse"
|
||||||
|
Stroke="{TemplateBinding Background}"
|
||||||
|
StrokeThickness="2"
|
||||||
|
Fill="{TemplateBinding Foreground}" />
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="ProgressBar" x:Key="MediaSliderProgressBarStyle">
|
||||||
|
<Setter Property="Height" Value="{ThemeResource SliderTrackThemeHeight}" />
|
||||||
|
<Setter Property="Minimum" Value="0" />
|
||||||
|
<Setter Property="Maximum" Value="100" />
|
||||||
|
<Setter Property="Foreground" Value="{ThemeResource SystemControlHighlightChromeAltLowBrush}" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
</Style>
|
||||||
|
</Grid.Resources>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
|
<VisualState x:Name="Normal" />
|
||||||
|
<VisualState x:Name="Pressed">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Disabled">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderHeaderForegroundDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TopTickBar" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BottomTickBar" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="LeftTickBar" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RightTickBar" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTickBarFillDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="PointerOver">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect" Storyboard.TargetProperty="Fill">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
<VisualStateGroup x:Name="FocusEngagementStates">
|
||||||
|
<VisualState x:Name="FocusDisengaged" />
|
||||||
|
<VisualState x:Name="FocusEngagedHorizontal">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="True" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="FocusEngagedVertical">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="True" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
|
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||||
|
x:DeferLoadStrategy="Lazy"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Foreground="{ThemeResource SliderHeaderForeground}"
|
||||||
|
Margin="{ThemeResource SliderHeaderThemeMargin}"
|
||||||
|
Content="{TemplateBinding Header}"
|
||||||
|
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||||
|
FontWeight="{ThemeResource SliderHeaderThemeFontWeight}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Grid x:Name="SliderContainer"
|
||||||
|
Background="{ThemeResource SliderContainerBackground}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Control.IsTemplateFocusTarget="True">
|
||||||
|
<Grid x:Name="HorizontalTemplate" MinHeight="44">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="15" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="15" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Rectangle x:Name="HorizontalTrackRect"
|
||||||
|
Fill="{TemplateBinding Background}"
|
||||||
|
Height="{ThemeResource SliderTrackThemeHeight}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="3" />
|
||||||
|
<ProgressBar x:Name="DownloadProgressIndicator"
|
||||||
|
Style="{StaticResource MediaSliderProgressBarStyle}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Rectangle x:Name="HorizontalDecreaseRect" Fill="{TemplateBinding Foreground}" Grid.Row="1" />
|
||||||
|
<TickBar x:Name="TopTickBar"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Fill="{ThemeResource SliderTickBarFill}"
|
||||||
|
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Margin="0,0,0,4"
|
||||||
|
Grid.ColumnSpan="3" />
|
||||||
|
<TickBar x:Name="HorizontalInlineTickBar"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Fill="{ThemeResource SliderInlineTickBarFill}"
|
||||||
|
Height="{ThemeResource SliderTrackThemeHeight}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="3" />
|
||||||
|
<TickBar x:Name="BottomTickBar"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Fill="{ThemeResource SliderTickBarFill}"
|
||||||
|
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="3" />
|
||||||
|
<Thumb x:Name="HorizontalThumb"
|
||||||
|
Style="{StaticResource SliderThumbStyle}"
|
||||||
|
Height="15"
|
||||||
|
Width="15"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.RowSpan="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
AutomationProperties.AccessibilityView="Raw">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip x:Name="ThumbnailTooltip">
|
||||||
|
<ContentPresenter Content="{Binding}" />
|
||||||
|
</ToolTip>
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
<Thumb.DataContext>
|
||||||
|
<Grid Height="112" Width="192">
|
||||||
|
<Image x:Name="ThumbnailImage"/>
|
||||||
|
<Border Background="{ThemeResource SystemControlBackgroundBaseMediumBrush}"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
HorizontalAlignment="Left">
|
||||||
|
<TextBlock x:Name="TimeElapsedPreview"
|
||||||
|
Margin="6,1,6,3"
|
||||||
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
|
IsTextScaleFactorEnabled="False"
|
||||||
|
Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Thumb.DataContext>
|
||||||
|
</Thumb>
|
||||||
|
</Grid>
|
||||||
|
<Grid x:Name="VerticalTemplate" MinWidth="44" Visibility="Collapsed">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="18" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="18" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Rectangle x:Name="VerticalTrackRect"
|
||||||
|
Fill="{TemplateBinding Background}"
|
||||||
|
Width="{ThemeResource SliderTrackThemeHeight}"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="3" />
|
||||||
|
<Rectangle x:Name="VerticalDecreaseRect"
|
||||||
|
Fill="{TemplateBinding Foreground}"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="2" />
|
||||||
|
<TickBar x:Name="LeftTickBar"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Fill="{ThemeResource SliderTickBarFill}"
|
||||||
|
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0,0,4,0"
|
||||||
|
Grid.RowSpan="3" />
|
||||||
|
<TickBar x:Name="VerticalInlineTickBar"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Fill="{ThemeResource SliderInlineTickBarFill}"
|
||||||
|
Width="{ThemeResource SliderTrackThemeHeight}"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="3" />
|
||||||
|
<TickBar x:Name="RightTickBar"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Fill="{ThemeResource SliderTickBarFill}"
|
||||||
|
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Margin="4,0,0,0"
|
||||||
|
Grid.Column="2"
|
||||||
|
Grid.RowSpan="3" />
|
||||||
|
<Thumb x:Name="VerticalThumb"
|
||||||
|
Style="{StaticResource SliderThumbStyle}"
|
||||||
|
DataContext="{TemplateBinding Value}"
|
||||||
|
Width="24"
|
||||||
|
Height="8"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
FocusVisualMargin="-6,-14,-6,-14"
|
||||||
|
AutomationProperties.AccessibilityView="Raw" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Grid.Resources>
|
||||||
|
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<!-- ControlPanel Visibility states -->
|
||||||
|
<VisualStateGroup x:Name="ControlPanelVisibilityStates">
|
||||||
|
<VisualState x:Name="ControlPanelFadeIn">
|
||||||
|
<Storyboard>
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Border">
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="center">
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="centerBgr">
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalBottom" From="50" To ="0" Duration="0:0:0.3"/>
|
||||||
|
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalTop" From="-50" To ="0" Duration="0:0:0.3"/>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="ControlPanelFadeOut">
|
||||||
|
<Storyboard>
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Border">
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="center">
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="centerBgr">
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
|
||||||
|
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0" />
|
||||||
|
</DoubleAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="Border">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="center">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalBottom" From="0" To ="50" Duration="0:0:0.7"/>
|
||||||
|
<DoubleAnimation Storyboard.TargetProperty="Y" Storyboard.TargetName="TranslateVerticalTop" From="0" To ="-50" Duration="0:0:0.7"/>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
<!-- ControlPanel Visibility states -->
|
||||||
|
<!-- Media state visual states -->
|
||||||
|
<VisualStateGroup x:Name="MediaStates">
|
||||||
|
<VisualState x:Name="Normal" />
|
||||||
|
<VisualState x:Name="Buffering">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="BufferingProgressBar">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0">
|
||||||
|
<DiscreteObjectKeyFrame.Value>
|
||||||
|
<Visibility>Visible</Visibility>
|
||||||
|
</DiscreteObjectKeyFrame.Value>
|
||||||
|
</DiscreteObjectKeyFrame>
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Loading">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="BufferingProgressBar">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0">
|
||||||
|
<DiscreteObjectKeyFrame.Value>
|
||||||
|
<Visibility>Visible</Visibility>
|
||||||
|
</DiscreteObjectKeyFrame.Value>
|
||||||
|
</DiscreteObjectKeyFrame>
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ProgressSlider"
|
||||||
|
Storyboard.TargetProperty="Opacity"
|
||||||
|
To="0"
|
||||||
|
Duration="0" />
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
<!--<VisualState x:Name="Error">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ErrorBorder">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0">
|
||||||
|
<DiscreteObjectKeyFrame.Value>
|
||||||
|
<Visibility>Visible</Visibility>
|
||||||
|
</DiscreteObjectKeyFrame.Value>
|
||||||
|
</DiscreteObjectKeyFrame>
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>-->
|
||||||
|
<VisualState x:Name="Disabled">
|
||||||
|
<Storyboard />
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
|
||||||
|
<!-- Focus states -->
|
||||||
|
<VisualStateGroup x:Name="FocusStates">
|
||||||
|
<VisualState x:Name="Focused">
|
||||||
|
<Storyboard>
|
||||||
|
<DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
|
||||||
|
Storyboard.TargetProperty="Opacity"
|
||||||
|
To="1"
|
||||||
|
Duration="0" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
|
||||||
|
Storyboard.TargetProperty="Opacity"
|
||||||
|
To="1"
|
||||||
|
Duration="0" />
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Unfocused" />
|
||||||
|
<VisualState x:Name="PointerFocused" />
|
||||||
|
</VisualStateGroup>
|
||||||
|
|
||||||
|
<VisualStateGroup x:Name="PlayPauseStates">
|
||||||
|
<VisualState x:Name="PlayState" />
|
||||||
|
<VisualState x:Name="PauseState">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlayPauseSymbol" Storyboard.TargetProperty="Symbol">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="Pause" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
<!-- FullWindow states -->
|
||||||
|
<VisualStateGroup x:Name="FullWindowStates">
|
||||||
|
<VisualState x:Name="NonFullWindowState" />
|
||||||
|
<VisualState x:Name="FullWindowState">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FullWindowSymbol" Storyboard.TargetProperty="Symbol">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="BackToWindow" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
|
|
||||||
|
<Border x:Name="Border">
|
||||||
|
<Grid x:Name="ControlPanelGrid">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid x:Name="header">
|
||||||
|
<Grid.RenderTransform>
|
||||||
|
<TranslateTransform x:Name="TranslateVerticalTop"/>
|
||||||
|
</Grid.RenderTransform>
|
||||||
|
|
||||||
|
<Grid.Background>
|
||||||
|
<AcrylicBrush TintColor="#CC000000" TintOpacity=".6"/>
|
||||||
|
</Grid.Background>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Button x:Name="minimize">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<StackPanel Margin="10,0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<TextBlock x:Name="title" TextTrimming="CharacterEllipsis" FontSize="20" MaxLines="1"/>
|
||||||
|
<TextBlock Foreground="LightGray" FontStyle="Italic" x:Name="author"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Grid.Column="2" x:Name="headerToolbar">
|
||||||
|
<Button x:Name="close">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="CastButton">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="CompactOverlayButton" VerticalAlignment="Top" HorizontalAlignment="Right">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" x:Name="centerBgr" Visibility="Collapsed" IsHitTestVisible="False" Background="#66000000"/>
|
||||||
|
<Grid Grid.Row="1" x:Name="center" Visibility="Collapsed">
|
||||||
|
<Button x:Name="maximize" VerticalAlignment="Top" HorizontalAlignment="Left" IsHitTestVisible="True">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="compactClose" VerticalAlignment="Top" HorizontalAlignment="Right">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="centralStack"/>
|
||||||
|
|
||||||
|
<ProgressBar VerticalAlignment="Bottom" x:Name="compactSeek" Background="Transparent"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="2" x:Name="footer">
|
||||||
|
<Grid.RenderTransform>
|
||||||
|
<TranslateTransform x:Name="TranslateVerticalBottom"/>
|
||||||
|
</Grid.RenderTransform>
|
||||||
|
|
||||||
|
<Grid.Background>
|
||||||
|
<AcrylicBrush TintColor="#CC000000" TintOpacity=".6"/>
|
||||||
|
</Grid.Background>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" x:Name="leftStack">
|
||||||
|
<Button x:Name="PlayPauseButton">
|
||||||
|
<SymbolIcon x:Name="PlayPauseSymbol" Symbol="Play"/>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="next">
|
||||||
|
<SymbolIcon Symbol="Next"/>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="volume">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="-10">
|
||||||
|
<Button x:Name="AudioMuteButton" Width="50" Height="50" Background="Transparent" FontFamily="Segoe MDL2 Assets" FontSize="25">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
<Slider Foreground="Red" Orientation="Horizontal" Width="150" Margin="10,5,10,0" VerticalAlignment="Center" x:Name="VolumeSlider"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid Grid.Column="1" Margin="10,5">
|
||||||
|
<TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Left" x:Name="TimeElapsedElement" Text="00:00"/>
|
||||||
|
<TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Right" x:Name="TimeRemainingElement" Text="00:00"/>
|
||||||
|
<Grid VerticalAlignment="Top" Height="3" Margin="0,15,0,0">
|
||||||
|
<ProgressBar Background="#66FFFFFF" Foreground="#66FFFFFF" x:Name="BufferingProgressBar"/>
|
||||||
|
</Grid>
|
||||||
|
<Slider x:Name="ProgressSlider" Style="{StaticResource PlayerSeek}" IsThumbToolTipEnabled="False" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Grid.Column="2" x:Name="rightStack">
|
||||||
|
<Button x:Name="SkipBackwardButton">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="SkipForwardButton">
|
||||||
|
<FontIcon Glyph=""/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Line Stroke="White" StrokeThickness="2" Y1="5" Y2="45"/>
|
||||||
|
|
||||||
|
<Button x:Name="cc">
|
||||||
|
<SymbolIcon Symbol="ClosedCaption"/>
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout>
|
||||||
|
<StackPanel Width="225">
|
||||||
|
<ToggleSwitch x:Name="ccSwitch" OnContent="Subtitles" OffContent="Subtitles" x:Uid="/VideoPage/subsSwitch"/>
|
||||||
|
<ComboBox x:Name="ccSelector" Header="Language" x:Uid="/VideoPage/subsSelector" PlaceholderText="No subtitles are available" HorizontalAlignment="Stretch"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button x:Name="quality">
|
||||||
|
<SymbolIcon Symbol="Setting"/>
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout>
|
||||||
|
<ComboBox Width="225" x:Uid="/VideoPage/qualitySelector" Header="Quality" x:Name="qualitySelector"/>
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button x:Name="FullWindowButton">
|
||||||
|
<SymbolIcon x:Name="FullWindowSymbol" Symbol="FullScreen"/>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<controls:LiveCaptions Visibility="Collapsed" x:Name="captions"/>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
Reference in New Issue
Block a user