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:
+14
-3
@@ -12,6 +12,7 @@ import type {
|
||||
Quality,
|
||||
RefreshProgress,
|
||||
RefreshSummary,
|
||||
Stream,
|
||||
} from "./types";
|
||||
|
||||
export const checkPrereqs = () => invoke<Prereqs>("check_prereqs");
|
||||
@@ -37,9 +38,19 @@ export const deleteDownload = (videoId: string) =>
|
||||
|
||||
export const getConnectivity = () => invoke<boolean>("get_connectivity");
|
||||
|
||||
/** A directly playable URL (HLS master playlist) for an undownloaded video. */
|
||||
export const resolveStream = (videoId: string) =>
|
||||
invoke<string>("resolve_stream", { videoId });
|
||||
/**
|
||||
* A directly playable URL for an undownloaded video. When a quality cap is set
|
||||
* the backend serves a rewritten playlist from its own loopback server, so this
|
||||
* is always a plain URL either way.
|
||||
*/
|
||||
export async function resolveStream(
|
||||
videoId: string,
|
||||
maxHeight: number | null,
|
||||
): Promise<string> {
|
||||
const s = await invoke<Stream>("resolve_stream", { videoId, maxHeight });
|
||||
if (!s.url) throw new Error("No playable stream was returned.");
|
||||
return s.url;
|
||||
}
|
||||
|
||||
export const setLibraryPath = (path: string) =>
|
||||
invoke<string>("set_library_path", { path });
|
||||
|
||||
Reference in New Issue
Block a user