Skip to content

Instantly share code, notes, and snippets.

@garywill
Last active June 25, 2025 08:51
Show Gist options
  • Save garywill/6f65915db7a890a1d95b4aecf5a3cb24 to your computer and use it in GitHub Desktop.
Save garywill/6f65915db7a890a1d95b4aecf5a3cb24 to your computer and use it in GitHub Desktop.
Browser bookmarklet: get formats from youtube video page
function extractAdaptiveFormatsInfo(in_formats) {
function getStreamType(f) {
const mimeType = f.mimeType || '';
if (mimeType.startsWith('audio/')) return 'audio';
if (mimeType.startsWith('video/')) {
if (f.audioChannels && f.audioChannels > 0) {
return 'video+audio';
}
return 'video';
}
return '-';
}
function getFormat(mimeType) {
if (!mimeType) return '-';
return mimeType.split(';')[0].split('/')[1] || '-';
}
function getCodec(mimeType) {
if (!mimeType) return '-';
const match = mimeType.match(/codecs="(.+?)"/);
return match ? match[1] : '-';
}
function prettyBitrate(bps) {
if (bps == null || bps === '-') return '-';
if (bps >= 1_000_000) return (bps / 1_000_000).toFixed(2) + ' Mb';
if (bps >= 1_000) return (bps / 1_000).toFixed(1) + ' kb';
return bps + ' bps';
}
function prettySize(size) {
if (!size || isNaN(size)) return '-';
size = Number(size);
if (size >= 1024 * 1024 * 1024)
return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
if (size >= 1024 * 1024)
return (size / (1024 * 1024)).toFixed(2) + ' MB';
if (size >= 1024)
return (size / 1024).toFixed(1) + ' KB';
return size + ' B';
}
function cleanQualityStr(q) {
if (!q) return '-';
return String(q).replace(/^AUDIO_QUALITY_/, '');
}
function pad(str, len) {
str = String(str ?? '-');
return str.length >= len ? str.slice(0, len) : str + ' '.repeat(len - str.length);
}
function getVideoAudioBitrate(f) {
let videoBitrate = '-';
let audioBitrate = '-';
if (f.approximateAudioBitrate || f.audioBitrate) {
audioBitrate = prettyBitrate(f.approximateAudioBitrate || f.audioBitrate);
if (f.bitrate) {
videoBitrate = prettyBitrate(f.bitrate - (f.approximateAudioBitrate || f.audioBitrate));
}
} else if (getStreamType(f).startsWith('audio')) {
audioBitrate = prettyBitrate(f.bitrate);
} else if (getStreamType(f).startsWith('video')) {
videoBitrate = prettyBitrate(f.bitrate);
}
return { videoBitrate, audioBitrate };
}
let rows = in_formats.map(f => {
const type = getStreamType(f);
const bitrate = prettyBitrate(f.bitrate);
const res = (f.width && f.height) ? `${f.width}x${f.height}` : '-';
const fps = f.fps ?? '-';
const format = getFormat(f.mimeType);
const codec = getCodec(f.mimeType);
let quality = f.qualityLabel || f.audioQuality || f.quality || '-';
quality = cleanQualityStr(quality);
const size = prettySize(f.contentLength || f.approximateContentLength);
const { videoBitrate, audioBitrate } = getVideoAudioBitrate(f);
let audioSampleRate = f.audioSampleRate ? f.audioSampleRate + ' Hz' : '-';
let itagStr = f.itag;
if (f.isDrc) itagStr = itagStr + '-drc';
let resSort = 0;
if (f.width && f.height) resSort = f.width * f.height;
let qualitySort = 0;
if (f.qualityLabel) {
const m = f.qualityLabel.match(/^(\d+)/);
if (m) qualitySort = parseInt(m[1]);
} else if (f.audioQuality) {
if (/LOW/i.test(f.audioQuality)) qualitySort = 1;
else if (/MEDIUM/i.test(f.audioQuality)) qualitySort = 2;
else if (/HIGH/i.test(f.audioQuality)) qualitySort = 3;
} else if (f.quality) {
const m = String(f.quality).match(/^(\d+)/);
if (m) qualitySort = parseInt(m[1]);
}
return {
itag: itagStr,
format,
type,
res,
bitrate,
videoBitrate,
audioBitrate,
audioSampleRate,
fps,
codec,
quality,
size,
resSort,
qualitySort
};
});
rows.sort((a, b) => {
if (a.resSort && b.resSort) return b.resSort - a.resSort;
if (a.resSort) return -1;
if (b.resSort) return 1;
return b.qualitySort - a.qualitySort;
});
let out = [
pad('itag', 10) +
pad('format', 8) +
pad('type', 12) +
pad('resolution', 13) +
pad('total br', 12) +
pad('video br', 12) +
pad('audio br', 12) +
pad('audio rate', 12) +
pad('fps', 5) +
pad('codec', 18) +
pad('quality', 12) +
pad('size', 12)
];
out.push('-'.repeat(138));
rows.forEach(r => {
out.push(
pad(r.itag, 10) +
pad(r.format, 8) +
pad(r.type, 12) +
pad(r.res, 13) +
pad(r.bitrate, 12) +
pad(r.videoBitrate, 12) +
pad(r.audioBitrate, 12) +
pad(r.audioSampleRate, 12) +
pad(r.fps, 5) +
pad(r.codec, 18) +
pad(r.quality, 12) +
pad(r.size, 12)
);
});
return out.join('\n');
}
// Usage example:
var plyRes = document.getElementsByTagName("ytd-app")[0].data.playerResponse;
var streamingData = plyRes.streamingData;
var arr_formats = (streamingData.adaptiveFormats || [])
.concat(streamingData.formats || []);
console.log(extractAdaptiveFormatsInfo(arr_formats));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment