newpipe-extractor-js
Version:
JavaScript/Node.js port of NewPipeExtractor
405 lines • 14.9 kB
JavaScript
;
// 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