diff --git a/tokimonkeyDownader.js b/tokimonkeyDownader.js index 2e8cd7b..0628f80 100644 --- a/tokimonkeyDownader.js +++ b/tokimonkeyDownader.js @@ -72,6 +72,8 @@ const { isListPage, isEpisodePage } = getListPageInfo(); if (isListPage) { menuCmdIds.push(GM_registerMenuCommand('[목록] 전체 에피소드 다운로드', () => downloadAllEpisodes())); + menuCmdIds.push(GM_registerMenuCommand('[목록] n화 이후 다운로드', () => downloadAfterEpisode())); + menuCmdIds.push(GM_registerMenuCommand('[목록] 범위 다운로드', () => downloadRangeEpisodes())); } else if (isEpisodePage) { menuCmdIds.push(GM_registerMenuCommand('현재 에피소드 다운로드', () => downloadCurrentEpisode())); } @@ -347,12 +349,10 @@ } } - async function downloadAllEpisodes() { - log('목록 페이지 처리 시작'); - + // 목록 페이지에서 에피소드 목록 추출 + const getEpisodesFromList = () => { const episodeList = document.querySelector('ul.ep-list-v2'); - if (!episodeList) { alert('에피소드 목록을 찾을 수 없습니다.'); return; } - + if (!episodeList) return null; const episodes = []; episodeList.querySelectorAll('li.ep-row-v2').forEach((li, index) => { const link = li.querySelector('a.ep-row-v2-link'); @@ -369,18 +369,73 @@ }); } }); + return episodes.length > 0 ? episodes : null; + }; - if (episodes.length === 0) { alert('에피소드 링크를 추출할 수 없습니다.'); return; } - - const seriesName = getSeriesName(); + // 큐 저장 후 첫 화로 이동 + 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; + }; + + async function downloadAllEpisodes() { + log('목록 페이지 처리 시작'); + const episodes = getEpisodesFromList(); + if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; } + const seriesName = getSeriesName(); + startQueue(episodes, seriesName); + } + + // n화 이후 다운로드 + async function downloadAfterEpisode() { + const episodes = getEpisodesFromList(); + if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; } + + const input = prompt('어느 에피소드부터 다운로드할까요?\n(번호 입력, 예: 50)'); + if (!input) return; + + const threshold = parseFloat(input.trim()); + if (isNaN(threshold)) { alert('올바른 숫자를 입력하세요.'); return; } + + const filtered = episodes + .filter(ep => parseFloat(ep.number) >= threshold) + .sort((a, b) => parseFloat(a.number) - parseFloat(b.number)); + + if (filtered.length === 0) { alert('조건에 맞는 에피소드가 없습니다.'); return; } + const seriesName = getSeriesName(); + startQueue(filtered, seriesName); + } + + // 범위 다운로드 + async function downloadRangeEpisodes() { + const episodes = getEpisodesFromList(); + if (!episodes) { alert('에피소드 목록을 찾을 수 없습니다.'); return; } + + const input = prompt('범위를 입력하세요.\n(예: 30-50 또는 50-끝)'); + if (!input) return; + + const match = input.trim().match(/^(\d+(?:\.\d+)?)\s*-\s*(?:(\d+(?:\.\d+)?)|(끝|end))?$/); + if (!match) { alert('올바른 형식이 아닙니다. 예: 30-50'); return; } + + const start = parseFloat(match[1]); + const end = match[2] !== undefined ? parseFloat(match[2]) : Infinity; + + if (isNaN(start)) { alert('올바른 숫자를 입력하세요.'); return; } + + const filtered = episodes + .filter(ep => { + const num = parseFloat(ep.number); + return num >= start && num <= end; + }) + .sort((a, b) => parseFloat(a.number) - parseFloat(b.number)); + + if (filtered.length === 0) { alert('조건에 맞는 에피소드가 없습니다.'); return; } + const seriesName = getSeriesName(); + startQueue(filtered, seriesName); } // 자동 큐 처리: sessionStorage에 큐가 있으면 현재 화면을 다운로드 후 다음으로 이동