// ==UserScript== // @name 抖音短视频下载 // @namespace http://tampermonkey.net/ // @version 0.3.0 // @description 下载抖音短视频 // @author 抖音兔不迟到 // @license MIT License // @run-at document-start // @grant GM_download // @include *://*.douyin.com/* // @require https://greasyfork.org/scripts/439696-mono-js/code/monojs.js?version=1016361 // @downloadURL none // ==/UserScript== var _META_URL_ = "https://www.douyin.com/web/api/v2/aweme/iteminfo/?item_ids="; var _TAG_CLASS = { user: 'd517474cdaa572104bddc8637c4eeb3c-scss', list: 'b388acfeaeef33f0122af9c4f71a93c9-scss', } var itemCache = {} //hook xhr var origOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, ...rest) { if (url.includes("mssdk.snssdk.com/web/report")) return; this.addEventListener('load', function() { if (url.includes("general/search/single")) { var json = JSON.parse(this.responseText); if (!json?.data?.length) return; for (let i in json.data) { var key = json.data[i].aweme_info?.video?.origin_cover?.uri; if (!key) continue; itemCache[key] = json.data[i].aweme_info; itemCache[key].video_id = itemCache[key].aweme_id; itemCache[key].title = itemCache[key].desc; itemCache[key].cover = itemCache[key].video?.origin_cover?.url_list[0]; itemCache[key].url = itemCache[key].video?.play_addr?.url_list[0]; } console.log({itemCache}); } }); origOpen.call(this, method, url, ...rest); }; (function () { var mono = window['mono-descargar']; var useDefaultErr = mono.FAIL_TO_DEFAULT; var $ = mono.jQuery; var md5 = mono.md5; var updateItems = async (items) => { if (items.length <= 0) return; var resp = await fetch(_META_URL_ + items.map(im => im.meta.video_id).join(',')) var json = await resp.json() metas = {} for (var i in json.item_list) { metas[json.item_list[i].aweme_id] = json.item_list[i] } for (var i in items) { meta = metas[items[i].meta.video_id] if (!meta) continue; meta.title = meta.desc; meta.cover = meta.video.cover.url_list[0]; items[i].url = meta.video.play_addr.url_list[0].replace("playwm", "play"); items[i].meta = Object.assign(meta, items[i].meta); } } var getItemByDetailUrl = (detail_url) => { var url = new URL(detail_url); var video_id = url.pathname.slice("/video/".length); var id = `mona-${md5(video_id)}` if ($(`#${id}`).length > 0) return null; var dom = $(`
`); var filename = `${video_id}.mp4`; var meta = { video_id, filename } return { id, url:"", dom, meta } } var getItemsByATag = async (className) => { var items = []; var a = $(`.${className}`); if (a.length <= 0) return; for (var i = 0; i < a.length; i++) { var item = getItemByDetailUrl(a[i].href); if (!item) continue; item.pdom = $(a[i]); items.push(item); // 每次返回N个,分多次 if (items.length >= 10) break; } if (items.length > 0) { await updateItems(items); } return items; } var getItemsBySearchRes = async () => { var items = []; var pdoms = $(`.playerContainer`); if (pdoms.length <= 0) return; for (var i = 0; i < pdoms.length; i++) { let key; var pdom = $(pdoms[i]); try { var img = pdom.find('.imgBackground img')[0]; var url = new URL(img.src.startsWith('//') ? 'https:' + img.src : img.src); if (url && url.pathname.includes('~')) { key = url.pathname.substring(1, url.pathname.indexOf('~')); } } catch (e) { console.log('err', e); continue; } if (!key || !itemCache[key]) continue; var meta = itemCache[key]; var id = `mona-${md5(meta.video_id)}` if ($(`#${id}`).length > 0) return null; var dom = $(``); var url = meta.url; meta.filename = `${meta.video_id}.mp4`; var item = { id, url, dom, pdom, meta } items.push(item); // 每次返回N个,分多次 if (items.length >= 10) break; } return items; } var pageParsers = { detail: async () => { var item = getItemByDetailUrl(window.location.href); if (!item) return []; video = $('video') if (video.length <= 0) return []; item.pdom = $(video[0]).parent(); await updateItems([item]); return [item]; }, user: async () => { return getItemsByATag(_TAG_CLASS.user) }, list: async () => { let items = await getItemsByATag(_TAG_CLASS.list); return items || getItemsBySearchRes() } } var getPageParser = () => { var url = new URL(window.location.href); var path = url.pathname.split('/')[1]; if (path === "video") { return pageParsers.detail; } else if (path === "user") { return pageParsers.user; } else if (["discover", "search", "channel", "hot"].includes(path)) { return pageParsers.list; } else { throw useDefaultErr; } } var parser = async function () { var pr = getPageParser() if (!pr) return [] return pr() } if (mono?.init) mono.init({ parser, interval: 100, }); })()