Poipiku Downloader

Download images or text from Poipiku

目前為 2022-01-25 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Poipiku Downloader
  3. // @name:zh-CN Poipiku下载器
  4. // @description Download images or text from Poipiku
  5. // @description:zh-cn 从Poipiku下载图片或文字
  6. // @author calary
  7. // @namespace http://tampermonkey.net/
  8. // @version 0.3.2
  9. // @license GPL-3.0
  10. // @include http*://poipiku.com*
  11. // @match https://poipiku.com/
  12. // @connect img.poipiku.com
  13. // @connect img-org.poipiku.com
  14. // @icon https://poipiku.com/favicon.ico
  15. // @require https://cdn.bootcdn.net/ajax/libs/jquery/2.2.4/jquery.min.js
  16. // @require https://cdn.bootcss.com/jszip/3.1.4/jszip.min.js
  17. // @require https://cdn.bootcss.com/FileSaver.js/1.3.2/FileSaver.min.js
  18. // @grant GM.xmlHttpRequest
  19. // @grant GM_xmlhttpRequest
  20. // @run-at document-end
  21. // ==/UserScript==
  22.  
  23. jQuery(function ($) {
  24. const lang = (
  25. window.navigator.language ||
  26. window.navigator.browserLanguage ||
  27. "en-us"
  28. ).toLowerCase();
  29.  
  30. const i18nMap = {
  31. "en-us": {
  32. ui_logined: "Logined",
  33. ui_password: "Password",
  34. ui_qualitytrue: "You can download high quality images.",
  35. ui_qualityfalse: "You cannot download high quality images.",
  36. ui_mode: "Rename image with page id",
  37. btn_downloadimages: "Save images (.zip)",
  38. btn_downloadimageseperately: "Save images Seperately",
  39. btn_downloadtext: "Save text (.txt)",
  40. error_default: "Something went wrong",
  41. error_fetch: "Fetch content error. Entered wrong password?",
  42. error_noimage: "No Images",
  43. txt_title: "Title: ",
  44. txt_author: "Author: ",
  45. txt_twitter: "Twitter: ",
  46. txt_link: "Link: ",
  47. },
  48. "zh-cn": {
  49. ui_logined: "登录(不可用)状态",
  50. ui_password: "密码",
  51. ui_qualitytrue: "可以下载高质量图片。",
  52. ui_qualityfalse: "不能下载高质量图片。",
  53. ui_mode: "图片命名包含当页ID",
  54. btn_downloadimages: "图片打包为(.zip)",
  55. btn_downloadimageseperately: "独立保存图片",
  56. btn_downloadtext: "保存文字为(.txt)",
  57. error_default: "出错了",
  58. error_fetch: "请求失败。是否输入密码有误?",
  59. error_noimage: "没有图片",
  60. txt_title: "标题:",
  61. txt_author: "作者:",
  62. txt_twitter: "推特:",
  63. txt_link: "地址:",
  64. },
  65. };
  66. const i18n = (key) =>
  67. (i18nMap[lang] && i18nMap[lang][key]) || i18nMap["en-us"][key];
  68.  
  69. const website = "poipiku";
  70. const url = window.location.href;
  71. const execResult = /\/(\d+)\/(\d+)/.exec(url);
  72. const authorId = execResult && execResult[1];
  73. const workId = execResult && execResult[2];
  74. const logined = $(".LoginButton").length === 0;
  75. const isText = $(".IllustItem").hasClass("Text");
  76. const hasPassword = $(".IllustItem").hasClass("Password");
  77. const fontFamily = "Arial, 'Microsoft Yahei', Helvetica, sans-serif";
  78.  
  79. if (!workId) {
  80. return;
  81. }
  82.  
  83. const $panel = $(`<div>
  84. <div>${i18n("ui_logined")}: <b style="color:red">${logined}</b>.</div>
  85. <div class="line-qualitytip" >${
  86. logined ? i18n("ui_qualitytrue") : i18n("ui_qualityfalse")
  87. }</div>
  88. <div class="line-password">${i18n(
  89. "ui_password"
  90. )} <input type='text' class="password"></div>
  91. <div class="line-mode" >${i18n(
  92. "ui_mode"
  93. )} <input type='checkbox' class="saveFileMode"></div>
  94. <div class="line-images">
  95. <button class="btn-downloadImagesSeperately" style="font-size:20px">${i18n(
  96. "btn_downloadimageseperately"
  97. )} <b class='status'></b></button></button><br>
  98. <button class="btn-downloadImages" style="font-size:20px">${i18n(
  99. "btn_downloadimages"
  100. )} <b class='status'></b></button>
  101. </div>
  102. <div class="line-text"><button class="btn-downloadText" style="font-size:20px">${i18n(
  103. "btn_downloadtext"
  104. )}</button></div>
  105. </div>`)
  106. .css({
  107. position: "fixed",
  108. left: 0,
  109. top: 50,
  110. zIndex: 999999,
  111. background: "#fff",
  112. color: "#333",
  113. fontSize: 18,
  114. fontFamily: fontFamily,
  115. padding: 10,
  116. })
  117. .appendTo($("body"));
  118.  
  119. const $password = $panel.find(".password");
  120. const $saveFileMode = $panel.find(".saveFileMode");
  121. $panel.find("button").css({
  122. fontFamily: fontFamily,
  123. });
  124.  
  125. if (!hasPassword) {
  126. $panel.find(".line-password").hide();
  127. }
  128. if (isText) {
  129. $panel.find(".line-images").hide();
  130. $panel.find(".line-qualitytip").hide();
  131. $panel.find(".line-mode").hide();
  132. } else {
  133. $panel.find(".line-text").hide();
  134. }
  135. $panel.find(".btn-downloadImages").on("click", downloadImagesAsZip);
  136. $panel
  137. .find(".btn-downloadImagesSeperately")
  138. .on("click", downloadImagesSeperately);
  139. $panel.find(".btn-downloadText").on("click", downloadText);
  140.  
  141. function request(config) {
  142. return new Promise((resolve, reject) => {
  143. $.ajax({
  144. ...config,
  145. success: (response) => {
  146. resolve(response);
  147. },
  148. error: () => {
  149. reject(new Error(i18n("error_default")));
  150. },
  151. });
  152. });
  153. }
  154.  
  155. function getBlob(url) {
  156. // return fetch(url).then((response) => response.blob());
  157.  
  158. return new Promise((resolve, reject) => {
  159. GM.xmlHttpRequest({
  160. method: "GET",
  161. url: url,
  162. responseType: "blob",
  163. headers: { referer: window.location.href },
  164. onload: (payload) => {
  165. resolve(payload.response);
  166. },
  167. onerror: () => {
  168. reject(new Error(i18n("error_default")));
  169. },
  170. });
  171. });
  172. }
  173.  
  174. // 过滤文件名非法字符
  175. function filterFilename(filename) {
  176. return filename.replace(/\?|\*|\:|\"|\<|\>|\\|\/|\|/g, "");
  177. }
  178.  
  179. // 生成保存文件名
  180. function getSaveFilename() {
  181. const twitter = $(".UserInfoProgile a").html();
  182. const username = $(".UserInfoUserName a").html();
  183. const username2 = twitter ? twitter.substring(1) : username;
  184. const desc = $(".IllustItemDesc").text().substring(0, 20);
  185.  
  186. return filterFilename(
  187. `[${username2}][${website}][${authorId}_${workId}]${desc}`
  188. );
  189. }
  190.  
  191. // 生成保存图片文件名
  192. // 默认:序号.后缀名
  193. // 选中:网站_作品id_序号.后缀名
  194. function getSaveImageFilename(src, index) {
  195. let suffix = src.split(".").splice(-1);
  196. const mode = $saveFileMode.is(":checked");
  197.  
  198. if (mode) {
  199. return `${website}_${workId}_${index + 1}.${suffix}`;
  200. }
  201.  
  202. return `${index + 1}.${suffix}`;
  203. }
  204.  
  205. // 批量下载图片的默认方法
  206. function saveImages(list, saveAsZip, $status) {
  207. let zip = new JSZip();
  208. let finishehCount = 0;
  209. let folder = zip.folder(getSaveFilename());
  210.  
  211. $status = $status || $("<div></div>");
  212. $status.text(`0/${list.length}`);
  213.  
  214. let promises = list.map((src, index) => {
  215. return getBlob(src).then((blob) => {
  216. finishehCount++;
  217. $status.text(`${finishehCount}/${list.length}`);
  218.  
  219. if (saveAsZip) {
  220. folder.file(getSaveImageFilename(src, index), blob, { binary: true });
  221. } else {
  222. let suffix = src.split(".").splice(-1);
  223. saveAs(
  224. new Blob([blob]),
  225. `${getSaveFilename()}_${index + 1}.${suffix}`
  226. );
  227.  
  228. console.log("save image", index);
  229. }
  230. });
  231. });
  232.  
  233. Promise.all(promises).then(() => {
  234. if (saveAsZip) {
  235. zip
  236. .generateAsync({ type: "blob", base64: true })
  237. .then((content) => saveAs(content, getSaveFilename()));
  238. }
  239. });
  240. }
  241.  
  242. // 保存文字的默认方法
  243. function saveText(option) {
  244. let str = "";
  245.  
  246. if (option.title) {
  247. str += `${i18n("txt_title")}${option.title}\n`;
  248. }
  249. if (option.author) {
  250. str += `${i18n("txt_author")}${option.author}\n`;
  251. }
  252. if (option.twitter) {
  253. str += `${i18n("txt_twitter")}${option.twitter}\n`;
  254. }
  255. str += `${i18n("txt_link")}${window.location.href}\n`;
  256. str += `\n\n`;
  257. str += option.content;
  258.  
  259. saveAs(
  260. new Blob([str], { type: "text/plain;charset=UTF-8" }),
  261. getSaveFilename() + ".txt"
  262. );
  263. }
  264.  
  265. // 下载图片
  266. function downloadImages(saveAsZip, $status) {
  267. const promise = logined
  268. ? request({
  269. url: "/f/ShowIllustDetailF.jsp",
  270. type: "POST",
  271. data: {
  272. ID: authorId,
  273. TD: workId,
  274. AD: "-1",
  275. PAS: $password.val(),
  276. },
  277. dataType: "json",
  278. }).then((payload) => {
  279. if (!payload.html) {
  280. throw new Error(i18n("error_fetch"));
  281. }
  282. return payload.html;
  283. })
  284. : request({
  285. url: "/f/ShowAppendFileF.jsp",
  286. type: "POST",
  287. data: {
  288. UID: authorId,
  289. IID: workId,
  290. PAS: $password.val(),
  291. MD: 0,
  292. TWF: -1,
  293. },
  294. dataType: "json",
  295. }).then((payload) => {
  296. if (payload.result_num < 0) {
  297. throw new Error(payload.html);
  298. }
  299. return $(".IllustItemThumb").eq(0).prop("outerHTML") + payload.html;
  300. });
  301.  
  302. promise
  303. .then((html) => {
  304. let $page = $(html);
  305. let list = [];
  306.  
  307. $page
  308. .find(logined ? ".DetailIllustItemImage" : ".IllustItemThumbImg")
  309. .each(function () {
  310. const src = $(this).attr("src");
  311.  
  312. if (src && !/warning\.png/.test(src)) {
  313. list.push(window.location.protocol + src);
  314. }
  315. });
  316.  
  317. if (list.length) {
  318. saveImages(list, saveAsZip, $status);
  319. } else {
  320. throw new Error(i18n("error_noimage"));
  321. }
  322. })
  323. .catch((e) => {
  324. alert(e.message || i18n("error_default"));
  325. });
  326. }
  327.  
  328. // 打包图片
  329. function downloadImagesAsZip() {
  330. downloadImages(true, $(this).find(".status"));
  331. }
  332.  
  333. // 独立下载图片
  334. function downloadImagesSeperately() {
  335. downloadImages(false, $(this).find(".status"));
  336. }
  337.  
  338. // 下载文字
  339. function downloadText() {
  340. request({
  341. url: "/f/ShowAppendFileF.jsp",
  342. type: "POST",
  343. data: {
  344. UID: authorId,
  345. IID: workId,
  346. PAS: $password.val(),
  347. MD: 0,
  348. TWF: -1,
  349. },
  350. dataType: "json",
  351. })
  352. .then((payload) => {
  353. if (payload.result_num < 0) {
  354. throw new Error(payload.html);
  355. }
  356.  
  357. let $page = $(payload.html);
  358.  
  359. saveText({
  360. title: $(".IllustItemDesc").text(),
  361. author: $(".UserInfoUserName a").html(),
  362. twitter: $(".UserInfoProgile a").prop("href"),
  363. content: $page.find(".NovelSection").text(),
  364. });
  365. })
  366. .catch((e) => {
  367. alert(e.message || i18n("error_default"));
  368. });
  369. }
  370. });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址