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

This commit is contained in:
user01
2026-07-01 16:26:03 +09:00
parent beb8b793dd
commit 31f591605d

View File

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