UNPKG

newpipe-extractor-js

Version:
159 lines 5.92 kB
"use strict"; // PoToken Provider - YouTube's Proof of Origin Token System // This is essential for bypassing YouTube's modern anti-bot protection Object.defineProperty(exports, "__esModule", { value: true }); exports.PoTokenCache = exports.PoTokenProvider = exports.WEBPO_CLIENTS = exports.PoTokenProviderRejectedRequest = exports.PoTokenError = exports.ContentBindingType = exports.PoTokenContext = void 0; exports.getWebPoContentBinding = getWebPoContentBinding; exports.generateWebPoCacheSpec = generateWebPoCacheSpec; var PoTokenContext; (function (PoTokenContext) { PoTokenContext["GVS"] = "gvs"; PoTokenContext["PLAYER"] = "player"; PoTokenContext["SUBS"] = "subs"; })(PoTokenContext || (exports.PoTokenContext = PoTokenContext = {})); var ContentBindingType; (function (ContentBindingType) { ContentBindingType["VISITOR_DATA"] = "visitor_data"; ContentBindingType["DATASYNC_ID"] = "datasync_id"; ContentBindingType["VIDEO_ID"] = "video_id"; ContentBindingType["VISITOR_ID"] = "visitor_id"; })(ContentBindingType || (exports.ContentBindingType = ContentBindingType = {})); class PoTokenError extends Error { constructor(message) { super(message); this.name = 'PoTokenError'; } } exports.PoTokenError = PoTokenError; class PoTokenProviderRejectedRequest extends PoTokenError { constructor(message) { super(message); this.name = 'PoTokenProviderRejectedRequest'; } } exports.PoTokenProviderRejectedRequest = PoTokenProviderRejectedRequest; // WEBPO clients that support PO tokens exports.WEBPO_CLIENTS = [ 'WEB', 'MWEB', 'TVHTML5', 'WEB_EMBEDDED_PLAYER', 'WEB_CREATOR', 'WEB_REMIX', 'TVHTML5_SIMPLY_EMBEDDED_PLAYER' ]; function getWebPoContentBinding(request, webpoClients = exports.WEBPO_CLIENTS, bindToVisitorId = false) { const clientName = request.innertubeContext.client.clientName; if (!clientName || !webpoClients.includes(clientName)) { return [null, null]; } if (request.context === PoTokenContext.GVS || clientName === 'WEB_REMIX') { if (request.isAuthenticated) { return [request.dataSyncId || null, ContentBindingType.DATASYNC_ID]; } else { if (bindToVisitorId) { const visitorId = extractVisitorId(request.visitorData); if (visitorId) { return [visitorId, ContentBindingType.VISITOR_ID]; } } return [request.visitorData || null, ContentBindingType.VISITOR_DATA]; } } else if (request.context === PoTokenContext.PLAYER || request.context === PoTokenContext.SUBS) { return [request.videoId || null, ContentBindingType.VIDEO_ID]; } return [null, null]; } function extractVisitorId(visitorData) { if (!visitorData) return null; try { // Decode the visitor_data protobuf const decoded = Buffer.from(decodeURIComponent(visitorData), 'base64'); const visitorId = decoded.slice(2, 13).toString(); // Check that visitor ID is valid format (11 chars, alphanumeric + _-) if (/^[A-Za-z0-9_-]{11}$/.test(visitorId)) { return visitorId; } } catch (error) { // Ignore errors in visitor ID extraction } return null; } function generateWebPoCacheSpec(request, bindToVisitorId = true) { const [contentBinding, contentBindingType] = getWebPoContentBinding(request, exports.WEBPO_CLIENTS, bindToVisitorId); if (!contentBinding || !contentBindingType) { return null; } const writePolicy = contentBindingType === ContentBindingType.VIDEO_ID ? 'WRITE_FIRST' : 'WRITE_ALL'; return { keyBindings: { 't': 'webpo', 'cb': contentBinding, 'cbt': contentBindingType, 'ip': request.innertubeContext.client.remoteHost || null, 'sa': request.requestSourceAddress || null, 'px': request.requestProxy || null }, // Integrity token usually has 12 hour TTL, we use 6 hours to be safe defaultTtl: 21600, // 6 hours in seconds writePolicy }; } class PoTokenProvider { constructor() { this.supportedContexts = [PoTokenContext.GVS, PoTokenContext.PLAYER]; } supportsContext(context) { return this.supportedContexts.includes(context); } validateRequest(request) { if (!this.supportsContext(request.context)) { throw new PoTokenProviderRejectedRequest(`Provider ${this.providerName} does not support context: ${request.context}`); } } } exports.PoTokenProvider = PoTokenProvider; // Simple in-memory cache for PO tokens class PoTokenCache { constructor() { this.cache = new Map(); } generateCacheKey(spec) { const sortedEntries = Object.entries(spec.keyBindings) .filter(([_, value]) => value !== null) .sort(([a], [b]) => a.localeCompare(b)); return JSON.stringify(sortedEntries); } get(request) { const spec = generateWebPoCacheSpec(request); if (!spec) return null; const key = this.generateCacheKey(spec); const cached = this.cache.get(key); if (!cached) return null; // Check if expired if (cached.expiresAt < Date.now()) { this.cache.delete(key); return null; } return cached.response; } store(request, response) { const spec = generateWebPoCacheSpec(request); if (!spec) return; const key = this.generateCacheKey(spec); const expiresAt = response.expiresAt || (Date.now() + spec.defaultTtl * 1000); this.cache.set(key, { response, expiresAt }); } clear() { this.cache.clear(); } } exports.PoTokenCache = PoTokenCache; //# sourceMappingURL=PoTokenProvider.js.map