Commit Graph
8 Commits
Author SHA1 Message Date
vincent dc1efccf87 feat: icon view toggle, failing filter, quality badges
List and Tiles are now icons rather than words, centred in their
buttons — Segmented takes a ReactNode label and an optional title.

The red "N failing" count in the sidebar is a button: click it to see
only the channels that failed to refresh, click again for all of them.
It clears itself once a refresh fixes everything, so the filter can
never strand you on an empty list.

The player states its quality as a badge instead of loose grey text,
and a downloaded video now says so too — "Downloaded · 1440p" — read
from the file itself. Quality is the short side, so a portrait Short
reads 1080p rather than 1920p, and it resets between videos so one
never inherits the previous one's number.
2026-08-29 15:47:32 +02:00
vincent 48acaa359f feat: channel failures, updater, and a tidier Settings
Failing channels are visible instead of buried in a toast count. Each
refresh records its outcome per channel, the sidebar marks the failures
and counts them in its heading, and selecting one explains why above its
videos. Yours turn out to be three channels returning HTTP 404 — removed
or renamed on YouTube.

Video lengths now fill for what is on screen. Filling the newest across
all subscriptions meant a channel's videos stayed blank forever, since
the global newest always won the queue.

yt-dlp can be updated from Settings, which also says whether it is
current. It breaks whenever YouTube changes something, so it lands in
app data and takes precedence over the bundled copy; ffmpeg is stable
and ships with each release, so it is shown but not updated.

Also: 'Downloaded only' is now 'Local'; Hide Shorts moved to Settings;
the seven-step Takeout guide moved behind a button, since it dominated
the panel; the player names the height it is actually streaming, which
changes as an adaptive stream switches rendition; the player's delete is
an icon; and the window minimum drops to 1080 now that the control row
has one fewer button, while still never wrapping.
2026-08-29 15:17:15 +02:00
vincent e556534459 fix: unbroken divider between sidebar and feed
The title bar was one band across the top of the window, so the
sidebar's right border only began below it and the pane read as
detached from the top of the window.

Each pane now reserves its own strip for the traffic lights instead.
The border runs from the very top, and because both insets are the same
height the sidebar header and the top bar share a baseline, so their
bottom borders form one continuous line across the window. Both strips
stay draggable, and the inset collapses in window fullscreen where
there are no traffic lights to clear.
2026-08-29 12:39:53 +02:00
vincent 9bb7b71225 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.
2026-08-29 11:28:05 +02:00
vincent 211823f265 feat: custom transport bar, live refresh, collapsible sidebar
Replace WebKit's native video controls with our own. WebKit puts
fullscreen, Picture-in-Picture and volume as overlay buttons in the
video's top corners with no way to move them; owning the bar is the only
way to get every control into one strip along the bottom. Right-click on
the player is suppressed — its menu acted on a video the app does not
control.

One spinner now covers both waits: resolving the stream URL and
buffering it. It no longer sticks after a resume-seek, which fires
'waiting' after 'playing'.

Also: Open on YouTube and a new Download are real buttons in the player;
refresh is a compact icon; Settings is a cog; the subscription sidebar
collapses and reappears on a left-edge hover; the feed auto-refreshes
every 10 minutes while online, skipped while the player is open; the
title-bar strip takes the panel colour, since it sits above panels
rather than the page; and downloaded-only, hide-Shorts and the sidebar
state are remembered between launches.
2026-08-29 11:03:29 +02:00
vincent f47bc3dab1 feat: tile view, in-app streaming, and self-painted title bar
Clicking an undownloaded video now plays it in the app instead of
handing off to the browser. YouTube's iframe embed cannot be used —
it rejects a tauri:// origin with Error 153 — so yt-dlp resolves
YouTube's HLS master playlist instead, whose H.264+AAC variants
AVFoundation streams natively in WKWebView, adaptive up to 1080p.

Adds an optional Tiles view alongside the list, remembered between
launches.

The title bar is now transparent with the title hidden, and the app
paints that strip itself in the page background so it matches instead
of showing macOS chrome beside the traffic lights.
2026-08-29 03:24:24 +02:00
vincent cac6727072 feat: Takeout walkthrough, design-system restyle, replacing import
Adds a seven-step in-app guide to exporting subscriptions from Google
Takeout, with the real URLs opened in the system browser.

Restyles the app onto the supplied design system: slate/sky palette,
9-15px type ladder, outline-first controls, tiered radii, borders for
separation and shadows only for elevation. Light and dark are both
designed, with a System/Light/Dark control and a pre-paint script so
the window does not flash light on a dark machine.

Importing now replaces the subscription list rather than merging, per
request. Because that can delete downloaded files, a confirmation
dialog names exactly what will go first.
2026-08-29 03:00:54 +02:00
vincent e75d896933 feat: feed UI, in-app player, offline mode, settings
Adds an end-to-end integration test that runs the real pipeline against
live YouTube Atom feeds, verifying the merged feed is newest-first
across channels.
2026-08-29 02:34:14 +02:00