Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

youtube: add support for OAuth2 tokens #558

Merged
merged 8 commits into from
Jun 8, 2024
47 changes: 44 additions & 3 deletions src/modules/processing/services/youtube.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Innertube, Session } from 'youtubei.js';
import { env } from '../../config.js';
import { cleanString } from '../../sub/utils.js';
import { fetch } from 'undici'
import { getCookie } from '../cookie/manager.js'
import { getCookie, updateCookieValues } from '../cookie/manager.js'

const ytBase = Innertube.create().catch(e => e);

Expand All @@ -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 innertube = await ytBase;
if (innertube instanceof Error) {
Expand All @@ -36,11 +56,32 @@ const cloneInnertube = async (customFetch) => {
innertube.session.api_version,
innertube.session.account_index,
innertube.session.player,
getCookie('youtube')?.toString(),
undefined,
customFetch ?? innertube.session.http.fetch,
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);
return yt;
}
Expand All @@ -62,7 +103,7 @@ export default async function(o) {
}

try {
info = await yt.getBasicInfo(o.id, 'IOS');
info = await yt.getBasicInfo(o.id, yt.session.logged_in ? 'ANDROID' : 'IOS');
} catch(e) {
if (e?.message === 'This video is unavailable') {
return { error: 'ErrorCouldntFetch' };
Expand Down
34 changes: 34 additions & 0 deletions src/modules/sub/generate-youtube-tokens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Innertube } from 'youtubei.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(
`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(credentials);
dumbmoron marked this conversation as resolved.
Show resolved Hide resolved

console.log(
dumbmoron marked this conversation as resolved.
Show resolved Hide resolved
JSON.stringify(
Object.entries(credentials)
.map(([k, v]) => `${k}=${v instanceof Date ? v.toISOString() : v}`)
.join('; ')
)
);
});

await tube.session.signIn();