feat: uniform control height, chrome auto-hide, streaming quality

Every control in the app now shares one height (CONTROL_H), with width
still growing to fit its label. The player transport is larger, Back is
an icon, and the footer buttons match the prev/next size.

Player chrome — transport and edge arrows — fades after 2.6s of
inactivity and never hides while paused.

The title-bar strip collapses in macOS window fullscreen, where the
traffic lights are gone and it was just a blank white bar.

Streaming quality is now selectable alongside download quality. A cap is
applied by rewriting YouTube's HLS master playlist down to the best
variant at or below the chosen height, keeping its audio group. That
playlist is served from a small loopback HTTP server: Safari's native
HLS is backed by AVFoundation, which cannot read blob: or custom-scheme
URLs, so a Blob URL silently fails to play.

Settings closes with an icon button and its selects match the shared
control height.
This commit is contained in:
vincent
2026-08-29 11:28:05 +02:00
parent 211823f265
commit 9bb7b71225
16 changed files with 503 additions and 92 deletions
+13
View File
@@ -89,6 +89,11 @@ export interface ImportPreview {
/** "best" or a maximum height in pixels. */
export type Quality = "best" | "2160" | "1440" | "1080" | "720" | "480";
export interface Stream {
url: string | null;
playlist: string | null;
}
export const QUALITIES: Array<{ value: Quality; label: string }> = [
{ value: "best", label: "Best available (up to 4K)" },
{ value: "2160", label: "2160p — 4K" },
@@ -97,3 +102,11 @@ export const QUALITIES: Array<{ value: Quality; label: string }> = [
{ value: "720", label: "720p" },
{ value: "480", label: "480p" },
];
/** Streaming tops out at 1080p — YouTube's HLS carries nothing higher. */
export const STREAM_QUALITIES: Array<{ value: Quality; label: string }> = [
{ value: "best", label: "Best available (adaptive)" },
{ value: "1080", label: "1080p" },
{ value: "720", label: "720p" },
{ value: "480", label: "480p" },
];