Fanbox Batch Downloader

Batch Download on creator, not post

当前为 2019-09-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Fanbox Batch Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.11
  5. // @description Batch Download on creator, not post
  6. // @author https://github.com/amarillys
  7. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.2.2/jszip.min.js
  8. // @match https://www.pixiv.net/fanbox/creator/*
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_download
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const fetchOptions = {
  17. credentials: 'include',
  18. headers: { Accept: 'application/json, text/plain, */*' }
  19. }
  20.  
  21. window.onload = () => {
  22. let baseBtn = document.querySelector('[href="/fanbox/notification"]')
  23. let className = baseBtn.parentNode.className
  24. let parent = baseBtn.parentNode.parentNode
  25. let downloadBtn = document.createElement('div')
  26. downloadBtn.className = className
  27. downloadBtn.innerHTML = '<a href="javascript:void(0)"><div style="width: 100px;height: 32px;background-color: rgba(102, 102, 102, 0.96);border-radius: 10px;color: #EEE;text-align: center;">Click Here to Download</div></a>'
  28. downloadBtn.addEventListener('click', () => { console.log('startDownloading'); downloadByFanboxId(parseInt(document.URL.split('/')[5])); })
  29. parent.appendChild(downloadBtn)
  30. }
  31.  
  32. function gmRequireImage(url) {
  33. return new Promise((resolve, reject) => {
  34. GM_xmlhttpRequest({
  35. method: 'GET',
  36. url,
  37. responseType: 'blob',
  38. onload: res => {
  39. resolve(res.response)
  40. }
  41. })
  42. })
  43. }
  44.  
  45. async function downloadByFanboxId(creatorId) {
  46. let creatorInfo = await getAllPostsByFanboxId(creatorId)
  47.  
  48. let zip = new JSZip()
  49. let amount = 0
  50. let processed = 0
  51. let waittime = 0
  52. zip.file('cover.jpg', await gmRequireImage(creatorInfo.cover), { compression: "STORE" })
  53. for (let i = 0, p = creatorInfo.posts; i < p.length; ++i) {
  54. let folder = p[i].title
  55. console.log(p[i].body)
  56. if (!p[i].body) continue
  57. let images = p[i].body.images
  58. if (!images) continue
  59. for (let j = 0; j < images.length; ++j) {
  60. let extension = images[j].extension === 'jpeg' ? 'jpg' : 'png'
  61. amount++
  62. gmRequireImage(images[j].originalUrl).then(blob => {
  63. zip.folder(folder).file(`${folder}_${j}.${extension}`, blob, { compression: "STORE" })
  64. waittime--
  65. processed++
  66. console.log(` Progress: ${ processed } / ${ amount }`)
  67. })
  68. }
  69. }
  70. // generate zip to download
  71. let timer = setInterval(() => {
  72. waittime++
  73. if (amount === processed || waittime > 10) {
  74. zip.generateAsync({ type: 'blob' }).then(zipBlob => saveBlob(zipBlob, `${creatorId}.zip`))
  75. clearInterval(timer)
  76. }
  77. }, 1000)
  78. }
  79.  
  80. async function getAllPostsByFanboxId(creatorId) {
  81. let fristUrl = `https://www.pixiv.net/ajax/fanbox/creator?userId=${ creatorId }`
  82. let creatorInfo = {
  83. cover: null,
  84. posts: []
  85. }
  86. let firstData = await (await fetch(fristUrl, fetchOptions)).json()
  87. let body = firstData.body
  88. creatorInfo.cover = body.creator.coverImageUrl
  89. creatorInfo.posts.push(...body.post.items)
  90. let nextPageUrl = body.post.nextUrl
  91. while (nextPageUrl) {
  92. let nextData = await (await fetch(nextPageUrl, fetchOptions)).json()
  93. creatorInfo.posts.push(...nextData.body.items)
  94. nextPageUrl = nextData.body.nextUrl
  95. }
  96. return creatorInfo
  97. }
  98.  
  99. function saveBlob(blob, fileName) {
  100. let downloadDom = document.createElement('a')
  101. downloadDom.id = 'fuck'
  102. document.body.appendChild(downloadDom)
  103. downloadDom.style = `display: none`
  104. let url = window.URL.createObjectURL(blob)
  105. downloadDom.href = url
  106. downloadDom.download = fileName
  107. downloadDom.click()
  108. window.URL.revokeObjectURL(url)
  109. }
  110.  
  111. })();