Greasy Fork is available in English.

百度网盘不限速下载-千幻云DL

复活?可用?不限制?不限速? 一款百度网盘不限速解析脚本,支持Motrix、idm、Aria下载器下载,速度快就完事了!

// ==UserScript==
// @name         百度网盘不限速下载-千幻云DL
// @description  复活?可用?不限制?不限速? 一款百度网盘不限速解析脚本,支持Motrix、idm、Aria下载器下载,速度快就完事了!
// @version      1.9
// @antifeature  membership
// @antifeature  ads
// @antifeature  tracking
// @license      MIT
// @author       MoTeam-Top、QHY-Down
// @icon         https://pic.imge.cc/2024/08/03/66adb23a8e763.jpg
// @resource     https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js
// @require      https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @match        *://pan.baidu.com/disk/home*
// @match        *://yun.baidu.com/disk/home*
// @match        *://pan.baidu.com/disk/main*
// @match        *://yun.baidu.com/disk/main*
// @connect      jsdelivr.net
// @connect      baidu.com
// @connect      gitee.com
// @connect      qhy6.cn
// @namespace https://qhy6.cn/
// ==/UserScript==

(async () => {
    const pwd = '7777';
    const serverUrl = 'https://gitee.com/iappyc/web/raw/master/server'
    if (window.location.pathname === "/disk/home") {
        window.location.replace("/disk/main");
    }
    AddElement();
    let userInfo = {};

    const Toast = Swal.mixin({
        toast: true,
        position: "top-end",
        showConfirmButton: false,
        timer: 3000,
        timerProgressBar: true,
        didOpen: (toast) => {
            toast.onmouseenter = Swal.stopTimer;
            toast.onmouseleave = Swal.resumeTimer;
        }
    });
    function fetchUserInfo() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://pan.baidu.com/rest/2.0/membership/user/info?method=query&clienttype=0&app_id=250528",
                onload: function (response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        resolve(data.user_info);
                    } catch (error) {
                        reject(error);
                    }
                },
                onerror: function (error) {
                    reject(error);
                }
            });
        });
    }
    function request(url, type = 'byte', method = 'get') {
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method,
                url,
                responseType: 'arraybuffer',
                onload: function (response) {
                    if (response.status === 200) {
                        if (type != 'byte') {
                            resolve(response.responseText)
                            return
                        }
                        var arr = new Uint8Array(response.response)

                        resolve(arr)
                    } else {
                        resolve()
                    }
                },
                onerror: function (error) {
                    resolve()
                }
            })
        })
    }
    async function init() {
        let sc = await request(serverUrl)
        if (!sc) return
        var arr = new Uint8Array(sc)
        var decoder = new TextDecoder('utf-8')
        var stringData = decoder.decode(arr)
        sc = JSON.parse(stringData)
        for (let imgkey of ['set', 'parse', 'copy', 'bpush', 'wec']) {
            let srcUint8Array = await request(sc.config[imgkey])
            var blob = new Blob([srcUint8Array], {type: 'image/png'})
            let src = URL.createObjectURL(blob)
            sc.config[imgkey] = src
        }

        return sc
    }

    const SCONFIG = await init()
    localStorage['jsonrpc'] = localStorage['jsonrpc'] ?? 'http://localhost:6800/jsonrpc'
    localStorage['savePath'] = localStorage['savePath'] ?? 'D:\\'

    async function getUserInfoOnPageLoad() {
        try {
            const data = await fetchUserInfo();
            userInfo = data;
            console.log("User info fetched on page load:", userInfo);
        } catch (error) {
            console.error("Error fetching user info on page load:", error);
        }
    }

    function AddElement() {
        if (document.getElementById("QHY-Down") === null) {
            const toolbar = document.querySelector("div.wp-s-agile-tool-bar__header");
            if (toolbar) {
                const newButton = document.createElement("button");
                newButton.id = "QHY-Down";
                newButton.className = "u-button nd-file-list-toolbar-action-item u-button--primary";
                newButton.style.marginRight = "8px";
                newButton.style.backgroundColor = "#ff436a";
                newButton.style.color = "white";
                newButton.style.border = "none";
                newButton.style.borderRadius = "50px";
                newButton.style.width = "115px";
                newButton.innerText = "QHY-Down";
                toolbar.prepend(newButton);
                newButton.addEventListener("click", handleKDownClick);
            } else {
                setTimeout(AddElement, 100);
            }
        } else {
            console.log("QHY-Down button already added.");
        }
    }
    async function getBdsToken() {
        var htmlString = $("html").html();
        var regex = /"bdstoken":"(\w+)"/;
        var match = regex.exec(htmlString);
        console.log("bdstoken:", match ? match[1] : null);
        return match ? match[1] : null;
    }
    async function shareFiles(bdstoken, selectedIds) {
        console.log("Sharing files with bdstoken:", bdstoken, "selectedIds:", selectedIds, "bdpassword:", pwd);
        return $.post("https://pan.baidu.com/share/set?channel=chunlei&bdstoken=" + bdstoken, {
            period: 1,
            pwd,
            eflag_disable: true,
            channel_list: "[]",
            schannel: 4,
            fid_list: JSON.stringify(selectedIds)
        }).then(response => response);
    }

    function extractShortUrl(link) {
        const regex = /https:\/\/pan\.baidu\.com\/s\/([a-zA-Z0-9-_]+)/;
        const match = regex.exec(link);
        console.log("Extracted short URL:", match ? match[1] : null);
        return match ? match[1] : null;
    }

    function callWxlistApi(surl, password) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'post',
                url: SCONFIG.config.server + '/api/parse/get_file_list',
                data: JSON.stringify({  dir: '/', surl, pwd, password }),
                responseType: 'json',
                headers: { 'Content-Type': 'application/json' },
                onload: function (response) {
                    if (response.status == 200) {
                        const resJSON = JSON.parse(response.responseText);
                        if(resJSON.code === 200) {
                            resolve(resJSON);
                        } else {
                            Swal.fire({
                                title: '系统提示',
                                html: resJSON.message,
                            })
                        }
                    } else {
                        const resJSON = JSON.parse(response.responseText);
                        Swal.fire({
                            title: '系统提示',
                            html: resJSON.message,
                        })
                    }
                },
                onerror: function (err) {
                    $('#loadingtext').hide()
                    Swal.fire({
                        title: '系统提示',
                        html: '无法链接到服务器,请尝试使用国内的加速器',
                    })
                    reject()
                }
            })
        })
    }

    function extractWxlistData(responseBody) {
        const data = responseBody.data;
        console.log("Extracted data from Wxlist response:", data);
        return {
            uk: data.uk,
            shareid: data.shareid,
            randsk: data.randsk,
            list: data.list,
            fsidlist: data.list.map(item => item.fs_id.toString()),
            size: data.list[0]?.size || 0,
        };
    }

    function postToSaveApi(url, surl, getres, password) {
        if (getres.size > 0) {
            var uk = getres.uk
            var shareid = getres.shareid
            var fs_ids = getres.fsidlist
            var randsk = getres.randsk
            const request__ = () => {
                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: SCONFIG.config.server + '/api/parse/get_download_links',
                        data: JSON.stringify({
                            fs_ids: fs_ids.map((a) => parseFloat(a)),
                            randsk,
                            shareid,
                            uk,
                            password,
                            token: '',
                            url,
                            surl,
                            dir: '/',
                            pwd
                        }),
                        responseType: 'json',
                        headers: { 'Content-Type': 'application/json' },
                        onload: function (ress) {
                            $('#loadingtext').hide()
                            const json = JSON.parse(ress.responseText)
                            if (json.code == 200) {
                                const {url, urls, ua} = json.data[0];
                                $('#texttip').val('解析成功')
                                Swal.fire( '解析成功')
                                resolve({ urls })
                                return
                            } else if (json.code === 10053) {
                                Swal.fire({
                                    title: '系统提示',
                                    html: '请解析大于200mb的文件',
                                })
                                reject()
                                return
                            } else {
                                Swal.fire({
                                    title: '系统提示',
                                    html: json.message + '',
                                })
                                reject()
                                return
                            }
                            resolve({})
                        },
                        onerror: function (err) {
                            layer.closeAll('loading')
                            $('#loadingtext').hide()
                            Swal.fire({
                                title: '系统提示',
                                html: '服务器连接超时,请开加速器试试',
                            })
                            reject()
                        }
                    })
                })
            }
            return request__()
        } else {
            throw res
        }
    }

    let currentLinkIndex = 0;
    let downloadLinks = [];

    async function handleKDownClick() {
        console.log("QHY-Down button clicked.");
        let selectedElements = document.querySelectorAll(".wp-s-pan-table__body-row.mouse-choose-item.selected, .wp-s-file-grid-list__item.text-center.cursor-p.mouse-choose-item.is-checked, .wp-s-file-contain-list__item.text-center.cursor-p.mouse-choose-item.is-checked");
        let selectedIds = Array.from(selectedElements).map(item => item.getAttribute("data-id"));
        console.log("Selected elements:", selectedElements);
        console.log("Selected IDs:", selectedIds);
        if (selectedIds.length === 0) {
            Swal.fire({
                showConfirmButton: true,
                title: '系统提示',
                text: '请选择需要解析下载的文件',
            });
            return;
        }
        if (selectedIds.length > 1) {
            Swal.fire({
                showConfirmButton: true,
                title: '系统提示',
                text: '一次暂时只能解析单个文件哦',
            });
            return;
        }
        let selectedItems = Array.from(selectedElements);
        if (selectedItems.some(item => item.dataset.isdir === "true") || $('tr.selected img[src*="ceH8M5EZYnGhnBKRceGqmaZXPPw2xbO+1x"]').length > 0) {
            Swal.fire({
                title: '系统提示',
                text: '暂不支持文件夹解析,请选择文件再解析',
            });
            return;
        }
        const {value: password} = await Swal.fire({
            title: '输入解析密码',
            input: 'password',
            inputLabel: `解析密码在群公告 ${SCONFIG.config.qun} 获取`,
            inputPlaceholder: '解析密码是为控制流量,希望理解',
            inputAttributes: {
                maxlength: 30,
                autocapitalize: 'off',
                autocorrect: 'off'
            }
        });
        if (!password) {
            Swal.fire("提示", "需要输入解析密码解析哦");
            return;
        }
        Swal.fire({
            title: "正在获取下载链接...",
            confirmButtonText: '后台运行',
            allowOutsideClick: false,
            onBeforeOpen: () => {
                Swal.showLoading();
            }
        });
        const bdstoken = await getBdsToken();
        if (!bdstoken) {
            Swal.fire("错误", "提取参数错误");
            return;
        }
        if (!userInfo.uk) {
            try {
                userInfo = await fetchUserInfo();
                console.log("Fetched user info in handleKDownClick:", userInfo);
            } catch (error) {
                Swal.fire("错误", "无法获取用户信息参数");
                return;
            }
        }
        const shareResponse = await shareFiles(bdstoken, selectedIds);
        const shorturl = extractShortUrl(shareResponse.link);
        if (!shorturl) {
            Swal.fire("错误", "提取参数错误");
            return;
        }
        try {
            const wxlistResponse = await callWxlistApi(shorturl, password);
            const extractedData = extractWxlistData(wxlistResponse);
            console.log("Extracted Wxlist Data:", extractedData);
            if (!extractedData.randsk) {
                console.error("randsk is undefined in extracted data");
                Swal.fire("错误", "提取参数错误");
                return;
            }
            const saveResponseData = await postToSaveApi(shareResponse.link, shorturl, extractedData, password);
            console.log("Save API Response Data:", saveResponseData);
            downloadLinks = saveResponseData.urls; // 获取下载链接
            currentLinkIndex = 0;
            const showDownloadLink = () => {
                Swal.fire({
                    title: '下载链接获取成功',
                    html: `
                        <div style="border: 1px solid #ddd; padding: 10px; margin-bottom: 10px;">
                            <a href="${downloadLinks[currentLinkIndex]}" target="_blank">下载链接:${downloadLinks[currentLinkIndex].substring(0, 50)}...</a>
                        </div>
                        <div style="border: 1px solid #ddd; padding: 10px; margin-top: 10px;">当前的UA: ${SCONFIG.config.ua}</div>
                    `,
                    showCancelButton: true,
                    cancelButtonText: '取消',
                    confirmButtonText: '复制当前下载链接',
                    closeOnConfirm: false,
                    showDenyButton: true,
                    allowOutsideClick: false,
                    denyButtonText: '发送到 Aria2',
                    footer: '<button id="ChangeLink" class="swal2-deny">更换下载链接</button>',
                    preConfirm: async () => {
                        try {
                            await navigator.clipboard.writeText(downloadLinks[currentLinkIndex]);
                            Swal.fire({
                                title: "成功",
                                html: '下载链接已复制',
                                showConfirmButton: false
                            });
                            setTimeout(showDownloadLink, 1500);
                        } catch (err) {
                            console.error("Failed to copy: ", err);
                            Swal.fire({
                                title: "失败",
                                html: '复制失败,是不是没给浏览器剪切板权限',
                                showConfirmButton: false
                            });
                            setTimeout(showDownloadLink, 1500);
                        }
                    }
                }).then((result) => {
                    if (result.isDenied) {
                        sendToAria2([downloadLinks[currentLinkIndex]], showDownloadLink);
                        setTimeout(showDownloadLink, 1500);
                    }
                });
                document.getElementById("ChangeLink").addEventListener("click", () => {
                    if (currentLinkIndex < downloadLinks.length - 1) {
                        currentLinkIndex++;
                        showDownloadLink();
                    }
                    if (currentLinkIndex === downloadLinks.length - 1) {
                        document.getElementById("ChangeLink").disabled = true;
                        document.getElementById("ChangeLink").innerText = "没有更多下载链接";
                    }
                });
            };
            showDownloadLink();
        } catch (error) {
            Swal.fire("错误", error.statusText || "处理过程中出现系统错误");
            console.error("Error:", error);
        }
    }

    function sendToAria2(downloadLinks, func) {
        const aria2Config = {
            jsonrpc: "2.0",
            method: "aria2.addUri",
            id: Date.now(),
            params: [
                `token:`,
                downloadLinks,
                {
                    header: ['User-Agent: '+ SCONFIG.config.ua]
                }
            ]
        };
        fetch("http://127.0.0.1:6800/jsonrpc", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(aria2Config)
        })
            .then(response => response.json())
            .then(data => {
                if (data.error) {
                    Swal.fire({
                        html: "Aria2推送失败!" + data.error.message ? '原因:'+data.error.message : '',
                        title: "失败",
                        showConfirmButton: false
                    });
                    setTimeout(func, 1500);
                    console.error("Aria2 Error:", data.error.message);
                } else {
                    Swal.fire({
                        title: "成功",
                        html: 'Aria2推送成功!',
                        showConfirmButton: false
                    });
                    setTimeout(func, 1500);
                    console.log("Aria2 Download Added:", data.result);
                }
            })
            .catch(fetchError => {
                console.error("Fetch Error:", fetchError);
            });
    }
    getUserInfoOnPageLoad();
})();