diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index c0f2ed0..dc925ae 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -1,7 +1,10 @@ -using FoxTube.Pages; -using System.Diagnostics; +using FoxTube.Classes; +using FoxTube.Controls; +using FoxTube.Pages; +using Microsoft.AppCenter.Analytics; +using System.Collections.Generic; using Windows.ApplicationModel.Activation; -using Windows.UI.Notifications; +using Windows.Globalization; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -9,47 +12,67 @@ namespace FoxTube { sealed partial class App : Application { - public App() - { + public App() => InitializeComponent(); - } - /*protected override void OnLaunched(LaunchActivatedEventArgs e) + /*void Initialize() { - if (!(Window.Current.Content is Frame)) - Window.Current.Content = new Frame(); + Settings.LoadData(); - if (e.PrelaunchActivated == false) + switch (Settings.Theme) { - if ((Window.Current.Content as Frame).Content == null) - (Window.Current.Content as Frame).Navigate(typeof(MainPage), e.Arguments); - - Window.Current.Activate(); + case 0: + RequestedTheme = ApplicationTheme.Light; + break; + case 1: + RequestedTheme = ApplicationTheme.Dark; + break; } + ApplicationLanguages.PrimaryLanguageOverride = Settings.Language; + + Processes.InitializeApp(); + + Suspending += (s, e) => Processes.SuspendApp(); + UnhandledException += (s, e) => Analytics.TrackEvent("The app crashed", new Dictionary() + { + { "Exception", e.Exception.GetType().ToString() }, + { "Details", e.Message }, + { "StackTrace", e.Exception.StackTrace } + }); }*/ - protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) + protected override void OnLaunched(LaunchActivatedEventArgs e) { + if (!e.PrelaunchActivated && Window.Current.Content == null) + Window.Current.Content = new MainPage(e.SplashScreen, e.Arguments); + Window.Current.Activate(); + } + + protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args) + { + var deferral = args.TaskInstance.GetDeferral(); base.OnBackgroundActivated(args); + + await Methods.ProcessBackgroundToast((args.TaskInstance.TriggerDetails as ToastNotificationActivatedEventArgs).Argument); + + deferral.Complete(); } protected override void OnActivated(IActivatedEventArgs e) { base.OnActivated(e); - //TODO: Check this shit - if (!(Window.Current.Content is Frame rootFrame)) - { - rootFrame = new Frame(); - Window.Current.Content = rootFrame; - } - - if (rootFrame.Content == null) - rootFrame.Navigate(typeof(MainPage)); - + if (Window.Current.Content == null) + Window.Current.Content = new MainPage(e.SplashScreen, e); Window.Current.Activate(); - Debug.WriteLine("Hello, World"); + /*if(e.Kind == ActivationKind.ToastNotification) + { + if (UserManagement.IsAuthorized) + Methods.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); + else + UserManagement.AuthorizationStateChanged += (arg) => Methods.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument); + }*/ } } } diff --git a/FoxTube/Classes/Downloads.cs b/FoxTube/Classes/Downloads.cs new file mode 100644 index 0000000..8b64ae2 --- /dev/null +++ b/FoxTube/Classes/Downloads.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FoxTube.Classes +{ + public static class Downloads + { + public static void Cancel(string id) + { + + } + } +} diff --git a/FoxTube/Classes/Extensions.cs b/FoxTube/Classes/Extensions.cs new file mode 100644 index 0000000..25da4d6 --- /dev/null +++ b/FoxTube/Classes/Extensions.cs @@ -0,0 +1,36 @@ +using Google.Apis.YouTube.v3.Data; +using System; +using System.Collections; +using System.Linq; + +namespace FoxTube +{ + public delegate void AuthorizationChangedEventHandler(bool isAuthorized); + public delegate void SubscriptionChangedEventHandler(string action, Subscription subscription); + public delegate void ProVersionStateChangedEventHandler(bool purchased, string price); + public delegate void NavigatingOccured(Type sourcePageType, object parameter); + public delegate void NavigationOccured(IList pivotCollection, int? selectedPivot); + public delegate void SimpleEventHandler(); + + public enum LoadingScreenState + { + Loading = 0, + Loaded = 1, + Error = 2, + Blocked = 3 + } + + public interface INavigationPage + { + object Parameter { get; set; } + } + + public static class Extensions + { + public static Uri ToUri(this string str) => + string.IsNullOrWhiteSpace(str) ? null : new Uri(str); + + public static bool Belongs(this T obj, params T[] values) => + values.Contains(obj); + } +} diff --git a/FoxTube/Classes/Methods.cs b/FoxTube/Classes/Methods.cs new file mode 100644 index 0000000..393ce1a --- /dev/null +++ b/FoxTube/Classes/Methods.cs @@ -0,0 +1,85 @@ +using FoxTube.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI; +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; + +namespace FoxTube +{ + public static class Methods + { + public static void ProcessToast(string argument) + { + string[] args = argument.Split('|'); + + switch (args[0]) + { + case "changelog": + case "inbox": + Navigation.GoToDeveloper(args[1]); + break; + + case "video": + Navigation.GoToVideo(args[1]); + break; + + case "channel": + Navigation.GoToChannel(args[1]); + break; + + case "download": + Navigation.GoToDownloads(); + break; + case "clipboard": + switch (args[1]) + { + case "video": + Navigation.GoToVideo(args[2]); + break; + case "channel": + Navigation.GoToChannel(args[2]); + break; + case "playlist": + Navigation.GoToPlaylist(args[2]); + break; + } + break; + } + } + + public static async Task ProcessBackgroundToast(string argument) + { + try + { + string[] args = argument.Split('|'); + + switch (args[0]) + { + case "dcancel": + Downloads.Cancel(args[1]); + break; + case "later": + await UserManagement.AddItemToWL(args[1]); + break; + } + } + catch { } + } + + public static void SetTitleBar() + { + var titleBar = ApplicationView.GetForCurrentView().TitleBar; + + titleBar.ButtonBackgroundColor = Colors.Transparent; + titleBar.ButtonHoverBackgroundColor = Colors.IndianRed; + titleBar.ButtonPressedBackgroundColor = Colors.DarkRed; + titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + titleBar.ButtonInactiveForegroundColor = Colors.Gray; + titleBar.ButtonForegroundColor = Colors.White; + } + } +} diff --git a/FoxTube/Classes/Navigation.cs b/FoxTube/Classes/Navigation.cs new file mode 100644 index 0000000..972e4dc --- /dev/null +++ b/FoxTube/Classes/Navigation.cs @@ -0,0 +1,56 @@ +using FoxTube.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FoxTube +{ + public static class Navigation + { + public static void GoToSubscriptions() + { + + } + public static void GoToSearch(SearchParameters args) + { + + } + + public static void GoToChannel(string id) + { + + } + + public static void GoToHome() + { + + } + + public static void GoToVideo(string id, string playlistId = null, bool incognito = false) + { + + } + + public static void GoToDeveloper(string id) + { + + } + + public static void GoToPlaylist(string id) + { + + } + + public static void GoToHistory() + { + + } + + public static void GoToDownloads() + { + + } + } +} diff --git a/FoxTube/Classes/Processes.cs b/FoxTube/Classes/Processes.cs new file mode 100644 index 0000000..347a201 --- /dev/null +++ b/FoxTube/Classes/Processes.cs @@ -0,0 +1,248 @@ +using Microsoft.AppCenter; +using Microsoft.AppCenter.Analytics; +using Microsoft.Services.Store.Engagement; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Windows.ApplicationModel.Background; +using Windows.ApplicationModel.DataTransfer; +using Windows.ApplicationModel.Resources; +using Windows.Data.Xml.Dom; +using Windows.System; +using Windows.System.Power; +using Windows.UI.Core; +using Windows.UI.Notifications; +using Windows.UI.Popups; +using Windows.UI.Xaml; + +namespace FoxTube.Classes +{ + public static class Processes + { + static CoreWindowActivationState windowState = CoreWindowActivationState.CodeActivated; + static ResourceLoader resources = ResourceLoader.GetForViewIndependentUse("Inbox"); + + static Stopwatch sw = new Stopwatch(); + public static async void InitializeApp() => await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + CheckVersion(); + + RegisterToastTask(); + if (Settings.DevNotifications || Settings.VideoNotifications) + RegisterTask(); + else + UnregisterTask(); + + AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics)); + AppCenter.SetCountryCode(Settings.Region); + + sw.Start(); + + Window.Current.Activated += (s, e) => windowState = e.WindowActivationState; + + // TODO: Initialize other stuff + + if (Settings.ProcessClipboard) + { + Clipboard.ContentChanged += ParseClipboard; + ParseClipboard(); + } + + PromptFeedback(); + }); + + public static void SuspendApp() + { + sw.Stop(); + + Settings.Uptime += sw.Elapsed; + + // TODO: Save other stuff + + Analytics.TrackEvent("Session terminated", new Dictionary + { + { "Uptime", sw.Elapsed.ToString() }, + { "Total time", Settings.Uptime.ToString() } + }); + } + + static async void ParseClipboard(object sender = null, object e = null) + { + if (windowState != CoreWindowActivationState.Deactivated || !Settings.ProcessClipboard) + return; + + try + { + string link = await Clipboard.GetContent().GetTextAsync(); + + if (!link.Contains("youtube") && !link.Contains("youtu.be")) + return; + + string type = string.Empty; + string name = string.Empty; + + if (YoutubeExplode.YoutubeClient.TryParseChannelId(link, out string id)) + { + type = "channel"; + name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title; + } + else if (YoutubeExplode.YoutubeClient.TryParsePlaylistId(link, out id)) + { + type = "playlist"; + name = (await new YoutubeExplode.YoutubeClient().GetPlaylistAsync(id)).Title; + } + else if (YoutubeExplode.YoutubeClient.TryParseUsername(link, out id)) + { + id = await new YoutubeExplode.YoutubeClient().GetChannelIdAsync(id); + type = "channel"; + name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title; + } + else if (YoutubeExplode.YoutubeClient.TryParseVideoId(link, out id)) + { + type = "video"; + name = (await new YoutubeExplode.YoutubeClient().GetVideoAsync(id)).Title; + } + + if (string.IsNullOrWhiteSpace(id)) + return; + + XmlDocument toastXml = new XmlDocument(); + toastXml.LoadXml($@" + + + {resources.GetString("/Toasts/clipboardHead")} + {name} + {resources.GetString($"/Generic/{type}")} + + + + + + "); + ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml)); + } + catch { } + } + + static void CheckVersion() + { + if (Settings.GetCurrentVersion() == Settings.Version) + return; + + try + { + XmlDocument toast = new XmlDocument(); + toast.LoadXml($@" + + + + + {resources.GetString("/Inbox/changelog")} + {resources.GetString("/Inbox/whatsNew")} {Settings.GetCurrentVersion()} + + + "); + + ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toast)); + + Settings.Version = Settings.GetCurrentVersion(); + } + catch { } + } + + static async void RegisterToastTask() + { + if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals("foxtubeToast"))) + return; + + var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync(); + if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || + backgroundRequest == BackgroundAccessStatus.DeniedByUser || + backgroundRequest == BackgroundAccessStatus.Unspecified) + return; + + BackgroundTaskBuilder builder = new BackgroundTaskBuilder() { Name = "foxtubeToast" }; + builder.SetTrigger(new ToastNotificationActionTrigger()); + + BackgroundTaskRegistration registration = builder.Register(); + } + + static async void RegisterTask() + { + if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals("foxtubeBackground"))) + return; + + var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync(); + if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || + backgroundRequest == BackgroundAccessStatus.DeniedByUser || + backgroundRequest == BackgroundAccessStatus.Unspecified || + PowerManager.EnergySaverStatus == EnergySaverStatus.On) + return; + + BackgroundTaskBuilder builder = new BackgroundTaskBuilder() + { + Name = "foxtubeBackgound", + IsNetworkRequested = true, + TaskEntryPoint = "FoxTube.Background.BackgroundProcessor" + }; + builder.SetTrigger(new TimeTrigger(15, false)); + + BackgroundTaskRegistration registration = builder.Register(); + } + + static void UnregisterTask() + { + if (!(BackgroundTaskRegistration.AllTasks.Values.ToList().Find(i => i.Name == "foxtubeBackground") is IBackgroundTaskRegistration task)) + return; + + task.Unregister(true); + } + + static async void PromptFeedback() + { + if (Settings.Uptime.TotalHours >= 12 && Settings.PromptFeedback) + { + Analytics.TrackEvent("Prompting feedback", new Dictionary + { + { "Total uptime", Settings.Uptime.ToString() } + }); + + MessageDialog dialog = new MessageDialog(resources.GetString("/Dialogs/feedbackMessage")); + dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/dontAsk"), (command) => Settings.PromptFeedback = false)); + dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/promptLater"))); + dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/sure"), async (command) => + { + Settings.PromptFeedback = false; + if (StoreServicesFeedbackLauncher.IsSupported()) + await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync(); + else + await Launcher.LaunchUriAsync("mailto:feedback@xfox111.net".ToUri()); + })); + dialog.DefaultCommandIndex = 2; + dialog.CancelCommandIndex = 1; + await dialog.ShowAsync(); + } + + if (Settings.Uptime.TotalHours >= 24 && Settings.PromptReview) + { + Analytics.TrackEvent("Prompting review", new Dictionary + { + { "Total uptime", Settings.Uptime.ToString() } + }); + + MessageDialog dialog = new MessageDialog(resources.GetString("/Dialogs/rate")); + dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/dontAsk"), (command) => Settings.PromptReview = false)); + dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/promptLater"))); + dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/sure"), async (command) => + { + Settings.PromptReview = false; + await Launcher.LaunchUriAsync("ms-windows-store://review/?ProductId=9NCQQXJTDLFH".ToUri()); + })); + dialog.DefaultCommandIndex = 2; + dialog.CancelCommandIndex = 1; + await dialog.ShowAsync(); + } + } + } +} diff --git a/FoxTube/Classes/SearchParameters.cs b/FoxTube/Classes/SearchParameters.cs new file mode 100644 index 0000000..f9c8967 --- /dev/null +++ b/FoxTube/Classes/SearchParameters.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FoxTube.Classes +{ + public class SearchParameters + { + public SearchParameters(string query) + { + + } + } +} diff --git a/FoxTube/Classes/Service.cs b/FoxTube/Classes/Service.cs new file mode 100644 index 0000000..03e2401 --- /dev/null +++ b/FoxTube/Classes/Service.cs @@ -0,0 +1,49 @@ +using System; +using System.Net; +using Windows.Services.Store; + +namespace FoxTube +{ + public static class Service + { + public static bool AdsCheckPerformed { get; private set; } = false; + public static bool AdsDisabled { get; private set; } = true; + public static event ProVersionStateChangedEventHandler Purchased; + + public static NetworkCredential EmailCredential => new NetworkCredential("foxtube.bot@xfox111.net", "JkY39w$.7?bT57O,8k3a"); + + private static bool TestAds => false; //TODO: Change this bool + public static string AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh"; + public static string AdUnitId => TestAds ? "test" : "1100037769"; + + public static async void CheckAddons() + { + try + { + StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" }); + + if (requset.Products["9NP1QK556625"].IsInUserCollection) + return; + + AdsDisabled = false; + Purchased?.Invoke(false, requset.Products["9NP1QK556625"].Price.FormattedPrice); + } + catch { } + } + + public static async void GetPro() + { + try + { + StorePurchaseResult request = await StoreContext.GetDefault().RequestPurchaseAsync("9NP1QK556625"); + + if (!request.Status.Belongs(StorePurchaseStatus.AlreadyPurchased, StorePurchaseStatus.Succeeded)) + return; + + Purchased?.Invoke(true, ""); + AdsDisabled = true; + } + catch { } + } + } +} diff --git a/FoxTube/Classes/Settings.cs b/FoxTube/Classes/Settings.cs new file mode 100644 index 0000000..c2cad10 --- /dev/null +++ b/FoxTube/Classes/Settings.cs @@ -0,0 +1,266 @@ +using Newtonsoft.Json; +using System; +using System.Globalization; +using System.Linq; +using Windows.ApplicationModel; +using Windows.Storage; +using Windows.UI.Xaml.Controls; + +namespace FoxTube.Classes +{ + public static class Settings + { + public class SettingsContainer + { + public string videoQuality = "remember"; + public string rememberedQuality = "1080p"; + + public bool videoNotifications = true; + public bool devNotifications = true; + + public bool checkConnection = true; + public bool autoplay = true; + public double volume = 100; + + public string language = GetLanguage(); + public string relevanceLanguage = CultureInfo.InstalledUICulture.TwoLetterISOLanguageName; + public string region = CultureInfo.InstalledUICulture.Name.Split('-')[1]; + public int safeSearch = 0; + + public bool[] hasAccount = new bool[5] { false, false, false, false, false }; + public int theme = 2; + + public string version = GetCurrentVersion(); + public bool mature = false; + + public TimeSpan uptime = TimeSpan.FromSeconds(0); + public bool promptReview = true; + public bool promptFeedback = true; + + public bool processClipboard = true; + public bool minimizeCommandbar = false; + + static string GetLanguage() + { + if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName)) + return "ru-RU"; + else + return "en-US"; + } + } + + public static string GetCurrentVersion() + { + PackageVersion ver = Package.Current.Id.Version; + return $"{ver.Major}.{ver.Minor}"; + } + + public static string VideoQuality + { + get => Container.videoQuality; + set + { + Container.videoQuality = value; + SaveData(); + } + } + public static string RememberedQuality + { + get => Container.rememberedQuality; + set + { + Container.rememberedQuality = value; + SaveData(); + } + } + + public static bool VideoNotifications + { + get => Container.videoNotifications; + set + { + Container.videoNotifications = value; + SaveData(); + } + } + public static bool DevNotifications + { + get => Container.devNotifications; + set + { + Container.devNotifications = value; + SaveData(); + } + } + + public static bool CheckConnection + { + get => Container.checkConnection; + set + { + Container.checkConnection = value; + SaveData(); + } + } + public static bool Autoplay + { + get => Container.autoplay; + set + { + Container.autoplay = value; + SaveData(); + } + } + public static double Volume + { + get => Container.volume; + set + { + Container.volume = value; + SaveData(); + } + } + + public static string Language + { + get => Container.language; + set + { + Container.language = value; + SaveData(); + } + } + public static string RelevanceLanguage + { + get => Container.relevanceLanguage; + set + { + Container.relevanceLanguage = value; + SaveData(); + } + } + public static string Region + { + get => Container.region; + set + { + Container.region = value; + SaveData(); + } + } + public static int SafeSearch + { + get => Container.safeSearch; + set + { + Container.safeSearch = value; + SaveData(); + } + } + public static bool[] HasAccount => Container.hasAccount; + public static void SetAccount(int index, bool isAuthorized) + { + HasAccount[index] = isAuthorized; + SaveData(); + } + public static int Theme + { + get => Container.theme; + set + { + Container.theme = value; + SaveData(); + } + } + + public static string Version + { + get => Container.version; + set + { + Container.version = value; + SaveData(); + } + } + public static bool Mature + { + get => Container.mature; + set + { + Container.mature = value; + SaveData(); + } + } + + public static TimeSpan Uptime + { + get => Container.uptime; + set + { + Container.uptime = value; + SaveData(); + } + } + public static bool PromptReview + { + get => Container.promptReview; + set + { + Container.promptReview = value; + SaveData(); + } + } + public static bool PromptFeedback + { + get => Container.promptFeedback; + set + { + Container.promptFeedback = value; + SaveData(); + } + } + + public static bool ProcessClipboard + { + get => Container.processClipboard; + set + { + Container.processClipboard = value; + SaveData(); + } + } + + public static AppBarClosedDisplayMode AppBarClosedMode + { + get => Container.minimizeCommandbar ? AppBarClosedDisplayMode.Minimal : AppBarClosedDisplayMode.Compact; + set + { + Container.minimizeCommandbar = value == AppBarClosedDisplayMode.Minimal; + SaveData(); + } + } + + //Settings storage + static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings; + static SettingsContainer Container = new SettingsContainer(); + + /// + /// Loads saved settings from storage + /// + public static void LoadData() + { + try + { + Container = JsonConvert.DeserializeObject(storage.Values["settings"] as string); + } + catch + { + Container = new SettingsContainer(); + SaveData(); + } + } + + static void SaveData() => + storage.Values["settings"] = JsonConvert.SerializeObject(Container); + } +} diff --git a/FoxTube/Classes/User.cs b/FoxTube/Classes/User.cs new file mode 100644 index 0000000..6b5398e --- /dev/null +++ b/FoxTube/Classes/User.cs @@ -0,0 +1,99 @@ +using Google.Apis.YouTube.v3.Data; +using YoutubeExplode.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Google.Apis.Oauth2.v2.Data; +using Google.Apis.Auth.OAuth2; +using Google.Apis.YouTube.v3; +using Google.Apis.Services; +using System.Threading; +using YoutubeExplode; +using System.Net.Http; + +namespace FoxTube.Classes +{ + public class User + { + public List Subscriptions { get; } + + public List WatchLater { get; } + public List WebHistory { get; } + public List AppHistory { get; } + + public Userinfoplus UserInfo { get; } + public UserCredential Credential { get; } + + public YouTubeService Service { get; } + + public YoutubeClient YoutubeClient { get; } + + public string AccountId { get; } + public Google.Apis.YouTube.v3.Data.Channel Channel { get; } + + public async void Deauthenticate() + { + await Credential.RevokeTokenAsync(CancellationToken.None); + } + + public User(UserCredential credential) + { + Credential = credential; + Service = new YouTubeService(new BaseClientService.Initializer + { + HttpClientInitializer = Credential, + ApplicationName = "FoxTube" + }); + + HttpClient httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", credential.Token.AccessToken); + YoutubeClient = new YoutubeClient(httpClient); + } + + public async void RefreshToken() => + await Credential.RefreshTokenAsync(CancellationToken.None); + + /// + /// Subscribes or unsibscribes authorized user from the channel + /// + /// The ID of channel which has to be added/removed + /// Returns 'true' if channel is in subscriptions now; 'false' if it's not + public async Task ChangeSubscriptionState(string id) + { + if (Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == id) is Subscription subscription) + { + try { await Service.Subscriptions.Delete(subscription.Id).ExecuteAsync(); } + catch { return true; } + + //SubscriptionsChanged?.Invoke("remove", subscription); + Subscriptions.Remove(subscription); + + return false; + } + else + { + var request = Service.Subscriptions.Insert(new Subscription + { + Snippet = new SubscriptionSnippet + { + ResourceId = new ResourceId + { + ChannelId = id, + Kind = "youtube#channel" + } + } + }, "snippet"); + + if (!(await request.ExecuteAsync() is Subscription sub)) + return false; + + Subscriptions.Add(sub); + //SubscriptionsChanged?.Invoke("add", sub); + + return true; + } + } + } +} diff --git a/FoxTube/Classes/UserManagement.cs b/FoxTube/Classes/UserManagement.cs new file mode 100644 index 0000000..3ef8c5e --- /dev/null +++ b/FoxTube/Classes/UserManagement.cs @@ -0,0 +1,87 @@ +using FoxTube.Classes; +using Google.Apis.Auth.OAuth2; +using Google.Apis.Oauth2.v2.Data; +using Google.Apis.Services; +using Google.Apis.YouTube.v3; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using YoutubeExplode; + +namespace FoxTube +{ + public static class UserManagement + { + public static event AuthorizationChangedEventHandler AuthorizationStateChanged; + public static event SubscriptionChangedEventHandler SubscriptionChanged; + + static ClientSecrets[] Secrets => new ClientSecrets[5] + { + new ClientSecrets + { + ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", + ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_" + }, + new ClientSecrets + { + ClientId = "1096685398208-ha281qmg52ga8g8nn4n6943oct1tq6nj.apps.googleusercontent.com", + ClientSecret = "Cew9WbJcsoisYnR-Wuyhukov" + }, + new ClientSecrets + { + ClientId = "1096685398208-59ktbv2jsvalrmli1pevur9aujnjhjeb.apps.googleusercontent.com", + ClientSecret = "eHvXHlIJ9r64yReOmMnuben7" + }, + new ClientSecrets + { + ClientId = "1096685398208-ebckt9ncp69qutsmdbbufov60nq4rvpa.apps.googleusercontent.com", + ClientSecret = "XCaBFmjoT2ZAKsGf-y80lBvc" + }, + new ClientSecrets + { + ClientId = "1096685398208-ligbo8gqkp4qar1uo0e5o5di434qklma.apps.googleusercontent.com", + ClientSecret = "oXauUkFY9I7Q1CmNkKGmszRQ" + }, + }; + + static YouTubeService NoAuthService { get; } = new YouTubeService(new BaseClientService.Initializer + { + ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0", + ApplicationName = "FoxTube" + }); + static YoutubeClient NoAuthClient { get; } = new YoutubeClient(); + + public static YouTubeService Service => Current == null ? NoAuthService : Current.Service; + public static YoutubeClient YoutubeClient => Current == null ? NoAuthClient : Current.YoutubeClient; + + public static bool IsAuthorized => Current != null; + + public static User Current { get; set; } + + static (Userinfoplus info, UserCredential credential)[] UserInfos = new (Userinfoplus, UserCredential)[5]; + + public static bool CheckPerformed { get; } + + public static void Initialize() + { + + } + + public static async Task AddItemToWL(string id) + { + + } + + public static async void CreateNew() + { + + } + + public static async void Logout() + { + Current?.Deauthenticate(); + } + } +} diff --git a/FoxTube/Classes/ViewModel.cs b/FoxTube/Classes/ViewModel.cs new file mode 100644 index 0000000..c6aecb1 --- /dev/null +++ b/FoxTube/Classes/ViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls; + +namespace FoxTube +{ + public class ViewModel : Page + { + public List PivotItems { get; } = new List(); + + public object Parameter { get; private set; } + + public void PivotChanged(int index) { } + } +} diff --git a/FoxTube/Controls/AccountManager.xaml b/FoxTube/Controls/AccountManager.xaml new file mode 100644 index 0000000..f0cc915 --- /dev/null +++ b/FoxTube/Controls/AccountManager.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FoxTube/Controls/AccountManager.xaml.cs b/FoxTube/Controls/AccountManager.xaml.cs new file mode 100644 index 0000000..1bfc668 --- /dev/null +++ b/FoxTube/Controls/AccountManager.xaml.cs @@ -0,0 +1,92 @@ +using Microsoft.AppCenter.Analytics; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Navigation; + +// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace FoxTube.Controls +{ + public sealed partial class AccountManager : StackPanel + { + public AccountManager() + { + InitializeComponent(); + + Service.Purchased += (purchased, price) => + { + /*removeAds.Visibility = purchased ? Visibility.Collapsed : Visibility.Visible; + removeAds.Content = $"{resources.GetString("/Main/adsFree/Content")} ({price})";*/ + }; + + if (Service.AdsCheckPerformed) + Service.CheckAddons(); + } + + void SignIn_Click(object sender, RoutedEventArgs e) + { + UserManagement.CreateNew(); + Analytics.TrackEvent("Initialized authorization sequence"); + } + + void Logout_Click(object sender, RoutedEventArgs e) + { + manager.ContextFlyout.Hide(); + UserManagement.Logout(); + } + + public async void Logged() //=> await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + account.Visibility = Visibility.Collapsed; + + /*ToolTipService.SetToolTip(avatar, $"{UserManagement.Current.UserInfo.Name} ({UserManagement.Current.UserInfo.Email})"); + name.Text = UserManagement.Current.UserInfo.Name; + email.Text = UserManagement.Current.UserInfo.Email; + avatar.ProfilePicture = new BitmapImage(UserManagement.Current.UserInfo.Picture.ToUri()) { DecodePixelHeight = 65, DecodePixelWidth = 65 }; + (icon.Fill as ImageBrush).ImageSource = new BitmapImage(UserManagement.Current.UserInfo.Picture.ToUri()) { DecodePixelHeight = 25, DecodePixelWidth = 25 };*/ + + manager.Visibility = Visibility.Visible; + }//); + + public void Quit() + { + manager.Visibility = Visibility.Collapsed; + account.Visibility = Visibility.Visible; + } + + private void NavigationViewItem_Tapped(object sender, TappedRoutedEventArgs e) + { + + } + + void Manager_Tapped(object sender, TappedRoutedEventArgs e) => + (sender as Microsoft.UI.Xaml.Controls.NavigationViewItem).ContextFlyout.ShowAt(sender as FrameworkElement); + + private void Settings_Click(object sender, RoutedEventArgs e) + { + + } + + private void Settings_Click(object sender, TappedRoutedEventArgs e) + { + + } + + private void RemoveAds_Tapped(object sender, TappedRoutedEventArgs e) + { + + } + } +} diff --git a/FoxTube/Controls/ContentFrame.xaml b/FoxTube/Controls/ContentFrame.xaml new file mode 100644 index 0000000..6b1eb67 --- /dev/null +++ b/FoxTube/Controls/ContentFrame.xaml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FoxTube/Themes/Resources.xaml b/FoxTube/Themes/Resources.xaml index 7680735..62c6fd4 100644 --- a/FoxTube/Themes/Resources.xaml +++ b/FoxTube/Themes/Resources.xaml @@ -7,6 +7,15 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Src/Trailer.mp4 b/Src/Trailer.mp4 new file mode 100644 index 0000000..a5d2e8e Binary files /dev/null and b/Src/Trailer.mp4 differ