Skip to content

Commit

Permalink
Move Netflix subtitle show/hide logic into custom hook
Browse files Browse the repository at this point in the history
  • Loading branch information
mwhirls committed Feb 14, 2024
1 parent ab96773 commit a657949
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 53 deletions.
73 changes: 73 additions & 0 deletions src/common/hooks/useNetflixSubtitles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useState, useEffect } from "react";

const NETFLIX_TEXT_SUBTITLE_CLASS = "player-timedtext";
const NETFLIX_IMAGE_SUBTITLE_CLASS = "image-based-timed-text";

class StyledNode {
element: HTMLElement;
style: CSSStyleDeclaration;

constructor(element: HTMLElement) {
this.element = element;
this.style = element.style;
}

show(show: boolean) {
if (this.element) {
this.element.style.visibility = show ? this.element.style.visibility : 'hidden';
}
}
}

function queryStyledNode(selector: string) {
const elem = document.querySelector(selector);
return elem ? new StyledNode(elem as HTMLElement) : null;
}

export function useNetflixSubtitleSuppressor() {
const [timedTextElem, setTimedTextElem] = useState<StyledNode | null>(queryStyledNode(NETFLIX_TEXT_SUBTITLE_CLASS));
const [imageTimedTextElem, setImageTimedTextElem] = useState<StyledNode | null>(queryStyledNode(NETFLIX_IMAGE_SUBTITLE_CLASS));

useEffect(() => {
// Get handles to relevant Netflix DOM elements
const netflixObserver = new MutationObserver((mutationsList: MutationRecord[]) => {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes') {
if (!(mutation.target instanceof Element)) {
continue;
}
// hide original Netflix subtitles
const node = new StyledNode(mutation.target as HTMLElement);
if (node.element.className === NETFLIX_TEXT_SUBTITLE_CLASS) {
node.show(false);
setTimedTextElem(node);
} else if (node.element.className === NETFLIX_IMAGE_SUBTITLE_CLASS) {
node.show(false);
setImageTimedTextElem(node);
}
}
}
});
const config = { attributes: true, attibuteFilter: ['style'], childList: true, subtree: true };
netflixObserver.observe(document.body, config);

const hideNetflixSubtitles = () => {
const timedText = queryStyledNode(`.${NETFLIX_TEXT_SUBTITLE_CLASS}`);
timedText?.show(false);
setTimedTextElem(timedTextElem);
const imageTimedText = queryStyledNode(`.${NETFLIX_IMAGE_SUBTITLE_CLASS}`);
imageTimedText?.show(false);
setImageTimedTextElem(imageTimedText);
};
hideNetflixSubtitles();

return () => {
netflixObserver.disconnect();
const showNetflixSubtitles = () => {
timedTextElem?.show(true);
imageTimedTextElem?.show(true);
};
showNetflixSubtitles();
};
}, []);
}
55 changes: 2 additions & 53 deletions src/content-scripts/components/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { DBStatusResult, Status } from '../../service-worker/database/dbstatus';
import { WordIndex } from './Word';
import { useResizeObserver } from '../../common/hooks/useResizeObserver';
import Track, { ParsedCue, extractCueText } from './Track';
import { useNetflixSubtitleSuppressor } from '../../common/hooks/useNetflixSubtitles';

const NETFLIX_BOTTOM_CONTROLS_CLASS = 'watch-video--bottom-controls-container';
const NETFLIX_TEXT_SUBTITLE_CLASS = "player-timedtext";
const NETFLIX_IMAGE_SUBTITLE_CLASS = "image-based-timed-text";

export interface WebvttSubtitles {
webvttUrl: string,
Expand All @@ -30,27 +29,6 @@ function calculateSubtitleOffset(videoRect: DOMRect, controlsElem: Element | nul
return defaultOffset + (subtitleBottom - controlsRect.top);
}

class StyledNode {
element: HTMLElement;
style: CSSStyleDeclaration;

constructor(element: HTMLElement) {
this.element = element;
this.style = element.style;
}

show(show: boolean) {
if (this.element) {
this.element.style.visibility = show ? this.element.style.visibility : 'hidden';
}
}
}

function queryStyledNode(selector: string) {
const elem = document.querySelector(selector);
return elem ? new StyledNode(elem as HTMLElement) : null;
}

interface VideoProps {
dbStatus: DBStatusResult | null;
webvttSubtitles: WebvttSubtitles;
Expand All @@ -62,10 +40,9 @@ function Video({ dbStatus, webvttSubtitles, videoElem }: VideoProps) {
const [parsedCues, setParsedCues] = useState<ParsedCue[]>([]);
const rect = useResizeObserver(videoElem);
const [controlsElem, setControlsElem] = useState(document.querySelector(`.${NETFLIX_BOTTOM_CONTROLS_CLASS}`));
const [timedTextElem, setTimedTextElem] = useState<StyledNode | null>(queryStyledNode(NETFLIX_TEXT_SUBTITLE_CLASS));
const [imageTimedTextElem, setImageTimedTextElem] = useState<StyledNode | null>(queryStyledNode(NETFLIX_TEXT_SUBTITLE_CLASS));
const [show, setShow] = useState(true);
const [selectedWord, setSelectedWord] = useState<WordIndex | null>(null);
useNetflixSubtitleSuppressor();

useEffect(() => {
const runtimeListener = (message: RuntimeMessage) => {
Expand All @@ -83,43 +60,15 @@ function Video({ dbStatus, webvttSubtitles, videoElem }: VideoProps) {
if (controls) {
setControlsElem(controls.type === ChildMutationType.Added ? controls.elem : null);
}
} else if (mutation.type === 'attributes') {
if (!(mutation.target instanceof Element)) {
continue;
}
// hide original Netflix subtitles
const node = new StyledNode(mutation.target as HTMLElement);
if (node.element.className === NETFLIX_TEXT_SUBTITLE_CLASS) {
node.show(false);
setTimedTextElem(node);
} else if (node.element.className === NETFLIX_IMAGE_SUBTITLE_CLASS) {
node.show(false);
setImageTimedTextElem(node);
}
}
}
});
const config = { attributes: true, attibuteFilter: ['style'], childList: true, subtree: true };
netflixObserver.observe(document.body, config);

const hideNetflixSubtitles = () => {
const timedText = queryStyledNode(`.${NETFLIX_TEXT_SUBTITLE_CLASS}`);
timedText?.show(false);
setTimedTextElem(timedTextElem);
const imageTimedText = queryStyledNode(`.${NETFLIX_IMAGE_SUBTITLE_CLASS}`);
imageTimedText?.show(false);
setImageTimedTextElem(imageTimedText);
};
hideNetflixSubtitles();

return () => {
chrome.runtime.onMessage.removeListener(runtimeListener);
netflixObserver.disconnect();
const showNetflixSubtitles = () => {
timedTextElem?.show(true);
imageTimedTextElem?.show(true);
};
showNetflixSubtitles();
};
}, []);

Expand Down

0 comments on commit a657949

Please sign in to comment.