diff --git a/extension/content.js b/extension/content.js index 7b6bd93..c9d6eca 100644 --- a/extension/content.js +++ b/extension/content.js @@ -3521,19 +3521,7 @@ function findActiveSubtitleIndex(currentTime) { } function findActiveChapterIndex(currentTime) { - const chapters = MdgSites.normalizeChapters(state.chapters || []); - for (let index = 0; index < chapters.length; index += 1) { - const item = chapters[index]; - const from = Number(item?.from || 0) || 0; - const next = chapters[index + 1]; - const explicitTo = Number(item?.to || 0) || 0; - const fallbackTo = next && Number(next.from) > from ? Number(next.from) : explicitTo; - const to = fallbackTo > from ? fallbackTo : Number.POSITIVE_INFINITY; - if (currentTime >= from && currentTime < to) { - return index; - } - } - return -1; + return MdgSites.chapterRanges(state.chapters || []).findIndex((item) => currentTime >= item.from && currentTime < item.to); } function setActiveReadingItems(subtitleIndex, chapterIndex, shouldScroll = false) { diff --git a/extension/content.selftest.js b/extension/content.selftest.js index 6b3b32e..76e53a7 100644 --- a/extension/content.selftest.js +++ b/extension/content.selftest.js @@ -492,6 +492,14 @@ const settle = async () => { t.byId = prev; } + // 专注模式 highlights the chapter the note files the line under: two chapters that start together end at the next later start. + { + const saved = state.chapters; + state.chapters = [{ title: "A", from: 0, to: 0 }, { title: "B", from: 0, to: 0 }, { title: "C", from: 60, to: 0 }]; + assert.deepStrictEqual([10, 90].map(t.findActiveChapterIndex), [0, 2]); + state.chapters = saved; + } + // A4: a jump from a 专注模式 subtitle row or chapter keeps a paused video paused, like the side panel's timestamps. { const prev = t.bindReadingViewVideo; diff --git a/extension/note.js b/extension/note.js index c4b977e..cf18c9d 100644 --- a/extension/note.js +++ b/extension/note.js @@ -635,7 +635,7 @@ return []; } - const chapterItems = MdgSites.normalizeChapters(chapters); + const chapterItems = MdgSites.chapterRanges(chapters); if (chapterItems.length === 0) { return subtitleItems.map((item) => formatSubtitleLine(item, settings, withHours)); } @@ -653,26 +653,16 @@ lines.push(""); } - chapterItems.forEach((chapter, idx) => { - const start = Number(chapter.from || 0) || 0; - // Chapters that start together: the first one ends where a later start is; each subtitle goes in one chapter. - const next = chapterItems.slice(idx + 1).find((item) => Number(item.from) > start); - const chapterTo = Number(chapter.to || 0) || 0; - let end = Infinity; - if (next) { - end = Number(next.from); - } else if (chapterTo > start) { - end = chapterTo; - } + chapterItems.forEach((chapter) => { + // Chapters that start together share a range; usedIndexes puts each subtitle in the first of them. + const { from: start, to: end } = chapter; const sectionItems = subtitleItems.filter((item) => { if (usedIndexes.has(item._index)) { return false; } const from = Number(item.from || 0) || 0; - const inStart = from + 0.001 >= start; - const inEnd = end === Infinity ? true : from < end; - return inStart && inEnd; + return from + 0.001 >= start && from < end; }); if (sectionItems.length === 0) { diff --git a/extension/sites.js b/extension/sites.js index 4f3a235..7fc195f 100644 --- a/extension/sites.js +++ b/extension/sites.js @@ -86,6 +86,15 @@ }); } + // Normalized chapters with `to` as where each one ends: the first later start, else its own `to`, else Infinity. + // Chapters that start together all end at the next later start, never at each other. + function chapterRanges(chapters) { + return normalizeChapters(chapters).map((item, index, all) => { + const next = all.slice(index + 1).find((later) => later.from > item.from); + return { ...item, to: next ? next.from : item.to > item.from ? item.to : Infinity }; + }); + } + function trackLanguagePriority(track) { const lang = String(track?.lang || "").toLowerCase(); const label = String(track?.label || "").toLowerCase(); @@ -1611,6 +1620,7 @@ biliWbiSigned, biliWbiReset, normalizeChapters, + chapterRanges, parseChaptersFromDescription, decodeXmlEntities, parseSrv3,