您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Добавляет кнопку скачивания рядом с каждой главой на сайте weloma.art
// ==UserScript== // @name Weloma Download // @name:en Weloma Download // @namespace https://github.com/xxrxtnxxov // @version 2.4.1 // @description Добавляет кнопку скачивания рядом с каждой главой на сайте weloma.art // @description:en Allows you to download manga chapters in ZIP format from weloma.art. // @author antiQuarianN // @match https://weloma.art/* // @grant GM_xmlhttpRequest // @run-at document-start // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js // @license MIT // ==/UserScript== (function() { 'use strict'; Object.defineProperty(window, 'location', { set: () => console.warn('Попытка перенаправления заблокирована!') }); window.open = () => console.warn('Попытка открытия нового окна заблокирована!'); const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.tagName === 'SCRIPT' && /disable-devtool/.test(node.src)) { node.remove(); console.warn('MEH'); } }); }); }); observer.observe(document.documentElement, { childList: true, subtree: true }); window.addEventListener('contextmenu', e => e.stopPropagation(), true); window.addEventListener('keydown', e => e.stopPropagation(), true); const originalSetInterval = window.setInterval; window.setInterval = function(callback, time) { if (time < 1000) return originalSetInterval(callback, 1000); return originalSetInterval(callback, time); }; function addDownloadButton() { const chapters = document.querySelectorAll('.list-chapters.at-series a'); chapters.forEach(chapterLink => { const liElement = chapterLink.querySelector('li'); const chapterNameElement = liElement.querySelector('.chapter-name.text-truncate'); const chapterTimeElement = liElement.querySelector('.chapter-time'); const mangaTitleElement = document.querySelector('h3[data-heading-tag="H3"]'); const mangaTitle = mangaTitleElement ? mangaTitleElement.textContent.trim() : "Unknown Manga"; const rowContainer = document.createElement('div'); rowContainer.style.display = 'flex'; rowContainer.style.alignItems = 'center'; rowContainer.style.width = '100%'; const downloadButton = document.createElement('button'); downloadButton.style.border = 'none'; downloadButton.style.background = 'none'; downloadButton.style.cursor = 'pointer'; downloadButton.style.marginRight = '10px'; downloadButton.innerHTML = '<img src="https://raw.githubusercontent.com/xxrxtnxxov/weloma-download/refs/heads/main/dload.png" width="16" height="16" />'; const progressSpan = document.createElement('span'); progressSpan.style.marginRight = '10px'; progressSpan.style.fontSize = '12px'; progressSpan.style.color = '#aaa'; progressSpan.style.display = 'none'; const loadingImage = document.createElement('img'); loadingImage.src = 'https://raw.githubusercontent.com/xxrxtnxxov/weloma-download/refs/heads/main/load.gif'; loadingImage.style.width = '16px'; loadingImage.style.height = '16px'; loadingImage.style.display = 'none'; const textContainer = document.createElement('div'); textContainer.style.display = 'flex'; textContainer.style.flexDirection = 'column'; textContainer.style.flex = '1'; textContainer.style.marginLeft = '5px'; textContainer.appendChild(chapterNameElement); textContainer.appendChild(chapterTimeElement); rowContainer.appendChild(downloadButton); rowContainer.appendChild(progressSpan); rowContainer.appendChild(loadingImage); rowContainer.appendChild(textContainer); liElement.innerHTML = ''; liElement.appendChild(rowContainer); downloadButton.addEventListener('click', async (event) => { event.preventDefault(); const chapterUrl = chapterLink.href; const chapterName = chapterNameElement.textContent.trim(); const finalFileName = `${mangaTitle} - ${chapterName}`.replace(/[<>:"/\\|?*]/g, ''); downloadButton.style.display = 'none'; progressSpan.style.display = 'inline'; progressSpan.textContent = '0/?'; await downloadChapter(chapterUrl, finalFileName, progressSpan, downloadButton, loadingImage); }); }); } async function downloadChapter(url, finalFileName, progressSpan, downloadButton, loadingImage) { const response = await fetch(url); const htmlText = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(htmlText, 'text/html'); const images = doc.querySelectorAll('.chapter-content img.chapter-img'); const imageUrls = Array.from(images).map(img => img.getAttribute('data-src') || img.getAttribute('data-srcset') || (img.getAttribute('data-img') && atob(img.getAttribute('data-img')))).filter(Boolean); progressSpan.textContent = `0/${imageUrls.length}`; const zip = new JSZip(); const chapterFolder = zip.folder(finalFileName); const imageBlobs = await loadImages(imageUrls, progressSpan, loadingImage); imageBlobs.forEach((blob, index) => { const fileName = `00${index + 1}`.slice(-3) + '.jpg'; chapterFolder.file(fileName, blob); }); progressSpan.style.display = 'none'; loadingImage.style.display = 'inline'; const content = await zip.generateAsync({type: 'blob'}); const link = document.createElement('a'); link.href = URL.createObjectURL(content); link.download = `${finalFileName}.zip`; document.body.appendChild(link); link.click(); document.body.removeChild(link); setTimeout(() => { loadingImage.style.display = 'none'; downloadButton.style.display = 'inline'; }, 1000); } function loadImages(imageUrls, progressSpan, loadingImage) { return new Promise((resolve, reject) => { const imageBlobs = []; let loadedCount = 0; imageUrls.forEach((imageUrl, index) => { GM_xmlhttpRequest({ method: 'GET', url: imageUrl, responseType: 'blob', onload: (response) => { if (response.status >= 200 && response.status < 300) { imageBlobs[index] = response.response; loadedCount++; updateProgress(loadedCount, imageUrls.length, progressSpan, loadingImage); if (loadedCount === imageUrls.length) resolve(imageBlobs); } else reject(new Error(`Ошибка загрузки изображения: ${response.status}`)); }, onerror: (error) => reject(new Error(`Ошибка загрузки: ${error}`)) }); }); }); } function updateProgress(loadedCount, total, progressSpan, loadingImage) { progressSpan.textContent = `${loadedCount}/${total}`; console.log(`Скачивание: ${loadedCount}/${total}`); if (loadedCount === total) { progressSpan.style.display = 'none'; loadingImage.style.display = 'inline'; } } window.addEventListener('load', addDownloadButton); })();