Greasy Fork is available in English.

ytdl link

Show youtube download link on the left side without external service.

Verzia zo dňa 11.06.2018. Pozri najnovšiu verziu.

// ==UserScript==
// @name         ytdl link
// @namespace    https://blog.maple3142.net/
// @version      0.2
// @description  Show youtube download link on the left side without external service.
// @author       maple3142
// @require      https://code.jquery.com/jquery-3.2.1.slim.min.js
// @match        https://www.youtube.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict'
    const xhrget=url=>new Promise((res,rej)=>{
        const xhr = new XMLHttpRequest()
        xhr.open('HEAD', url);
        xhr.onreadystatechange = ()=>{
            if (xhr.readyState === xhr.DONE) {
                res(xhr.responseText)
            }
        };
        xhr.onerror=rej
        xhr.send()
    })
    const fallback = a => {
        // 2018/05/04
        const Sy = {
            kJ: function(a, b) {
                var c = a[0]
                a[0] = a[b % a.length]
                a[b % a.length] = c
            },
            ZK: function(a) {
                a.reverse()
            },
            Ug: function(a, b) {
                a.splice(0, b)
            }
        }
        a = a.split('')
        Sy.Ug(a, 2)
        Sy.ZK(a, 72)
        Sy.Ug(a, 2)
        Sy.ZK(a, 10)
        Sy.Ug(a, 3)
        Sy.kJ(a, 60)
        return a.join('')
    }
    const getdecsig= () => {
        return xhrget('https://www.youtube.com' + ytplayer.config.assets.js)
            .then(data => {
            const fnname = /\"signature\"\),.+?\.set\(.+?,(.+?)\(/.exec(data)[1]
            const [_, argname, fnbody] = new RegExp(fnname + '=function\\((.+?)\\){(.+?)}').exec(data)
            //console.log(fnbody)
            const helpername = /;(.+?)\..+?\(/.exec(fnbody)[1]
            //console.log(helpername)
            const helper = new RegExp('var ' + helpername + '={[\\s\\S]+?};').exec(data)[0]
            //console.log(helper)
            return new Function([argname], helper + ';' + fnbody)
        }).catch(e => fallback)
    }
    const parseQuery = s =>
    Object.assign(
        ...s
        .split('&')
        .map(x => x.split('='))
        .map(p => ({ [p[0]]: decodeURIComponent(p[1]) }))
    )
    const getVideo = id =>fetch(`https://www.youtube.com/get_video_info?video_id=${id}&el=embedded&ps=default&eurl=&gl=US&hl=en`)
    .then(r=>r.text())
    .then(async data => {
        const obj = parseQuery(data)
        if (obj.status === 'fail') {
            throw obj
        }
        const decsig = await getdecsig(id)
        let stream = []
        if(obj.url_encoded_fmt_stream_map){
            stream=obj.url_encoded_fmt_stream_map.split(',').map(parseQuery)
            if (stream[0].sp && stream[0].sp.includes('signature')) {
                stream = stream
                    .map(x => ({ ...x, s: decsig(x.s) }))
                    .map(x => ({ ...x, url: x.url + `&signature=${x.s}` }))
            }
        }

        let adaptive = []
        if (obj.adaptive_fmts) {
            adaptive = obj.adaptive_fmts.split(',').map(parseQuery)
            if (adaptive[0].sp && adaptive[0].sp.includes('signature')) {
                adaptive = adaptive
                    .map(x => ({ ...x, s: decsig(x.s) }))
                    .map(x => ({ ...x, url: x.url + `&signature=${x.s}` }))
            }
        }
        return { stream, adaptive }
    })
    const $box=$('<div>').attr('id','ytdl-box').css('position','absolute').css('z-index','10000').css('top','20%').css('left','0px').css('height','10%').css('width','10%')
    const $ul=$('<ul>')
    $box.append($ul)
    const load=id=>getVideo(id).then(data=>{
        console.log('load new')
        $ul.empty()
        $ul.append(`<li>${id}</li>`)
        $ul.append('<li>stream</li>')
        data.stream.map(x=>$('<a>').text(x.type).attr('href',x.url).attr('target','_blank')).map($a=>$('<li>').append($a)).forEach($li=>$ul.append($li))
        $ul.append('<li>adaptive</li>')
        data.adaptive.map(x=>$('<a>').text(x.type).attr('href',x.url).attr('target','_blank')).map($a=>$('<li>').append($a)).forEach($li=>$ul.append($li))
        $box.html($ul)
    }).catch(console.error)
    document.addEventListener('transitionend', e=> {
        const q=parseQuery(location.search.slice(1))
        if (e.target.id === 'progress'){
            load(q.v)
        }
        if(!q.v){
            $ul.empty()
        }
    })
    document.body.appendChild($box.get(0))
    const q=parseQuery(location.search.slice(1))
    if(q.v)load(q.v)
})();