feat: subtitles, delete-all, auto refresh, and menu cleanup
Subtitles: a language preference in Settings drives what is shown and what is downloaded. yt-dlp saves WebVTT sidecars next to each download, including YouTube's auto-generated track, and the player attaches them as <track> elements so they work offline. Sidecars are removed with their video, and by Delete all. WebVTT rather than muxed subtitle streams because WebKit reads a <track> reliably and largely ignores subtitle tracks inside an MP4. Downloads in progress now appear under the downloaded-only filter, so a download you just started does not vanish from the list you are watching it in. That view also gains a Delete all, behind a confirmation naming what goes. The feed refreshes on launch and whenever the player closes, so the Refresh button is only for staleness. Player: Delete moved to the footer and shortened, Open on YouTube is now an external-link icon. Removes the Edit and Help menus. Cut/Copy/Paste move to the app menu, without which their shortcuts would stop working in the search field. The webview context menu is suppressed outside text fields — its Reload and Back items act on a page the app does not present as one. Settings now warns that 4K AV1 plays back with artefacts: the files decode cleanly in ffmpeg, so it is the built-in decoder, not the download.
This commit is contained in:
@@ -4,7 +4,8 @@ import {
|
||||
} from "../api";
|
||||
import { APPEARANCE_MODES, type Appearance } from "../hooks/useAppearance";
|
||||
import {
|
||||
QUALITIES, STREAM_QUALITIES, type ImportPreview, type Prereqs, type Quality,
|
||||
QUALITIES, STREAM_QUALITIES, SUB_LANGS,
|
||||
type ImportPreview, type Prereqs, type Quality,
|
||||
} from "../types";
|
||||
import TakeoutGuide from "./TakeoutGuide";
|
||||
import {
|
||||
@@ -20,6 +21,8 @@ interface Props {
|
||||
onQuality: (q: Quality) => void;
|
||||
streamQuality: Quality;
|
||||
onStreamQuality: (q: Quality) => void;
|
||||
subLang: string;
|
||||
onSubLang: (l: string) => void;
|
||||
onError: (message: string) => void;
|
||||
}
|
||||
|
||||
@@ -44,7 +47,7 @@ function StatusRow({ label, value }: { label: string; value: string | null }) {
|
||||
|
||||
export default function Settings({
|
||||
onClose, onImported, appearance, onAppearance, quality, onQuality,
|
||||
streamQuality, onStreamQuality, onError,
|
||||
streamQuality, onStreamQuality, subLang, onSubLang, onError,
|
||||
}: Props) {
|
||||
const [prereqs, setPrereqs] = useState<Prereqs | null>(null);
|
||||
const [pending, setPending] = useState<{ path: string; preview: ImportPreview } | null>(null);
|
||||
@@ -182,6 +185,26 @@ export default function Settings({
|
||||
Streaming quality applies when you play something you have not downloaded.
|
||||
Best lets the player adapt to your connection; a fixed height pins it.
|
||||
</p>
|
||||
|
||||
<label className="mt-3 grid grid-cols-[92px_1fr] items-center gap-2">
|
||||
<span className={LABEL}>Subtitles</span>
|
||||
<select
|
||||
value={subLang}
|
||||
onChange={(e) => onSubLang(e.target.value)}
|
||||
className={SELECT}
|
||||
>
|
||||
{SUB_LANGS.map((l) => (
|
||||
<option key={l.value} value={l.value}>
|
||||
{l.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<p className={`mt-2 ${HELP}`}>
|
||||
Shown automatically when a video has subtitles in this language, including
|
||||
YouTube's auto-generated ones, and saved alongside anything you download so
|
||||
they work offline. You can still switch tracks from the player.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="border-b border-slate-200 px-5 py-4 dark:border-slate-800">
|
||||
|
||||
Reference in New Issue
Block a user