Greasy Fork is available in English.

Local YouTube Downloader

Download YouTube videos without external service.

< Feedback em Local YouTube Downloader

Pergunta/comentário

§
Publicado: 24/03/2019

显示/隐藏下载链接 无法点开

运行正常,显示启用状态,但发现页面里“显示/隐藏下载链接 ”点不开,不知道什么原因。

maple3142Autor
§
Publicado: 24/03/2019

請提供瀏覽器與腳本管理器的版本與名稱

§
Publicado: 25/03/2019

显示/隐藏下载链接 无法点开

maple3142Autor
§
Publicado: 25/03/2019

@"xie xltz" 说道: 显示/隐藏下载链接 无法点开 請提供詳細資訊,我這邊測試是沒問題的 如果不是使用Chrome或FireFox,請換瀏覽器

https://i.imgur.com/0s13sBs.png

§
Publicado: 25/03/2019

@"3142 maple" 说道: 請提供瀏覽器與腳本管理器的版本與名稱

chrome 72.0.3626.81

maple3142Autor
§
Publicado: 25/03/2019

能在影片頁面打開瀏覽器的開發人員工具,把 console 頁面中的完整內容截圖給我嗎?

§
Publicado: 25/03/2019

@"3142 maple" 说道: 能在影片頁面打開瀏覽器的開發人員工具,把 console 頁面中的完整內容截圖給我嗎?

内容太长已私信~

§
Publicado: 25/03/2019
Editado: 25/03/2019

@"3142 maple" 说道: 能在影片頁面打開瀏覽器的開發人員工具,把 console 頁面中的完整內容截圖給我嗎?

[Deprecation] HTML Imports is deprecated and will be removed in M73, around March 2019. Please use ES modules instead. See https://www.chromestatus.com/features/5144752345317376 for more details.
desktop_polymer.js:23 [Deprecation] document.registerElement is deprecated and will be removed in M73, around March 2019. Please use window.customElements.define instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
(anonymous) @ desktop_polymer.js:23
desktop_polymer.js:1902 <link rel=preload> must have a valid `as` value
e @ desktop_polymer.js:1902
VM28:1 Active resource loading counts reached a per-frame limit while the tab was in background. Network requests will be delayed until a previous loading finishes, or the tab is brought to the foreground. See https://www.chromestatus.com/feature/5527160148197376 for more details
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:11975 YTDL: default language: zh
/video_masthead?video_id=L4TuuNgxkWI&textLine1=Redmi+Note+7&textLine2=Redmi+Note+7+ju%C5%BC+w+sprzeda%C5%BCy+za+899+z%C5%82%21&autocrop=0&autoplay_start_time=0&autoplay_duration=30000&channel_banner=1&video_wall=0&subscribe_button=1&subscriber_count=1&site_cta=1&cta_label=Kup+teraz%21&layout=material&line_item_id=4976602468&creative_id=138265088714:1 Active resource loading counts reached a per-frame limit while the tab was in background. Network requests will be delayed until a previous loading finishes, or the tab is brought to the foreground. See https://www.chromestatus.com/feature/5527160148197376 for more details
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:9054 Download the Vue Devtools extension for a better development experience:
https://github.com/vuejs/vue-devtools
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:9063 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
/embed/?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&mute=1&nologo=1&rel=0&showinfo=0&wmode=transparent&widget_referrer=https%3A%2F%2Fpubads.g.doubleclick.net%2Fgampad%2Fads%3Fsz%3D850x250%26gdfp_req%3D1%26ad_rule%3D0%26iu%3D%2F4061%2Fcom.ythome%26impl%3Difr%26loeid%3D23761460%26scp%3Dssl%253D1%2526dc_yt%253D1%2526kauth%253D1%2526klg%253Den-gb%2526kmyd%253Dvideo-masthead%2526ytdevice%253D1%2526ytexp%253D23801216%2C23797561%2C23757411%2C9471239%26d_&origin=https%3A%2F%2Fwww.youtube.com&widgetid=1:1 Active resource loading counts reached a per-frame limit while the tab was in background. Network requests will be delayed until a previous loading finishes, or the tab is brought to the foreground. See https://www.chromestatus.com/feature/5527160148197376 for more details
(unknown) [LOOPER FOR YOUTUBE] Debug Mode: false 
(unknown) [LOOPER FOR YOUTUBE] Browser is in Incognito window: false 
network.js:15 [Deprecation] chrome.loadTimes() is deprecated, instead use standardized API: nextHopProtocol in Navigation Timing 2. https://www.chromestatus.com/features/5637885046816768.
g.onreadystatechange @ network.js:15
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:11975 YTDL: start loading new video: ovB0R2RjQtQ
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:11975 YTDL: No ytplayer is founded
(unknown) [LOOPER FOR YOUTUBE] Debug Mode: false 
(unknown) [LOOPER FOR YOUTUBE] Browser is in Incognito window: false 
(unknown) [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
_ax.open._ax.o
/get_video_info?html5=1&video_id=PMZUnvnCuzc&cpn=L8UmgKodnqWVw7gS&eurl&ps=desktop-polymer&el=adunit&hl=en_GB&aqi=FJaYXOjFHdeklgT0n6OYDw&sts=17976&lact=2073&c=WEB&cver=2.20190321&cplayer=UNIPLAYER&cbr=Chrome&cbrver=72.0.3626.81&cos=Windows&cosver=6.1&adformat=15_2_1&encoded_ad_playback_context=CA8QAhgBKgtvdkIwUjJSalF0UUIWRkphWVhPakZIZGVrbGdUMG42T1lEd2ABdW9Oez8%253D&iv_load_policy=1&autoplay=1&width=727&height=409&content_v=ovB0R2RjQtQ:1 Failed to load resource: net::ERR_BLOCKED_BY_CLIENT
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:11975 YTDL: ytplayer fetched: Object
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:11975 YTDL: workerGetVideo start: ovB0R2RjQtQ /yts/jsbin/player_ias-vflGPko2h/en_GB/base.js
blob:https://www.youtube.com/4c9504fb-9155-404c-b2a2-1c44530102e7:6 YTDL: parsedecsig result: a => { var av={Bh:function(a){a.reverse()},
fK:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c},
Zs:function(a,b){a.splice(0,b)}};
a=a.split("");av.fK(a,23);av.Zs(a,1);av.Bh(a,20);av.Zs(a,2);av.Bh(a,10);av.Zs(a,1);av.Bh(a,10);av.fK(a,23);av.Zs(a,2);return a.join("")}
blob:https://www.youtube.com/4c9504fb-9155-404c-b2a2-1c44530102e7:6 YTDL: parsedecsig result: a => { var av={Bh:function(a){a.reverse()},
fK:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c},
Zs:function(a,b){a.splice(0,b)}};
a=a.split("");av.fK(a,23);av.Zs(a,1);av.Bh(a,20);av.Zs(a,2);av.Bh(a,10);av.Zs(a,1);av.Bh(a,10);av.fK(a,23);av.Zs(a,2);return a.join("")}
blob:https://www.youtube.com/4c9504fb-9155-404c-b2a2-1c44530102e7:6 YTDL: video ovB0R2RjQtQ data: Object
blob:https://www.youtube.com/4c9504fb-9155-404c-b2a2-1c44530102e7:6 YTDL: video ovB0R2RjQtQ data: %o
blob:https://www.youtube.com/4c9504fb-9155-404c-b2a2-1c44530102e7:6 YTDL: video ovB0R2RjQtQ result: Object
blob:https://www.youtube.com/4c9504fb-9155-404c-b2a2-1c44530102e7:6 YTDL: video ovB0R2RjQtQ result: %o
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:11975 YTDL: workerGetVideo end: Object
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:11975 YTDL: video loaded: ovB0R2RjQtQ
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9881c696-8471-4ccf-b11d-1381cacf552a:639 [Vue warn]: Error in render: "TypeError: Cannot read property 'togglelinks' of undefined"
maple3142Autor
§
Publicado: 25/03/2019

把腳本移除重新安裝看看,我看錯誤訊息應該有你有不小心改到了什麼

§
Publicado: 25/03/2019

chrome 和 脚本都是最新版的, 显示和隐藏下载链接还是打不开

maple3142Autor
§
Publicado: 25/03/2019

我用 chrome 也都沒問題

§
Publicado: 25/03/2019

@"3142 maple" 说道: 把腳本移除重新安裝看看,我看錯誤訊息應該有你有不小心改到了什麼 我试过重新安装,也试过移除后安装,发现错误信息还是在,blocked by client啥的。我现在试试重装chrome

§
Publicado: 25/03/2019

@"3142 maple" 说道: 我用 chrome 也都沒問題

我截图上传不了,一直显示file failed to upload.

“chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:639 [Vue warn]: Error in render: "TypeError: Cannot read property 'togglelinks' of undefined"

(found in ) warn @ chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:639 chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:1902 TypeError: Cannot read property 'togglelinks' of undefined at Proxy.eval (eval at createFunction (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2), :3:156) at Vue._render (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:3550) at Vue.updateComponent (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:4066) at Watcher.get (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:4477) at Watcher.run (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:4552) at flushSchedulerQueue (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:4310) at Array.eval (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:1994) at flushCallbacks (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?id=9e36a4dc-6b43-4528-9690-1660d79303a2:1920)”

maple3142Autor
§
Publicado: 26/03/2019

你能提供你安裝的這個腳本的完整內容嗎?好像和我的不太一樣

§
Publicado: 26/03/2019

@"3142 maple" 说道: 你能提供你安裝的這個腳本的完整內容嗎?好像和我的不太一樣

// ==UserScript== // @name Local YouTube Downloader // @name:zh-TW 本地 YouTube 下載器 // @name:zh-CN 本地 YouTube 下载器 // @namespace https://blog.maple3142.net/ // @version 0.7.0 // @description Get youtube raw link without external service. // @description:zh-TW 不需要透過第三方的服務就能下載 YouTube 影片。 // @description:zh-CN 不需要透过第三方的服务就能下载 YouTube 影片。 // @author maple3142 // @match https://.youtube.com/ // @require https://unpkg.com/vue@2.6.10/dist/vue.js // @require https://unpkg.com/xfetch-js@0.3.4/xfetch.min.js // @compatible firefox >=52 // @compatible chrome >=55 // @license MIT // ==/UserScript==

;(function() { 'use strict' const DEBUG = true const create$p = console => Object.keys(console) .map(k => [k, (...args) => (DEBUG ? console[k]('YTDL: ' + args[0], ...args.slice(1)) : void 0)]) .reduce((acc, [k, fn]) => ((acc[k] = fn), acc), {}) const $p = create$p(console)

const LANG_FALLBACK = 'en'
const LOCALE = {
    en: {
        togglelinks: 'Show/Hide Links',
        stream: 'Stream',
        adaptive: 'Adaptive',
        videoid: 'Video Id: ',
        inbrowser_adaptive_merger: 'In browser adaptive video & audio merger'
    },
    'zh-tw': {
        togglelinks: '顯示 / 隱藏連結',
        stream: '串流 Stream',
        adaptive: '自適應 Adaptive',
        videoid: '影片 ID: ',
        inbrowser_adaptive_merger: '瀏覽器版自適應影片及聲音合成器'
    },
    zh: {
        togglelinks: '显示 / 隐藏 下载链接',
        stream: '串流 Stream',
        adaptive: '自适应 Adaptive',
        videoid: '视频 ID: ',
        inbrowser_adaptive_merger: '浏览器版自适应视频及声音合成器'
    }
}
const findLang = l => {
    // language resolution logic: zh-tw --(if not exists)--> zh --(if not exists)--> LANG_FALLBACK(en)
    l = l.toLowerCase()
    if (l in LOCALE) return l
    else if (l.length > 2) return findLang(l.split('-')[0])
    else return LANG_FALLBACK
}
const $ = (s, x = document) => x.querySelector(s)
const $el = (tag, opts) => {
    const el = document.createElement(tag)
    Object.assign(el, opts)
    return el
}
const xhrget = url =>
    // not sure why `fetch` doesn't work here
    new Promise((res, rej) => {
        const xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.onreadystatechange = () => {
            if (xhr.readyState === xhr.DONE) {
                res(xhr.responseText)
            }
        }
        xhr.onerror = rej
        xhr.send()
    })
const getytplayer = async () => {
    if (typeof ytplayer !== 'undefined' && ytplayer.config) return ytplayer
    $p.log('No ytplayer is founded')
    const html = await xf.get(location.href).text()
    const d = /<script >(var ytplayer[\s\S]*?)ytplayer\.load/.exec(html)
    let config = eval(d[1])
    unsafeWindow.ytplayer = {
        config
    }
    $p.log('ytplayer fetched: %o', unsafeWindow.ytplayer)
    return ytplayer
}
const parsedecsig = data => {
    try {
        if (data.startsWith('var script')) {
            // they inject the script via script tag
            const obj = {}
            const document = { createElement: () => obj, head: { appendChild: () => {} } }
            eval(data)
            data = obj.innerHTML
        }
§
Publicado: 26/03/2019

@"3142 maple" 说道: 你能提供你安裝的這個腳本的完整內容嗎?好像和我的不太一樣

const fnnameresult = /yt.akamaized.net.*encodeURIComponent((\w+)/.exec(data) const fnname = fnnameresult[1] const argnamefnbodyresult = new RegExp(fnname + '=function\((.+?)\){(.+?)}').exec(data) const [, argname, fnbody] = argnamefnbodyresult const helpernameresult = /;(.+?)..+?(/.exec(fnbody) const helpername = helpernameresult[1] const helperresult = new RegExp('var ' + helpername + '={[\s\S]+?};').exec(data) const helper = helperresult[0] $p.log(parsedecsig result: ${argname} => { ${helper}\n${fnbody}}) return new Function([argname], helper + '\n' + fnbody) } catch (e) { $p.error('parsedecsig error: %o', e) $p.info('script content: %s', data) $p.info( 'If you encounter this error, please copy the full "script content" to https://pastebin.com/ for me.' ) } } const getdecsig = path => xhrget('https://www.youtube.com' + path).then(parsedecsig) const parseQuery = s => [...new URLSearchParams(s).entries()].reduce((acc, [k, v]) => ((acc[k] = v), acc), {}) const getVideo = async (id, decsig) => { return xf .get(`https://www.youtube.com/getvideoinfo?videoid=${id}&el=detailpage) .text() .then(async data => { const obj = parseQuery(data) $p.log(video ${id} data: %o, obj) if (obj.status === 'fail') { throw obj } 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}` }))
                }
            }
            $p.log(`video ${id} result: %o`, { stream, adaptive })
            return { stream, adaptive, meta: obj }
        })
}
const workerMessageHandler = async e => {
    const decsig = await getdecsig(e.data.path)
    const result = await getVideo(e.data.id, decsig)
    self.postMessage(result)
}
const ytdlWorkerCode = `

importScripts('https://unpkg.com/xfetch-js@0.3.4/xfetch.min.js') const DEBUG=${DEBUG} const $p=(${create$p.toString()})(console) const parseQuery=${parseQuery.toString()} const xhrget=${xhrget.toString()} const parsedecsig=${parsedecsig.toString()} const getdecsig=${getdecsig.toString()} const getVideo=${getVideo.toString()} self.onmessage=${workerMessageHandler.toString()} const ytdlWorker = new Worker(URL.createObjectURL(new Blob([ytdlWorkerCode]))) const workerGetVideo = (id, path) => { $p.log(workerGetVideo start: ${id} ${path}`) return new Promise((res, rej) => { const callback = e => { ytdlWorker.removeEventListener('message', callback) $p.log('workerGetVideo end: %o', e.data) res(e.data) } ytdlWorker.addEventListener('message', callback) ytdlWorker.postMessage({ id, path }) }) }

§
Publicado: 26/03/2019

@"3142 maple" 说道: 你能提供你安裝的這個腳本的完整內容嗎?好像和我的不太一樣

const template = `

.slice(1) const app = new Vue({ data() { return { hide: true, id: '', stream: [], adaptive: [], dark: false, lang: findLang(navigator.language) } }, computed: { strings() { return LOCALE[this.lang.toLowerCase()] } }, template }) $p.log(default language: ${app.lang}`)

// attach element
const shadowHost = $el('div')
const shadow = shadowHost.attachShadow ? shadowHost.attachShadow({ mode: 'closed' }) : shadowHost // no shadow dom
const container = $el('div')
shadow.appendChild(container)
app.$mount(container)

if (DEBUG) unsafeWindow.$app = app
const load = async id => {
    const ytplayer = await getytplayer()
    return workerGetVideo(id, ytplayer.config.assets.js)
        .then(async data => {
            $p.log('video loaded: %s', id)
            app.id = id
            app.stream = data.stream
            app.adaptive = data.adaptive
            app.meta = data.meta
            if (ytplayer.config.args.host_language) app.lang = ytplayer.config.args.host_language
        })
        .catch(err => $p.error('load', err))
}
let prevurl = null
setInterval(() => {
    const el = $('#info-contents') || $('#watch-header') || $('ytm-item-section-renderer>lazy-list')
    if (el && !el.contains(shadowHost)) el.appendChild(shadowHost)
    if (location.href !== prevurl && location.pathname === '/watch') {
        prevurl = location.href
        app.hide = true
        const id = parseQuery(location.search).v
        $p.log(`start loading new video: ${id}`)
        load(id)
    }
}, 1000)
§
Publicado: 26/03/2019

@"3142 maple" 说道: 你能提供你安裝的這個腳本的完整內容嗎?好像和我的不太一樣

// listen to dark mode toggle
const $html = $('html')
new MutationObserver(() => {
    app.dark = $html.getAttribute('dark') === 'true'
}).observe($html, { attributes: true })
app.dark = $html.getAttribute('dark') === 'true'
const css = `

.hide{ display: none; } .t-center{ text-align: center; } .d-flex{ display: flex; } .f-1{ flex: 1; } .fs-14px{ font-size: 14px; } .of-h{ overflow: hidden; } .box{ border-bottom: 1px solid var(--yt-border-color); font-family: Arial; } .box-toggle{ margin: 3px; user-select: none; -moz-user-select: -moz-none; } .box-toggle:hover{ color: blue; } .ytdl-link-btn{ display: block; border: 1px solid !important; border-radius: 3px; text-decoration: none !important; outline: 0; text-align: center; padding: 2px; margin: 5px; color: black; } a.ytdl-link-btn{ text-decoration: none; } a.ytdl-link-btn:hover{ color: blue; } .box.dark{ color: var(--ytd-video-primary-info-renderer-title-color, var(--yt-primary-text-color)); } .box.dark .ytdl-link-btn{ color: var(--ytd-video-primary-info-renderer-title-color, var(--yt-primary-text-color)); } .box.dark .ytdl-link-btn:hover{ color: rgba(200, 200, 255, 0.8); } .box.dark .box-toggle:hover{ color: rgba(200, 200, 255, 0.8); } ` shadow.appendChild($el('style', { textContent: css })) })()

全部内容如上。麻烦您了,我还是无法上传截图╮(╯▽╰)╭

maple3142Autor
§
Publicado: 26/03/2019

看來是 youtube ui 語言導致的問題,使用英文和繁中能正常 但使用簡體正好會遇到 bug,已經在 0.7.1 修復了

§
Publicado: 26/03/2019

@"3142 maple" 说道: 看來是 youtube ui 語言導致的問題,使用英文和繁中能正常 但使用簡體正好會遇到 bug,已經在 0.7.1 修復了

非常感谢,我youtube语言是英文,但chrome是中文简体,现在把chrome改为英文后可以正常使用脚本了。这两天麻烦您了!

Publicar resposta

Faça o login para publicar uma resposta.