시리즈, 작가명, 파일이름 수정
This commit is contained in:
@@ -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(`이미지 없음 - 스킵`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user