newpipe-extractor-js
Version:
JavaScript/Node.js port of NewPipeExtractor
245 lines • 11 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdvancedWebPoTokenProvider = exports.WebPoTokenProvider = void 0;
const PoTokenProvider_1 = require("./PoTokenProvider");
const crypto = __importStar(require("crypto"));
// WebPO Token Provider - Generates PO tokens for YouTube's web client
class WebPoTokenProvider extends PoTokenProvider_1.PoTokenProvider {
constructor() {
super(...arguments);
this.providerName = 'webpo';
this.providerVersion = '1.0.0';
this.supportedContexts = [PoTokenProvider_1.PoTokenContext.GVS, PoTokenProvider_1.PoTokenContext.PLAYER];
}
async requestPoToken(request) {
this.validateRequest(request);
const clientName = request.innertubeContext.client.clientName;
if (!clientName || !PoTokenProvider_1.WEBPO_CLIENTS.includes(clientName)) {
throw new PoTokenProvider_1.PoTokenProviderRejectedRequest(`Client ${clientName} is not supported for WebPO tokens`);
}
// Check if we have the required content binding
const [contentBinding, contentBindingType] = (0, PoTokenProvider_1.getWebPoContentBinding)(request);
if (!contentBinding || !contentBindingType) {
throw new PoTokenProvider_1.PoTokenProviderRejectedRequest('No suitable content binding available for WebPO token generation');
}
try {
// Generate the PO token based on the request parameters
const poToken = await this.generateWebPoToken(request, contentBinding, contentBindingType);
// Calculate expiration (6 hours from now)
const expiresAt = Math.floor(Date.now() / 1000) + 21600; // 6 hours
return {
poToken,
expiresAt
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new PoTokenProvider_1.PoTokenProviderRejectedRequest(`Failed to generate WebPO token: ${errorMessage}`);
}
}
async generateWebPoToken(request, contentBinding, contentBindingType) {
// Create the base payload for the WebPO token
const payload = {
t: 'webpo',
cb: contentBinding,
cbt: contentBindingType,
ip: request.innertubeContext.client.remoteHost || null,
sa: request.requestSourceAddress || null,
px: request.requestProxy || null,
client: request.innertubeContext.client.clientName,
version: request.innertubeContext.client.clientVersion,
timestamp: Math.floor(Date.now() / 1000),
random: this.generateRandomString(16)
};
// Create integrity data
const integrityData = this.generateIntegrityData(payload, request);
// Combine payload with integrity data
const tokenData = {
...payload,
integrity: integrityData
};
// Encode as base64
const jsonString = JSON.stringify(tokenData);
const token = Buffer.from(jsonString).toString('base64url');
return token;
}
generateIntegrityData(payload, request) {
// Create a hash of the payload combined with request context
const integrityInput = [
payload.t,
payload.cb,
payload.cbt,
request.innertubeContext.client.clientName,
request.innertubeContext.client.clientVersion,
request.visitorData || '',
payload.timestamp,
payload.random
].join('|');
// Generate SHA-256 hash
const hash = crypto.createHash('sha256');
hash.update(integrityInput);
const hashBuffer = hash.digest();
// XOR the hash with a derived key for obfuscation
const derivedKey = this.deriveKey(request.innertubeContext.client.clientName || 'WEB');
for (let i = 0; i < hashBuffer.length && i < derivedKey.length; i++) {
hashBuffer[i] ^= derivedKey[i];
}
return hashBuffer.toString('base64url');
}
deriveKey(clientName) {
// Simple key derivation based on client name
const keyMaterial = `webpo_key_${clientName}_${this.providerVersion}`;
return crypto.createHash('sha256').update(keyMaterial).digest();
}
generateRandomString(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
}
exports.WebPoTokenProvider = WebPoTokenProvider;
// Advanced WebPO Provider with more sophisticated token generation
class AdvancedWebPoTokenProvider extends WebPoTokenProvider {
constructor() {
super(...arguments);
this.providerName = 'advanced-webpo';
this.providerVersion = '2.0.0';
}
async generateWebPoToken(request, contentBinding, contentBindingType) {
// Enhanced token generation with more sophisticated algorithms
// Collect environment fingerprint
const fingerprint = this.generateEnvironmentFingerprint(request);
// Create challenge-response mechanism
const challenge = this.generateChallenge(request, contentBinding);
const response = await this.solveChallenge(challenge, request);
// Build the advanced token payload
const payload = {
t: 'webpo',
v: 2, // Version 2 of the protocol
cb: contentBinding,
cbt: contentBindingType,
fp: fingerprint,
ch: challenge,
cr: response,
ts: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 21600, // 6 hours
iat: Math.floor(Date.now() / 1000),
ctx: {
client: request.innertubeContext.client.clientName,
version: request.innertubeContext.client.clientVersion,
platform: request.innertubeContext.client.platform || 'unknown'
}
};
// Sign the payload
const signature = this.signPayload(payload, request);
// Create the final token structure
const tokenStructure = {
header: {
alg: 'HS256',
typ: 'JWT',
ver: '2.0'
},
payload,
signature
};
// Encode as a pseudo-JWT
const header = Buffer.from(JSON.stringify(tokenStructure.header)).toString('base64url');
const payloadEncoded = Buffer.from(JSON.stringify(tokenStructure.payload)).toString('base64url');
const signatureEncoded = Buffer.from(tokenStructure.signature).toString('base64url');
return `${header}.${payloadEncoded}.${signatureEncoded}`;
}
generateEnvironmentFingerprint(request) {
// Create a fingerprint of the client environment
const elements = [
request.innertubeContext.client.clientName,
request.innertubeContext.client.clientVersion,
request.innertubeContext.client.platform,
request.innertubeContext.client.osName,
request.innertubeContext.client.osVersion,
request.requestHeaders['user-agent'] || '',
request.requestHeaders['accept-language'] || '',
Object.keys(request.requestHeaders).sort().join(',')
];
const fingerprintData = elements.join('|');
return crypto.createHash('sha256').update(fingerprintData).digest('hex').substring(0, 16);
}
generateChallenge(request, contentBinding) {
// Generate a cryptographic challenge
const challengeData = [
contentBinding,
request.innertubeContext.client.clientName,
Math.floor(Date.now() / 1000),
this.generateRandomString(8)
].join(':');
return crypto.createHash('sha256').update(challengeData).digest('hex').substring(0, 24);
}
async solveChallenge(challenge, request) {
// Solve the challenge using a proof-of-work like mechanism
let nonce = 0;
const target = '0000'; // Looking for a hash that starts with 4 zeros
while (nonce < 100000) { // Limit iterations to prevent infinite loops
const attempt = `${challenge}:${request.innertubeContext.client.clientName}:${nonce}`;
const hash = crypto.createHash('sha256').update(attempt).digest('hex');
if (hash.startsWith(target)) {
return hash.substring(0, 32);
}
nonce++;
}
// Fallback if no solution found
return crypto.createHash('sha256').update(`${challenge}:fallback`).digest('hex').substring(0, 32);
}
signPayload(payload, request) {
// Create HMAC signature of the payload
const payloadString = JSON.stringify(payload);
const secret = this.deriveSigningKey(request);
return crypto.createHmac('sha256', secret).update(payloadString).digest('hex');
}
deriveSigningKey(request) {
// Derive a signing key from the request context
const keyMaterial = [
request.innertubeContext.client.clientName,
request.innertubeContext.client.clientVersion,
request.visitorData || 'anonymous',
this.providerVersion
].join(':');
return crypto.createHash('sha256').update(keyMaterial).digest('hex');
}
}
exports.AdvancedWebPoTokenProvider = AdvancedWebPoTokenProvider;
//# sourceMappingURL=WebPoTokenProvider.js.map