Greasy Fork

抖音主页视频下载

在抖音主页右小角显示视频下载按钮

当前为 2024-07-25 提交的版本,查看 最新版本

// ==UserScript==
// @name         抖音主页视频下载
// @namespace    douyin_homepage_downloader
// @version      2024-07-25
// @description  在抖音主页右小角显示视频下载按钮
// @author       hunmer
// @match        https://www.douyin.com/user/*
// @icon         https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico
// @grant        GM_download
// @license      MIT
// ==/UserScript==

(function() {
    const TARGET_URL = 'https://www.douyin.com/aweme/v1/web/aweme/post/'
    var originalSend = XMLHttpRequest.prototype.send;
    var resources = [], fv
    var callback = json => {
         console.log(json)
        let cnt = resources.push(...json.aweme_list)
        if(!cnt > 0) return
        if(!fv){
            fv = document.createElement('div')
            fv.style.cssText = `position: fixed;bottom: 50px;right: 50px;border-radius: 20px;background-color: #fe2c55;color: white;z-index: 999;cursor: pointer;`
            fv.onclick = () => {
                if(confirm(`确定要下载${resources.length}个视频吗?`)){
                    let done = 0
                    const next = () => {
                        let item = resources.splice(0, 1)
                        fv.innerHTML = `${++done}/${resources.length}`
                        if(!item) return alert("下载完成")

                        let {video, desc} = item[0]
                        if(video.format != 'mp4') return
                        let url = video.play_addr.url_list[0]
                        GM_download({
                            url,
                            name: safeFileName(desc) + '.mp4',
                            conflictAction: 'overwrite',
                            onload: () => console.log(`下载完成...`) & next(),
                            onerror: () => console.log(`下载失败...`) & next(),
                            ontimeout: () => console.log(`下载失败...`) & next(),
                            // onprogress: console.log,
                            headers: {
                                'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
                                'Referer': url
                            }
                        })
                    }
                    next()
                }
            }
            document.body.append(fv)
        }
        fv.innerHTML = `下载 ${cnt} 个视频`
    }
    XMLHttpRequest.prototype.send = function() {
        this.addEventListener('load', function() {
            if (this.responseURL.startsWith(TARGET_URL)) {
                callback(JSON.parse(this.responseText))
            }
        });
        originalSend.apply(this, arguments);
    };
    const originalFetch = window.fetch;
    window.fetch = function() {
        return originalFetch.apply(this, arguments)
            .then(response => {
            if (response.url.startsWith(TARGET_URL)) {
                response.clone().json().then(callback);
            }
            return response;
        });
    }

    function safeFileName(str) {
        return str
            .replaceAll('(', '(')
            .replaceAll(')', ')')
            .replaceAll(':', ':')
            .replaceAll('*', '*')
            .replaceAll('?', '?')
            .replaceAll('"', '"')
            .replaceAll('<', '<')
            .replaceAll('>', '>')
            .replaceAll("|", "|")
            .replaceAll('\\', '\')
            .replaceAll('/', '/')
    }
})();