refactor: changed how the API is called

This commit is contained in:
Alex 2022-03-18 19:15:19 +13:00
parent ecc29af977
commit c2e4036080
6 changed files with 142 additions and 47 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ dist
package-lock.json
temp.js
temp.json
.vscode/settings.json

View File

@ -7,18 +7,61 @@
<p class="grey--text">Please read the VueTube FAQ for more information.</p>
<!-- <button @click="debugRecommend">Test Button</button>
<button @click="debugVideo">Test Button (Video)</button> -->
<div>
<center style="padding-top: 3em;" v-if="recommends == null">
<v-progress-circular
size="50"
indeterminate
color="primary"
/>
</center>
<v-list-item v-for="(video, index) in recommends" :key="index">
<v-card class="entry" :to="`/watch?v=${video.videoId}`">
<v-card-text>
<div style="position: relative;">
<v-img :src="video.thumbnail.thumbnails[0].url" />
<p v-text="video.lengthText.runs[0].text" class="videoRuntimeFloat" style="color: #fff;" />
</div>
<div v-text="video.title.runs[0].text" style="margin-top: 0.5em;" />
<div v-text="`${video.viewCountText.runs[0].text} ${video.publishedTimeText.runs[0].text}`" />
</v-card-text>
</v-card>
</v-list-item>
</div>
</center>
</template>
<script>
export default {
methods: {
// debugRecommend () {
// console.log(this.$youtube.recommend("test", false))
// },
// debugVideo () {
// console.log(this.$youtube.getVid("WhWc3b3KhnY"))
// }
}
data() {
return {
recommends: null
}
},
// The following code is only a demo for debugging purposes, note that each "shelfRenderer" has a "title" value that seems to align to the categories at the top of the vanilla yt app
mounted() {
const vm = this;
this.$youtube.recommend().then(
result => {
const videoList = []
console.log(result)
const recommendContent = result.contents.singleColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents // I feel like I've committed programming sin
recommendContent.forEach(function (contents, index) {
contents.shelfRenderer.content.horizontalListRenderer.items.forEach(function (item, index) {
const video = item.gridVideoRenderer
console.log(video)
console.log(video.onTap)
videoList.push(video)
})
})
vm.recommends = videoList;
}
).catch ((error) => {
this.$logger("Home Page", error, true)
})
}
}
</script>

View File

@ -12,7 +12,9 @@
export default {
layout: "empty",
mounted() {
this.$router.push(`/${localStorage.getItem("startPage") || "home"}`);
this.$youtube.getAPI().then(
this.$router.push(`/${localStorage.getItem("startPage") || "home"}`)
);
}
}
</script>

View File

@ -18,37 +18,37 @@ class Innertube {
return typeof this.ErrorCallback === "function"
}
init() {
Http.get({ url: constants.URLS.YT_URL, params: { hl: "en" } })
.then(result => {
if (result instanceof Error && this.checkErrorCallback) this.ErrorCallback(result.message, true);
try {
const data = JSON.parse(getBetweenStrings(result.data, 'ytcfg.set(', ');'));
if (data.INNERTUBE_CONTEXT) {
this.key = data.INNERTUBE_API_KEY;
this.context = data.INNERTUBE_CONTEXT;
this.context.client.clientName = "ANDROID";
this.context.client.clientVersion = "16.25";
}
} catch (err) {
console.log(err)
if (this.checkErrorCallback) this.ErrorCallback(err, true)
if (this.retry_count >= 10) { this.init() } else { if (this.checkErrorCallback) this.ErrorCallback("Failed to retrieve Innertube session", true); }
async initAsync() {
const html = await Http.get({ url: constants.URLS.YT_URL, params: { hl: "en" } }).catch((error) => error);
try {
if (html instanceof Error && this.checkErrorCallback) this.ErrorCallback(html.message, true);
try {
const data = JSON.parse(getBetweenStrings(html.data, 'ytcfg.set(', ');'));
if (data.INNERTUBE_CONTEXT) {
this.key = data.INNERTUBE_API_KEY;
this.context = data.INNERTUBE_CONTEXT;
this.context.client = constants.INNERTUBE_CLIENT(this.context.client)
}
})
.catch((error) => error);
} catch (err) {
console.log(err)
if (this.checkErrorCallback) this.ErrorCallback(err, true)
if (this.retry_count >= 10) { this.initAsync() } else { if (this.checkErrorCallback) this.ErrorCallback("Failed to retrieve Innertube session", true); }
}
} catch (error) {
this.ErrorCallback(error, true)
};
};
static create(ErrorCallback) {
static async createAsync(ErrorCallback) {
const created = new Innertube(ErrorCallback);
created.init();
await created.initAsync();
return created;
}
//--- API Calls ---//
async browse(action_type) {
async browseAsync(action_type) {
let data = { context: this.context }
switch (action_type) {
@ -69,7 +69,7 @@ class Innertube {
headers: { "Content-Type": "application/json" }
}).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
if (response instanceof Error) return { success: false, status_code: response.status, message: response.message };
return {
success: true,
@ -78,13 +78,13 @@ class Innertube {
};
}
async getVidInfo(id) {
async getVidInfoAsync(id) {
let data = { context: this.context, videoId: id }
const response = await Http.post({
url: `${constants.URLS.YT_BASE_API}/player?key=${this.key}`,
data: data,
headers: { "Content-Type": "application/json" }
headers: constants.INNERTUBE_HEADER(this.context)
}).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
@ -97,8 +97,10 @@ class Innertube {
}
// Simple Wrappers
async getRecommendations() {
return await this.browse("recommendations")
async getRecommendationsAsync() {
const rec = await this.browseAsync("recommendations");
console.log(rec.data)
return rec.data;
}

View File

@ -128,23 +128,32 @@ const searchModule = {
//--- Recommendations --//
// Immediately create an Innertube object. This will be the object used in all future Inntertube API calls
let InnertubeAPI;
// Lazy loads Innertube object. This will be the object used in all future Innertube API calls. Code provided by Lightfire228 (https://github.com/Lightfire228)
// These are just a way for the backend Javascript to communicate with the front end Vue scripts. Essentially a wrapper inside a wrapper
const recommendationModule = {
recommendAPI: Innertube.create((message, isError) => { logger("Innertube", message, isError); }), // There's definitely a better way to do this, but it's 2 am and I just can't anymore
async getAPI() {
InnertubeAPI = await Innertube.createAsync((message, isError) => { logger("Innertube", message, isError); })
console.log(InnertubeAPI)
return InnertubeAPI;
},
async getVid(id) {
console.log(this.recommendAPI)
return this.recommendAPI.getVidInfo(id);
console.log(InnertubeAPI)
return InnertubeAPI.getVidInfoAsync(id).data;
},
async recommend() {
return this.recommendAPI.getRecommendations();
console.log(InnertubeAPI)
return InnertubeAPI.getRecommendationsAsync();
},
}
//--- Start ---//
export default ({ app }, inject) => {
inject('youtube', {...searchModule, ...recommendationModule })
inject("logger", logger)
}
logger("Initialize", "Program Started");

View File

@ -1,11 +1,49 @@
// To centeralize certain values and URLs as for easier debugging and refactoring
const url = {
YT_URL: 'https://www.youtube.com',
YT_MUSIC_URL: 'https://music.youtube.com',
YT_BASE_API: 'https://www.youtube.com/youtubei/v1',
YT_SUGGESTIONS: "https://suggestqueries.google.com/complete",
VT_GITHUB: "https://api.github.com/repos/Frontesque/VueTube",
}
const ytApiVal = {
VERSION: "16.25",
CLIENTNAME: "ANDROID",
}
module.exports = {
URLS: {
YT_URL: 'https://www.youtube.com',
YT_MUSIC_URL: 'https://music.youtube.com',
YT_BASE_API: 'https://www.youtube.com/youtubei/v1',
YT_SUGGESTIONS: "https://suggestqueries.google.com/complete",
VT_GITHUB: "https://api.github.com/repos/Frontesque/VueTube",
URLS: url,
YT_API_VALUES: ytApiVal,
INNERTUBE_HEADER: (info) => {
let headers = {
'accept': '*/*',
'user-agent': info.client.userAgent,
'content-type': 'application/json',
'x-goog-authuser': 0,
'x-youtube-client-name': 2,
'x-youtube-client-version': info.client.clientVersion,
'x-youtube-chrome-connected': 'source=Chrome,mode=0,enable_account_consistency=true,supervised=false,consistency_enabled_by_default=false',
};
return headers
},
INNERTUBE_CLIENT: (info) => {
let client = {
"gl": info.gl,
"hl": info.hl,
"deviceMake": info.deviceMake,
"deviceModel": info.deviceModel,
"userAgent": info.userAgent,
"clientName": ytApiVal.CLIENTNAME,
"clientVersion": ytApiVal.VERSION,
"osName": info.osName,
"osVersion": info.osVersion,
"platform": "MOBILE",
"originalUrl": info.originalUrl
};
return client
}
}