Greasy Fork is available in English.

秒传链接提取

大道至简,这是一个全新开发的百度网盘秒传链接提取脚本。

// ==UserScript==
// @name         秒传链接提取
// @namespace    https://github.com/vscodev
// @version      0.0.3
// @author       vscodev
// @description  大道至简,这是一个全新开发的百度网盘秒传链接提取脚本。
// @license      gpl-3.0
// @icon         https://vitejs.dev/logo.svg
// @homepageURL  https://github.com/vscodev/rapid-upload-userscript
// @match        *://pan.baidu.com/disk/main*
// @match        *://yun.baidu.com/disk/main*
// @match        *://wangpan.baidu.com/disk/main*
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11.7.12/dist/sweetalert2.all.min.js
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(t=>{const e=document.createElement("style");e.dataset.source="vite-plugin-monkey",e.textContent=t,document.head.append(e)})(" #rapid-upload-button{-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;vertical-align:middle;font:inherit;overflow:visible;text-transform:none;font-family:SFUIText,PingFangSC-Regular,Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-user-select:none;display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;-webkit-appearance:none;text-align:center;box-sizing:border-box;outline:0;margin:0 16px;transition:.1s;color:#fff;background-color:#06a7ff;font-weight:700;padding:8px 24px;height:32px;font-size:14px;border-radius:16px;border:none} ");

(function (Swal) {
  'use strict';

  var __defProp = Object.defineProperty;
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  var __publicField = (obj, key, value) => {
    __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
    return value;
  };
  var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  const HOST = location.host;
  const CHECK_LOGIN_URL = `https://${HOST}/api/loginStatus?clienttype=0&app_id=250528&web=1&channel=chunlei`;
  const PRE_CREATE_URL = `https://${HOST}/api/precreate?app_id=250528&clienttype=21`;
  class ApiError extends Error {
    constructor(code, message) {
      super(message ?? getErrorMessage(code));
      __publicField(this, "code");
      this.code = code;
      this.name = "ApiError";
    }
    toString() {
      return `${this.message}(错误码:${this.code})`;
    }
  }
  function getErrorMessage(errrno) {
    switch (errrno) {
      case 0:
        return "请求成功";
      case 2:
      case 31023:
        return "参数错误";
      case 111:
        return "access token 失效";
      case -6:
        return "身份验证失败";
      case 6:
        return "不允许接入用户数据";
      case 31034:
        return "命中接口频控";
      case 2131:
        return "该分享不存在";
      case 10:
        return "转存文件已经存在";
      case -3:
      case -31066:
        return "文件不存在";
      case 11:
        return "自己发送的分享";
      case 255:
        return "转存数量太多";
      case 12:
        return "批量转存出错";
      case -1:
        return "权益已过期";
      case -7:
        return "文件或目录名错误或无权访问";
      case -10:
        return "容量不足";
      case 404:
        return "秒传md5不匹配";
      case 406:
        return "秒传创建文件失败";
      case 407:
        return "fileModify接口返回错误,未返回requestid";
      default:
        return "未知错误";
    }
  }
  function checkLogin() {
    return new Promise((resolve, reject) => {
      _GM_xmlhttpRequest({
        method: "GET",
        url: CHECK_LOGIN_URL,
        responseType: "json",
        onload: (result) => {
          const resp = result.response;
          if (resp.errno === 0) {
            resolve();
          } else {
            reject(new ApiError(resp.errno, resp.show_msg));
          }
        },
        onerror: (err) => {
          reject(err);
        }
      });
    });
  }
  function doRapidUpload(targetPath, fi) {
    return new Promise((resolve, reject) => {
      _GM_xmlhttpRequest({
        method: "POST",
        url: PRE_CREATE_URL,
        data: `path=${encodeURIComponent(targetPath + "/" + fi.path)}&size=${fi.size}&isdir=0&block_list=${JSON.stringify(
        fi.blockList.map((v) => v.toLowerCase())
      )}&autoinit=1&content-md5=${fi.contentMd5}&slice-md5=${fi.sliceMd5.toLowerCase()}&rtype=2`,
        headers: {
          "Content-Type": "application/x-www-form-urlencoded"
        },
        responseType: "json",
        onload: (result) => {
          const resp = result.response;
          if (resp.errno === 0) {
            if (resp.return_type === 2) {
              resolve();
            } else {
              reject(new ApiError(404, "秒传未生效"));
            }
          } else {
            reject(new ApiError(resp.errno));
          }
        },
        onerror: (err) => {
          reject(err);
        }
      });
    });
  }
  const MAX_ATTEMPTS = 3;
  function parseRapidUploadLinks(inputValue) {
    return inputValue.split(/\r?\n/g).reduce((filtered, link) => {
      const matched = link.trim().match(/^([a-f0-9]{32})#([a-f0-9]{32})#([0-9]{1,20})#(.+)/i);
      if (matched) {
        filtered.push({
          path: matched[4],
          size: matched[3],
          blockList: [""],
          contentMd5: matched[1],
          sliceMd5: matched[2]
        });
      }
      return filtered;
    }, []);
  }
  function getCurrentPath() {
    const matched = location.href.match(/path=(.+?)(?:&|$)/);
    if (matched) {
      return decodeURIComponent(matched[1]);
    }
    return "";
  }
  function randomizeTextCase(text) {
    let ret = "";
    for (let i = 0; i < text.length; i++) {
      const char = text[i].toLowerCase();
      if (Math.random() < 0.5) {
        ret += char;
      } else {
        ret += char.toUpperCase();
      }
    }
    return ret;
  }
  function sleep(ms = 300) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  function getRandomIntInclusive(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1) + min);
  }
  async function tryRapidUpload(targetPath, fi, attempts) {
    fi.contentMd5 = attempts === 0 ? fi.contentMd5.toLowerCase() : randomizeTextCase(fi.contentMd5);
    try {
      await doRapidUpload(targetPath, fi);
    } catch (err) {
      if (err instanceof ApiError && err.code === 404 && attempts < MAX_ATTEMPTS) {
        await sleep(getRandomIntInclusive(300, 1e3));
        return tryRapidUpload(targetPath, fi, ++attempts);
      }
      throw err;
    }
  }
  async function newRapidUploadTask() {
    const result = await Swal.fire({
      title: "秒传链接提取",
      input: "textarea",
      inputPlaceholder: "请输入标准格式的秒传提取码,支持批量转存,多个链接以换行符分隔。",
      preConfirm: (inputValue) => {
        const fileList = parseRapidUploadLinks(inputValue);
        if (fileList.length === 0) {
          Swal.showValidationMessage(`未检测到有效的的秒传链接`);
        }
        return fileList;
      },
      showCancelButton: true,
      confirmButtonText: "确定",
      cancelButtonText: "取消"
    });
    if (result.isConfirmed) {
      const fileList = result.value ?? [];
      if (fileList.length > 0) {
        const targetPath = getCurrentPath();
        let successCount = 0;
        let failureCount = 0;
        await Swal.fire({
          title: "文件转存中",
          html: `正在转存第 <b id="file-num"></b>/<b>${fileList.length}</b> 个文件`,
          willOpen: async () => {
            var _a;
            Swal.showLoading();
            const ele = (_a = Swal.getHtmlContainer()) == null ? void 0 : _a.querySelector("#file-num");
            for (const [index, fi] of fileList.entries()) {
              if (ele) {
                ele.textContent = `${index + 1}`;
              }
              try {
                await tryRapidUpload(targetPath, fi, 0);
                successCount++;
              } catch (err) {
                console.error(err);
                failureCount++;
              }
            }
            Swal.close();
          },
          showConfirmButton: false
        });
        Swal.fire({
          title: `转存完毕,成功${successCount}个,失败${failureCount}个`,
          confirmButtonText: "确定"
        }).then(() => {
          location.reload();
        });
      }
    }
  }
  function injectRapidUploadButton() {
    const btn = document.createElement("button");
    btn.setAttribute("id", "rapid-upload-button");
    btn.innerText = "秒传";
    btn.addEventListener("click", newRapidUploadTask);
    const toolBar = document.querySelector(".wp-s-agile-tool-bar__header");
    toolBar == null ? void 0 : toolBar.appendChild(btn);
  }
  function main() {
    checkLogin().then(() => {
      injectRapidUploadButton();
    }).catch((err) => {
      console.error(err);
    });
  }
  main();

})(Swal);