mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
fix custom subs + download
This commit is contained in:
parent
0883942093
commit
ca402a219d
5 changed files with 40 additions and 12 deletions
|
@ -13,7 +13,7 @@ interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
href?: string;
|
href?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
download?: boolean;
|
download?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Button(props: Props) {
|
export function Button(props: Props) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { Input } from "@/components/player/internals/ContextMenu/Input";
|
||||||
import { SelectableLink } from "@/components/player/internals/ContextMenu/Links";
|
import { SelectableLink } from "@/components/player/internals/ContextMenu/Links";
|
||||||
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
||||||
import { usePlayerStore } from "@/stores/player/store";
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
|
import { useSubtitleStore } from "@/stores/subtitles";
|
||||||
|
|
||||||
export function CaptionOption(props: {
|
export function CaptionOption(props: {
|
||||||
countryCode?: string;
|
countryCode?: string;
|
||||||
|
@ -94,6 +95,7 @@ function searchSubs(
|
||||||
function CustomCaptionOption() {
|
function CustomCaptionOption() {
|
||||||
const lang = usePlayerStore((s) => s.caption.selected?.language);
|
const lang = usePlayerStore((s) => s.caption.selected?.language);
|
||||||
const setCaption = usePlayerStore((s) => s.setCaption);
|
const setCaption = usePlayerStore((s) => s.setCaption);
|
||||||
|
const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs);
|
||||||
const fileInput = useRef<HTMLInputElement>(null);
|
const fileInput = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -118,6 +120,7 @@ function CustomCaptionOption() {
|
||||||
language: "custom",
|
language: "custom",
|
||||||
srtData: converted,
|
srtData: converted,
|
||||||
});
|
});
|
||||||
|
setCustomSubs();
|
||||||
});
|
});
|
||||||
reader.readAsText(e.target.files[0], "utf-8");
|
reader.readAsText(e.target.files[0], "utf-8");
|
||||||
}}
|
}}
|
||||||
|
@ -126,9 +129,7 @@ function CustomCaptionOption() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO on initialize, download captions
|
|
||||||
// TODO fix language names, some are unknown
|
// TODO fix language names, some are unknown
|
||||||
// TODO delay setting for captions
|
|
||||||
export function CaptionsView({ id }: { id: string }) {
|
export function CaptionsView({ id }: { id: string }) {
|
||||||
const router = useOverlayRouter(id);
|
const router = useOverlayRouter(id);
|
||||||
const lang = usePlayerStore((s) => s.caption.selected?.language);
|
const lang = usePlayerStore((s) => s.caption.selected?.language);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Button } from "@/components/Button";
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
import { OverlayPage } from "@/components/overlays/OverlayPage";
|
import { OverlayPage } from "@/components/overlays/OverlayPage";
|
||||||
import { Menu } from "@/components/player/internals/ContextMenu";
|
import { Menu } from "@/components/player/internals/ContextMenu";
|
||||||
import { convertSubtitlesToDataurl } from "@/components/player/utils/captions";
|
import { convertSubtitlesToSrtDataurl } from "@/components/player/utils/captions";
|
||||||
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
||||||
import { usePlayerStore } from "@/stores/player/store";
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
|
|
||||||
|
@ -24,11 +24,13 @@ export function DownloadView({ id }: { id: string }) {
|
||||||
const downloadUrl = useDownloadLink();
|
const downloadUrl = useDownloadLink();
|
||||||
|
|
||||||
const selectedCaption = usePlayerStore((s) => s.caption?.selected);
|
const selectedCaption = usePlayerStore((s) => s.caption?.selected);
|
||||||
const subtitleUrl = selectedCaption
|
const subtitleUrl = useMemo(
|
||||||
? convertSubtitlesToDataurl(selectedCaption?.srtData)
|
() =>
|
||||||
: null;
|
selectedCaption
|
||||||
|
? convertSubtitlesToSrtDataurl(selectedCaption?.srtData)
|
||||||
console.log(subtitleUrl);
|
: null,
|
||||||
|
[selectedCaption]
|
||||||
|
);
|
||||||
|
|
||||||
if (!downloadUrl) return null;
|
if (!downloadUrl) return null;
|
||||||
|
|
||||||
|
@ -66,7 +68,7 @@ export function DownloadView({ id }: { id: string }) {
|
||||||
href={subtitleUrl ?? undefined}
|
href={subtitleUrl ?? undefined}
|
||||||
disabled={!subtitleUrl}
|
disabled={!subtitleUrl}
|
||||||
theme="secondary"
|
theme="secondary"
|
||||||
download
|
download="subtitles.srt"
|
||||||
>
|
>
|
||||||
Download current caption
|
Download current caption
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -35,13 +35,31 @@ export function convertSubtitlesToVtt(text: string): string {
|
||||||
return vtt;
|
return vtt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertSubtitlesToSrt(text: string): string {
|
||||||
|
const textTrimmed = text.trim();
|
||||||
|
if (textTrimmed === "") {
|
||||||
|
throw new Error("Given text is empty");
|
||||||
|
}
|
||||||
|
const srt = convert(textTrimmed, "srt");
|
||||||
|
if (detect(srt) === "") {
|
||||||
|
throw new Error("Invalid subtitle format");
|
||||||
|
}
|
||||||
|
return srt;
|
||||||
|
}
|
||||||
|
|
||||||
export function parseSubtitles(text: string): CaptionCueType[] {
|
export function parseSubtitles(text: string): CaptionCueType[] {
|
||||||
const vtt = convertSubtitlesToVtt(text);
|
const vtt = convertSubtitlesToVtt(text);
|
||||||
return parse(vtt).filter((cue) => cue.type === "caption") as CaptionCueType[];
|
return parse(vtt).filter((cue) => cue.type === "caption") as CaptionCueType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertSubtitlesToDataurl(text: string): string {
|
function stringToBase64(input: string): string {
|
||||||
return `data:text/vtt,${convertSubtitlesToVtt(text)}`;
|
return btoa(String.fromCodePoint(...new TextEncoder().encode(input)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertSubtitlesToSrtDataurl(text: string): string {
|
||||||
|
return `data:application/x-subrip;base64,${stringToBase64(
|
||||||
|
convertSubtitlesToSrt(text)
|
||||||
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertSubtitlesToObjectUrl(text: string): string {
|
export function convertSubtitlesToObjectUrl(text: string): string {
|
||||||
|
|
|
@ -27,6 +27,7 @@ export interface SubtitleStore {
|
||||||
delay: number;
|
delay: number;
|
||||||
updateStyling(newStyling: Partial<SubtitleStyling>): void;
|
updateStyling(newStyling: Partial<SubtitleStyling>): void;
|
||||||
setLanguage(language: string | null): void;
|
setLanguage(language: string | null): void;
|
||||||
|
setCustomSubs(): void;
|
||||||
setOverrideCasing(enabled: boolean): void;
|
setOverrideCasing(enabled: boolean): void;
|
||||||
setDelay(delay: number): void;
|
setDelay(delay: number): void;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +61,12 @@ export const useSubtitleStore = create(
|
||||||
if (lang) s.lastSelectedLanguage = lang;
|
if (lang) s.lastSelectedLanguage = lang;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setCustomSubs() {
|
||||||
|
set((s) => {
|
||||||
|
s.enabled = true;
|
||||||
|
s.lastSelectedLanguage = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
setOverrideCasing(enabled) {
|
setOverrideCasing(enabled) {
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.overrideCasing = enabled;
|
s.overrideCasing = enabled;
|
||||||
|
|
Loading…
Reference in a new issue