youtube: add support for OAuth2 tokens (#558)
This commit is contained in:
commit
a434de0809
6 changed files with 85 additions and 6 deletions
|
@ -11,9 +11,9 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/cobalt",
|
"start": "node src/cobalt",
|
||||||
"setup": "node src/modules/setup",
|
"setup": "node src/modules/setup",
|
||||||
"test": "node src/test/test",
|
"test": "node src/util/test",
|
||||||
"build": "node src/modules/buildStatic",
|
"build": "node src/modules/buildStatic",
|
||||||
"testFilenames": "node src/test/testFilenamePresets"
|
"token:youtube": "node src/util/generate-youtube-tokens"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Innertube, Session } from 'youtubei.js';
|
||||||
import { env } from '../../config.js';
|
import { env } from '../../config.js';
|
||||||
import { cleanString } from '../../sub/utils.js';
|
import { cleanString } from '../../sub/utils.js';
|
||||||
import { fetch } from 'undici'
|
import { fetch } from 'undici'
|
||||||
import { getCookie } from '../cookie/manager.js'
|
import { getCookie, updateCookieValues } from '../cookie/manager.js'
|
||||||
|
|
||||||
const ytBase = Innertube.create().catch(e => e);
|
const ytBase = Innertube.create().catch(e => e);
|
||||||
|
|
||||||
|
@ -24,6 +24,26 @@ const codecMatch = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const transformSessionData = (cookie) => {
|
||||||
|
if (!cookie)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const values = cookie.values();
|
||||||
|
const REQUIRED_VALUES = [
|
||||||
|
'access_token', 'refresh_token',
|
||||||
|
'client_id', 'client_secret',
|
||||||
|
'expires'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (REQUIRED_VALUES.some(x => typeof values[x] !== 'string')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...values,
|
||||||
|
expires: new Date(values.expires),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const cloneInnertube = async (customFetch) => {
|
const cloneInnertube = async (customFetch) => {
|
||||||
const innertube = await ytBase;
|
const innertube = await ytBase;
|
||||||
if (innertube instanceof Error) {
|
if (innertube instanceof Error) {
|
||||||
|
@ -36,11 +56,32 @@ const cloneInnertube = async (customFetch) => {
|
||||||
innertube.session.api_version,
|
innertube.session.api_version,
|
||||||
innertube.session.account_index,
|
innertube.session.account_index,
|
||||||
innertube.session.player,
|
innertube.session.player,
|
||||||
getCookie('youtube')?.toString(),
|
undefined,
|
||||||
customFetch ?? innertube.session.http.fetch,
|
customFetch ?? innertube.session.http.fetch,
|
||||||
innertube.session.cache
|
innertube.session.cache
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const cookie = getCookie('youtube_oauth');
|
||||||
|
const oauthData = transformSessionData(cookie);
|
||||||
|
|
||||||
|
if (!session.logged_in && oauthData) {
|
||||||
|
await session.oauth.init(oauthData);
|
||||||
|
session.logged_in = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.logged_in) {
|
||||||
|
await session.oauth.refreshIfRequired();
|
||||||
|
const oldExpiry = new Date(cookie.values().expires);
|
||||||
|
const newExpiry = session.oauth.credentials.expires;
|
||||||
|
|
||||||
|
if (oldExpiry.getTime() !== newExpiry.getTime()) {
|
||||||
|
updateCookieValues(cookie, {
|
||||||
|
...session.oauth.credentials,
|
||||||
|
expires: session.oauth.credentials.expires.toISOString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const yt = new Innertube(session);
|
const yt = new Innertube(session);
|
||||||
return yt;
|
return yt;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +103,7 @@ export default async function(o) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
info = await yt.getBasicInfo(o.id, 'IOS');
|
info = await yt.getBasicInfo(o.id, yt.session.logged_in ? 'ANDROID' : 'IOS');
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if (e?.message === 'This video is unavailable') {
|
if (e?.message === 'This video is unavailable') {
|
||||||
return { error: 'ErrorCouldntFetch' };
|
return { error: 'ErrorCouldntFetch' };
|
||||||
|
|
38
src/util/generate-youtube-tokens.js
Normal file
38
src/util/generate-youtube-tokens.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { Innertube } from 'youtubei.js';
|
||||||
|
import { Red } from '../modules/sub/consoleText.js'
|
||||||
|
|
||||||
|
const bail = (...msg) => {
|
||||||
|
console.error(...msg);
|
||||||
|
throw new Error(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tube = await Innertube.create();
|
||||||
|
|
||||||
|
tube.session.once(
|
||||||
|
'auth-pending',
|
||||||
|
({ verification_url, user_code }) => {
|
||||||
|
console.log(`${Red('[!]')} The token generated by this script is sensitive and you should not share it with anyone!`);
|
||||||
|
console.log(` By using this token, you are risking your Google account getting terminated.`);
|
||||||
|
console.log(` You should ${Red('NOT')} use your personal account!`);
|
||||||
|
console.log();
|
||||||
|
console.log(`Open ${verification_url} in a browser and enter ${user_code} when asked for the code.`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
tube.session.once('auth-error', (err) => bail('An error occurred:', err));
|
||||||
|
tube.session.once('auth', ({ status, credentials, ...rest }) => {
|
||||||
|
if (status !== 'SUCCESS') {
|
||||||
|
bail('something went wrong', rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'add this cookie to the youtube_oauth array in your cookies file:',
|
||||||
|
JSON.stringify(
|
||||||
|
Object.entries(credentials)
|
||||||
|
.map(([k, v]) => `${k}=${v instanceof Date ? v.toISOString() : v}`)
|
||||||
|
.join('; ')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await tube.session.signIn();
|
|
@ -9,7 +9,7 @@ import { normalizeRequest } from "../modules/processing/request.js";
|
||||||
import { env } from "../modules/config.js";
|
import { env } from "../modules/config.js";
|
||||||
|
|
||||||
env.apiURL = 'http://localhost:9000'
|
env.apiURL = 'http://localhost:9000'
|
||||||
let tests = loadJSON('./src/test/tests.json');
|
let tests = loadJSON('./src/util/tests.json');
|
||||||
|
|
||||||
let noTest = [];
|
let noTest = [];
|
||||||
let failed = [];
|
let failed = [];
|
Loading…
Reference in a new issue