mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-21 13:32:19 +00:00
Merge branch 'p-stream:production' into substranslate
This commit is contained in:
commit
e02af4931c
2 changed files with 81 additions and 23 deletions
|
|
@ -264,10 +264,46 @@ export function CustomCaptionOption() {
|
||||||
const setCaption = usePlayerStore((s) => s.setCaption);
|
const setCaption = usePlayerStore((s) => s.setCaption);
|
||||||
const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs);
|
const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs);
|
||||||
const fileInput = useRef<HTMLInputElement>(null);
|
const fileInput = useRef<HTMLInputElement>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleFileSelect = (file: File) => {
|
||||||
|
setError(null);
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.addEventListener("load", (event) => {
|
||||||
|
if (!event.target || typeof event.target.result !== "string") {
|
||||||
|
setError("Failed to read file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const converted = convert(event.target.result, "srt");
|
||||||
|
setCaption({
|
||||||
|
language: "custom",
|
||||||
|
srtData: converted,
|
||||||
|
id: "custom-caption",
|
||||||
|
});
|
||||||
|
setCustomSubs();
|
||||||
|
} catch (err) {
|
||||||
|
setError(
|
||||||
|
err instanceof Error
|
||||||
|
? err.message
|
||||||
|
: "Failed to convert subtitle file",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
reader.addEventListener("error", () => {
|
||||||
|
setError("Failed to read file");
|
||||||
|
});
|
||||||
|
|
||||||
|
reader.readAsText(file, "utf-8");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CaptionOption
|
<CaptionOption
|
||||||
selected={lang === "custom"}
|
selected={lang === "custom"}
|
||||||
|
error={error}
|
||||||
onClick={() => fileInput.current?.click()}
|
onClick={() => fileInput.current?.click()}
|
||||||
>
|
>
|
||||||
{t("player.menus.subtitles.customChoice")}
|
{t("player.menus.subtitles.customChoice")}
|
||||||
|
|
@ -277,20 +313,22 @@ export function CustomCaptionOption() {
|
||||||
accept={subtitleTypeList.join(",")}
|
accept={subtitleTypeList.join(",")}
|
||||||
type="file"
|
type="file"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (!e.target.files) return;
|
const files = e.target.files;
|
||||||
const reader = new FileReader();
|
if (!files || files.length === 0) return;
|
||||||
reader.addEventListener("load", (event) => {
|
|
||||||
if (!event.target || typeof event.target.result !== "string")
|
const file = files[0];
|
||||||
return;
|
const fileExtension = `.${file.name.split(".").pop()?.toLowerCase()}`;
|
||||||
const converted = convert(event.target.result, "srt");
|
|
||||||
setCaption({
|
if (!subtitleTypeList.includes(fileExtension)) {
|
||||||
language: "custom",
|
setError(
|
||||||
srtData: converted,
|
`Unsupported file type. Supported: ${subtitleTypeList.join(", ")}`,
|
||||||
id: "custom-caption",
|
);
|
||||||
});
|
e.target.value = ""; // Reset input
|
||||||
setCustomSubs();
|
return;
|
||||||
});
|
}
|
||||||
reader.readAsText(e.target.files[0], "utf-8");
|
|
||||||
|
handleFileSelect(file);
|
||||||
|
e.target.value = ""; // Reset input so same file can be selected again
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</CaptionOption>
|
</CaptionOption>
|
||||||
|
|
@ -405,6 +443,7 @@ export function CaptionsView({
|
||||||
);
|
);
|
||||||
const delay = useSubtitleStore((s) => s.delay);
|
const delay = useSubtitleStore((s) => s.delay);
|
||||||
const appLanguage = useLanguageStore((s) => s.language);
|
const appLanguage = useLanguageStore((s) => s.language);
|
||||||
|
const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs);
|
||||||
|
|
||||||
// Get combined caption list
|
// Get combined caption list
|
||||||
const captions = useMemo(
|
const captions = useMemo(
|
||||||
|
|
@ -479,29 +518,41 @@ export function CaptionsView({
|
||||||
}, [srtData, selectedLanguage, delay, videoTime, selectedCaptionId]);
|
}, [srtData, selectedLanguage, delay, videoTime, selectedCaptionId]);
|
||||||
|
|
||||||
function onDrop(event: DragEvent<HTMLDivElement>) {
|
function onDrop(event: DragEvent<HTMLDivElement>) {
|
||||||
|
event.preventDefault();
|
||||||
const files = event.dataTransfer.files;
|
const files = event.dataTransfer.files;
|
||||||
const firstFile = files[0];
|
const firstFile = files[0];
|
||||||
if (!files || !firstFile) return;
|
if (!files || !firstFile) return;
|
||||||
|
|
||||||
const fileExtension = `.${firstFile.name.split(".").pop()}`;
|
const fileExtension = `.${firstFile.name.split(".").pop()?.toLowerCase()}`;
|
||||||
if (!fileExtension || !subtitleTypeList.includes(fileExtension)) {
|
if (!fileExtension || !subtitleTypeList.includes(fileExtension)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.addEventListener("load", (e) => {
|
reader.addEventListener("load", (e) => {
|
||||||
if (!e.target || typeof e.target.result !== "string") return;
|
if (!e.target || typeof e.target.result !== "string") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const converted = convert(e.target.result, "srt");
|
try {
|
||||||
|
const converted = convert(e.target.result, "srt");
|
||||||
|
|
||||||
setCaption({
|
setCaption({
|
||||||
language: "custom",
|
language: "custom",
|
||||||
srtData: converted,
|
srtData: converted,
|
||||||
id: "custom-caption",
|
id: "custom-caption",
|
||||||
});
|
});
|
||||||
|
setCustomSubs();
|
||||||
|
} catch (err) {
|
||||||
|
// Silently fail on drop - user can use the upload button for better error feedback
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
reader.readAsText(firstFile);
|
reader.addEventListener("error", () => {
|
||||||
|
// Silently fail on drop - user can use the upload button for better error feedback
|
||||||
|
});
|
||||||
|
|
||||||
|
reader.readAsText(firstFile, "utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,13 @@ export function useCaptions() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedCaption) return;
|
if (!selectedCaption) return;
|
||||||
|
|
||||||
|
// Skip validation for custom/pasted captions that aren't in the caption list
|
||||||
|
const isCustomCaption =
|
||||||
|
selectedCaption.id === "custom-caption" ||
|
||||||
|
selectedCaption.id === "pasted-caption";
|
||||||
|
|
||||||
|
if (isCustomCaption) return;
|
||||||
|
|
||||||
const isSelectedCaptionStillAvailable = captions.some(
|
const isSelectedCaptionStillAvailable = captions.some(
|
||||||
(caption) => caption.id === selectedCaption.id,
|
(caption) => caption.id === selectedCaption.id,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue