YouTube Qualities Size

Shows file size for each quality in YouTube

Per 09-06-2020. Zie de nieuwste versie.

// ==UserScript==
// @name         YouTube Qualities Size
// @namespace    ytqz.smz.k
// @version      1.1.3
// @description  Shows file size for each quality in YouTube
// @author       Abdelrahman Khalil
// @match        https://www.youtube.com/*
// @grant        none
// ==/UserScript==

const API = 'https://www.youtube.com/get_video_info?video_id='
const cache = {}
let videoId, settingsMenuEl, observer

// Fetching Info
const fetchInfo = () => {
    return fetch(API + videoId)
}

const parseResponse = data => {
    // Youtube sends data as url queries.
    // we parse it then get value of player_response which is stringified JSON.
    let playerResponse = JSON.parse(getQuery(data, 'player_response'))

    return playerResponse.streamingData.adaptiveFormats
}

// ---------
const findFormats = (formats, quality) => {
    let allCodecs = [],
        audioCL = 0,
        vp9 = ''

    let audioFormat = formats.find(
        format =>
            format.audioQuality === 'AUDIO_QUALITY_MEDIUM' &&
            format.mimeType.includes('opus')
    )
    if (audioFormat) audioCL = audioFormat.contentLength

    formats.forEach(format => {
        if (format.qualityLabel === quality) {
            let codec = format.mimeType.replace(REGEX.codec, '')
            let sizeMB = toMB(format.contentLength, audioCL)

            allCodecs.push(`${codec}: ${sizeMB} MB`)

            if (codec === 'vp9') vp9 = sizeMB
        }
    })

    let allCodecsStr = allCodecs.join('\n')
    return { allCodecsStr, vp9 }
}

// Injection
const addSizesToChildren = menu => {
    let qualityElements = menu.querySelectorAll('span')
    let formats = cache[videoId]
    let isRTL = document.body.getAttribute('dir') === 'rtl'

    if (!formats) return

    qualityElements.forEach((el, index) => {
        let qualityLabel = el.textContent.replace(REGEX.qlabel, '')
        if (index === qualityElements.length - 1) return

        let { allCodecsStr, vp9 } = findFormats(formats, qualityLabel)

        el.innerHTML += `<yt-qualities-size ${ isRTL ? 'dir="ltr"' : ''} style="float: ${ isRTL ? 'left' : 'right' }; text-align: right" title="${allCodecsStr}">${vp9} MB</yt-qualities-size>`
    })
}

// Making sure it's /watch
addEventListener('yt-page-data-updated', () => {
    settingsMenuEl = document.querySelector('.ytp-settings-menu')
    videoId = getQuery(location.search, 'v')

    if (location.pathname === '/watch' && !observer) {
        observer = new MutationObserver(async ([{ addedNodes }]) => {
            let node = addedNodes[0]
            if (
                node &&
                node.classList.contains('ytp-quality-menu') &&
                !document.querySelector('yt-qualities-size')
            ) {
                if (!cache[videoId]) {
                    let info = await (await fetchInfo()).text()
                    cache[videoId] = parseResponse(info)
                }

                addSizesToChildren(node)
            }
        })

        observer.observe(settingsMenuEl, {
            childList: true
        })
    }
})

// Utils/Helpers/Stuff
const getQuery = (string, query) => new URLSearchParams(string).get(query)
const toMB = (vbytes, abytes) =>
    Math.round((parseInt(vbytes) + parseInt(abytes)) / 1048576)
const REGEX = {
    qlabel: /\s(hd|4k|8k|16k)/i,
    codec: /(?!=").+="|\..+|"/g
}