시리즈, 작가명, 파일이름 수정

This commit is contained in:
user01
2026-07-01 16:26:03 +09:00
parent beb8b793dd
commit 31f591605d
+44 -32
View File
@@ -1,11 +1,11 @@
// ==UserScript== // ==UserScript==
// @name sbxh6Downloader // @name sbxh6Downloader
// @namespace https://github.com/crossSiteKikyo/tokiDownloader // @namespace https://github.com/crossSiteKikyo/tokiDownloader
// @version 0.2.0 // @version 0.1.3
// @description sbxh6.com (뉴토끼) 만화 다운로더 // @description sbxh6.com (뉴토끼) 만화 다운로더
// @author hehaho // @author hehaho
// @match https://sbxh*.com/manhwa/* // @match https://sbxh9.com/manhwa/*
// @match https://sbxh*.com/manhwa/*/* // @match https://sbxh9.com/manhwa/*/*
// @icon https://github.com/user-attachments/assets/99f5bb36-4ef8-40cc-8ae5-e3bf1c7952ad // @icon https://github.com/user-attachments/assets/99f5bb36-4ef8-40cc-8ae5-e3bf1c7952ad
// @grant GM_registerMenuCommand // @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand // @grant GM_unregisterMenuCommand
@@ -16,7 +16,6 @@
// @run-at document-start // @run-at document-start
// @license MIT // @license MIT
// ==/UserScript== // ==/UserScript==
(function () { (function () {
'use strict'; 'use strict';
@@ -243,21 +242,24 @@
} }
function getSeriesName() { function getSeriesName() {
let seriesName = '제목없음';
let writer = '';
const metaEl = document.querySelector('div.vw-bot-meta strong'); const metaEl = document.querySelector('div.vw-bot-meta strong');
if (metaEl) return metaEl.textContent.trim(); let rawTitle = '';
const titleEl = document.querySelector('h1');
if (titleEl && !titleEl.closest('.hero-v2')) {
return titleEl.textContent.trim();
}
if (metaEl) {
rawTitle = metaEl.textContent.trim();
} else {
const ogTitle = document.querySelector('meta[property="og:title"]'); const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) { rawTitle = (ogTitle ? ogTitle.getAttribute('content') || '' : document.title).split('|')[0].trim();
const content = ogTitle.getAttribute('content') || '';
return content.split('|')[0].trim() || content;
} }
return document.title.split('|')[0].trim() || '제목없음'; const parts = rawTitle.split(/\s-\s/);
seriesName = parts[0] || rawTitle;
if (parts.length > 1) writer = parts[1];
return { seriesName, writer };
} }
function getEpisodeTitle() { function getEpisodeTitle() {
@@ -487,9 +489,9 @@
return urls; return urls;
}; };
async function downloadImagesAsZip(imageUrls, seriesName, episodeTitle, episodeNumber) { async function downloadImagesAsZip(imageUrls, seriesInfo, episodeTitle, episodeNumber) {
const zip = new JSZip(); const zip = new JSZip();
const folderName = `${seriesName}_${episodeNumber}`.replace(/[<>:"/\\|?*]/g, '_').slice(0, 200); const folderName = `${seriesInfo.seriesName}_${episodeNumber}`.replace(/[<>:"/\\|?*]/g, '_').slice(0, 200);
const folder = zip.folder(folderName); const folder = zip.folder(folderName);
const fetchImage = (url) => new Promise((resolve, reject) => { const fetchImage = (url) => new Promise((resolve, reject) => {
@@ -523,12 +525,17 @@
} }
const comicInfoXml = generateComicInfo( const comicInfoXml = generateComicInfo(
episodeTitle, seriesName, '', episodeNumber, total, total episodeTitle,
seriesInfo.seriesName,
seriesInfo.writer,
episodeNumber,
total,
total
); );
folder.file('ComicInfo.xml', comicInfoXml); folder.file('ComicInfo.xml', comicInfoXml);
const zipBlob = await zip.generateAsync({ type: 'blob' }); const zipBlob = await zip.generateAsync({ type: 'blob' });
const safeName = `${seriesName}_${episodeNumber}.zip`.replace(/[<>:"/\\|?*]/g, '_'); const safeName = `${seriesInfo.seriesName}_${episodeNumber}.zip`.replace(/[<>:"/\\|?*]/g, '_');
downloadZip(zipBlob, safeName); downloadZip(zipBlob, safeName);
log(`downloadImagesAsZip: ${safeName} 저장 완료 (${total}개 중 ${folder.files ? Object.keys(folder.files).length - 1 : '?'}개 성공)`); log(`downloadImagesAsZip: ${safeName} 저장 완료 (${total}개 중 ${folder.files ? Object.keys(folder.files).length - 1 : '?'}개 성공)`);
} }
@@ -543,11 +550,14 @@
return; return;
} }
const seriesName = getSeriesName(); const seriesInfo = getSeriesName();
const episodeTitle = getEpisodeTitle(); const episodeTitleText = getEpisodeTitle();
log(`시리즈: ${seriesName}, 에피소드: ${episodeTitle}, 이미지: ${imageUrls.length}개`); const episodeTitle = `${seriesInfo.seriesName} ${episodeTitleText}`;
const episodeNumber = episodeTitleText.match(/\d+/)?.[0] || '1';
await downloadImagesAsZip(imageUrls, seriesName, episodeTitle, '1'); log(`시리즈: ${seriesInfo.seriesName}, 작가: ${seriesInfo.writer}, 에피소드: ${episodeTitle}, 이미지: ${imageUrls.length}개`);
await downloadImagesAsZip(imageUrls, seriesInfo, episodeTitle, episodeNumber);
log('완료'); log('완료');
} catch (error) { } catch (error) {
alert('다운로드 중 오류 발생:\n' + error); alert('다운로드 중 오류 발생:\n' + error);
@@ -582,7 +592,7 @@
const startQueue = (episodes, seriesName) => { const startQueue = (episodes, seriesName) => {
log(`${episodes.length}개 에피소드를 큐에 저장, 첫 화로 이동합니다...`); log(`${episodes.length}개 에피소드를 큐에 저장, 첫 화로 이동합니다...`);
sessionStorage.setItem(QUEUE_KEY, JSON.stringify(episodes)); sessionStorage.setItem(QUEUE_KEY, JSON.stringify(episodes));
sessionStorage.setItem(SERIES_KEY, seriesName); sessionStorage.setItem(SERIES_KEY, JSON.stringify(seriesName));
sessionStorage.setItem(IDX_KEY, '0'); sessionStorage.setItem(IDX_KEY, '0');
sessionStorage.setItem(LIST_KEY, window.location.href); sessionStorage.setItem(LIST_KEY, window.location.href);
window.location.href = episodes[0].url; window.location.href = episodes[0].url;
@@ -592,8 +602,8 @@
log('목록 페이지 처리 시작'); log('목록 페이지 처리 시작');
const episodes = getEpisodesFromList(); const episodes = getEpisodesFromList();
if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; } if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; }
const seriesName = getSeriesName(); const seriesInfo = getSeriesName();
startQueue(episodes, seriesName); startQueue(episodes, seriesInfo);
} }
// n화 이후 다운로드 // n화 이후 다운로드
@@ -612,8 +622,8 @@
.sort((a, b) => parseFloat(a.number) - parseFloat(b.number)); .sort((a, b) => parseFloat(a.number) - parseFloat(b.number));
if (filtered.length === 0) { alert('조건에 맞는 에피소드가 없습니다.'); return; } if (filtered.length === 0) { alert('조건에 맞는 에피소드가 없습니다.'); return; }
const seriesName = getSeriesName(); const seriesInfo = getSeriesName();
startQueue(filtered, seriesName); startQueue(filtered, seriesInfo);
} }
// 범위 다운로드 // 범위 다운로드
@@ -640,8 +650,8 @@
.sort((a, b) => parseFloat(a.number) - parseFloat(b.number)); .sort((a, b) => parseFloat(a.number) - parseFloat(b.number));
if (filtered.length === 0) { alert('조건에 맞는 에피소드가 없습니다.'); return; } if (filtered.length === 0) { alert('조건에 맞는 에피소드가 없습니다.'); return; }
const seriesName = getSeriesName(); const seriesInfo = getSeriesName();
startQueue(filtered, seriesName); startQueue(filtered, seriesInfo);
} }
// 자동 큐 처리: sessionStorage에 큐가 있으면 현재 화면을 다운로드 후 다음으로 이동 // 자동 큐 처리: sessionStorage에 큐가 있으면 현재 화면을 다운로드 후 다음으로 이동
@@ -661,9 +671,11 @@
const imageUrls = await getImagesWithLazyLoad(); const imageUrls = await getImagesWithLazyLoad();
log(`자동 다운로드: ${idx + 1}/${queue.length} - ${queue[idx].title}, 이미지 ${imageUrls.length}개`); log(`자동 다운로드: ${idx + 1}/${queue.length} - ${queue[idx].title}, 이미지 ${imageUrls.length}개`);
if (imageUrls.length > 0) { if (imageUrls.length > 0) {
const seriesName = sessionStorage.getItem(SERIES_KEY) || getSeriesName(); const seriesInfoStr = sessionStorage.getItem(SERIES_KEY);
const episodeTitle = getEpisodeTitle() || queue[idx].title; const seriesInfo = seriesInfoStr ? JSON.parse(seriesInfoStr) : getSeriesName();
await downloadImagesAsZip(imageUrls, seriesName, episodeTitle, queue[idx].number); const episodeTitleText = getEpisodeTitle() || queue[idx].title;
const fullEpisodeTitle = `${seriesInfo.seriesName} ${episodeTitleText}`;
await downloadImagesAsZip(imageUrls, seriesInfo, fullEpisodeTitle, queue[idx].number);
} else { } else {
log(`이미지 없음 - 스킵`); log(`이미지 없음 - 스킵`);
} }