您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
一键下载图片 (压缩下载/单图下载) , 页面数据创建 json 下载 , 一键开启当前所有帖子
当前为
// ==UserScript== // @name Kemono 下載工具 // @name:zh-TW Kemono 下載工具 // @name:zh-CN Kemono 下载工具 // @name:ja Kemono ダウンロードツール // @name:en Kemono DownloadTool // @version 0.0.6 // @author HentiSaru // @description 一鍵下載圖片 (壓縮下載/單圖下載) , 頁面數據創建 json 下載 , 一鍵開啟當前所有帖子 // @description:zh-TW 一鍵下載圖片 (壓縮下載/單圖下載) , 頁面數據創建 json 下載 , 一鍵開啟當前所有帖子 // @description:zh-CN 一键下载图片 (压缩下载/单图下载) , 页面数据创建 json 下载 , 一键开启当前所有帖子 // @description:ja 画像をワンクリックでダウンロード(圧縮ダウンロード/単一画像ダウンロード)、ページデータを作成してjsonでダウンロード、現在のすべての投稿をワンクリックで開く // @description:en One-click download of images (compressed download/single image download), create page data for json download, one-click open all current posts // @match *://kemono.su/* // @match *://*.kemono.su/* // @match *://kemono.party/* // @match *://*.kemono.party/* // @icon https://cdn-icons-png.flaticon.com/512/2381/2381981.png // @license MIT // @namespace https://greasyfork.org/users/989635 // @run-at document-end // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_download // @grant GM_addElement // @grant GM_notification // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // ==/UserScript== const regex = /^https:\/\/[^/]+/, pattern = /^(https?:\/\/)?(www\.)?kemono\..+\/.+\/user\/.+\/post\/.+$/, language = display_language(navigator.language); var CompressMode = GM_getValue("壓縮下載", []), parser = new DOMParser(), url = window.location.href.match(regex), dict = {}, ModeDisplay, Pages=0; let observer = new MutationObserver((mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.type === "childList") { if (pattern.test(window.location.href) && !document.querySelector("#DBExist")) {ButtonCreation()} } } }); (function() { if (pattern.test(window.location.href)) { observer.observe(document.body, {childList: true, subtree: true}); } GM_registerMenuCommand(language[0], function() {DownloadModeSwitch()}, "C") GM_registerMenuCommand(language[1], function() { const section = document.querySelector("section"); if (section) { GetPageData(section); } }, "J") GM_registerMenuCommand(language[2], function() {OpenData()}, "O") })(); async function DownloadModeSwitch() { if (GM_getValue("壓縮下載", [])){ GM_setValue("壓縮下載", false); GM_notification({ title: language[3], text: language[6], timeout: 2500 }); } else { GM_setValue("壓縮下載", true); GM_notification({ title: language[3], text: language[4], timeout: 2500 }); } location.reload(); } async function ButtonCreation() { GM_addStyle(` .File_Span { padding: 1rem; font-size: 20% !important; } .Download_Button { color: hsl(0, 0%, 45%); padding: 6px; border-radius: 8px; border: 2px solid rgba(59, 62, 68, 0.7); background-color: rgba(29, 31, 32, 0.8); font-family: Arial, sans-serif; } .Download_Button:hover { color: hsl(0, 0%, 95%); background-color: hsl(0, 0%, 45%); font-family: Arial, sans-serif; } .Download_Button:disabled { color: hsl(0, 0%, 95%); background-color: hsl(0, 0%, 45%); } `); let download_button; try { const Files = document.querySelectorAll("div.post__body h2") const spanElement = GM_addElement(Files[Files.length - 1], "span", {class: "File_Span"}); download_button = GM_addElement(spanElement, "button", { class: "Download_Button", id: "DBExist" }); if (CompressMode) { ModeDisplay = language[5]; } else { ModeDisplay = language[7]; } download_button.textContent = ModeDisplay; download_button.addEventListener("click", function() { DownloadTrigger(download_button); }); } catch { download_button.textContent = language[9]; download_button.disabled = true; } } function IllegalFilter(Name) { return Name.replace(/[\/\?<>\\:\*\|":]/g, ''); } function Conversion(Name) { return Name.replace(/[\[\]]/g, ''); } function GetExtension(link) { const match = link.match(/\.([^.]+)$/); if (match) {return match[1].toLowerCase()} return "png"; } async function DownloadTrigger(button) { let interval = setInterval(function() { let imgdata = document.querySelectorAll("a.fileThumb.image-link"); let title = document.querySelector("h1.post__title").textContent.trim(); let user = document.querySelector("a.post__user-name").textContent.trim(); if (imgdata && title && user) { button.textContent = language[8]; button.disabled = true; if (CompressMode) { ZipDownload(`[${user}] ${title}`, imgdata, button); } else { ImageDownload(`[${user}] ${title}`, imgdata, button) } clearInterval(interval); } }, 500); } async function ZipDownload(Folder, ImgData, Button) { const zip = new JSZip(), Data = Object.values(ImgData), File = Conversion(Folder), Total = Data.length, name = IllegalFilter(Folder.split(" ")[1]); let pool = [], poolSize = 5, progress = 1, mantissa, link, extension; function createPromise(i) { link = Data[i].href.split("?f=")[0]; extension = GetExtension(link); return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: link, responseType: "blob", onload: response => { if (response.status === 200 && response.response instanceof Blob && response.response.size > 0) { mantissa = progress.toString().padStart(3, '0'); zip.file(`${File}/${name}_${mantissa}.${extension}`, response.response); Button.textContent = `${language[10]} [${progress}/${Total}]`; progress++; } else { i--; } resolve(); } }); }); } for (let i = 0; i < Total; i++) { let promise = createPromise(i); pool.push(promise); if (pool.length >= poolSize) { await Promise.all(pool); pool = []; } } if (pool.length > 0) {await Promise.all(pool)} Compression(); function Compression() { Button.textContent = language[11]; zip.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { level: 5 // 壓縮級別,範圍從 0(無壓縮)到 9(最大壓縮) } }).then(zip => { Button.textContent = language[13]; saveAs(zip, `${Folder}.zip`); setTimeout(() => {Button.textContent = ModeDisplay}, 4000); Button.disabled = false; }).catch(result => { Button.textContent = language[12]; setTimeout(() => {Button.textContent = ModeDisplay}, 6000); Button.disabled = false; }); } } async function ImageDownload(Folder, ImgData, Button) { const name = IllegalFilter(Folder.split(" ")[1]), Data = Object.values(ImgData), Total = Data.length; let progress = 1, link, extension; for (let i = 0; i < Total; i++) { link = Data[i].href.split("?f=")[0]; extension = GetExtension(link); GM_download({ url: link, name: `${name}_${(progress+i).toString().padStart(3, '0')}.${extension}`, ontimeout: 10000, onload: () => { Button.textContent = `${language[10]} [${progress}/${Total}]`; progress++; }, onerror: () => { i--; } }); } Button.textContent = language[13]; setTimeout(() => {Button.textContent = ModeDisplay}, 4000); Button.disabled = false; } async function GetPageData(section) { const menu = section.querySelector("a.pagination-button-after-current"); const item = section.querySelectorAll(".card-list__items article"); let title, link; item.forEach(card => { title = card.querySelector(".post-card__header").textContent.trim() link = card.querySelector("a").href dict[`${link}`] = title; }) try { // 當沒有下一頁連結就會發生例外 let NextPage = menu.href; if (NextPage) { Pages++; GM_notification({ title: language[14], text: `${language[15]} : ${Pages}`, image: "https://cdn-icons-png.flaticon.com/512/2582/2582087.png", timeout: 800 }); GM_xmlhttpRequest({ method: "GET", url: NextPage, nocache: false, ontimeout: 8000, onload: response => { const DOM = parser.parseFromString(response.responseText, "text/html"); GetPageData(DOM.querySelector("section")); } }); } } catch { try { // 進行簡單排序 Object.keys(dict).sort(); const author = document.querySelector('span[itemprop="name"]').textContent; const json = document.createElement("a"); json.href = "data:application/json;charset=utf-8," + encodeURIComponent(JSON.stringify(dict, null, 4)); json.download = `${author}.json`; json.click(); json.remove(); GM_notification({ title: language[16], text: language[17], image: "https://cdn-icons-png.flaticon.com/512/2582/2582087.png", timeout: 2000 }); } catch { alert(language[18]); } } } function OpenData() { try { let content = document.querySelector('.card-list__items').querySelectorAll('article.post-card'); content.forEach(function(content) { let link = content.querySelector('a').getAttribute('href'); setTimeout(() => { window.open("https://kemono.party" + link , "_blank"); }, 300); }); } catch { alert(language[19]); } } function display_language(language) { let display = { "zh-TW": [ "🔁 切換下載模式", "📑 獲取所有帖子 Json 數據", "📃 開啟當前頁面所有帖子", "模式切換", "壓縮下載模式", "壓縮下載", "單圖下載模式", "單圖下載", "開始下載", "無法下載", "下載進度", "壓縮封裝中[請稍後]", "壓縮封裝失敗", "下載完成", "數據處理中", "當前處理頁數", "數據處理完成", "Json 數據下載", "錯誤的請求頁面", "錯誤的開啟頁面", ], "zh-CN": [ "🔁 切换下载模式", "📑 获取所有帖子 Json 数据", "📃 打开当前页面所有帖子", "模式切换", "压缩下载模式", "压缩下载", "单图下载模式", "单图下载", "开始下载", "无法下载", "下载进度", "压缩封装中[请稍后]", "压缩封装失败", "下载完成", "数据处理中", "当前处理页数", "数据处理完成", "Json 数据下载", "错误的请求页面", "错误的打开页面" ], "ja": [ '🔁 ダウンロードモードの切り替え', '📑 すべての投稿のJsonデータを取得する', '📃 現在のページのすべての投稿を開く', 'モード切り替え', '圧縮ダウンロードモード', '圧縮ダウンロード', 'シングル画像ダウンロードモード', 'シングル画像ダウンロード', 'ダウンロードを開始する', 'ダウンロードできません', 'ダウンロードの進行状況', '圧縮パッケージング中[しばらくお待ちください]', '圧縮パッケージングに失敗しました', 'ダウンロードが完了しました', 'データ処理中', '現在の処理ページ数', 'データ処理が完了しました', 'Jsonデータのダウンロード', '間違ったリクエストページ', '間違ったページを開く' ], "en": [ '🔁 Switch download mode', '📑 Get all post Json data', '📃 Open all posts on the current page', 'Mode switch', 'Compressed download mode', 'Compressed download', 'Single image download mode', 'Single image download', 'Start downloading', 'Unable to download', 'Download progress', 'Compressing packaging [please wait]', 'Compression packaging failed', 'Download completed', 'Data processing', 'Current processing page number', 'Data processing completed', 'Json data download', 'Wrong request page', 'Wrong page to open' ] }; return display[language] || display["en"]; }