UNPKG

newpipe-extractor-js

Version:
405 lines 14.9 kB
"use strict"; // Advanced Codec Parsing Utilities for NewPipe Extractor JS // Based on yt-dlp's sophisticated codec analysis and RFC 6381 compliance // Provides comprehensive codec detection, HDR analysis, and format scoring Object.defineProperty(exports, "__esModule", { value: true }); exports.parseCodecs = parseCodecs; exports.getCodecQualityScore = getCodecQualityScore; exports.isHDRCodec = isHDRCodec; exports.getCodecDescription = getCodecDescription; exports.getOptimalContainer = getOptimalContainer; exports.getCodecCompatibility = getCodecCompatibility; exports.parseCodecsFromMimeType = parseCodecsFromMimeType; /** * Advanced codec parsing based on yt-dlp implementation * Follows RFC 6381 specification for codec strings * http://tools.ietf.org/html/rfc6381 */ function parseCodecs(codecsStr) { if (!codecsStr) { return {}; } // Split and clean codec strings const splitCodecs = codecsStr .trim() .replace(/,$/, '') .split(',') .map(codec => codec.trim()) .filter(codec => codec.length > 0); let vcodec; let acodec; let scodec; let hdr; let dynamicRange; let profile; let level; let bitDepth; for (let fullCodec of splitCodecs) { // Convert first part to lowercase for consistent matching fullCodec = fullCodec.replace(/^([^.]+)/, (_match, p1) => p1.toLowerCase()); // Remove leading zeros and split by dots const parts = fullCodec.replace(/0+(?=\d)/g, '').split('.'); const codecBase = parts[0]; // Video codec detection with enhanced HDR support if (['avc1', 'avc2', 'avc3', 'avc4', 'vp9', 'vp8', 'hev1', 'hev2', 'h263', 'h264', 'mp4v', 'hvc1', 'av01', 'av1', 'theora', 'dvh1', 'dvhe'].includes(codecBase)) { if (!vcodec) { vcodec = fullCodec; // Enhanced HDR detection if (['dvh1', 'dvhe'].includes(codecBase)) { hdr = 'DV'; // Dolby Vision dynamicRange = 'HDR'; // Dolby Vision profile detection if (parts[1]) { const profileNum = parseInt(parts[1], 10); if (profileNum >= 5) { profile = `DV Profile ${profileNum}`; } } } else if (codecBase === 'av01' || codecBase === 'av1') { // AV1 HDR detection if (parts[3] === '10') { hdr = 'HDR10'; dynamicRange = 'HDR'; bitDepth = 10; } else if (parts[3] === '12') { hdr = 'HDR12'; dynamicRange = 'HDR'; bitDepth = 12; } // AV1 profile and level detection if (parts[1]) { profile = `AV1 Profile ${parts[1]}`; } if (parts[2]) { level = parts[2]; } } else if (codecBase === 'vp9') { // VP9 HDR detection if (parts[1] === '2') { hdr = 'HDR10'; dynamicRange = 'HDR'; bitDepth = 10; profile = 'VP9 Profile 2'; } else if (parts[1] === '3') { hdr = 'HDR10'; dynamicRange = 'HDR'; bitDepth = 12; profile = 'VP9 Profile 3'; } } else if (['hev1', 'hev2', 'hvc1'].includes(codecBase)) { // HEVC/H.265 HDR detection if (parts[3] && ['L120', 'L123', 'L150', 'L153'].includes(parts[3])) { hdr = 'HDR10'; dynamicRange = 'HDR'; bitDepth = 10; } // HEVC profile detection if (parts[1]) { const profileIdc = parseInt(parts[1], 10); if (profileIdc === 2) { profile = 'Main10'; } else if (profileIdc === 3) { profile = 'Main Still Picture'; } } } else if (['avc1', 'avc2', 'avc3', 'avc4'].includes(codecBase)) { // H.264 profile detection if (parts[1]) { const profileIdc = parseInt(parts[1].substring(0, 2), 16); if (profileIdc === 0x42) { profile = 'Baseline'; } else if (profileIdc === 0x4D) { profile = 'Main'; } else if (profileIdc === 0x58) { profile = 'Extended'; } else if (profileIdc === 0x64) { profile = 'High'; } } } } } // Audio codec detection with enhanced format support else if (['flac', 'mp4a', 'opus', 'vorbis', 'mp3', 'aac', 'ac-4', 'ac-3', 'ec-3', 'eac3', 'dtsc', 'dtse', 'dtsh', 'dtsl', 'alac', 'pcm', 'adts', 'mp2', 'amr'].includes(codecBase)) { if (!acodec) { acodec = fullCodec; // Enhanced audio codec analysis if (codecBase === 'mp4a') { // MP4A object type detection if (parts[1] === '40') { if (parts[2] === '2') { acodec = 'aac-lc'; } else if (parts[2] === '5') { acodec = 'aac-he'; } else if (parts[2] === '29') { acodec = 'aac-he-v2'; } } } else if (codecBase === 'opus') { // Opus channel configuration if (parts[1]) { const channels = parseInt(parts[1], 10); if (channels > 2) { profile = `Opus ${channels}ch`; } } } else if (codecBase === 'flac') { // FLAC bit depth detection if (parts[1]) { bitDepth = parseInt(parts[1], 10); } } } } // Subtitle codec detection else if (['stpp', 'wvtt', 'c608', 'c708', 'dvbs', 'teletext'].includes(codecBase)) { if (!scodec) { scodec = fullCodec; } } // Unknown codec warning (in production, you might want to log this) else { console.debug(`[CodecUtils] Unknown codec: ${fullCodec}`); } } // Fallback logic for simple codec strings if (!vcodec && !acodec && !scodec && splitCodecs.length === 2) { // Simple two-codec format (video, audio) return { vcodec: splitCodecs[0], acodec: splitCodecs[1] }; } // Build result object const result = {}; if (vcodec) result.vcodec = vcodec; if (acodec) result.acodec = acodec; if (scodec) result.scodec = scodec; if (hdr) result.hdr = hdr; if (dynamicRange) result.dynamicRange = dynamicRange; if (profile) result.profile = profile; if (level) result.level = level; if (bitDepth) result.bitDepth = bitDepth; return result; } /** * Enhanced codec quality scoring based on yt-dlp preferences */ function getCodecQualityScore(codec, type) { if (!codec || codec === 'none') return 0; const codecLower = codec.toLowerCase(); if (type === 'audio') { // Audio codec preferences (higher score = better) if (codecLower.includes('flac')) return 100; if (codecLower.includes('alac')) return 95; if (codecLower.includes('wav') || codecLower.includes('pcm')) return 90; if (codecLower.includes('opus')) return 85; if (codecLower.includes('vorbis') || codecLower.includes('ogg')) return 80; if (codecLower.includes('aac-he-v2')) return 75; if (codecLower.includes('aac-he')) return 70; if (codecLower.includes('aac') || codecLower.includes('mp4a')) return 65; if (codecLower.includes('mp3')) return 60; if (codecLower.includes('ac-4')) return 55; if (codecLower.includes('ac-3') || codecLower.includes('eac3') || codecLower.includes('ec-3')) return 50; if (codecLower.includes('dts')) return 45; if (codecLower.includes('mp2')) return 30; if (codecLower.includes('amr')) return 20; return 10; } else if (type === 'video') { // Video codec preferences (higher score = better) if (codecLower.includes('av01') || codecLower.includes('av1')) return 100; if (codecLower.includes('vp9.2')) return 95; if (codecLower.includes('vp9')) return 90; if (codecLower.includes('h265') || codecLower.includes('hevc') || codecLower.includes('hev')) return 85; if (codecLower.includes('h264') || codecLower.includes('avc')) return 80; if (codecLower.includes('vp8')) return 70; if (codecLower.includes('mp4v')) return 60; if (codecLower.includes('h263')) return 50; if (codecLower.includes('theora')) return 40; return 30; } return 10; } /** * Check if codec supports HDR content */ function isHDRCodec(codecInfo) { return !!(codecInfo.hdr || codecInfo.dynamicRange === 'HDR'); } /** * Get human-readable codec description */ function getCodecDescription(codecInfo) { const parts = []; if (codecInfo.vcodec) { let videoDesc = codecInfo.vcodec.toUpperCase(); if (codecInfo.profile) { videoDesc += ` (${codecInfo.profile})`; } if (codecInfo.hdr) { videoDesc += ` ${codecInfo.hdr}`; } parts.push(`Video: ${videoDesc}`); } if (codecInfo.acodec) { let audioDesc = codecInfo.acodec.toUpperCase(); if (codecInfo.bitDepth) { audioDesc += ` ${codecInfo.bitDepth}-bit`; } parts.push(`Audio: ${audioDesc}`); } if (codecInfo.scodec) { parts.push(`Subtitles: ${codecInfo.scodec.toUpperCase()}`); } return parts.join(', '); } /** * Determine container format from codec information */ function getOptimalContainer(codecInfo) { const { vcodec, acodec } = codecInfo; // AV1 or VP9 with Opus/Vorbis -> WebM if ((vcodec?.includes('av01') || vcodec?.includes('av1') || vcodec?.includes('vp9')) && (acodec?.includes('opus') || acodec?.includes('vorbis'))) { return 'webm'; } // H.264/H.265 with AAC -> MP4 if ((vcodec?.includes('avc') || vcodec?.includes('hev') || vcodec?.includes('h264') || vcodec?.includes('h265')) && (acodec?.includes('aac') || acodec?.includes('mp4a'))) { return 'mp4'; } // FLAC audio -> FLAC container if (acodec?.includes('flac')) { return 'flac'; } // Opus audio -> OGG container if (acodec?.includes('opus') && !vcodec) { return 'ogg'; } // Default fallbacks if (vcodec) { return vcodec.includes('vp') || vcodec.includes('av01') ? 'webm' : 'mp4'; } return acodec?.includes('opus') || acodec?.includes('vorbis') ? 'webm' : 'mp4'; } /** * Check codec compatibility for different platforms */ function getCodecCompatibility(codecInfo) { const { vcodec, acodec } = codecInfo; // Web browser compatibility const webSupport = { video: !vcodec || ['h264', 'vp8', 'vp9', 'av01'].some(c => vcodec.includes(c)), audio: !acodec || ['aac', 'opus', 'vorbis', 'mp3'].some(c => acodec.includes(c)) }; // Mobile device compatibility (more conservative) const mobileSupport = { video: !vcodec || ['h264', 'h265'].some(c => vcodec.includes(c)), audio: !acodec || ['aac', 'mp3'].some(c => acodec.includes(c)) }; // Desktop compatibility (broad support) const desktopSupport = { video: !vcodec || true, // Desktop players support most codecs audio: !acodec || true }; // Smart TV compatibility (conservative) const smartTvSupport = { video: !vcodec || ['h264', 'h265'].some(c => vcodec.includes(c)), audio: !acodec || ['aac', 'ac-3', 'eac3'].some(c => acodec.includes(c)) }; return { web: webSupport.video && webSupport.audio, mobile: mobileSupport.video && mobileSupport.audio, desktop: desktopSupport.video && desktopSupport.audio, smart_tv: smartTvSupport.video && smartTvSupport.audio }; } /** * Extract codec information from MIME type */ function parseCodecsFromMimeType(mimeType) { if (!mimeType) return {}; // Extract codecs parameter from MIME type const codecsMatch = mimeType.match(/codecs="([^"]+)"/i) || mimeType.match(/codecs=([^;,\s]+)/i); if (codecsMatch && codecsMatch[1]) { return parseCodecs(codecsMatch[1]); } // Fallback to basic MIME type analysis const result = {}; if (mimeType.includes('video/')) { result.container = mimeType.split('/')[1].split(';')[0]; if (mimeType.includes('mp4')) { result.vcodec = 'h264'; result.acodec = 'aac'; } else if (mimeType.includes('webm')) { result.vcodec = 'vp9'; result.acodec = 'opus'; } } else if (mimeType.includes('audio/')) { result.container = mimeType.split('/')[1].split(';')[0]; if (mimeType.includes('mp4') || mimeType.includes('m4a')) { result.acodec = 'aac'; } else if (mimeType.includes('webm')) { result.acodec = 'opus'; } else if (mimeType.includes('ogg')) { result.acodec = 'vorbis'; } } return result; } //# sourceMappingURL=CodecUtils.js.map