Only create one chapter bar, support videos with no segments and preview segments

This commit is contained in:
Ajay Ramachandran
2021-11-01 20:59:04 -04:00
parent b4a2f31520
commit 9ed9f9b873

View File

@@ -33,7 +33,9 @@ class PreviewBar {
videoDuration = 0; videoDuration = 0;
// For chapter bar // For chapter bar
hoveredSection?: HTMLElement; hoveredSection: HTMLElement;
customChaptersBar: HTMLElement;
chaptersBarSegments: PreviewBarSegment[];
constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean) { constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean) {
this.container = document.createElement('ul'); this.container = document.createElement('ul');
@@ -44,6 +46,7 @@ class PreviewBar {
this.onInvidious = onInvidious; this.onInvidious = onInvidious;
this.createElement(parent); this.createElement(parent);
this.createChapterMutationObserver();
this.setupHoverText(); this.setupHoverText();
} }
@@ -218,7 +221,93 @@ class PreviewBar {
const progressBar = document.querySelector('.ytp-progress-bar'); const progressBar = document.querySelector('.ytp-progress-bar');
const chapterBar = document.querySelector(".ytp-chapters-container:not(.sponsorBlockChapterBar)") as HTMLElement; const chapterBar = document.querySelector(".ytp-chapters-container:not(.sponsorBlockChapterBar)") as HTMLElement;
if (!progressBar || !chapterBar || segments?.length <= 0) return; if (!progressBar || !chapterBar) return;
if (segments === this.chaptersBarSegments) return;
this.customChaptersBar?.remove();
if (segments?.length <= 0) {
chapterBar.style.removeProperty("display");
return;
}
// Create it from cloning
const newChapterBar = chapterBar.cloneNode(true) as HTMLElement;
newChapterBar.classList.add("sponsorBlockChapterBar");
newChapterBar.style.removeProperty("display");
const originalSection = newChapterBar.querySelector(".ytp-chapter-hover-container");
this.customChaptersBar = newChapterBar;
this.chaptersBarSegments = segments;
// Merge overlapping chapters
const mergedSegments = segments.filter((segment) => getCategoryActionType(segment.category) !== CategoryActionType.POI
&& segment.segment.length === 2)
.reduce((acc, curr) => {
if (acc.length === 0 || curr.segment[0] > acc[acc.length - 1].segment[1]) {
acc.push(curr);
} else {
acc[acc.length - 1].segment[1] = Math.max(acc[acc.length - 1].segment[1], curr.segment[1]);
}
return acc;
}, [] as PreviewBarSegment[]);
// Modify it to have sections for each segment
for (let i = 0; i < mergedSegments.length; i++) {
const segment = mergedSegments[i];
if (i === 0 && segment.segment[0] > 0) {
const newBlankSection = originalSection.cloneNode(true) as HTMLElement;
const blankDuration = segment.segment[0];
this.setupChapterSection(newBlankSection, blankDuration);
newChapterBar.appendChild(newBlankSection);
}
const duration = segment.segment[1] - segment.segment[0];
const newSection = originalSection.cloneNode(true) as HTMLElement;
this.setupChapterSection(newSection, duration);
newChapterBar.appendChild(newSection);
if (segment.segment[1] < this.videoDuration) {
const nextSegment = mergedSegments[i + 1];
const newBlankSection = originalSection.cloneNode(true) as HTMLElement;
const nextTime = nextSegment ? nextSegment.segment[0] : this.videoDuration;
const blankDuration = nextTime - segment.segment[1];
this.setupChapterSection(newBlankSection, blankDuration);
newChapterBar.appendChild(newBlankSection);
}
}
// Hide old bar
chapterBar.style.display = "none";
originalSection.remove();
if (this.container?.parentElement === progressBar) {
progressBar.insertBefore(newChapterBar, this.container.nextSibling);
} else {
progressBar.prepend(newChapterBar);
}
}
private setupChapterSection(section: HTMLElement, duration: number): void {
section.style.marginRight = "2px";
section.style.width = `calc(${this.timeToPercentage(duration)} - 2px)`;
section.setAttribute("decimal-width", String(this.timeToDecimal(duration)));
section.addEventListener("mouseenter", () => {
this.hoveredSection?.classList.remove("ytp-exp-chapter-hover-effect");
section.classList.add("ytp-exp-chapter-hover-effect");
this.hoveredSection = section;
});
}
private createChapterMutationObserver(): void {
const progressBar = document.querySelector('.ytp-progress-bar');
const chapterBar = document.querySelector(".ytp-chapters-container:not(.sponsorBlockChapterBar)") as HTMLElement;
if (!progressBar || !chapterBar) return;
const observer = new MutationObserver((mutations) => { const observer = new MutationObserver((mutations) => {
const changes: Record<string, MutationRecord> = {}; const changes: Record<string, MutationRecord> = {};
@@ -230,13 +319,23 @@ class PreviewBar {
} }
} }
this.updateChapterMutation(changes, progressBar);
});
observer.observe(chapterBar, {
subtree: true,
attributes: true,
attributeFilter: ["style", "class"],
});
}
private updateChapterMutation(changes: Record<string, MutationRecord>, progressBar: HTMLElement): void {
// Go through each newly generated chapter bar and update the width based on changes array // Go through each newly generated chapter bar and update the width based on changes array
const generatedChapterBar = document.querySelector(".sponsorBlockChapterBar"); if (this.customChaptersBar) {
if (generatedChapterBar) {
// Width reached so far in decimal percent // Width reached so far in decimal percent
let cursor = 0; let cursor = 0;
const sections = generatedChapterBar.querySelectorAll(".ytp-chapter-hover-container") as NodeListOf<HTMLElement>; const sections = this.customChaptersBar.querySelectorAll(".ytp-chapter-hover-container") as NodeListOf<HTMLElement>;
for (const section of sections) { for (const section of sections) {
const sectionWidthDecimal = parseFloat(section.getAttribute("decimal-width")); const sectionWidthDecimal = parseFloat(section.getAttribute("decimal-width"));
@@ -267,80 +366,6 @@ class PreviewBar {
cursor += sectionWidthDecimal; cursor += sectionWidthDecimal;
} }
} }
});
observer.observe(chapterBar, {
subtree: true,
attributes: true,
attributeFilter: ["style", "class"],
});
// Create it from cloning
const newChapterBar = chapterBar.cloneNode(true) as HTMLElement;
newChapterBar.classList.add("sponsorBlockChapterBar");
const originalSectionClone = newChapterBar.querySelector(".ytp-chapter-hover-container");
// Merge overlapping chapters
const mergedSegments = segments.filter((segment) => getCategoryActionType(segment.category) !== CategoryActionType.POI)
.reduce((acc, curr) => {
if (acc.length === 0 || curr.segment[0] > acc[acc.length - 1].segment[1]) {
acc.push(curr);
} else {
acc[acc.length - 1].segment[1] = Math.max(acc[acc.length - 1].segment[1], curr.segment[1]);
}
return acc;
}, [] as PreviewBarSegment[]);
// Modify it to have sections for each segment
for (let i = 0; i < mergedSegments.length; i++) {
const segment = mergedSegments[i];
if (i === 0 && segment.segment[0] > 0) {
const newBlankSection = originalSectionClone.cloneNode(true) as HTMLElement;
const blankDuration = segment.segment[0];
this.setupChapterSection(newBlankSection, blankDuration);
newChapterBar.appendChild(newBlankSection);
}
const duration = segment.segment[1] - segment.segment[0];
const newSection = originalSectionClone.cloneNode(true) as HTMLElement;
this.setupChapterSection(newSection, duration);
newChapterBar.appendChild(newSection);
if (segment.segment[1] < this.videoDuration) {
const nextSegment = mergedSegments[i + 1];
const newBlankSection = originalSectionClone.cloneNode(true) as HTMLElement;
const nextTime = nextSegment ? nextSegment.segment[0] : this.videoDuration;
const blankDuration = nextTime - segment.segment[1];
this.setupChapterSection(newBlankSection, blankDuration);
newChapterBar.appendChild(newBlankSection);
}
}
originalSectionClone.remove();
if (this.container?.parentElement === progressBar) {
progressBar.insertBefore(newChapterBar, this.container.nextSibling);
} else {
progressBar.prepend(newChapterBar);
}
// Hide old bar
chapterBar.style.display = "none";
}
private setupChapterSection(section: HTMLElement, duration: number): void {
section.style.marginRight = "2px";
section.style.width = `calc(${this.timeToPercentage(duration)} - 2px)`;
section.setAttribute("decimal-width", String(this.timeToDecimal(duration)));
section.addEventListener("mouseenter", () => {
this.hoveredSection?.classList.remove("ytp-exp-chapter-hover-effect");
section.classList.add("ytp-exp-chapter-hover-effect");
this.hoveredSection = section;
});
} }
updateChapterText(segments: SponsorTime[], currentTime: number): void { updateChapterText(segments: SponsorTime[], currentTime: number): void {