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

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

// ==UserScript==
// @name        百度网盘不限速下载-千幻云DL
// @description  一款百度网盘不限速解析脚本,复活?可用?不限制?不限速? 支持Motrix、idm、Aria下载器下载,速度快就完事了!
// @version      2.3
// @antifeature  membership
// @antifeature  ads
// @antifeature  tracking
// @license      MIT
// @author       huan_kong、QHY-Down
// @icon         https://www.42kx.com/tc/view.php/95e1f47a9fd0dd02fbd0f7f0044f4640.jpg
// @require      https://registry.npmmirror.com/jquery/3.7.1/files/dist/jquery.min.js
// @resource     https://registry.npmmirror.com/sweetalert2/11.10.3/files/dist/sweetalert2.min.css
// @require      https://registry.npmmirror.com/sweetalert2/11.10.3/files/dist/sweetalert2.all.min.js
// @grant        GM_xmlhttpRequest
// @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      pan.baidu.com
// @connect      yun.baidu.com
// @connect      gitee.com
// @connect      jx.oxah.cn
// @namespace    https://jx.oxah.cn/
// ==/UserScript==

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

  async function sendRequest(method, url, data = null) {
    return new Promise((resolve, reject) => {
      console.log('发起请求:', {
        method,
        url,
        ...(['get', 'head'].includes(method) ? {} : { data: JSON.stringify(data), headers: { 'Content-Type': 'application/json' } }),
      })
      GM_xmlhttpRequest({
        method,
        url,
        ...(['get', 'head'].includes(method) ? {} : { data: JSON.stringify(data), headers: { 'Content-Type': 'application/json' } }),
        onload: (response) => {
          console.log('请求成功:', response)

          let returnValue = response.responseText

          try {
            returnValue = typeof returnValue === 'string' ? JSON.parse(returnValue) : returnValue
          } catch (error) {
            console.log('JSON 解析失败', returnValue)
            resolve(returnValue)
          }

          if (!returnValue.code || returnValue.code === 200) {
            resolve(returnValue)
            return
          }

          Swal.fire({
            title: '系统提示',
            html: returnValue.message,
          })
          reject(returnValue.message)
        },
        onerror: (error) => {
          console.log('请求出错:', error)
          reject(error)
        },
      })
    })
  }

  async function init() {
    try {
      const res = await sendRequest('get', serverUrl)
      const SCONFIG = res.config

      // for (const imgkey of ['set', 'parse', 'copy', 'bpush', 'wec']) {
      //   const blob = await sendRequest('GET', SCONFIG[imgkey], undefined, undefined, 'blob')
      //   SCONFIG[imgkey] = URL.createObjectURL(blob)
      // }

      return SCONFIG
    } catch (error) {
      console.log('获取服务器数据失败:', error)
      Swal.fire({
        title: '系统提示',
        html: '连接到服务器失败,请尝试使用国内网络',
      })
    }
  }

  function getBdsToken() {
    const htmlString = $('html').html()
    const regex = /"bdstoken":"(\w+)"/
    const match = regex.exec(htmlString)
    return match ? match[1] : null
  }

  async function fetchUserInfo() {
    const response = await sendRequest('get', 'https://pan.baidu.com/rest/2.0/membership/user/info?method=query&clienttype=0&app_id=250528')
    response.bdstoken = getBdsToken()
    return response.user_info
  }

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

  async function shareFiles(selectedIds) {
    const response = await $.ajax({
      method: 'post',
      url: `https://pan.baidu.com/share/set?channel=chunlei&bdstoken=${USERINFO.bdstoken}&clienttype=0&app_id=250528&web=1`,
      data: {
        period: 1,
        pwd: '1111',
        eflag_disable: true,
        channel_list: '[]',
        schannel: 4,
        fid_list: JSON.stringify(selectedIds),
      },
    })
    return extractShortUrl(response.link)
  }

  async function getFileList(surl, parse_password) {
    const response = await sendRequest('post', `${SCONFIG.server}/api/v1/user/parse/get_file_list`, {
      dir: '/',
      surl,
      pwd: '1111',
      parse_password,
    })
    return response.data
  }

  async function getDownloadLinks(randsk, uk, shareid, fs_id, surl, parse_password) {
    const response = await sendRequest('post', `${SCONFIG.server}/api/v1/user/parse/get_download_links`, {
      randsk,
      uk,
      shareid,
      fs_id,
      surl,
      dir: '/',
      pwd: '1111',
      token: 'guest',
      parse_password,
      rand2: '油猴脚本',
    })
    return response.data[0]
  }

  function addElement() {
    const alreadyExists = document.getElementById('QHY-Down')
    if (alreadyExists) return

    const toolbar = document.querySelector('div.wp-s-agile-tool-bar__header')
    if (!toolbar) {
      setTimeout(addElement, 100)
      return
    }

    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', handleDownloadButton)
  }

  let downloadLinks = []
  let currentLinkIndex = 0

  function CopyLink() {
    GM_setClipboard(downloadLinks.urls[currentLinkIndex])
    Swal.fire({
      title: '系统通知',
      html: '下载链接已复制',
      showConfirmButton: false,
      allowOutsideClick: false,
    })
    setTimeout(showDownloadLinks, 1500)
  }

  async function SendLink() {
    const aria2Config = {
      jsonrpc: '2.0',
      method: 'aria2.addUri',
      id: Date.now(),
      params: [
        `token:`,
        [downloadLinks.urls[currentLinkIndex]],
        {
          header: [`User-Agent: ${downloadLinks.ua}`],
        },
      ],
    }

    const data = await $.ajax({
      method: 'POST',
      url: 'http://127.0.0.1:6800/jsonrpc',
      data: JSON.stringify(aria2Config),
      contentType: 'application/json',
    })

    if (data.error) {
      Swal.fire({
        html: `Aria2推送失败 ${data.error.message ? '原因:' + data.error.message : ''}`,
        title: '失败',
        showConfirmButton: false,
        allowOutsideClick: false,
      })
      setTimeout(showDownloadLinks, 1500)
    } else {
      Swal.fire({
        title: '成功',
        html: 'Aria2推送成功',
        showConfirmButton: false,
        allowOutsideClick: false,
      })
      setTimeout(showDownloadLinks, 1500)
    }
  }

  function ChangeLink() {
    if (currentLinkIndex < downloadLinks.urls.length - 1) {
      currentLinkIndex++
      showDownloadLinks()
    } else {
      Swal.fire({
        title: '系统通知',
        html: '没有更多下载链接啦',
        showConfirmButton: false,
        allowOutsideClick: false,
      })
      setTimeout(showDownloadLinks, 1500)
    }
  }

  async function handleDownloadButton() {
    const selectedRawElements = 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',
    )
    const selectedElements = Array.from(selectedRawElements)
    const selectedIds = selectedElements.map((item) => parseFloat(item.getAttribute('data-id')))

    if (selectedIds.length === 0) {
      Swal.fire({
        title: '系统提示',
        text: '请先选择需要解析下载的文件',
      })
      return
    }

    if (selectedIds.length > 1) {
      Swal.fire({
        title: '系统提示',
        text: '一次暂时只能解析单个文件哦',
      })
      return
    }

    if (selectedElements.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.qun} 获取`,
      inputPlaceholder: '解析密码是为控制流量,希望理解',
      inputAttributes: {
        maxlength: 30,
        autocapitalize: 'off',
        autocorrect: 'off',
      },
    })

    if (!password) {
      Swal.fire({
        title: '系统提示',
        text: '需要输入解析密码解析哦',
      })
      return
    }

    Swal.fire({
      title: '正在获取下载链接...',
      allowOutsideClick: false,
      showConfirmButton: false,
    })

    let surl

    try {
      Swal.fire({
        title: '正在创建分享链接...',
        allowOutsideClick: false,
        showConfirmButton: false,
      })
      // 分享文件
      surl = await shareFiles(selectedIds)
      console.log('SURL:', surl)
    } catch (error) {
      Swal.fire({
        title: '系统提示',
        text: '分享文件失败,请稍后再试',
      })
      return
    }

    Swal.fire({
      title: '正在获取文件信息...',
      allowOutsideClick: false,
      showConfirmButton: false,
    })
    const { shareid, uk, randsk } = await getFileList(surl, password)
    console.log('FileList获取成功:', shareid, uk, randsk)

    Swal.fire({
      title: '正在获取下载链接...',
      allowOutsideClick: false,
      showConfirmButton: false,
    })
    const links = await getDownloadLinks(randsk, uk, shareid, selectedIds, surl, password)
    console.log('DownloadLinks获取成功:', links)

    downloadLinks = links
    currentLinkIndex = 0

    showDownloadLinks()
  }

  function showDownloadLinks() {
    Swal.fire({
      title: '下载链接获取成功',
      html: `
      <div style="border: 1px solid #ddd; padding: 10px; margin-bottom: 10px;">
        <a href="${downloadLinks.urls[currentLinkIndex]}" target="_blank">下载链接:${downloadLinks.urls[currentLinkIndex].substring(0, 50)}...</a>
      </div>
      <div style="border: 1px solid #ddd; padding: 10px; margin-top: 10px;">
        当前的UA: ${downloadLinks.ua}
      </div>`,
      showConfirmButton: false,
      allowOutsideClick: false,
      footer: `
      <div style="display:flex; gap: 10px;">
        <button id="CopyLink" class="u-button nd-file-list-toolbar-action-item u-button--primary">复制下载链接</button>
        <button id="SendLink" class="u-button nd-file-list-toolbar-action-item u-button--primary">发送到Aria2</button>
        <button id="ChangeLink" class="u-button nd-file-list-toolbar-action-item u-button--primary">更换下载链接</button>
      </div>`,
    })
    document.querySelector('#CopyLink').addEventListener('click', CopyLink)
    document.querySelector('#SendLink').addEventListener('click', SendLink)
    document.querySelector('#ChangeLink').addEventListener('click', ChangeLink)
  }

  const SCONFIG = await init()
  const USERINFO = await fetchUserInfo()

  // 最后创建
  addElement()
})()