탭 이동 → 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 + 스크롤
|
// lazy loading 트리거: data-src 복사 + loading=eager + 스크롤
|
||||||
const triggerLazyLoading = async () => {
|
const triggerLazyLoading = async (doc, win) => {
|
||||||
const viewer = document.querySelector('.vw-imgs, [class*="vw-imgs"]') || document;
|
const d = doc || document;
|
||||||
// 우선 현재 img들 강제 로딩
|
const w = win || window;
|
||||||
|
const viewer = d.querySelector('.vw-imgs, [class*="vw-imgs"]') || d;
|
||||||
forceLoadImages(viewer);
|
forceLoadImages(viewer);
|
||||||
// 동적 스크롤: 새 콘텐츠가 계속 로드되면 scrollHeight도 계속 증가
|
|
||||||
let prevHeight = 0;
|
let prevHeight = 0;
|
||||||
let stuckCount = 0;
|
let stuckCount = 0;
|
||||||
while (stuckCount < 3) {
|
while (stuckCount < 3) {
|
||||||
const maxScroll = Math.max(
|
const maxScroll = Math.max(
|
||||||
document.documentElement.scrollHeight,
|
d.documentElement.scrollHeight,
|
||||||
document.body.scrollHeight, 0
|
d.body.scrollHeight, 0
|
||||||
);
|
);
|
||||||
if (maxScroll === prevHeight) {
|
if (maxScroll === prevHeight) {
|
||||||
stuckCount++;
|
stuckCount++;
|
||||||
@@ -255,21 +255,18 @@
|
|||||||
stuckCount = 0;
|
stuckCount = 0;
|
||||||
}
|
}
|
||||||
prevHeight = maxScroll;
|
prevHeight = maxScroll;
|
||||||
// 현재 위치에서 끝까지 step 단위로 스크롤 (느리게)
|
|
||||||
const step = Math.min(300, maxScroll);
|
const step = Math.min(300, maxScroll);
|
||||||
for (let s = window.scrollY; s <= maxScroll; s += step) {
|
for (let s = w.scrollY; s <= maxScroll; s += step) {
|
||||||
window.scrollTo(0, Math.min(s, maxScroll));
|
w.scrollTo(0, Math.min(s, maxScroll));
|
||||||
await sleep(300);
|
await sleep(300);
|
||||||
}
|
}
|
||||||
window.scrollTo(0, maxScroll);
|
w.scrollTo(0, maxScroll);
|
||||||
// 스크롤 후 추가된 img들도 강제 로딩 (백그라운드 탭 IO 대체)
|
|
||||||
forceLoadImages(viewer);
|
forceLoadImages(viewer);
|
||||||
await sleep(1500);
|
await sleep(1500);
|
||||||
}
|
}
|
||||||
// 끝까지 다 내려간 상태에서 여유 대기 + 최종 강제 로딩
|
|
||||||
forceLoadImages(viewer);
|
forceLoadImages(viewer);
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
window.scrollTo(0, 0);
|
w.scrollTo(0, 0);
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -309,20 +306,22 @@
|
|||||||
log(`저장: ${filename} (${imageUrls.length}개)`);
|
log(`저장: ${filename} (${imageUrls.length}개)`);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 공통: 현재 페이지에서 이미지 획득 + lazy 로딩 트리거
|
// 공통: 페이지/iframe에서 이미지 획득 + lazy 로딩 트리거
|
||||||
const getImagesWithLazyLoad = async () => {
|
const getImagesWithLazyLoad = async (doc, win) => {
|
||||||
let urls = getEpisodeImages();
|
const d = doc || document;
|
||||||
|
const w = win || window;
|
||||||
|
let urls = getEpisodeImages(d);
|
||||||
if (urls.length === 0) {
|
if (urls.length === 0) {
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
urls = getEpisodeImages();
|
urls = getEpisodeImages(d);
|
||||||
if (urls.length > 0) break;
|
if (urls.length > 0) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (urls.length <= 3) {
|
if (urls.length <= 3) {
|
||||||
log(`lazy loading 트리거 (${urls.length}개 → 전체)...`);
|
log(`lazy loading 트리거 (${urls.length}개 → 전체)...`);
|
||||||
await triggerLazyLoading();
|
await triggerLazyLoading(d, w);
|
||||||
urls = getEpisodeImages();
|
urls = getEpisodeImages(d);
|
||||||
}
|
}
|
||||||
return urls;
|
return urls;
|
||||||
};
|
};
|
||||||
@@ -330,7 +329,7 @@
|
|||||||
async function downloadCurrentEpisode() {
|
async function downloadCurrentEpisode() {
|
||||||
try {
|
try {
|
||||||
log('에피소드 다운로드 시작...');
|
log('에피소드 다운로드 시작...');
|
||||||
const imageUrls = await getImagesWithLazyLoad();
|
const imageUrls = await getImagesWithLazyLoad(document, window);
|
||||||
|
|
||||||
if (imageUrls.length === 0) {
|
if (imageUrls.length === 0) {
|
||||||
alert('이미지를 찾을 수 없습니다.');
|
alert('이미지를 찾을 수 없습니다.');
|
||||||
@@ -372,14 +371,68 @@
|
|||||||
return episodes.length > 0 ? episodes : null;
|
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) => {
|
const startQueue = (episodes, seriesName) => {
|
||||||
log(`${episodes.length}개 에피소드를 큐에 저장, 첫 화로 이동합니다...`);
|
log(`${episodes.length}개 에피소드 다운로드 시작 (iframe)...`);
|
||||||
sessionStorage.setItem(QUEUE_KEY, JSON.stringify(episodes));
|
processEpisodeInFrame(episodes, 0, seriesName);
|
||||||
sessionStorage.setItem(SERIES_KEY, seriesName);
|
|
||||||
sessionStorage.setItem(IDX_KEY, '0');
|
|
||||||
sessionStorage.setItem(LIST_KEY, window.location.href);
|
|
||||||
window.location.href = episodes[0].url;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function downloadAllEpisodes() {
|
async function downloadAllEpisodes() {
|
||||||
@@ -390,7 +443,6 @@
|
|||||||
startQueue(episodes, seriesName);
|
startQueue(episodes, seriesName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// n화 이후 다운로드
|
|
||||||
async function downloadAfterEpisode() {
|
async function downloadAfterEpisode() {
|
||||||
const episodes = getEpisodesFromList();
|
const episodes = getEpisodesFromList();
|
||||||
if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; }
|
if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; }
|
||||||
@@ -410,7 +462,6 @@
|
|||||||
startQueue(filtered, seriesName);
|
startQueue(filtered, seriesName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 범위 다운로드
|
|
||||||
async function downloadRangeEpisodes() {
|
async function downloadRangeEpisodes() {
|
||||||
const episodes = getEpisodesFromList();
|
const episodes = getEpisodesFromList();
|
||||||
if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; }
|
if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; }
|
||||||
@@ -452,7 +503,7 @@
|
|||||||
await sleep(7000);
|
await sleep(7000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const imageUrls = await getImagesWithLazyLoad();
|
const imageUrls = await getImagesWithLazyLoad(document, window);
|
||||||
if (imageUrls.length > 0) {
|
if (imageUrls.length > 0) {
|
||||||
const seriesName = sessionStorage.getItem(SERIES_KEY) || getSeriesName();
|
const seriesName = sessionStorage.getItem(SERIES_KEY) || getSeriesName();
|
||||||
const episodeTitle = getEpisodeTitle() || queue[idx].title;
|
const episodeTitle = getEpisodeTitle() || queue[idx].title;
|
||||||
|
|||||||
Reference in New Issue
Block a user