Greasy Fork is available in English.

you-get-fork

视频下载 bilibili acfun youtube 快手 抖音 腾讯视频 cctv youku

// ==UserScript==
// @name               you-get-fork
// @description        视频下载 bilibili acfun youtube 快手 抖音 腾讯视频 cctv youku
// @description:en-US  fork from [460910]
// @namespace          https://greasyfork.org/users/135090
// @version            1.5.0
// @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://www.youtube.com/watch/*
// @match              https://v.qq.com/x/*
// @match              https://v.cctv.com/2*/V*.shtml*
// @match              https://v.cctv.cn/2*/V*.shtml*
// @match              https://tv.cctv.cn/2*/V*.shtml*
// @match              https://tv.cctv.com/2*/V*.shtml*
// @match              https://v.youku.com/v_*
// @license            MIT
// @icon               data:image/png;base64,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
// @grant              none
// ==/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+".mp4");
    };

    // 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/youtube.js
    var getYoutubeVideoInfo = async () => {
        const res = await fetch(window.location.href);
        const str = await res.text();
        const parser = new DOMParser();
        const doc = parser.parseFromString(str, "text/html");
        const data = safetyParse(
            doc.body.innerHTML.match(/("formats":)([\d\D]+?}]+?)/)[2]
        );
        const video = data.sort((a, b) => b?.width - a?.width)?.[0];
        return {
            url: video.url,
            fileName: `youtube.mp4`
    };
    };
    var youtube_default = async () => {
        const url = await getYoutubeVideoInfo();
        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 brmax="2000";
        const m3u8FileUrl =data?.hls_url?.replaceAll("main",brmax);
        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;
        if (data) {
            const m3u8FileUrl = data.streamList.sort((a, b) => b.width - a.width)[0].uri.HLS;
            const urls = await getUrlsByM3u8(m3u8FileUrl, (i) => i);
            const files = await getFiles(urls.map((url) => ({ url })));
            download(new Blob(files), `${data.title}.mpeg`);
        }
    };

    // src/module/index.js
    var module_default = {
        bilibili: bilibili_default,
        douyin: douyin_default,
        kuaishou: kuaishou_default,
        youtube: youtube_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");
        }
    };
    const you = 1;
    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);
})();