Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
allowBuilds:
esbuild: true
33 changes: 33 additions & 0 deletions src/assets/style/content/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,39 @@
}
}

.es-jellyfin {
.es-settings-icon {
svg {
width: 28px;
}
}

.es-settings-content {
position: fixed;
z-index: 2147483647;
bottom: 80px;
right: 20px;
pointer-events: auto;
}
}

.es-jellyfin.es-enabled {
#es {
position: fixed;
z-index: 2147483647;
bottom: 100px;
pointer-events: auto;

@media screen and (max-width: 1520px) {
bottom: 80px;
}

* {
pointer-events: auto;
}
}
}

.es-enabled {
#es {
display: block;
Expand Down
11 changes: 11 additions & 0 deletions src/assets/style/content/progress-bar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@
}
}

.es-jellyfin.es-enabled.es-progress-bar-enabled {
.es-progress-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 100%;
z-index: 9998;
}
}

.es-kinopub.es-enabled.es-progress-bar-enabled {
.jw-controlbar {
bottom: 15px;
Expand Down
12 changes: 0 additions & 12 deletions src/models/subs/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,6 @@ $rawSubs.on(
(_, subs) => subs
);

$rawSubs.on(rawSubsAdded, (oldSubs, newSubs) => {
const lastSub = oldSubs[oldSubs.length - 1];
if (!lastSub) {
return [...oldSubs, ...newSubs];
}
if (lastSub.text != newSubs[0].text && lastSub.start != newSubs[0].start) {
const subs = oldSubs.slice(0, -1);
lastSub.end = lastSub.start;
return [...subs, ...[lastSub], ...newSubs];
}
});

$rawSubs.reset(resetSubs);
$currentSubs.on([updateCurrentSubsFx.doneData, autoPauseFx.doneData], (oldSubs, subs) =>
JSON.stringify(oldSubs) === JSON.stringify(subs) ? oldSubs : subs
Expand Down
69 changes: 69 additions & 0 deletions src/pages/content/components/Settings/JellyfinSubTrack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { FC, useEffect, useState } from "react";
import { useUnit } from "effector-react";
import { $video } from "@src/models/videos";
import { esSubsChanged } from "@src/models/subs";
import { Select } from "../ui/Select/Select";

type TrackOption = { value: number; label: string };

const DISABLED_OPTION: TrackOption = { value: -1, label: "Disabled" };

export const JellyfinSubTrack: FC = () => {
const video = useUnit($video);
const [options, setOptions] = useState<TrackOption[]>([]);
const [selected, setSelected] = useState<TrackOption>(DISABLED_OPTION);

useEffect(() => {
if (!video) return;

const buildOptions = () => {
const opts: TrackOption[] = [DISABLED_OPTION];
let active: TrackOption = DISABLED_OPTION;
for (let i = 0; i < video.textTracks.length; i++) {
const t = video.textTracks[i];
if (t.kind !== "subtitles" && t.kind !== "captions") continue;
const label = t.label || t.language || `Track ${i}`;
opts.push({ value: i, label });
if (t.mode !== "disabled") active = { value: i, label };
}
setOptions(opts);
setSelected(active);
};

buildOptions();
video.textTracks.addEventListener("change", buildOptions);
video.textTracks.addEventListener("addtrack", buildOptions);
return () => {
video.textTracks.removeEventListener("change", buildOptions);
video.textTracks.removeEventListener("addtrack", buildOptions);
};
}, [video]);

const handleChange = (opt: TrackOption | null) => {
if (!video || !opt) return;
for (let i = 0; i < video.textTracks.length; i++) {
const t = video.textTracks[i];
if (t.kind !== "subtitles" && t.kind !== "captions") continue;
t.mode = i === opt.value ? "showing" : "disabled";
}
if (opt.value === -1) {
esSubsChanged(""); // Clear the EasySubs overlay immediately
}
setSelected(opt);
};

if (options.length <= 1) return null;

return (
<div className="es-settings-content__element">
<div className="es-settings-content__element__left">Subtitle track</div>
<div className="es-settings-content__element__right">
<Select
options={options}
value={selected}
onChange={(opt) => handleChange(opt as TrackOption)}
/>
</div>
</div>
);
};
12 changes: 10 additions & 2 deletions src/pages/content/components/Settings/SettingsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import {
} from "@src/models/settings";
import { EnableNetflixOnFlight } from "./EnableNetflixOnFlight";
import { EnableAutoStop } from "./EnableAutoStop";
import { JellyfinSubTrack } from "./JellyfinSubTrack";
import { createPortal } from "react-dom";
import { $streaming } from "@src/models/streamings";

interface TabProps {
isActive: boolean;
Expand All @@ -48,17 +50,18 @@ const Tab: FC<PropsWithChildren<TabProps>> = ({
};

export const SettingsContent: FC<{ onClose: () => void }> = ({ onClose }) => {
const [activeSettingsTab, handleActiveSettingsTabChanged] = useUnit([
const [activeSettingsTab, handleActiveSettingsTabChanged, streaming] = useUnit([
$activeSettingsTab,
activeSettingsTabChanged,
$streaming,
]);
const contentRef = useRef();

useClickOutside(contentRef, onClose);

return (
<>
<div className="es-settings-content" ref={contentRef}>
<div className="es-settings-content" ref={contentRef} onClick={(e) => e.stopPropagation()}>
<div className="es-settings-content__menu">
<div className="es-settings-content__menu__items">
<Tab
Expand Down Expand Up @@ -130,6 +133,11 @@ export const SettingsContent: FC<{ onClose: () => void }> = ({ onClose }) => {
<div className="es-settings-content__item">
<SubsDelay />
</div>
{streaming.name === "jellyfin" && (
<div className="es-settings-content__item">
<JellyfinSubTrack />
</div>
)}
<div className="es-settings-content__item">
<CustomSubs />
</div>
Expand Down
10 changes: 6 additions & 4 deletions src/pages/content/components/Subs/Subs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ export const Subs: FC<TSubsProps> = () => {
};

return (
<Draggable>
<Draggable cancel=".es-sub">
<div
id="es-subs"
onMouseLeave={handleOnMouseLeave}
onMouseEnter={handleOnMouseEnter}
onClick={(e) => e.stopPropagation()}
style={{ fontSize: `${((video.clientWidth / 100) * subsFontSize) / 43}px` }}
>
{currentSubs.map((sub) => (
Expand Down Expand Up @@ -132,9 +133,10 @@ const SubItem: FC<TSubItemProps> = ({ subItem, index }) => {
handleSubItemMouseEntered(subItem.cleanedText);
};

const handleClick = () => {
setShowTranslation(false);
handleSubItemMouseLeft();
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
setShowTranslation(true);
handleSubItemMouseEntered(subItem.cleanedText);
};

return (
Expand Down
4 changes: 2 additions & 2 deletions src/pages/content/components/ui/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const customStyles = {
minHeight: "24px",
height: "24px",
}),
menuPortal: (provided) => ({ ...provided, zIndex: 10000, fontSize: "14px" }),
menu: (provided) => ({ ...provided, zIndex: 10000 }),
menuPortal: (provided) => ({ ...provided, zIndex: 2147483647, fontSize: "14px" }),
menu: (provided) => ({ ...provided, zIndex: 2147483647 }),
valueContainer: (provided, _state) => ({
...provided,
height: "24px",
Expand Down
13 changes: 11 additions & 2 deletions src/pages/content/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,22 @@ const handleTimeUpdate = () => {
videoTimeUpdate();
};

let detectionRetries = 0;

$streaming.watch((streaming) => {
console.log("streaming changed", streaming);
document.body.classList.add("es-" + streaming.name);

if (streaming == null) {
if (streaming.name === "stub") {
if (detectionRetries < 5) {
detectionRetries++;
setTimeout(() => fetchCurrentStreamingFx(), 1500);
}
return;
}

detectionRetries = 0;
document.body.classList.add("es-" + streaming.name);

esRenderSetings.watch(() => {
console.log("Event:", "esRenderSetings");
document.querySelectorAll(".es-settings").forEach((e) => e.remove());
Expand Down Expand Up @@ -56,6 +64,7 @@ esSubsChanged.watch((language) => {
const subsContainer = $streaming.getState().getSubsContainer();
const subsNode = document.createElement("div");
subsNode.id = "es";
subsNode.addEventListener("click", (e) => e.stopPropagation());
subsContainer?.appendChild(subsNode);
createRoot(subsNode).render(<Subs />);

Expand Down
23 changes: 21 additions & 2 deletions src/pages/popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,26 @@ const Popup = () => {
permissions: ["scripting", "storage", "activeTab"],
origins: [tab.url],
});
if (isGranted) {
if (!isGranted) return;

// Inject into the current tab without reloading, so SPA players
// (e.g. Jellyfin) don't lose their playback state. Future page loads
// are handled automatically by webext-dynamic-content-scripts.
const [contentScript] = chrome.runtime.getManifest().content_scripts ?? [];
try {
if (contentScript?.css?.length) {
await chrome.scripting.insertCSS({
target: { tabId: tab.id },
files: contentScript.css,
});
}
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: contentScript.js,
});
window.close();
} catch (e) {
// Fallback: reload if programmatic injection fails
chrome.tabs.reload(tab.id);
}
};
Expand All @@ -39,7 +58,7 @@ const Popup = () => {
</a>
</li>
<li onClick={handleRequestPermissions}>
<a className="es-popup-kinopub">Enable on Kinopub</a>
<a className="es-popup-kinopub">Enable on this site</a>
</li>
<li onClick={handleFaqLinkClick}>
<a>FAQ</a>
Expand Down
Loading