您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Embeds a "Download" button before each file element and starts downloading and saving it to your computer, can use constant to change replace kemono image server if standart not work.
当前为
// ==UserScript== // @name Kemono FIX+Download // @namespace GPT // @version 1.0.9 // @description Embeds a "Download" button before each file element and starts downloading and saving it to your computer, can use constant to change replace kemono image server if standart not work. // @description:ru Встраивает кнопку Download перед каждым элементом с файлами и запускает скачивание с сохранением на компьютер, так же имеет константу для смены сервера изображений если стандартный не работает. // @author Wizzergod // @icon https://www.google.com/s2/favicons?sz=64&domain=kemono.su // @homepageURL https://greasyfork.org/ru/users/712283-wizzergod // @grant GM_download // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM_addStyle // @grant GM_registerMenuCommand // @match *://kemono.su/* // @match *://kemono.party/* // @run-at document-idle // @license MIT // ==/UserScript== (function() { 'use strict'; // Константы для включения/выключения функций let ENABLE_IMAGE_REPLACEMENT = 0; // 1 - включено, 0 - выключено let ENABLE_URL_REPLACEMENT = 0; // 1 - включено, 0 - выключено const ENABLE_MUTATION_OBSERVER = 1; // 1 - включено, 0 - выключено // Дебаунс функция для оптимизации MutationObserver let debounceTimeout; const debounce = (callback, delay = 300) => { clearTimeout(debounceTimeout); debounceTimeout = setTimeout(callback, delay); }; // Добавляем стиль для кнопки Download и контейнера GM_addStyle(` .download-container { margin-top: 2px; padding: 5px; background-position: center; background-repeat: no-repeat; text-align: center; border: none; width: 100%; display: inline-block; transition: transform 0.3s ease, background-color 0.3s ease; justify-content: center; font-size: 30px; opacity: 0.9; transform: translate(0%, -110%); } .download-button { padding: 1px 7%; background-color: #4CAF50; background-position: center; background-repeat: no-repeat; color: white; border: none; cursor: pointer; font-size: 14px; display: inline-block; min-width: 100px; width: 98%; text-align: center; border-radius: 5px; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.57), -3px -3px rgba(0, 0, 0, .1) inset; transition: transform 0.3s ease, background-color 0.3s ease; justify-content: center; font-size: 20px; opacity: 0.8; } .download-button:hover { background-color: #4C9CAF; } .post__thumbnail { max-width: 10%; min-width: 20%; height: auto; cursor: pointer; padding: 5px; border: 5px; display: inline-flex; flex-direction: column; flex-wrap: wrap; align-content: center; justify-content: space-between; align-items: center; margin-top: -3.5%; } .post__thumbnail img:hover { transform: scale(1.1); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); transition: transform 0.3s ease, background-color 0.3s ease; } .post__thumbnail img { cursor: pointer; border-radius: 5px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); transition: transform 0.3s ease, background-color 0.3s ease; } .post-card__image-container { max-width: 100%; height: auto; } .post__files { display: flex; padding: 5px; border: 5px; max-width: 100%; min-width: 70%; flex-direction: row; flex-wrap: wrap; align-content: center; justify-content: center; align-items: center; } [data-testid='tracklist-row'] .newButtonClass { position: absolute; top: 50%; right: calc(100% + 10px); transform: translateY(-50%); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); } `); // Функция для асинхронной загрузки изображения const downloadImage = async (url) => { try { const response = await fetch(url); const blob = await response.blob(); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = url.split('/').pop().split('?')[0]; link.click(); URL.revokeObjectURL(link.href); } catch (error) { console.error('Ошибка загрузки файла:', error); } }; // Функция для замены атрибутов src и data-src изображений function replaceImageSources() { const imageLinks = document.querySelectorAll('.post__files .post__thumbnail a.fileThumb.image-link'); imageLinks.forEach((link) => { const imgElement = link.querySelector('img'); if (imgElement) { const originalSrc = link.href; const dataSrc = imgElement.getAttribute('data-src'); if (ENABLE_IMAGE_REPLACEMENT === 1) { imgElement.setAttribute('src', originalSrc); imgElement.setAttribute('data-src', originalSrc); } } }); } // Функция для замены URL для миниатюр function replaceThumbnailURLs() { const imageLinks = document.querySelectorAll('.post__files .post__thumbnail a.fileThumb.image-link, .post-card__image-container'); imageLinks.forEach((link) => { const imgElement = link.querySelector('img'); if (imgElement) { let src = imgElement.getAttribute('src'); let dataSrc = imgElement.getAttribute('data-src'); if (ENABLE_URL_REPLACEMENT === 1) { if (src && src.includes('img.kemono.su/thumbnail/data/')) { src = src.replace('img.kemono.su/thumbnail/data/', 'n3.kemono.su/data/'); imgElement.setAttribute('src', src); } if (dataSrc && dataSrc.includes('img.kemono.su/thumbnail/data/')) { dataSrc = dataSrc.replace('img.kemono.su/thumbnail/data/', 'n3.kemono.su/data/'); imgElement.setAttribute('data-src', dataSrc); } } } }); } // Функция для создания кнопки Download function createDownloadButton(onClickCallback) { const button = document.createElement('button'); button.textContent = 'Download'; button.classList.add('download-button'); button.addEventListener('click', onClickCallback); return button; } // Функция для добавления кнопок Download function addDownloadButtons() { try { const fileElements = document.querySelectorAll('.post__files .post__thumbnail a.fileThumb.image-link'); if (!fileElements.length) throw new Error('Элементы не найдены'); fileElements.forEach((fileElement) => { if (fileElement.parentElement.querySelector('.download-container')) { return; } const downloadContainer = document.createElement('div'); downloadContainer.classList.add('download-container'); const downloadButton = createDownloadButton((event) => { event.preventDefault(); const fileUrl = fileElement.href; downloadImage(fileUrl); }); downloadContainer.appendChild(downloadButton); fileElement.parentElement.appendChild(downloadContainer); }); } catch (error) { console.error('Ошибка при добавлении кнопок загрузки:', error); } } // Двойная проверка добавления кнопок с таймаутом function checkAndAddButtons() { addDownloadButtons(); setTimeout(() => { addDownloadButtons(); }, 500); } // Регистрируем команды меню для включения/выключения функций GM_registerMenuCommand('Включить замену изображений', () => { ENABLE_IMAGE_REPLACEMENT = 1; replaceImageSources(); }, 'r'); GM_registerMenuCommand('Отключить замену изображений', () => { ENABLE_IMAGE_REPLACEMENT = 0; }, 'r'); // Вызываем проверку и добавление кнопок checkAndAddButtons(); // Используем MutationObserver с дебаунсом для отслеживания изменений в DOM if (ENABLE_MUTATION_OBSERVER) { const observer = new MutationObserver(() => { debounce(() => { if (ENABLE_IMAGE_REPLACEMENT === 1) replaceImageSources(); if (ENABLE_URL_REPLACEMENT === 1) replaceThumbnailURLs(); }); }); observer.observe(document.body, { childList: true, subtree: true }); } // Если включены замены, выполняем их сразу после загрузки if (ENABLE_IMAGE_REPLACEMENT === 1) { replaceImageSources(); } if (ENABLE_URL_REPLACEMENT === 1) { replaceThumbnailURLs(); } })();