您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Rips all images and videos from Kemono and downloads them as a zip
// ==UserScript== // @name Kemono File Ripper // @namespace Tampermonkey/Violentmonkey Scripts // @version 1.0 // @description Rips all images and videos from Kemono and downloads them as a zip // @author Ementhia // @icon https://raw.githubusercontent.com/Ementhia/kemono-file-ripper/main/kr-png.png // @match https://kemono.party/* // @match https://kemono.cr/* // @copyright 2025+, Ementhia (https://github.com/Ementhia/kemono-file-ripper) // @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt // @require https://code.jquery.com/jquery-3.6.0.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // @grant none // @description Rips all the static images and videos and files from Kemono and downloads them as a zip // ==/UserScript== /* global JSZip, saveAs, jQuery */ (function waitForLibs() { if (typeof JSZip === 'undefined' || typeof saveAs === 'undefined' || typeof jQuery === 'undefined') { setTimeout(waitForLibs, 100); return; } (function($) { 'use strict'; function sanitizeString(str) { return str.replace(/[\\/:*?"<>|]/g, '_').trim(); } function showLoadingBar(percent) { let bar = $('#kemono-download-loading'); if (!bar.length) { bar = $('<div id="kemono-download-loading">') .css({ position: 'fixed', top: '0', left: '0', width: '100%', height: '5px', background: '#eee', zIndex: 99999 }) .append($('<div id="kemono-download-progress">').css({ height: '100%', width: '0%', background: '#ff9500' })); $('body').append(bar); } $('#kemono-download-progress').css('width', percent + '%'); } function hideLoadingBar() { $('#kemono-download-loading').remove(); } async function downloadKemonoMedia() { const zip = new JSZip(); let mediaLinks = []; $('.post__thumbnail a.fileThumb').each(function() { const url = $(this).attr('href'); const filename = $(this).attr('download') || url.split('/').pop().split('?')[0]; mediaLinks.push({url, filename}); }); $('.post__video').each(function() { const url = $(this).attr('src'); if (url) { let filename = url.split('/').pop().split('?')[0]; const att = $('.post__attachment-link[href*="' + filename + '"]'); if (att.length) filename = att.attr('download') || filename; mediaLinks.push({url, filename}); } }); $('.post__attachment-link').each(function() { const url = $(this).attr('href'); const filename = $(this).attr('download') || url.split('/').pop().split('?')[0]; if (!mediaLinks.some(m => m.filename === filename)) { mediaLinks.push({url, filename}); } }); // filters duplicates (feel free to remove it if it conflicts with the file you want) mediaLinks = mediaLinks.filter((media, idx, arr) => arr.findIndex(m => m.filename === media.filename) === idx ); const total = mediaLinks.length; let completed = 0; const title = sanitizeString($('.post__title span').first().text() || 'Kemono_Post'); const creator = sanitizeString($('.post__user-name').first().text() || 'Unknown_Creator'); const zipName = `${title} by ${creator}.zip`; if (total === 0) { alert('No images or videos found!'); return; } showLoadingBar(0); let saveHandler = saveAs; let usePicker = false; if ('showSaveFilePicker' in window) { usePicker = true; } const fetchPromises = mediaLinks.map(media => fetch(media.url) .then(response => { if (!response.ok) throw new Error('Network error'); return response.blob(); }) .then(blob => { zip.file(media.filename, blob); }) .catch(() => { alert(`Failed to download ${media.filename}`); }) .finally(() => { completed++; showLoadingBar(Math.round((completed / total) * 100)); }) ); await Promise.all(fetchPromises); zip.generateAsync({type:"blob"}).then(async function(content) { hideLoadingBar(); if (usePicker) { try { const handle = await window.showSaveFilePicker({ suggestedName: zipName, types: [{ description: 'Zip Files', accept: {'application/zip': ['.zip']} }] }); const writable = await handle.createWritable(); await writable.write(content); await writable.close(); } catch (e) { saveHandler(content, zipName); } } else { saveHandler(content, zipName); } }); } function insertDownloadButton() { if ($('#kemono-download-btn').length) return; const actions = $('.post__actions'); if (!actions.length) return; const btn = $('<button id="kemono-download-btn">') .text('Download Media') .css({ marginLeft: '10px', padding: '5px 10px', background: '#ff9500', color: '#fff', border: 'none', borderRadius: '5px', cursor: 'pointer' }) .click(function() { $(this).prop('disabled', true); downloadKemonoMedia().finally(() => { $(this).prop('disabled', false); }); }); actions.append(btn); } $(document).ready(function() { insertDownloadButton(); }); const observer = new MutationObserver(() => { insertDownloadButton(); }); observer.observe(document.body, {childList: true, subtree: true}); })(jQuery); })();