feat: 4K downloads, watch progress, player controls, new icon
Window chrome: titleBarStyle Overlay (Transparent left the native bar in place, white in dark mode, with a dead strip beneath it). The app now paints that strip itself, so it matches the page background. Player: prev/next through the feed, a full-screen button, a spinner while a stream resolves, Open on YouTube on downloaded videos too, and the description collapsed behind a disclosure. Downloads default to Best, which reaches real 4K — above 1080p YouTube serves VP9/AV1, verified to play natively in WKWebView here. Audio stays pinned to AAC because Opus in MP4 would be silent. A Compatible setting keeps the old 1080p H.264 behaviour. Files are now named '<ISO date> - <title> [<id>].mp4'. Watch progress is recorded and drawn under thumbnails like YouTube's, and reopening a video resumes where it left off. Tiles clamp every text line to a fixed height so they share a baseline, and the search field no longer clips its placeholder. Fixes a Picture-in-Picture leak: WebKit kept a detached video playing after the player closed, so a second video could play over the first with no way to stop it. Every exit path now tears the element down.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import type { FeedItem } from "../types";
|
||||
|
||||
/** Fraction of the video watched, or null if it was never opened. */
|
||||
export function watchedFraction(item: FeedItem): number | null {
|
||||
const { position, duration } = item;
|
||||
if (position == null || duration == null || duration <= 0) return null;
|
||||
return Math.min(1, Math.max(0, position / duration));
|
||||
}
|
||||
|
||||
/**
|
||||
* The red sliver across the bottom of a thumbnail, same idea as YouTube's.
|
||||
* Red rather than the sky accent on purpose: the accent means "the action to
|
||||
* take" or "currently selected", and this is neither.
|
||||
*/
|
||||
export default function WatchBar({ item }: { item: FeedItem }) {
|
||||
const f = watchedFraction(item);
|
||||
if (f == null || f < 0.01) return null;
|
||||
const nearlyDone = f > 0.97;
|
||||
return (
|
||||
<span
|
||||
className="absolute inset-x-0 bottom-0 h-[3px] bg-slate-950/45"
|
||||
title={nearlyDone ? "Watched" : `${Math.round(f * 100)}% watched`}
|
||||
>
|
||||
<span
|
||||
className="block h-full bg-red-600"
|
||||
style={{ width: `${Math.max(2, f * 100)}%` }}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user