diff --git a/NUXT/components/CompactRenderers/compactChannelRenderer.vue b/NUXT/components/CompactRenderers/compactChannelRenderer.vue index 1afdda0..0928284 100644 --- a/NUXT/components/CompactRenderers/compactChannelRenderer.vue +++ b/NUXT/components/CompactRenderers/compactChannelRenderer.vue @@ -2,6 +2,17 @@
diff --git a/NUXT/components/ListRenderers/horizontalListRenderer.vue b/NUXT/components/ListRenderers/horizontalListRenderer.vue index d0ac32c..5e54c3b 100644 --- a/NUXT/components/ListRenderers/horizontalListRenderer.vue +++ b/NUXT/components/ListRenderers/horizontalListRenderer.vue @@ -3,7 +3,7 @@ -
-
+ -
+
@@ -28,10 +28,6 @@ width: 100%; /* Prevent Loading Weirdness */ padding: 10px; } - -.fill-screen { - width: 100vw; /* Very Hacky */ -} diff --git a/NUXT/plugins/classes/backHander.js b/NUXT/plugins/classes/backHander.js new file mode 100644 index 0000000..7daaf06 --- /dev/null +++ b/NUXT/plugins/classes/backHander.js @@ -0,0 +1,61 @@ +import { App as CapacitorApp } from "@capacitor/app"; +import backType from "./backType"; +export default class backHandler { + constructor() { + this.backStack = []; // This should only contain instances of backType. Any other type will be ignored. + this.startUp(); + } + + startUp() { + this.reset(); + + // Add a listener for the back button. + this.backHandler = CapacitorApp.addListener("backButton", (context) => { this.back(context) }); + + // Start garbage collection. Run every 5 minutes. + setInterval(() => { + () => { this.garbageCollect }; + }, 5 * 60 * 1000); + } + + reset() { + this.backStack = []; + } + + back({ canGoBack }) { + console.log("backStack", this.backStack) + // Check if backStack contains any backType objects. If so, call the goBack() function. + if (this.backStack.length > 0) { + // Loop through the backStack array. + let lastResult = false; + while (!lastResult && this.backStack.length > 0) { + const backAction = this.backStack.pop(); + lastResult = backAction.goBack(); + } + // Since a function was successfully called, no need to continue. + if (lastResult) return; + } + if (!canGoBack) { + // If we can't go back, then we should exit the app. + CapacitorApp.exitApp(); + } else { + // If we can go back, then we should go back. + window.history.back(); + } + } + + addAction(callback) { + if (callback instanceof backType) { + this.backStack.push(callback); + } else { + throw new TypeError("backType object expected"); + } + } + + // Loops through the backStack array if array larger than 10. If backType.check() returns false, then remove it from the backStack array. + garbageCollect() { + if (this.backStack.length > 10) { + this.backStack = this.backStack.filter((backType) => backType.check()); + } + } +} diff --git a/NUXT/plugins/classes/backType.js b/NUXT/plugins/classes/backType.js new file mode 100644 index 0000000..d921459 --- /dev/null +++ b/NUXT/plugins/classes/backType.js @@ -0,0 +1,16 @@ +// This is the class that populates the backStack array in the backHandler class. +export default class backType { + constructor(callback, check) { + this.callback = callback; + this.check = check || (() => true); + } + + goBack() { + if (this.check()) { + this.callback(); + return true; + } else { + return false; + } + } +} diff --git a/NUXT/plugins/constants.js b/NUXT/plugins/constants.js index 74c188f..3ad81f0 100644 --- a/NUXT/plugins/constants.js +++ b/NUXT/plugins/constants.js @@ -33,6 +33,7 @@ module.exports = { recommendations: "Recommendations", init: "Initialize", innertube: "Innertube", + channel: "Channel", }, INNERTUBE_HEADER: (info) => { diff --git a/NUXT/plugins/innertube.js b/NUXT/plugins/innertube.js index 2fa313b..94aa176 100644 --- a/NUXT/plugins/innertube.js +++ b/NUXT/plugins/innertube.js @@ -76,18 +76,23 @@ class Innertube { //--- API Calls ---// - async browseAsync(action_type) { + async browseAsync(action_type, args) { let data = { context: this.context }; switch (action_type) { case "recommendations": - data.browseId = "FEwhat_to_watch"; + args.browseId = "FEwhat_to_watch"; break; case "playlist": - data.browseId = args.browse_id; - break; + case "channel": + if (args && args.browseId) { + break; + } else { + throw new ReferenceError("No browseId provided"); + } default: } + data.browseId = { ...data, args }; console.log(data); @@ -217,6 +222,28 @@ class Innertube { }; } + async getEndPoint(url) { + let data = { context: this.context, url: url }; + const response = await Http.post({ + url: `${constants.URLS.YT_BASE_API}/navigation/resolve_url?key=${this.key}`, + data: data, + headers: { "Content-Type": "application/json" }, + }).catch((error) => error); + + if (response instanceof Error) + return { + success: false, + status_code: response.status, + message: response.message, + }; + + return { + success: true, + status_code: response.status, + data: response.data, + }; + } + // WARNING: This is tracking the user's activity, but is required for recommendations to properly work async apiStats(params, url) { console.log(params); @@ -252,10 +279,24 @@ class Innertube { // Simple Wrappers async getRecommendationsAsync() { const rec = await this.browseAsync("recommendations"); - console.log(rec.data); return rec; } + async getChannelAsync(url) { + const channelEndpoint = await this.getEndPoint(url); + if ( + channelEndpoint.success && + channelEndpoint.data.endpoint?.browseEndpoint + ) { + return await this.browseAsync( + "channel", + channelEndpoint.data.endpoint?.browseEndpoint + ); + } else { + throw new ReferenceError("Cannot find channel"); + } + } + async VidInfoAsync(id) { let response = await this.getVidAsync(id); @@ -298,7 +339,9 @@ class Innertube { isLive: details.isLiveContent, channelName: details.author, channelSubs: ownerData?.collapsedSubtitle?.runs[0]?.text, - channelUrl: rendererUtils.getNavigationEndpoints(ownerData), + channelUrl: rendererUtils.getNavigationEndpoints( + ownerData.navigationEndpoint + ), channelImg: ownerData?.thumbnail?.thumbnails[0].url, availableResolutions: resolutions?.formats, availableResolutionsAdaptive: resolutions?.adaptiveFormats, diff --git a/NUXT/plugins/renderers.js b/NUXT/plugins/renderers.js index 0ab08c2..a8556c4 100644 --- a/NUXT/plugins/renderers.js +++ b/NUXT/plugins/renderers.js @@ -1,25 +1,21 @@ // General utility functions for the renderers class rendererUtils { static getNavigationEndpoints(base) { - const navEndpoint = base.navigationEndpoint; - if (!navEndpoint) return; - if (navEndpoint.urlEndpoint) { - const params = new Proxy( - new URLSearchParams(navEndpoint.urlEndpoint.url), - { - get: (searchParams, prop) => searchParams.get(prop), - } - ); + if (!base) return; + if (base.urlEndpoint) { + const params = new Proxy(new URLSearchParams(base.urlEndpoint.url), { + get: (searchParams, prop) => searchParams.get(prop), + }); if (params.q) return decodeURI(params.q); - else return new URL(navEndpoint.urlEndpoint.url).pathname; - } else if (navEndpoint.browseEndpoint) { - return navEndpoint.browseEndpoint.canonicalBaseUrl; - } else if (navEndpoint.watchEndpoint) { - return `/watch?v=${navEndpoint.watchEndpoint.videoId}`; - } else if (navEndpoint.navigationEndpoint) { + else return new URL(base.urlEndpoint.url).pathname; + } else if (base.browseEndpoint) { + return base.browseEndpoint.canonicalBaseUrl; + } else if (base.watchEndpoint) { + return `/watch?v=${base.watchEndpoint.videoId}`; + } else if (base.navigationEndpoint) { return; //for now - } else if (navEndpoint.searchEndpoint) { - return `/search?q=${encodeURI(navEndpoint.searchEndpoint.query)}`; + } else if (base.searchEndpoint) { + return `/search?q=${encodeURI(base.searchEndpoint.query)}`; } } diff --git a/NUXT/plugins/vuetube.js b/NUXT/plugins/vuetube.js index 428964a..50d5af6 100644 --- a/NUXT/plugins/vuetube.js +++ b/NUXT/plugins/vuetube.js @@ -6,6 +6,7 @@ import constants from "./constants"; import { hexToRgb, rgbToHex, parseEmoji } from "./utils"; import { Haptics, ImpactStyle } from "@capacitor/haptics"; import Vue from "vue"; +import backHandler from "./classes/backHander"; Vue.directive("emoji", { inserted: function (el) { @@ -14,6 +15,8 @@ Vue.directive("emoji", { }, }); +let backActions; + const module = { //--- Get GitHub Commits ---// commits: new Promise((resolve, reject) => { @@ -109,6 +112,23 @@ const module = { rgbToHex(r, g, b) { return rgbToHex(r, g, b); }, + + async launchBackHandling() { + backActions = new backHandler(); + return true; + }, + + resetBackActions() { + backActions.reset(); + }, + + addBackAction(action) { + backActions.addAction(action); + }, + + back(listenerFunc) { + backActions.back(listenerFunc); + }, }; //--- Start ---// diff --git a/NUXT/plugins/youtube.js b/NUXT/plugins/youtube.js index 0b784e2..87e1b3f 100644 --- a/NUXT/plugins/youtube.js +++ b/NUXT/plugins/youtube.js @@ -109,6 +109,15 @@ const innertubeModule = { else return `https://img.youtube.com/vi/${id}/mqdefault.jpg`; }, + async getChannel(url) { + try { + const response = await InnertubeAPI.getChannelAsync(url); + return response.data; + } catch (error) { + logger(constants.LOGGER_NAMES.channel, error, true); + } + }, + // It just works™ // Front page recommendation async recommend() {