@np-dev/youtubei-js
Version:
A wrapper around YouTube's private API. Supports YouTube, YouTube Music, YouTube Kids and YouTube Studio (WIP).
208 lines • 11.6 kB
JavaScript
var _OAuth_instances, _OAuth_identity, _OAuth_session, _OAuth_credentials, _OAuth_polling_interval, _OAuth_loadCachedCredentials, _OAuth_getUserCode, _OAuth_startPolling, _OAuth_refreshAccessToken, _OAuth_getClientIdentity;
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import * as Constants from '../utils/Constants.js';
import { OAuthError, Platform } from '../utils/Utils.js';
class OAuth {
constructor(session) {
_OAuth_instances.add(this);
_OAuth_identity.set(this, void 0);
_OAuth_session.set(this, void 0);
_OAuth_credentials.set(this, void 0);
_OAuth_polling_interval.set(this, 5);
__classPrivateFieldSet(this, _OAuth_session, session, "f");
}
/**
* Starts the auth flow in case no valid credentials are available.
*/
init(credentials) {
return __awaiter(this, void 0, void 0, function* () {
__classPrivateFieldSet(this, _OAuth_credentials, credentials, "f");
if (this.validateCredentials()) {
if (!this.has_access_token_expired)
__classPrivateFieldGet(this, _OAuth_session, "f").emit('auth', {
credentials: __classPrivateFieldGet(this, _OAuth_credentials, "f"),
status: 'SUCCESS'
});
}
else if (!(yield __classPrivateFieldGet(this, _OAuth_instances, "m", _OAuth_loadCachedCredentials).call(this))) {
yield __classPrivateFieldGet(this, _OAuth_instances, "m", _OAuth_getUserCode).call(this);
}
});
}
cacheCredentials() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(__classPrivateFieldGet(this, _OAuth_credentials, "f")));
yield ((_a = __classPrivateFieldGet(this, _OAuth_session, "f").cache) === null || _a === void 0 ? void 0 : _a.set('youtubei_oauth_credentials', data.buffer));
});
}
removeCache() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
yield ((_a = __classPrivateFieldGet(this, _OAuth_session, "f").cache) === null || _a === void 0 ? void 0 : _a.remove('youtubei_oauth_credentials'));
});
}
/**
* Refresh access token if the same has expired.
*/
refreshIfRequired() {
return __awaiter(this, void 0, void 0, function* () {
if (this.has_access_token_expired) {
yield __classPrivateFieldGet(this, _OAuth_instances, "m", _OAuth_refreshAccessToken).call(this);
}
});
}
revokeCredentials() {
return __awaiter(this, void 0, void 0, function* () {
if (!__classPrivateFieldGet(this, _OAuth_credentials, "f"))
return;
yield this.removeCache();
return __classPrivateFieldGet(this, _OAuth_session, "f").http.fetch_function(new URL(`/o/oauth2/revoke?token=${encodeURIComponent(__classPrivateFieldGet(this, _OAuth_credentials, "f").access_token)}`, Constants.URLS.YT_BASE), {
method: 'post'
});
});
}
get credentials() {
return __classPrivateFieldGet(this, _OAuth_credentials, "f");
}
get has_access_token_expired() {
const timestamp = __classPrivateFieldGet(this, _OAuth_credentials, "f") ? new Date(__classPrivateFieldGet(this, _OAuth_credentials, "f").expires).getTime() : -Infinity;
return new Date().getTime() > timestamp;
}
validateCredentials() {
return __classPrivateFieldGet(this, _OAuth_credentials, "f") &&
Reflect.has(__classPrivateFieldGet(this, _OAuth_credentials, "f"), 'access_token') &&
Reflect.has(__classPrivateFieldGet(this, _OAuth_credentials, "f"), 'refresh_token') &&
Reflect.has(__classPrivateFieldGet(this, _OAuth_credentials, "f"), 'expires') || false;
}
}
_OAuth_identity = new WeakMap(), _OAuth_session = new WeakMap(), _OAuth_credentials = new WeakMap(), _OAuth_polling_interval = new WeakMap(), _OAuth_instances = new WeakSet(), _OAuth_loadCachedCredentials = function _OAuth_loadCachedCredentials() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const data = yield ((_a = __classPrivateFieldGet(this, _OAuth_session, "f").cache) === null || _a === void 0 ? void 0 : _a.get('youtubei_oauth_credentials'));
if (!data)
return false;
const decoder = new TextDecoder();
const credentials = JSON.parse(decoder.decode(data));
__classPrivateFieldSet(this, _OAuth_credentials, {
access_token: credentials.access_token,
refresh_token: credentials.refresh_token,
expires: new Date(credentials.expires)
}, "f");
__classPrivateFieldGet(this, _OAuth_session, "f").emit('auth', {
credentials: __classPrivateFieldGet(this, _OAuth_credentials, "f"),
status: 'SUCCESS'
});
return true;
});
}, _OAuth_getUserCode = function _OAuth_getUserCode() {
return __awaiter(this, void 0, void 0, function* () {
__classPrivateFieldSet(this, _OAuth_identity, yield __classPrivateFieldGet(this, _OAuth_instances, "m", _OAuth_getClientIdentity).call(this), "f");
const data = {
client_id: __classPrivateFieldGet(this, _OAuth_identity, "f").client_id,
scope: Constants.OAUTH.SCOPE,
device_id: Platform.shim.uuidv4(),
device_model: Constants.OAUTH.MODEL_NAME
};
const response = yield __classPrivateFieldGet(this, _OAuth_session, "f").http.fetch_function(new URL('/o/oauth2/device/code', Constants.URLS.YT_BASE), {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const response_data = yield response.json();
__classPrivateFieldGet(this, _OAuth_session, "f").emit('auth-pending', response_data);
__classPrivateFieldSet(this, _OAuth_polling_interval, response_data.interval, "f");
__classPrivateFieldGet(this, _OAuth_instances, "m", _OAuth_startPolling).call(this, response_data.device_code);
});
}, _OAuth_startPolling = function _OAuth_startPolling(device_code) {
const poller = setInterval(() => __awaiter(this, void 0, void 0, function* () {
const data = Object.assign(Object.assign({}, __classPrivateFieldGet(this, _OAuth_identity, "f")), { code: device_code, grant_type: Constants.OAUTH.GRANT_TYPE });
try {
const response = yield __classPrivateFieldGet(this, _OAuth_session, "f").http.fetch_function(new URL('/o/oauth2/token', Constants.URLS.YT_BASE), {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const response_data = yield response.json();
if (response_data.error) {
switch (response_data.error) {
case 'access_denied':
__classPrivateFieldGet(this, _OAuth_session, "f").emit('auth-error', new OAuthError('Access was denied.', { status: 'ACCESS_DENIED' }));
break;
case 'expired_token':
__classPrivateFieldGet(this, _OAuth_session, "f").emit('auth-error', new OAuthError('The device code has expired, restarting auth flow.', { status: 'DEVICE_CODE_EXPIRED' }));
clearInterval(poller);
__classPrivateFieldGet(this, _OAuth_instances, "m", _OAuth_getUserCode).call(this);
break;
default:
break;
}
return;
}
const expiration_date = new Date(new Date().getTime() + response_data.expires_in * 1000);
__classPrivateFieldSet(this, _OAuth_credentials, {
access_token: response_data.access_token,
refresh_token: response_data.refresh_token,
expires: expiration_date
}, "f");
__classPrivateFieldGet(this, _OAuth_session, "f").emit('auth', {
credentials: __classPrivateFieldGet(this, _OAuth_credentials, "f"),
status: 'SUCCESS'
});
clearInterval(poller);
}
catch (err) {
clearInterval(poller);
return __classPrivateFieldGet(this, _OAuth_session, "f").emit('auth-error', new OAuthError('Could not obtain user code.', { status: 'FAILED', error: err }));
}
}), __classPrivateFieldGet(this, _OAuth_polling_interval, "f") * 1000);
}, _OAuth_refreshAccessToken = function _OAuth_refreshAccessToken() {
return __awaiter(this, void 0, void 0, function* () {
if (!__classPrivateFieldGet(this, _OAuth_credentials, "f"))
return;
__classPrivateFieldSet(this, _OAuth_identity, yield __classPrivateFieldGet(this, _OAuth_instances, "m", _OAuth_getClientIdentity).call(this), "f");
const data = Object.assign(Object.assign({}, __classPrivateFieldGet(this, _OAuth_identity, "f")), { refresh_token: __classPrivateFieldGet(this, _OAuth_credentials, "f").refresh_token, grant_type: 'refresh_token' });
const response = yield __classPrivateFieldGet(this, _OAuth_session, "f").http.fetch_function(new URL('/o/oauth2/token', Constants.URLS.YT_BASE), {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const response_data = yield response.json();
const expiration_date = new Date(new Date().getTime() + response_data.expires_in * 1000);
__classPrivateFieldSet(this, _OAuth_credentials, {
access_token: response_data.access_token,
refresh_token: response_data.refresh_token || __classPrivateFieldGet(this, _OAuth_credentials, "f").refresh_token,
expires: expiration_date
}, "f");
__classPrivateFieldGet(this, _OAuth_session, "f").emit('update-credentials', {
credentials: __classPrivateFieldGet(this, _OAuth_credentials, "f"),
status: 'SUCCESS'
});
});
}, _OAuth_getClientIdentity = function _OAuth_getClientIdentity() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const response = yield __classPrivateFieldGet(this, _OAuth_session, "f").http.fetch_function(new URL('/tv', Constants.URLS.YT_BASE), { headers: Constants.OAUTH.HEADERS });
const response_data = yield response.text();
const url_body = (_a = Constants.OAUTH.REGEX.AUTH_SCRIPT.exec(response_data)) === null || _a === void 0 ? void 0 : _a[1];
if (!url_body)
throw new OAuthError('Could not obtain script url.', { status: 'FAILED' });
const script = yield __classPrivateFieldGet(this, _OAuth_session, "f").http.fetch(url_body, { baseURL: Constants.URLS.YT_BASE });
const client_identity = (yield script.text())
.replace(/\n/g, '')
.match(Constants.OAUTH.REGEX.CLIENT_IDENTITY);
const groups = client_identity === null || client_identity === void 0 ? void 0 : client_identity.groups;
if (!groups)
throw new OAuthError('Could not obtain client identity.', { status: 'FAILED' });
return groups;
});
};
export default OAuth;
//# sourceMappingURL=OAuth.js.map