Greasy Fork is available in English.
在抖音/快手主页右小角显示视频下载按钮
当前为
// ==UserScript==
// @name 抖音/快手主页视频下载
// @namespace shortvideo_homepage_downloader
// @version 0.0.2
// @description 在抖音/快手主页右小角显示视频下载按钮
// @author hunmer
// @match https://www.douyin.com/user/*
// @match https://www.kuaishou.com/profile/*
// @icon https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico
// @grant GM_download
// @license MIT
// ==/UserScript==
(function() {
const HOSTS = {
'www.kuaishou.com': {
url: 'https://www.kuaishou.com/graphql',
type: 'json',
list: json => json?.data?.visionProfilePhotoList?.feeds,
item: ({photo}) => {
console.log(photo)
return {
video_url: photo.videoResource.h264.adaptationSet[0].representation[0].url,
title: photo.originCaption,
}
}
},
'www.douyin.com': {
url: 'https://www.douyin.com/aweme/v1/web/aweme/post/',
type: 'network',
list: json => json?.aweme_list,
item: ({video, desc}) => {
if(video.format == 'mp4') return {
video_url: video.play_addr.url_list[0],
title: desc,
}
}
},
}
const DETAIL = HOSTS[location.host]
console.log({DETAIL, host: location.host})
if(!DETAIL) return
var originalSend = XMLHttpRequest.prototype.send;
var resources = [], fv
var callback = json => {
let cnt = resources.push(...DETAIL.list(json) || [])
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 = () => {
console.log(resources)
if(confirm(`确定要下载${resources.length}个视频吗?`)){
let done = 0
const next = () => {
let item = resources.splice(0, 1)
fv.innerHTML = `${++done}/${resources.length}`
if(!item.length) return alert("下载完成")
let {video_url, title} = DETAIL.item(item[0]) || {}
if(!video_url) return
let detail = {
url: video_url,
name: safeFileName(title) + '.mp4',
conflictAction: 'overwrite',
onload: () => endOutput(`下载完成...`),
onerror: () => endOutput(`下载失败...`),
ontimeout: () => endOutput(`下载失败...`),
// 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': video_url
}
}
console.log(detail)
GM_download(detail)
}
const endOutput = (...args) => console.log(args) & next()
endOutput('开始下载')
}
}
document.body.append(fv)
}
fv.innerHTML = `下载 ${cnt} 个视频`
}
switch(DETAIL.type){
case 'json':
var parse = JSON.parse;
JSON.parse = function(raw) {
let json = parse(raw)
callback(json)
return json;
}
return
case 'network':
XMLHttpRequest.prototype.send = function() {
this.addEventListener('load', function() {
if (this.responseURL.startsWith(DETAIL.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(DETAIL.url)) {
response.clone().json().then(callback);
}
return response;
});
}
return
}
function safeFileName(str) {
return str
.replaceAll('(', '(')
.replaceAll(')', ')')
.replaceAll(':', ':')
.replaceAll('*', '*')
.replaceAll('?', '?')
.replaceAll('"', '"')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll("|", "|")
.replaceAll('\\', '\')
.replaceAll('/', '/')
}
})();