// ==UserScript== // @name you-get-fork // @description 视频下载 bilibili acfun 快手 抖音 腾讯视频 youku // @description:en-US fork from [460910],由于官方API变更,CCTV和油管已失效 // @namespace https://greasyfork.org/users/135090 // @version 0.0.2 // @author You // @run-at document-end // @match https://www.bilibili.com/video/BV* // @match https://www.douyin.com/video/* // @match https://www.kuaishou.com/short-video/* // @match https://www.acfun.cn/v/ac* // @match https://v.qq.com/x/* // @match https://v.youku.com/v_* // @license MIT // @icon data:image/png;base64,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 // @grant none // @downloadURL https://update.greasyfork.icu/scripts/518308/you-get-fork.user.js // @updateURL https://update.greasyfork.icu/scripts/518308/you-get-fork.meta.js // ==/UserScript== (() => { // src/util/index.js var download = async (blob, fileName) => { let link = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.download = `${fileName}`; a.href = link; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(link); }; var safetyParse = (str) => { try { return JSON.parse(str); } catch (error) { return null; } }; var getFile = async (url) => { const res = await fetch(url); const reader = res.body.getReader(); const contentLength = +res.headers.get("Content-Length"); if (!contentLength) { const data = await res.arrayBuffer(); return new Uint8Array(data); } let receivedLength = 0; let chunks = []; while (true) { const { done, value } = await reader.read(); if (done) { break; } chunks.push(value); receivedLength += value.length; console.log( `fileSize: ${contentLength} %c downloaded ${receivedLength}`, "background: #222; color: #bada55" ); } return new Blob(chunks); }; var getUrlsByM3u8 = async (url, parser) => { const urlObj = new URL(url); urlObj.pathname = urlObj.pathname.split("/").slice(0, -1).join("/"); urlObj.search = ""; const base = urlObj.toString(); const res = await fetch(url); const data = await res.text(); return data.split("\n").filter((i) => !!i && !i.startsWith("#")).map((i) => { if (parser) { return parser(i); } return i.startsWith("/") ? `${base}${i}` : `${base}/${i}`; }); }; var getFiles = (urls, max = 8) => { let connections = 0; let files = []; urls = urls.map((i, index) => ({ ...i, index })); return new Promise((resolve, reject) => { const getSingleFile = async ({ url, index, ...rest }) => { if (connections < max) { try { connections = connections + 1; const data = await getFile(url); connections = connections - 1; files[index] = data; if (urls?.length) { getSingleFile(urls.shift()); } else { connections === 0 && resolve(files); } } catch (error) { console.log(error); urls.push({ url, index, ...rest }); } } }; new Array(max).fill(0).forEach((i) => { getSingleFile(urls.shift()); }); }); }; // src/module/acfun.js var getVideoInfo = async () => { const m3u8FileUrl = safetyParse(window?.pageInfo?.currentVideoInfo?.ksPlayJson)?.adaptationSet?.[0]?.representation?.[0]?.url; const urls = await getUrlsByM3u8(m3u8FileUrl); return urls.map((url) => ({ url })); }; var acfun_default = async () => { const urls = await getVideoInfo(); const files = await getFiles(urls); var tname = document.title; download(new Blob(files), tname+".mpeg"); }; // src/module/bilibili.js var getBilibiliVideoInfo = async () => { const res = await fetch(window.location.href); const str = await res.text(); const data = JSON.parse( str.match(/window.__playinfo__=([\d\D]+?)<\/script>/)[1] ); const dash = data?.data?.dash; const video = dash.video.sort((a, b) => b?.width - a?.width)?.[0]; const audio = dash.audio[0]; const btit = document.title.substring(0,document.title.length-14); return [ { url: video.baseUrl, fileName: btit+".m4v" }, { url: audio.baseUrl, fileName: btit+".m4a" } ]; }; var bilibili_default = async () => { const urls = await getBilibiliVideoInfo(); while (urls?.length) { const { url, fileName } = urls.shift(); const file = await getFile(url); download(file, fileName); } }; // src/module/douyin.js var getDouyinVideoInfo = () => { const urls = [...document.querySelectorAll("video source")].map((i) => i.src); var dyvt=document.title.concat(".mp4"); return { url: urls[0], fileName: dyvt }; }; var douyin_default = async () => { const url = await getDouyinVideoInfo(); const file = await getFile(url.url); download(file, url.fileName); }; // src/module/kuaishou.js var getKuaishouVideoInfo = () => { var ktname = document.title; const urls = [...document.querySelectorAll("video")].map((i) => i.src); return { url: urls[0], fileName: ktname+".mp4" }; }; var kuaishou_default = async () => { const url = await getKuaishouVideoInfo(); const file = await getFile(url.url); download(file, url.fileName); }; // src/module/qq.js var proxyhttpBody = null; var monitor = async () => { try { window.__PLAYER__.pluginMsg.emit = new Proxy( window.__PLAYER__.pluginMsg.emit, { apply: (...args) => { if (args?.[2]?.[0] === "PROXY_HTTP_START" && args?.[2]?.[1]?.vinfoparam) { console.log(args?.[2]?.[1]); proxyhttpBody = JSON.stringify(args?.[2]?.[1]); } return Reflect.apply(...args); } } ); } catch (error) { console.log("monitor error"); } }; var getVideoInfo2 = async () => { let m3u8FileUrl = null; if (!m3u8FileUrl) { let res = await fetch("https://vd6.l.qq.com/proxyhttp", { method: "post", body: proxyhttpBody }); res = await res.json(); if (res?.errCode === 0 && res?.vinfo) { const data = safetyParse(res?.vinfo); const m3u8 = data?.vl?.vi?.sort((a, b) => b.vw - a.vw)?.[0]?.ul; if (m3u8) { m3u8FileUrl = m3u8.ui[0].url; } } } const urls = await getUrlsByM3u8(m3u8FileUrl); return urls.map((url) => ({ url, fileName: `qqvideo.mp4` })); }; if (window.location.host === "v.qq.com") { monitor(); } var qq_default = async () => { const urls = await getVideoInfo2(); const files = await getFiles(urls); download(new Blob(files), "qqvideo.mp4"); }; /* // src/module/cctv.js var cctv_default = async () => { let data = await fetch(`https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid=${window.guid}`); data = await data.json(); const m3u8FileUrl =data?.hls_url; var mbase=m3u8FileUrl?.substring(0,m3u8FileUrl?.lastIndexOf("/")+1); const urls = await getUrlsByM3u8(m3u8FileUrl, (i) => i); const files = await getFiles(urls.map((url) => ({ url:mbase+url }))); download(new Blob(files), `${data.title}.mpeg`); }; */ // src/module/youku.js var youku_default = async () => { if (!document.querySelector("meta#you-get-youku")) { const meta = document.createElement("meta"); meta.httpEquiv = "Content-Security-Policy"; meta.content = "upgrade-insecure-requests"; meta.id = "you-get-youku"; document.querySelector("head").appendChild(meta); } const data =window?.videoPlayer?.context?.mediaData?.mediaResource?._model?.streamList; if (data) { const m3u8FileUrl = data.sort((a, b) => b.width - a.width)[0].uri.HLS; const vtitle =window?.videoPlayer?.context?.mediaData?.mediaResource?._model?.video?.title; const urls = await getUrlsByM3u8(m3u8FileUrl, (i) => i); const files = await getFiles(urls.map((url) => ({ url }))); document.title=vtitle+"---下载中..."; download(new Blob(files), `${vtitle}.mpeg`); document.title=vtitle; } }; // src/module/index.js var module_default = { bilibili: bilibili_default, douyin: douyin_default, kuaishou: kuaishou_default, acfun: acfun_default, qq: qq_default, //cctv: cctv_default, youku: youku_default }; // src/index.js var youget = () => { const handler = Object.entries(module_default).find( ([key, fn]) => window.location.host.toLocaleLowerCase().includes(key) )?.[1]; if (handler) { handler(); } else { console.log("not support"); } }; window.youget = youget; var bae=document.createElement("a"); bae.style="position:fixed;top:10vh;right:1vw;font-size:2vw;z-index:99;color:#89FF89"; bae.textContent="YouGet"; bae.href="javascript:youget()"; document.body.append(bae); })();