mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
Drop subtitles
This commit is contained in:
parent
2a0e46a97d
commit
57d3f69efa
3 changed files with 82 additions and 3 deletions
|
@ -316,7 +316,7 @@
|
||||||
"unknownOption": "Unknown"
|
"unknownOption": "Unknown"
|
||||||
},
|
},
|
||||||
"subtitles": {
|
"subtitles": {
|
||||||
"customChoice": "Select subtitle from file",
|
"customChoice": "Drop or upload file",
|
||||||
"customizeLabel": "Customize",
|
"customizeLabel": "Customize",
|
||||||
"offChoice": "Off",
|
"offChoice": "Off",
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
51
src/components/DropFile.tsx
Normal file
51
src/components/DropFile.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import type { DragEvent, ReactNode } from "react";
|
||||||
|
|
||||||
|
interface FileDropHandlerProps {
|
||||||
|
children: ReactNode;
|
||||||
|
className: string;
|
||||||
|
onDrop: (event: DragEvent<HTMLDivElement>) => void;
|
||||||
|
onDraggingChange: (isDragging: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FileDropHandler(props: FileDropHandlerProps) {
|
||||||
|
const [dragging, setDragging] = useState(false);
|
||||||
|
|
||||||
|
const handleDragEnter = (event: DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setDragging(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragLeave = (event: DragEvent<HTMLDivElement>) => {
|
||||||
|
if (!event.currentTarget.contains(event.relatedTarget as Node)) {
|
||||||
|
setDragging(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setDragging(false);
|
||||||
|
|
||||||
|
props.onDrop(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.onDraggingChange(dragging);
|
||||||
|
}, [dragging, props]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
className={props.className}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import { useAsyncFn } from "react-use";
|
||||||
import { convert } from "subsrt-ts";
|
import { convert } from "subsrt-ts";
|
||||||
|
|
||||||
import { subtitleTypeList } from "@/backend/helpers/subs";
|
import { subtitleTypeList } from "@/backend/helpers/subs";
|
||||||
|
import { FileDropHandler } from "@/components/DropFile";
|
||||||
import { FlagIcon } from "@/components/FlagIcon";
|
import { FlagIcon } from "@/components/FlagIcon";
|
||||||
import { useCaptions } from "@/components/player/hooks/useCaptions";
|
import { useCaptions } from "@/components/player/hooks/useCaptions";
|
||||||
import { Menu } from "@/components/player/internals/ContextMenu";
|
import { Menu } from "@/components/player/internals/ContextMenu";
|
||||||
|
@ -123,6 +124,8 @@ export function CaptionsView({ id }: { id: string }) {
|
||||||
const { selectCaptionById, disable } = useCaptions();
|
const { selectCaptionById, disable } = useCaptions();
|
||||||
const captionList = usePlayerStore((s) => s.captionList);
|
const captionList = usePlayerStore((s) => s.captionList);
|
||||||
const getHlsCaptionList = usePlayerStore((s) => s.display?.getCaptionList);
|
const getHlsCaptionList = usePlayerStore((s) => s.display?.getCaptionList);
|
||||||
|
const [dragging, setDragging] = useState(false);
|
||||||
|
const setCaption = usePlayerStore((s) => s.setCaption);
|
||||||
|
|
||||||
const captions = useMemo(
|
const captions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -162,7 +165,31 @@ export function CaptionsView({ id }: { id: string }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<FileDropHandler
|
||||||
|
className={`transition duration-300 ${dragging ? "brightness-50" : ""}`}
|
||||||
|
onDraggingChange={(isDragging) => {
|
||||||
|
setDragging(isDragging);
|
||||||
|
}}
|
||||||
|
onDrop={(event) => {
|
||||||
|
const files = event.dataTransfer.files;
|
||||||
|
if (!files || files.length === 0) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener("load", (e) => {
|
||||||
|
if (!e.target || typeof e.target.result !== "string") return;
|
||||||
|
|
||||||
|
const converted = convert(e.target.result, "srt");
|
||||||
|
|
||||||
|
setCaption({
|
||||||
|
language: "custom",
|
||||||
|
srtData: converted,
|
||||||
|
id: "custom-caption",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
reader.readAsText(files[0]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<Menu.BackLink
|
<Menu.BackLink
|
||||||
onClick={() => router.navigate("/")}
|
onClick={() => router.navigate("/")}
|
||||||
|
@ -182,6 +209,7 @@ export function CaptionsView({ id }: { id: string }) {
|
||||||
<Input value={searchQuery} onInput={setSearchQuery} />
|
<Input value={searchQuery} onInput={setSearchQuery} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Menu.ScrollToActiveSection className="!pt-1 mt-2 pb-3">
|
<Menu.ScrollToActiveSection className="!pt-1 mt-2 pb-3">
|
||||||
<CaptionOption onClick={() => disable()} selected={!selectedCaptionId}>
|
<CaptionOption onClick={() => disable()} selected={!selectedCaptionId}>
|
||||||
{t("player.menus.subtitles.offChoice")}
|
{t("player.menus.subtitles.offChoice")}
|
||||||
|
@ -189,7 +217,7 @@ export function CaptionsView({ id }: { id: string }) {
|
||||||
<CustomCaptionOption />
|
<CustomCaptionOption />
|
||||||
{content}
|
{content}
|
||||||
</Menu.ScrollToActiveSection>
|
</Menu.ScrollToActiveSection>
|
||||||
</>
|
</FileDropHandler>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue