// ==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 // @downloadURL none // ==/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('/', '/') } })();