YouTube - Video Download Buttons

Adds download buttons for all items on the videos page of a channel as well as in the top bar of each individual video

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name          YouTube - Video Download Buttons
// @description   Adds download buttons for all items on the videos page of a channel as well as in the top bar of each individual video
// @author        TheRealHawk
// @license       MIT
// @namespace     https://greasyfork.org/en/users/18936-therealhawk
// @match         https://www.youtube.com/*/videos*
// @match         https://www.youtube.com/watch*
// @version       1.2
// @require       https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js
// @require       https://ajax.googleapis.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js
// @run-at        document-body
// @grant         GM_xmlhttpRequest
// @grant         GM_download
// ==/UserScript==

// Workaround to get rid of "is not defined" warnings
/* globals $, jQuery */

// Wait until DOM element becomes available
function waitForEl(selector, callback, maxtries = false, interval = 100) {
    const poller = setInterval(() => {
        const el = $(selector)
        const retry = maxtries === false || maxtries-- > 0
        if (retry && el.length < 1) return
        clearInterval(poller)
        callback(el || null)
    }, interval)
}

// Download video
function dlVideo(videoUrl, videoTitle) {
    GM_xmlhttpRequest( {
        method: "GET",
        responseType: "json",
        headers: { "Referer": "https://en1.y2mate.is/" },
        url: "https://srvcdn7.2convert.me/api/json?url=" + videoUrl,
        onload: function(response) {
            if (response.status == 200) {
                let videos = JSON.parse(response.responseText).formats.video.filter(({needConvert}) => needConvert === false);

                const url = videos.sort(function(a, b) {
                    return parseInt(b.quality) - parseInt(a.quality);
                } )[0].url;

                const details = { url: url,
                                 name: videoTitle.replace(/[\\\?\.\*<>]/g, '').replace(/[\|:/]/g, ' - ').replace(/[\s]{2,}/g, ' ') + '.mp4',
                                 saveAs: true };
                GM_download(details);
            }
        }
    } );
}

// Add download buttons for all items on the video page of a channel
if (window.location.pathname.match(/\/@.+\/videos/)) {

    function addDLButtons() {
        $(selector + '> #contents > ytd-rich-item-renderer').each(function() {
            if ($('#metadata-line > span', this).length != 2) return;
            if ($('#thumbnail > #overlays > ytd-thumbnail-overlay-time-status-renderer', this).attr('overlay-style') == 'UPCOMING') return;

            const elem = $('#metadata-line > span + span', this);

            elem.after('<span class="ytd-video-meta-block video-download-button" style="color:white; font-size: 120%;">🖫</span>');

            elem.next().hover(function() {
                $(this).css("font-weight", "bold");
            }, function(){
                $(this).css("font-weight", "normal");
            } );

            const title = $('#meta > h3 > a', this).attr('title');
            const url = $('#meta > h3 > a', this).attr('href');
            elem.next().on('click', function(event) {
                event.stopPropagation();
                $(this).effect('bounce');
                dlVideo('https://youtube.com' + url, title);
            } );
        } );
    }

    const selector = '#primary > ytd-rich-grid-renderer > #contents > ytd-rich-grid-row';

    waitForEl(selector + ':nth-child(7)', function() {
        addDLButtons();
    }, 100, 100);

    let contents = $(selector).length;
    let timer;
    $(window).on('scroll', function() {
        if (timer) {
            window.clearTimeout(timer);
        }

        timer = window.setTimeout( function() {
            if (contents != $(selector).length) {
                contents = $(selector).length;
                addDLButtons();
            }
        }, 200);
    } );

}

// Add a download button in the top bar of each individual video
if (window.location.pathname.match(/\/watch/)) {

    function addDLButton() {
        const button = '<div id="video-download-button" style="color:white; font-size:24px; margin-bottom: 4px; margin-right: 20px;">🖫</div>';
        $('#end:first > #buttons').before(button);

        $('#video-download-button').hover(function() {
            $(this).css("font-weight", "bold");
        }, function(){
            $(this).css("font-weight", "normal");
        } );

        const title = $('meta[itemprop="name"]').prop('content');
        const videoId = $('meta[itemprop="videoId"]').prop('content');
        $('#video-download-button').on('click', function(event) {
            event.stopPropagation();
            $(this).effect('bounce');
            dlVideo('https://www.youtube.com/watch?v=' + videoId, title);
        } );
    }

    waitForEl('#end:first > #buttons', function() {
        addDLButton();
    }, 100, 100);

}