Kemono FIX+Download

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.

Od 04.12.2024.. Pogledajte najnovija verzija.

// ==UserScript==
// @name         Kemono FIX+Download
// @namespace    GPT
// @version      1.0.8
// @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
// @match        *://kemono.su/*
// @run-at       document-idle
// @license MIT
// ==/UserScript==


(function() {
    'use strict';

    // Константа для включения/выключения замены изображений
    const ENABLE_IMAGE_REPLACEMENT = 0;  // 1 - включено, 0 - выключено

    // Константа для включения/выключения замены URL для миниатюр
    const ENABLE_URL_REPLACEMENT = 0;   // 1 - включено, 0 - выключено

    // Добавляем стиль для кнопки Download и контейнера
    GM_addStyle(`
        .download-container {
            margin-top: 10px;
            padding: 10px;
            border-radius: 5px;
            background-color: #0d0d0d52;
            background-position: center;
            background-repeat: no-repeat;
            text-align: center;
            border: none;
            width: 100%;
            min-width: 150px;
            display: inline-block;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            transition: transform 0.3s ease, background-color 0.3s ease;
            justify-content: center;
            font-size: 30px;
            opacity: 0.9;
        }

        .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: 100%;
            text-align: center;
            border-radius: 5px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            transition: transform 0.3s ease, background-color 0.3s ease;
            justify-content: center;
            font-size: 20px;
            opacity: 0.8;
        }

        .download-button:hover {
            background-color: #45a049;
            transform: scale(1.1);
        }

.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;
}
.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);
    }
    `);

    // Функция для скачивания изображения с использованием GM_xmlhttpRequest
    const downloadImage = (url) => {
        const originalFileName = url.split('/').pop().split('?')[0];
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            responseType: 'blob',
            onload: (response) => {
                const link = document.createElement('a');
                link.href = URL.createObjectURL(response.response);
                link.download = originalFileName;
                link.click();
                URL.revokeObjectURL(link.href);
            },
            onerror: (error) => {
                alert('Error fetching image: ' + 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');

                // Если ENABLE_IMAGE_REPLACEMENT включен, заменяем src и 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');

                // Если ENABLE_URL_REPLACEMENT включен, заменяем URL
                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 addDownloadButtons() {
        // Ищем все элементы с ссылками на файлы
        const fileElements = document.querySelectorAll('.post__files .post__thumbnail a.fileThumb.image-link');

        fileElements.forEach((fileElement) => {
            // Проверяем, если кнопка уже добавлена, пропускаем этот элемент
            if (fileElement.parentElement.querySelector('.download-container')) {
                return;
            }

            // Создаем контейнер для кнопки Download
            const downloadContainer = document.createElement('div');
            downloadContainer.classList.add('download-container');

            // Создаем кнопку Download
            const downloadButton = document.createElement('button');
            downloadButton.textContent = 'Download';
            downloadButton.classList.add('download-button');

            // Вставляем кнопку в контейнер
            downloadContainer.appendChild(downloadButton);

            // Вставляем контейнер под элементом с файлом
            fileElement.parentElement.appendChild(downloadContainer);

            // Слушаем клик по кнопке для скачивания файла
            downloadButton.addEventListener('click', (event) => {
                event.preventDefault(); // Предотвращаем стандартное действие ссылки
                const fileUrl = fileElement.href;

                // Вызываем функцию скачивания
                downloadImage(fileUrl);
            });
        });
    }

    // Двойная проверка добавления кнопок с таймаутом
    function checkAndAddButtons() {
        addDownloadButtons(); // Первая попытка добавить кнопки

        // Проверим через 500ms, если кнопки еще не добавлены
        setTimeout(() => {
            addDownloadButtons(); // Вторая попытка добавить кнопки, если они не появились
        }, 500);
    }

    // Вызываем проверку и добавление кнопок
    checkAndAddButtons();

    // Используем MutationObserver для отслеживания изменений в DOM
    if (ENABLE_IMAGE_REPLACEMENT === 1 || ENABLE_URL_REPLACEMENT === 1) {
        const observer = new MutationObserver(() => {
            if (ENABLE_IMAGE_REPLACEMENT === 1) replaceImageSources();
            if (ENABLE_URL_REPLACEMENT === 1) replaceThumbnailURLs();
        });

        // Начинаем отслеживание изменений в DOM
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // Если включены замены, выполняем их сразу после загрузки
    if (ENABLE_IMAGE_REPLACEMENT === 1) {
        replaceImageSources();
    }

    if (ENABLE_URL_REPLACEMENT === 1) {
        replaceThumbnailURLs();
    }

})();