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:
@@ -2,6 +2,7 @@ import { thumbSrc } from "../api";
|
||||
import type { LiveDownload } from "../hooks/useDownloads";
|
||||
import type { FeedItem } from "../types";
|
||||
import DownloadButton from "./DownloadButton";
|
||||
import WatchBar from "./WatchBar";
|
||||
import { compactViews, relativeTime } from "./format";
|
||||
|
||||
interface Props {
|
||||
@@ -45,35 +46,49 @@ export default function VideoTile({
|
||||
)}
|
||||
{downloaded && (
|
||||
<span
|
||||
className="absolute bottom-1.5 right-1.5 rounded bg-sky-500 px-1 py-0.5 text-[9px]
|
||||
className="absolute bottom-2 right-1.5 rounded bg-sky-500 px-1 py-0.5 text-[9px]
|
||||
font-bold uppercase tracking-widest leading-none text-white"
|
||||
>
|
||||
Offline
|
||||
</span>
|
||||
)}
|
||||
<WatchBar item={item} />
|
||||
</button>
|
||||
|
||||
<div className="mt-2 flex min-w-0 flex-1 flex-col">
|
||||
{/* Every text block is a fixed height and every line is clamped, so tiles
|
||||
stay on a shared baseline no matter how long a title runs. */}
|
||||
<div className="mt-2 flex min-w-0 flex-col">
|
||||
<button onClick={onOpen} className="cursor-pointer text-left">
|
||||
<h3 className="line-clamp-2 text-[13px] font-medium leading-snug">{item.title}</h3>
|
||||
<h3
|
||||
className="line-clamp-2 h-[2.25rem] text-[13px] font-medium leading-snug"
|
||||
title={item.title}
|
||||
>
|
||||
{item.title}
|
||||
</h3>
|
||||
</button>
|
||||
<div className="mt-1 truncate text-[12px] text-slate-500 dark:text-slate-400">
|
||||
<div
|
||||
className="mt-1 h-4 truncate text-[12px] leading-4 text-slate-500 dark:text-slate-400"
|
||||
title={item.channel_title}
|
||||
>
|
||||
{item.channel_title}
|
||||
</div>
|
||||
<div className="mt-0.5 text-[11px] text-slate-400 dark:text-slate-500">
|
||||
<div className="mt-0.5 h-4 truncate text-[11px] leading-4 text-slate-400 dark:text-slate-500">
|
||||
{[compactViews(item.views), relativeTime(item.published)].filter(Boolean).join(" · ")}
|
||||
</div>
|
||||
|
||||
{(live?.error ?? item.error) && (live?.state ?? item.state) === "failed" && (
|
||||
<div className="mt-1 line-clamp-2 text-[11px] text-red-600 dark:text-red-400">
|
||||
{live?.error ?? item.error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-2 flex">
|
||||
<div className="mt-2 flex h-7 items-start">
|
||||
<DownloadButton item={item} live={live} online={online}
|
||||
onDownload={onDownload} onCancel={onCancel} onDelete={onDelete} />
|
||||
</div>
|
||||
|
||||
{(live?.error ?? item.error) && (live?.state ?? item.state) === "failed" && (
|
||||
<div
|
||||
className="mt-1 line-clamp-1 text-[11px] text-red-600 dark:text-red-400"
|
||||
title={live?.error ?? item.error ?? undefined}
|
||||
>
|
||||
{live?.error ?? item.error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user