mirror of
https://github.com/VueTubeApp/VueTube
synced 2024-11-22 11:15:14 +00:00
feat: simple test to see if backend API works
This commit is contained in:
parent
0a20f33374
commit
505b69365f
4 changed files with 198 additions and 98 deletions
|
@ -3,5 +3,16 @@
|
|||
<img style="margin-top: 5em; max-width: 80%; max-height: 15em;" src="/dev.svg" />
|
||||
<h1 class="grey--text">Page Under Construction</h1>
|
||||
<p class="grey--text">Please read the VueTube FAQ for more information.</p>
|
||||
<button @click="debug">Test Button</button>
|
||||
</center>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
debug () {
|
||||
console.log(this.$youtube.recommend("test", false))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
67
NUXT/plugins/innertube.js
Normal file
67
NUXT/plugins/innertube.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Code specific to working with the innertube API
|
||||
// https://www.youtube.com/youtubei/v1
|
||||
|
||||
import { Http } from '@capacitor-community/http';
|
||||
import { getBetweenStrings } from './utils';
|
||||
|
||||
class Innertube {
|
||||
constructor(ErrorCallback) {
|
||||
this.ErrorCallback = ErrorCallback;
|
||||
this.init();
|
||||
this.retry_count = 0
|
||||
}
|
||||
|
||||
async init() {
|
||||
const html = await Http.request({ method: 'GET', url: 'https://www.youtube.com', params: { hl: "en" } }).catch((error) => error);
|
||||
if (html instanceof Error) 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;
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
this.ErrorCallback(err, true)
|
||||
this.retry_count >= 10 ? this.init() : this.ErrorCallback("Failed to retrieve Innertube session", true);
|
||||
}
|
||||
};
|
||||
|
||||
async browse(action_type) {
|
||||
let data = { context: this.context }
|
||||
|
||||
switch (action_type) {
|
||||
case 'recommendations':
|
||||
data.browseId = 'FEwhat_to_watch'
|
||||
|
||||
break;
|
||||
case 'playlist':
|
||||
data.browseId = args.browse_id
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
const response = await Http.request({
|
||||
method: 'POST',
|
||||
url: `https://www.youtube.com/youtubei/v1/browse?key=${this.key}`,
|
||||
data: JSON.stringify(data)
|
||||
}).catch((error) => error);
|
||||
|
||||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
|
||||
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
data: response.data
|
||||
};
|
||||
}
|
||||
|
||||
async getRecommendations() {
|
||||
const response = await this.browse("recommendations")
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default Innertube;
|
9
NUXT/plugins/utils.js
Normal file
9
NUXT/plugins/utils.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Collection of functions that are useful but non-specific to any particular files
|
||||
|
||||
function getBetweenStrings(data, start_string, end_string) {
|
||||
const regex = new RegExp(`${start_string}(.*?)${end_string}`, "s");
|
||||
const match = data.match(regex);
|
||||
return match ? match[1] : undefined;
|
||||
}
|
||||
|
||||
module.exports = { getBetweenStrings };
|
|
@ -1,131 +1,144 @@
|
|||
//--- Modules/Imports ---//
|
||||
import { Http } from '@capacitor-community/http';
|
||||
import Innertube from './innertube'
|
||||
|
||||
//--- Logger Function ---//
|
||||
function logger(func, data, isError=false) {
|
||||
module.logs.unshift({
|
||||
name: func,
|
||||
time: Date.now(),
|
||||
data: data,
|
||||
error: isError
|
||||
})
|
||||
function logger(func, data, isError = false) {
|
||||
searchModule.logs.unshift({
|
||||
name: func,
|
||||
time: Date.now(),
|
||||
data: data,
|
||||
error: isError
|
||||
})
|
||||
}
|
||||
|
||||
//--- Youtube Base Parser ---//
|
||||
function youtubeParse(html, callback) {
|
||||
//--- Replace Encoded Characters ---///
|
||||
html = html.replace(/\\x([0-9A-F]{2})/ig, (...items) => { return String.fromCharCode(parseInt(items[1], 16)); });
|
||||
//--- Properly Format JSON ---//
|
||||
html = html.replaceAll("\\\\\"", "");
|
||||
//--- Parse JSON ---//
|
||||
html = JSON.parse(html);
|
||||
//--- Replace Encoded Characters ---///
|
||||
html = html.replace(/\\x([0-9A-F]{2})/ig, (...items) => { return String.fromCharCode(parseInt(items[1], 16)); });
|
||||
//--- Properly Format JSON ---//
|
||||
html = html.replaceAll("\\\\\"", "");
|
||||
//--- Parse JSON ---//
|
||||
html = JSON.parse(html);
|
||||
|
||||
//--- Get Results ---// ( Thanks To appit-online On Github ) -> https://github.com/appit-online/youtube-search/blob/master/src/lib/search.ts
|
||||
let results;
|
||||
if (html && html.contents && html.contents.sectionListRenderer && html.contents.sectionListRenderer.contents
|
||||
&& html.contents.sectionListRenderer.contents.length > 0
|
||||
&& html.contents.sectionListRenderer.contents[0].itemSectionRenderer
|
||||
&& html.contents.sectionListRenderer.contents[0].itemSectionRenderer.contents.length > 0) {
|
||||
results = html.contents.sectionListRenderer.contents[0].itemSectionRenderer.contents;
|
||||
logger("search", results);
|
||||
callback(results);
|
||||
} else {
|
||||
try {
|
||||
results = JSON.parse(html.split('{"itemSectionRenderer":{"contents":')[html.split('{"itemSectionRenderer":{"contents":').length - 1].split(',"continuations":[{')[0]);
|
||||
logger("search", results);
|
||||
callback(results);
|
||||
} catch (e) {}
|
||||
try {
|
||||
results = JSON.parse(html.split('{"itemSectionRenderer":')[html.split('{"itemSectionRenderer":').length - 1].split('},{"continuationItemRenderer":{')[0]).contents;
|
||||
logger("search", results);
|
||||
callback(results);
|
||||
} catch(e) {}
|
||||
}
|
||||
//--- Get Results ---// ( Thanks To appit-online On Github ) -> https://github.com/appit-online/youtube-search/blob/master/src/lib/search.ts
|
||||
let results;
|
||||
if (html && html.contents && html.contents.sectionListRenderer && html.contents.sectionListRenderer.contents &&
|
||||
html.contents.sectionListRenderer.contents.length > 0 &&
|
||||
html.contents.sectionListRenderer.contents[0].itemSectionRenderer &&
|
||||
html.contents.sectionListRenderer.contents[0].itemSectionRenderer.contents.length > 0) {
|
||||
results = html.contents.sectionListRenderer.contents[0].itemSectionRenderer.contents;
|
||||
logger("search", results);
|
||||
callback(results);
|
||||
} else {
|
||||
try {
|
||||
results = JSON.parse(html.split('{"itemSectionRenderer":{"contents":')[html.split('{"itemSectionRenderer":{"contents":').length - 1].split(',"continuations":[{')[0]);
|
||||
logger("search", results);
|
||||
callback(results);
|
||||
} catch (e) {}
|
||||
try {
|
||||
results = JSON.parse(html.split('{"itemSectionRenderer":')[html.split('{"itemSectionRenderer":').length - 1].split('},{"continuationItemRenderer":{')[0]).contents;
|
||||
logger("search", results);
|
||||
callback(results);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Search Main Function ---//
|
||||
function youtubeSearch(text, callback) {
|
||||
Http.request({
|
||||
method: 'GET',
|
||||
url: 'https://youtube.com/results',
|
||||
params: { q: text, hl: "en" }
|
||||
})
|
||||
.then((res) => {
|
||||
//--- Get HTML Only ---//
|
||||
let html = res.data;
|
||||
//--- Isolate The Script Containing Video Information ---//
|
||||
html = html.split("var ytInitialData = '")[1].split("';</script>")[0];
|
||||
Http.request({
|
||||
method: 'GET',
|
||||
url: 'https://youtube.com/results',
|
||||
params: { q: text, hl: "en" }
|
||||
})
|
||||
.then((res) => {
|
||||
//--- Get HTML Only ---//
|
||||
let html = res.data;
|
||||
//--- Isolate The Script Containing Video Information ---//
|
||||
html = html.split("var ytInitialData = '")[1].split("';</script>")[0];
|
||||
|
||||
youtubeParse(html, (data) => {
|
||||
callback(data);
|
||||
})
|
||||
youtubeParse(html, (data) => {
|
||||
callback(data);
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
.catch((err) => {
|
||||
logger("search", err, true);
|
||||
callback(err);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
logger("search", err, true);
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
const module = {
|
||||
logs: new Array(),
|
||||
const searchModule = {
|
||||
logs: new Array(),
|
||||
|
||||
//--- Get YouTube's Search Auto Complete ---//
|
||||
autoComplete(text, callback) {
|
||||
Http.request({
|
||||
method: 'GET',
|
||||
url: 'https://suggestqueries-clients6.youtube.com/complete/search',
|
||||
params: { client: 'youtube', q: text }
|
||||
})
|
||||
.then((res) => {
|
||||
logger("autoComplete", res);
|
||||
callback(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger("autoComplete", err, true);
|
||||
callback(err);
|
||||
});
|
||||
},
|
||||
//--- Get YouTube's Search Auto Complete ---//
|
||||
autoComplete(text, callback) {
|
||||
Http.request({
|
||||
method: 'GET',
|
||||
url: 'https://suggestqueries-clients6.youtube.com/complete/search',
|
||||
params: { client: 'youtube', q: text }
|
||||
})
|
||||
.then((res) => {
|
||||
logger("autoComplete", res);
|
||||
callback(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger("autoComplete", err, true);
|
||||
callback(err);
|
||||
});
|
||||
},
|
||||
|
||||
search(text, callback) {
|
||||
search(text, callback) {
|
||||
|
||||
let results = new Array();
|
||||
youtubeSearch(text, (videos) => {
|
||||
for (const i in videos) {
|
||||
const video = videos[i];
|
||||
let results = new Array();
|
||||
youtubeSearch(text, (videos) => {
|
||||
for (const i in videos) {
|
||||
const video = videos[i];
|
||||
|
||||
if (video.compactVideoRenderer) {
|
||||
//--- If Entry Is A Video ---//
|
||||
results.push({
|
||||
id: video.compactVideoRenderer.videoId,
|
||||
title: video.compactVideoRenderer.title.runs[0].text,
|
||||
runtime: video.compactVideoRenderer.lengthText.runs[0].text,
|
||||
uploaded: video.compactVideoRenderer.publishedTimeText.runs[0].text,
|
||||
views: video.compactVideoRenderer.viewCountText.runs[0].text,
|
||||
thumbnails: video.compactVideoRenderer.thumbnail.thumbnails
|
||||
})
|
||||
} else {
|
||||
//--- If Entry Is Not A Video ---//
|
||||
//logger("search", { type: "Error Caught Successfully", error: video }, true);
|
||||
}
|
||||
if (video.compactVideoRenderer) {
|
||||
//--- If Entry Is A Video ---//
|
||||
results.push({
|
||||
id: video.compactVideoRenderer.videoId,
|
||||
title: video.compactVideoRenderer.title.runs[0].text,
|
||||
runtime: video.compactVideoRenderer.lengthText.runs[0].text,
|
||||
uploaded: video.compactVideoRenderer.publishedTimeText.runs[0].text,
|
||||
views: video.compactVideoRenderer.viewCountText.runs[0].text,
|
||||
thumbnails: video.compactVideoRenderer.thumbnail.thumbnails
|
||||
})
|
||||
} else {
|
||||
//--- If Entry Is Not A Video ---//
|
||||
//logger("search", { type: "Error Caught Successfully", error: video }, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
callback(results);
|
||||
}
|
||||
})
|
||||
callback(results);
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
getVideo(id) {
|
||||
return id;
|
||||
}
|
||||
async recommend() {
|
||||
const recommendAPI = new Innertube((message, isError) => {
|
||||
logger("recommendation", message, isError)
|
||||
});
|
||||
return await recommendAPI.getRecommendations();
|
||||
},
|
||||
|
||||
getVideo(id) {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//--- Recommendations --//
|
||||
const recommendationModule = {
|
||||
|
||||
}
|
||||
|
||||
//--- Start ---//
|
||||
export default ({ app }, inject) => {
|
||||
inject('youtube', module)
|
||||
inject('youtube', searchModule, recommendationModule)
|
||||
}
|
||||
logger("Initialize","Program Started");
|
||||
logger("Initialize", "Program Started");
|
Loading…
Reference in a new issue