Hifini 快捷下载(蓝奏云)

一键回复/下载,无需填写提取码快速下载

  1. // ==UserScript==
  2. // @name Hifini 快捷下载(蓝奏云)
  3. // @namespace https://github.com/s0urcelab/userscripts
  4. // @match https://www.hifini.com/thread-*.htm
  5. // @grant GM_xmlhttpRequest
  6. // @grant GM_notification
  7. // @version 1.3
  8. // @author s0urce
  9. // @description 一键回复/下载,无需填写提取码快速下载
  10. // @icon https://www.hifini.com/view/img/logo.png
  11. // @run-at document-idle
  12. // @license MIT
  13. // ==/UserScript==
  14. // 匹配详情页id
  15. const threadId = window.location.pathname.match(/thread-(\d+)/)[1]
  16. if (!threadId) return;
  17. const REPLY_TEXT = '感谢上传~'
  18. const BTN_ROOT = '.card-thread .card-body .media'
  19. const DL_LINKS = '.alert.alert-success'
  20. const UA = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.52`
  21. const QS = q => document.querySelector(q)
  22. const QSA = q => document.querySelectorAll(q)
  23. const asRequest = (detail) => new Promise((resolve, reject) => {
  24. GM_xmlhttpRequest({
  25. ...detail,
  26. onload: (response) => {
  27. resolve(response.response || 'success')
  28. },
  29. })
  30. })
  31. // 插入快速回复按钮
  32. const replyBtn = document.createElement('div')
  33. replyBtn.innerHTML = '快捷回复'
  34. replyBtn.classList = 'btn btn-primary mr-3'
  35. replyBtn.onclick = async () => {
  36. await autoReply()
  37. window.location.reload()
  38. }
  39. // 插入下载按钮
  40. const downloadBtn = document.createElement('div')
  41. downloadBtn.innerHTML = '快捷下载'
  42. downloadBtn.classList = 'btn btn-primary'
  43. downloadBtn.onclick = async () => {
  44. const addr = await getLanZouAddr()
  45. if (addr) {
  46. GM_notification({
  47. text: '下载开始',
  48. title: 'Hifini快捷下载',
  49. })
  50. window.open(addr, '_blank')
  51. }
  52. }
  53. QS(BTN_ROOT).append(replyBtn)
  54. QS(BTN_ROOT).append(downloadBtn)
  55. // 自动回复
  56. async function autoReply() {
  57. const reResp = await asRequest({
  58. url: `/post-create-${threadId}-1.htm`,
  59. method: 'POST',
  60. headers: {
  61. origin: `https://www.hifini.com`,
  62. referer: window.location.href,
  63. 'user-agent': UA,
  64. 'x-requested-with': `XMLHttpRequest`,
  65. },
  66. data: new URLSearchParams({
  67. doctype: 1,
  68. return_html: 1,
  69. quotepid: 0,
  70. message: REPLY_TEXT,
  71. }),
  72. responseType: 'json',
  73. })
  74. if (reResp.code === '0') {
  75. GM_notification({
  76. text: '快捷回复成功',
  77. title: 'Hifini快捷下载',
  78. })
  79. }
  80. }
  81. // 获取蓝奏云下载
  82. async function getLanZouAddr() {
  83. const isHidden = el => window.getComputedStyle(el).display === 'none'
  84. let link
  85. let pass
  86. QSA(DL_LINKS).forEach(dl => {
  87. const dlText = dl.innerText
  88. if (dlText.includes('hifini')) {
  89. const ln_re = dlText.match(/链接:\s*(\S+)\s*提取码:\s*(\S*)/)
  90. link = ln_re[1]
  91. pass = ln_re[2]
  92. const passIframe = Array.from(dl.children).find(v => v.tagName === 'IFRAME')
  93. if (passIframe) {
  94. const charList = Array.from(passIframe.contentDocument.querySelectorAll('span'))
  95. pass = charList.reduce((acc, curr) => `${acc}${isHidden(curr) ? '' : curr.textContent}`, '')
  96. }
  97. }
  98. })
  99. if (!link || !pass) {
  100. GM_notification({
  101. text: '找不到蓝奏云链接,请手动下载',
  102. title: 'Hifini快捷下载',
  103. })
  104. return null
  105. }
  106. console.warn(`解析提取码:${link} ${pass}`)
  107. const lzText = await asRequest({
  108. url: link,
  109. method: 'GET',
  110. headers: {
  111. 'user-agent': UA,
  112. },
  113. })
  114. // 解析html中的签名
  115. const sign = lzText.match(/skdklds(\s+)=(\s+)'(.+)'/)[3]
  116. console.warn(`解析sign${sign}`)
  117. const { dom, url } = await asRequest({
  118. url: `https://hifini.lanzoum.com/ajaxm.php`,
  119. method: 'POST',
  120. headers: {
  121. Host: 'hifini.lanzoum.com',
  122. Origin: 'https://hifini.lanzoum.com',
  123. Referer: link,
  124. 'X-Requested-With': 'XMLHttpRequest',
  125. 'user-agent': UA,
  126. },
  127. data: new URLSearchParams({
  128. action: 'downprocess',
  129. sign,
  130. p: pass,
  131. }),
  132. responseType: 'json',
  133. })
  134. console.warn(`解析蓝奏云下载地址:${dom}/file/${url}`)
  135. return `${dom}/file/${url}`
  136. }