Skip to content
9 changes: 7 additions & 2 deletions manifest/chrome-manifest-extra.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"host_permissions": [
"https://*.youtube.com/*",
"https://sponsor.ajay.app/*"
"https://sponsor.ajay.app/*",
"https://rutube.ru/*",
"https://*.rutube.ru/*",
"https://sponsorblock.futuba.ru/*"
],
"optional_host_permissions": [
"*://*/*"
Expand Down Expand Up @@ -96,7 +99,9 @@
],
"matches": [
"https://*.youtube.com/*",
"https://www.youtube-nocookie.com/embed/*"
"https://www.youtube-nocookie.com/embed/*",
"https://rutube.ru/*",
"https://*.rutube.ru/*"
],
"exclude_matches": [
"https://accounts.youtube.com/RotateCookiesPage*"
Expand Down
9 changes: 7 additions & 2 deletions manifest/manifest-v2-extra.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2"
],
"permissions": [
"https://sponsor.ajay.app/*"
"https://sponsor.ajay.app/*",
"https://rutube.ru/*",
"https://*.rutube.ru/*",
"https://sponsorblock.futuba.ru/*"
],
"optional_permissions": [
"*://*/*"
Expand Down Expand Up @@ -120,7 +123,9 @@
"run_at": "document_start",
"matches": [
"https://*.youtube.com/*",
"https://www.youtube-nocookie.com/embed/*"
"https://www.youtube-nocookie.com/embed/*",
"https://rutube.ru/*",
"https://*.rutube.ru/*"
],
"exclude_matches": [
"https://accounts.youtube.com/RotateCookiesPage*"
Expand Down
201 changes: 148 additions & 53 deletions src/content.ts

Large diffs are not rendered by default.

23 changes: 16 additions & 7 deletions src/js-components/previewBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { isVorapisInstalled } from "../utils/compatibility";
import { isOnYTTV } from "../../maze-utils/src/video";
import { getCategorySelection } from "../utils/skipRule";
import { getSkipProfileBool } from "../utils/skipProfiles";
import { getActiveVideoService } from "../videoServices";

const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
const MIN_CHAPTER_SIZE = 0.003;
Expand Down Expand Up @@ -70,15 +71,15 @@ class PreviewBar {
hoveredSection: HTMLElement;
customChaptersBar: HTMLElement;
chaptersBarSegments: PreviewBarSegment[];
chapterVote: ChapterVote;
chapterVote: ChapterVote | null;
originalChapterBar: HTMLElement;
originalChapterBarBlocks: NodeListOf<HTMLElement>;
chapterMargin: number;
lastRenderedSegments: PreviewBarSegment[];
unfilteredChapterGroups: ChapterGroup[];
chapterGroups: ChapterGroup[];

constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean, onYTTV: boolean, chapterVote: ChapterVote, updateExistingChapters: () => void, test=false) {
constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean, onYTTV: boolean, chapterVote: ChapterVote | null, updateExistingChapters: () => void, test=false) {
if (test) return;
this.container = document.createElement('ul');
this.container.id = 'previewbar';
Expand Down Expand Up @@ -288,6 +289,8 @@ class PreviewBar {

if (this.onMobileYouTube) {
this.container.style.transform = "none";
} else if (getActiveVideoService()?.previewBarClass) {
this.container.classList.add(getActiveVideoService()!.previewBarClass);
} else if (!this.onInvidious) {
this.container.classList.add("sbNotInvidious");
}
Expand Down Expand Up @@ -350,7 +353,13 @@ class PreviewBar {

private updatePageElements(): void {
// YT, Vorapis v3
const allProgressBars = document.querySelectorAll(".ytp-progress-bar, .ytp-progress-bar-container > .html5-progress-bar > .ytp-progress-list") as NodeListOf<HTMLElement>;
const servicePreviewBarSelector = getActiveVideoService()?.selectors?.previewBar;
const progressBarSelector = [
".ytp-progress-bar",
".ytp-progress-bar-container > .html5-progress-bar > .ytp-progress-list",
servicePreviewBarSelector
].filter(Boolean).join(", ");
const allProgressBars = document.querySelectorAll(progressBarSelector) as NodeListOf<HTMLElement>;
this.progressBar = findValidElement(allProgressBars) ?? allProgressBars?.[0];

if (this.progressBar) {
Expand Down Expand Up @@ -971,7 +980,7 @@ class PreviewBar {
chapterTitle.classList.remove("sponsorBlock-segment-title");
}

if (chosenSegment.source === SponsorSourceType.Server) {
if (chosenSegment.source === SponsorSourceType.Server && this.chapterVote) {
const chapterVoteContainer = this.chapterVote.getContainer();
if (document.location.host === "tv.youtube.com") {
if (!chaptersContainer.contains(chapterVoteContainer)) {
Expand All @@ -993,7 +1002,7 @@ class PreviewBar {
this.chapterVote.setVisibility(true);
this.chapterVote.setSegment(chosenSegment);
} else {
this.chapterVote.setVisibility(false);
this.chapterVote?.setVisibility(false);
}
} else if (!getSkipProfileBool("showAutogeneratedChapters") && hasAutogeneratedChapters()) {
// Keep original hidden
Expand All @@ -1006,15 +1015,15 @@ class PreviewBar {
const titlePrefixDot = chaptersContainer.querySelector(".ytp-chapter-title-prefix") as HTMLElement;
if (titlePrefixDot) titlePrefixDot.style.display = "none";

this.chapterVote.setVisibility(false);
this.chapterVote?.setVisibility(false);
} else {
chaptersContainer.querySelector(".sponsorChapterText")?.remove();
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;

chapterTitle.style.removeProperty("display");
chaptersContainer.classList.remove("sponsorblock-chapter-visible");

this.chapterVote.setVisibility(false);
this.chapterVote?.setVisibility(false);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { isSafari } from "../maze-utils/src/config";
import { asyncRequestToServer } from "./utils/requests";
import { FetchResponse, logRequest } from "../maze-utils/src/background-request-proxy";
import { formatJSErrorMessage, getLongErrorMessage } from "../maze-utils/src/formating";
import { getActiveVideoService } from "./videoServices";

export default class Utils {

Expand Down Expand Up @@ -242,7 +243,8 @@ export default class Utils {
".main-video-section > .video-container", // Cloudtube
".shaka-video-container", // Piped
"#player-container.ytk-player", // YT Kids
"#id-tv-container" // YTTV
"#id-tv-container", // YTTV
...(getActiveVideoService()?.selectors?.referenceNode ?? [])
];

let referenceNode = findValidElementFromSelector(selectors)
Expand Down
4 changes: 3 additions & 1 deletion src/utils/pageUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ActionType, Category, SponsorSourceType, SponsorTime, VideoID } from "../types";
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
import { getSkipProfileBool } from "./skipProfiles";
import { getActiveVideoService } from "../videoServices";

export function getControls(): HTMLElement {
const controlsSelectors = [
Expand All @@ -13,7 +14,8 @@ export function getControls(): HTMLElement {
// Vorapis v3
".html5-player-chrome",
// tv.youtube.com
".ypcs-control-buttons-right"
".ypcs-control-buttons-right",
...(getActiveVideoService()?.selectors?.controls ?? [])
];

for (const controlsSelector of controlsSelectors) {
Expand Down
26 changes: 26 additions & 0 deletions src/utils/segmentData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getHashParams } from "./pageUtils";
import { asyncRequestToServer } from "./requests";
import { extensionUserAgent } from "../../maze-utils/src";
import { logRequest, serializeOrStringify } from "../../maze-utils/src/background-request-proxy";
import { getActiveVideoService } from "../videoServices";

const segmentDataCache = new DataCache<VideoID, SegmentResponse>(() => {
return {
Expand Down Expand Up @@ -55,6 +56,31 @@ export async function getSegmentsForVideo(videoID: VideoID, ignoreCache: boolean
}

async function fetchSegmentsForVideo(videoID: VideoID): Promise<SegmentResponse> {
const activeVideoService = getActiveVideoService();
if (activeVideoService?.getSegments) {
const response = await activeVideoService.getSegments(videoID);
if (response.ok && response.segments?.length) {
const result = {
segments: response.segments,
status: response.status
};

segmentDataCache.setupCache(videoID).segments = result.segments;
return result;
}

if (response.ok || response.status === 404) {
segmentDataCache.setupCache(videoID);
} else {
logRequest(response, "SB", `${activeVideoService.id} skip segments`);
}

return {
segments: null,
status: response.status
};
}

const extraRequestData: Record<string, unknown> = {};
const hashParams = getHashParams();
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
Expand Down
12 changes: 12 additions & 0 deletions src/videoServices/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { rutubeVideoService } from "./rutube";
import type { VideoService } from "./types";

export type { VideoService } from "./types";

const videoServices = [
rutubeVideoService
];

export function getActiveVideoService(): VideoService | null {
return videoServices.find((service) => service.isCurrentHost()) ?? null;
}
Loading