Kemono 下载工具

仅支持Files内图片下载

  1. // ==UserScript==
  2. // @name Kemono 下载工具
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description 仅支持Files内图片下载
  6. // @author ljw2487
  7. // @match *://*.kemono.su/*
  8. // @match *://*.kemono.party/*
  9. // @match *://kemono.su/*
  10. // @match *://kemono.party/*
  11. // @license GNU General Public License v3.0 or later
  12. // @grant GM_addStyle
  13. // @grant GM_addElement
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM_download
  16. // ==/UserScript==
  17.  
  18. GM_addStyle(".ad-container {display: none;}");
  19. GM_addStyle(".root--ujvuu {display: none;}");
  20.  
  21. // 下载文件命名规则:
  22. // 包含:作者 | 主标题 | 副标题(来源) | 页码 | 共多少页 (副标题一般是(Pixiv Fanbox) (Patreon))
  23. // 标题示例:Yelan 1 Part 3 (Loop) (Patreon) 其中(Patreon)为副标题
  24. // 1 -> 页码.格式 示例:1.jpg
  25. // 2 -> 页码_共多少页.格式 示例:1_20.jpg
  26. // 3 -> 主标题 页码_共多少页.格式 示例:Yelan 1 Part 3 (Loop) 1_20.jpg
  27. // 4 -> 主标题 副标题(来源) 页码_共多少页.格式 示例:Yelan 1 Part 3 (Loop) (Patreon) 1_20.jpg
  28. // 5 -> [作者] 主标题 副标题(来源) [页码_共多少页].格式 示例:[Abcde] Yelan 1 Part 3 (Loop) (Patreon) [1_20].jpg
  29. const nameRule = 5
  30.  
  31. function script() {
  32. const host = location.host
  33. const path = location.pathname
  34. const regular = {
  35. "/": homePage, // 首页
  36. "/artists": artistsPage, // 画师列表
  37. "/(\\w+)/user/(\\d+)": userPage, // 画师详情
  38. "/(\\w+)/user/(\\d+)/post/(\\d+)": postPage, // 作品页
  39. };
  40. Object.keys(regular).some((pattern) => {
  41. if (new RegExp(`^${pattern}$`).test(path)) {
  42. regular[pattern]();
  43. return true;
  44. }
  45. })
  46. }
  47. script()
  48.  
  49. function homePage () {
  50. // console.log('homepage');
  51. }
  52. function artistsPage () {
  53. // console.log('artistsPage');
  54. }
  55. function userPage () {
  56. // console.log('userPage');
  57. }
  58. function postPage () {
  59. GM_addStyle(".download-button {color: white; font-size: 16px; background-color: #3e3e44; padding: 3px 15px; border-radius: 5px; appearance: none; border: none; outline: none; transition: color 0.2s ease-in-out;}");
  60. GM_addStyle(".download-button:hover {color: #f8b696; transition: color 0.2s ease-in-out;}");
  61. GM_addStyle(".modify-h2 {display:flex; align-items: center; justify-content: space-between;}");
  62.  
  63. // GET ARTIST {String} "name"
  64. let artist = ""
  65. artist = document.getElementsByClassName('post__user-name')[0].innerText
  66. // GET TITLES {Array} [mainTitle, subTitle]
  67. let titles = []
  68. let titleNode = document.getElementsByClassName('post__title')[0]
  69. titleNode = titleNode.getElementsByTagName('span')
  70. Array.from(titleNode).forEach(item => { titles.push(item.innerText) })
  71. // GET FILE URL LIST {Array} [Url, Url, ...]
  72. let fileList = []
  73. let filesNode = document.getElementsByClassName('post__thumbnail')
  74. Array.from(filesNode).forEach(file => {
  75. let href = file.querySelector('a').getAttribute('href')
  76. fileList.push(href)
  77. })
  78. // Modify-DOM
  79. let h2Node = document.getElementsByTagName("h2")
  80. Array.from(h2Node).forEach(element => {
  81. // Files
  82. if(element.innerHTML == "Files") {
  83. let fileTitle = createElement("h2", "Files")
  84. let fileBtn = createElement("button", "下载全部", {
  85. id: "fileButton",
  86. class: "download-button",
  87. type: "button"
  88. })
  89. element.innerHTML = ""
  90. element.classList.add("modify-h2")
  91. element.appendChild(fileTitle)
  92. element.appendChild(fileBtn)
  93.  
  94. fileBtn.addEventListener("click", function() {
  95. fileBtn.disabled = true
  96. fileBtn.innerHTML = "下载中"
  97. downloadingFiles(fileBtn, artist, titles, fileList)
  98. })
  99. }
  100. })
  101. }
  102. function createElement(tag, text, attributes = undefined) {
  103. let element = document.createElement(tag);
  104. element.innerHTML = text
  105. if (attributes) {
  106. for (let k in attributes) {
  107. element.setAttribute(k, attributes[k])
  108. }
  109. }
  110. return element
  111. }
  112. function createFileName(artist, titles, index, pages) {
  113. // console.log(artist, titles, index, pages);
  114. if (nameRule === 1) {
  115. return `${index}` // 1.jpg
  116. } else if (nameRule === 2) {
  117. return `${index}_${pages}` // 1_20.jpg
  118. } else if (nameRule === 3) {
  119. return `${titles[0]} ${index}_${pages}` // title 1_20.jpg
  120. } else if (nameRule === 4) {
  121. return `${titles[0]} ${titles[1]} ${index}_${pages}` // title (fanbox) 1_20.jpg
  122. } else if (nameRule === 5) {
  123. return `[${artist}] ${titles[0]} ${titles[1]} [${index}_${pages}]` // [artist] title (fanbox) [1_20].jpg
  124. }
  125. }
  126. function downloadingFiles(btn, artist, titles, fileList) {
  127. let totalQuest = fileList.length, success = 0, faild = 0
  128. fileList.forEach((url, index) => {
  129. let fileName = createFileName(artist, titles, index + 1, fileList.length)
  130. GM_download({
  131. url: url,
  132. name: fileName,
  133. onload: function() {
  134. console.log(`下载 ${index + 1}/${fileList.length} 完成`)
  135. totalQuest -= 1
  136. success += 1
  137. downloadingFilesFinished(btn, totalQuest, fileList.length, success, faild)
  138. },
  139. onerror: function(err) {
  140. console.error(`下载 ${index + 1}/${fileList.length} 失败 准备二次尝试`, err)
  141. GM_download({
  142. url: url,
  143. name: fileName,
  144. onload: function() {
  145. console.log(`下载 ${index + 1}/${fileList.length} 完成 (二次尝试)`)
  146. totalQuest -= 1
  147. success += 1
  148. downloadingFilesFinished(btn, totalQuest, fileList.length, success, faild)
  149. },
  150. onerror: function(err) {
  151. console.log(`下载 ${index + 1}/${fileList.length} 失败 (二次尝试) 请尝试手动下载`, err, {
  152. name: fileName,
  153. url: url
  154. })
  155. totalQuest -= 1
  156. faild += 1
  157. downloadingFilesFinished(btn, totalQuest, fileList.length, success, faild)
  158. }
  159. })
  160. }
  161. })
  162. })
  163. }
  164. function downloadingFilesFinished(btn, totalQuest, total, success, faild) {
  165. if (totalQuest === 0) {
  166. btn.disabled = false
  167. btn.innerHTML = "已下载"
  168. alert(`下载完成, ${total}张, 成功${success}张, 失败${faild}张`)
  169. }
  170. }