탭 이동 → iframe 방식으로 변경 (상단 1/3 영역)
- triggerLazyLoading, getImagesWithLazyLoad에 doc/win 파라미터 추가 → iframe의 document/window로도 lazy 로딩 트리거 가능 - processEpisodeInFrame() 추가: iframe 생성 후 에피소드 순차 처리 - startQueue: window.location.href 이동 대신 iframe 생성/재사용 - processAutoQueue 유지 (기존 탭 방식 fallback) - downloadCurrentEpisode에도 getImagesWithLazyLoad(document, window) 명시
This commit is contained in:
@@ -237,17 +237,17 @@
|
||||
};
|
||||
|
||||
// lazy loading 트리거: data-src 복사 + loading=eager + 스크롤
|
||||
const triggerLazyLoading = async () => {
|
||||
const viewer = document.querySelector('.vw-imgs, [class*="vw-imgs"]') || document;
|
||||
// 우선 현재 img들 강제 로딩
|
||||
const triggerLazyLoading = async (doc, win) => {
|
||||
const d = doc || document;
|
||||
const w = win || window;
|
||||
const viewer = d.querySelector('.vw-imgs, [class*="vw-imgs"]') || d;
|
||||
forceLoadImages(viewer);
|
||||
// 동적 스크롤: 새 콘텐츠가 계속 로드되면 scrollHeight도 계속 증가
|
||||
let prevHeight = 0;
|
||||
let stuckCount = 0;
|
||||
while (stuckCount < 3) {
|
||||
const maxScroll = Math.max(
|
||||
document.documentElement.scrollHeight,
|
||||
document.body.scrollHeight, 0
|
||||
d.documentElement.scrollHeight,
|
||||
d.body.scrollHeight, 0
|
||||
);
|
||||
if (maxScroll === prevHeight) {
|
||||
stuckCount++;
|
||||
@@ -255,21 +255,18 @@
|
||||
stuckCount = 0;
|
||||
}
|
||||
prevHeight = maxScroll;
|
||||
// 현재 위치에서 끝까지 step 단위로 스크롤 (느리게)
|
||||
const step = Math.min(300, maxScroll);
|
||||
for (let s = window.scrollY; s <= maxScroll; s += step) {
|
||||
window.scrollTo(0, Math.min(s, maxScroll));
|
||||
for (let s = w.scrollY; s <= maxScroll; s += step) {
|
||||
w.scrollTo(0, Math.min(s, maxScroll));
|
||||
await sleep(300);
|
||||
}
|
||||
window.scrollTo(0, maxScroll);
|
||||
// 스크롤 후 추가된 img들도 강제 로딩 (백그라운드 탭 IO 대체)
|
||||
w.scrollTo(0, maxScroll);
|
||||
forceLoadImages(viewer);
|
||||
await sleep(1500);
|
||||
}
|
||||
// 끝까지 다 내려간 상태에서 여유 대기 + 최종 강제 로딩
|
||||
forceLoadImages(viewer);
|
||||
await sleep(2000);
|
||||
window.scrollTo(0, 0);
|
||||
w.scrollTo(0, 0);
|
||||
await sleep(1000);
|
||||
};
|
||||
|
||||
@@ -309,20 +306,22 @@
|
||||
log(`저장: ${filename} (${imageUrls.length}개)`);
|
||||
};
|
||||
|
||||
// 공통: 현재 페이지에서 이미지 획득 + lazy 로딩 트리거
|
||||
const getImagesWithLazyLoad = async () => {
|
||||
let urls = getEpisodeImages();
|
||||
// 공통: 페이지/iframe에서 이미지 획득 + lazy 로딩 트리거
|
||||
const getImagesWithLazyLoad = async (doc, win) => {
|
||||
const d = doc || document;
|
||||
const w = win || window;
|
||||
let urls = getEpisodeImages(d);
|
||||
if (urls.length === 0) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await sleep(500);
|
||||
urls = getEpisodeImages();
|
||||
urls = getEpisodeImages(d);
|
||||
if (urls.length > 0) break;
|
||||
}
|
||||
}
|
||||
if (urls.length <= 3) {
|
||||
log(`lazy loading 트리거 (${urls.length}개 → 전체)...`);
|
||||
await triggerLazyLoading();
|
||||
urls = getEpisodeImages();
|
||||
await triggerLazyLoading(d, w);
|
||||
urls = getEpisodeImages(d);
|
||||
}
|
||||
return urls;
|
||||
};
|
||||
@@ -330,7 +329,7 @@
|
||||
async function downloadCurrentEpisode() {
|
||||
try {
|
||||
log('에피소드 다운로드 시작...');
|
||||
const imageUrls = await getImagesWithLazyLoad();
|
||||
const imageUrls = await getImagesWithLazyLoad(document, window);
|
||||
|
||||
if (imageUrls.length === 0) {
|
||||
alert('이미지를 찾을 수 없습니다.');
|
||||
@@ -372,14 +371,68 @@
|
||||
return episodes.length > 0 ? episodes : null;
|
||||
};
|
||||
|
||||
// 큐 저장 후 첫 화로 이동
|
||||
let downloadFrame = null;
|
||||
|
||||
// iframe에서 에피소드 처리
|
||||
const processEpisodeInFrame = async (episodes, index, seriesName) => {
|
||||
if (index >= episodes.length) {
|
||||
log('모든 에피소드 다운로드 완료!');
|
||||
if (downloadFrame) {
|
||||
downloadFrame.remove();
|
||||
downloadFrame = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const episode = episodes[index];
|
||||
log(`${index + 1}/${episodes.length} ${episode.title} 로딩 중...`);
|
||||
|
||||
// iframe 생성 (상단 1/3)
|
||||
if (!downloadFrame) {
|
||||
downloadFrame = document.createElement('iframe');
|
||||
downloadFrame.style.position = 'fixed';
|
||||
downloadFrame.style.top = '0';
|
||||
downloadFrame.style.left = '0';
|
||||
downloadFrame.style.width = '100%';
|
||||
downloadFrame.style.height = '33.33vh';
|
||||
downloadFrame.style.border = 'none';
|
||||
downloadFrame.style.zIndex = '999999';
|
||||
document.body.appendChild(downloadFrame);
|
||||
}
|
||||
|
||||
// 에피소드 로드
|
||||
downloadFrame.src = episode.url;
|
||||
await new Promise((resolve) => {
|
||||
downloadFrame.addEventListener('load', () => resolve(), { once: true });
|
||||
});
|
||||
log(`${episode.title} 로드 완료, 이미지 대기 중...`);
|
||||
await sleep(7000);
|
||||
|
||||
// iframe에서 이미지 추출
|
||||
let imageUrls = [];
|
||||
try {
|
||||
const iframeWin = downloadFrame.contentWindow;
|
||||
const iframeDoc = iframeWin.document;
|
||||
imageUrls = await getImagesWithLazyLoad(iframeDoc, iframeWin);
|
||||
} catch (e) {
|
||||
log('iframe 접근 오류, GM_xmlhttpRequest로 대체:', e);
|
||||
// GM_xmlhttpRequest로 페이지 HTML 받아서 이미지 추출 시도
|
||||
// (fallback - 추후 구현)
|
||||
}
|
||||
|
||||
if (imageUrls.length > 0) {
|
||||
await downloadImagesAsZip(imageUrls, seriesName, episode.title, episode.number);
|
||||
} else {
|
||||
log('이미지 없음 - 스킵');
|
||||
}
|
||||
|
||||
await sleep(2000);
|
||||
processEpisodeInFrame(episodes, index + 1, seriesName);
|
||||
};
|
||||
|
||||
const startQueue = (episodes, seriesName) => {
|
||||
log(`${episodes.length}개 에피소드를 큐에 저장, 첫 화로 이동합니다...`);
|
||||
sessionStorage.setItem(QUEUE_KEY, JSON.stringify(episodes));
|
||||
sessionStorage.setItem(SERIES_KEY, seriesName);
|
||||
sessionStorage.setItem(IDX_KEY, '0');
|
||||
sessionStorage.setItem(LIST_KEY, window.location.href);
|
||||
window.location.href = episodes[0].url;
|
||||
log(`${episodes.length}개 에피소드 다운로드 시작 (iframe)...`);
|
||||
processEpisodeInFrame(episodes, 0, seriesName);
|
||||
};
|
||||
|
||||
async function downloadAllEpisodes() {
|
||||
@@ -390,7 +443,6 @@
|
||||
startQueue(episodes, seriesName);
|
||||
}
|
||||
|
||||
// n화 이후 다운로드
|
||||
async function downloadAfterEpisode() {
|
||||
const episodes = getEpisodesFromList();
|
||||
if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; }
|
||||
@@ -410,7 +462,6 @@
|
||||
startQueue(filtered, seriesName);
|
||||
}
|
||||
|
||||
// 범위 다운로드
|
||||
async function downloadRangeEpisodes() {
|
||||
const episodes = getEpisodesFromList();
|
||||
if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; }
|
||||
@@ -452,7 +503,7 @@
|
||||
await sleep(7000);
|
||||
|
||||
try {
|
||||
const imageUrls = await getImagesWithLazyLoad();
|
||||
const imageUrls = await getImagesWithLazyLoad(document, window);
|
||||
if (imageUrls.length > 0) {
|
||||
const seriesName = sessionStorage.getItem(SERIES_KEY) || getSeriesName();
|
||||
const episodeTitle = getEpisodeTitle() || queue[idx].title;
|
||||
|
||||
Reference in New Issue
Block a user