Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

How we cut releases: [docs/RELEASE.md](docs/RELEASE.md).

## [Unreleased]

### Changed
- Portainer stack uses host networking so yt-dlp reaches YouTube over the host's IPv6. On Docker's default bridge the container was IPv4-only and went out through the household's shared NAT address, which YouTube blocked with "Sign in to confirm you're not a bot" even when the same network worked from a laptop. Louis now listens directly on host port 4000 (`LOUIS_PORT` to change). See [docs/HOSTING.md](docs/HOSTING.md#why-the-stack-uses-host-networking).
- Portainer stack sets `pull_policy: always`, so redeploys fetch the current image instead of reusing a stale cached `latest` with an old yt-dlp.

## [1.3.0] - 2026-09-30

### Added
- **Pipeline journal** — YouTube preview, save, normalize, and remux write JSONL under `$LOUIS_AUDIO_WORK_DIR/logs/pipeline.jsonl` (on by default). `npm run diagnose:pipeline` prints preview-vs-save headlines. `LOUIS_PIPELINE_LOG=0` disables; `LOUIS_PIPELINE_LOG_VERBOSE=1` adds yt-dlp `-v` for a short capture.
- Packaged macOS and Windows apps check the latest stable GitHub Release at startup. When a newer Louis version is available, the status bar and Settings offer Download, which opens the matching DMG or NSIS installer in the system browser; installation remains manual while desktop releases are unsigned.

### Changed
- Splash **Louis!** shout is quieter and plays at most once per local day. The intro still runs every time; `npm run dev` and `?splash=debug` always play the cue so it stays easy to hear while iterating ([#25](https://github.com/stuartromanek/louis/issues/25)).
- After a successful preview, Update remuxes those bytes into the save cache instead of hitting YouTube a second time for the same video.
- Playlist covers, cropper, and library cards use Yoto’s default cover frame (638×1011) instead of 5:7, so `imageL` fills the pane. The playlist name stays in a white band inside the same bordered card.
- Report Issues links prefill the running Louis version; desktop builds also include Electron, platform, and architecture without device identifiers.

### Fixed
- Desktop Windows builds fetch FFmpeg from `autobuild-2026-09-30-00-16`. The previous autobuild’s Windows zip had been removed, so the 1.3.0 installer job 404’d.
- Open playlists refresh from Yoto when explicitly refreshed or when Louis regains focus, becomes visible, or comes back online. Unsaved and in-progress edits remain protected, and desktop file-backed OAuth sessions no longer get shadowed by stale Electron cookies ([#29](https://github.com/stuartromanek/louis/issues/29)).
- Desktop Track Art Apply no longer 403s when `user:content:manage` is only in `yoto-session.json` (cookie scope empty). Icon patch now reads scope the same way as Update ([#24](https://github.com/stuartromanek/louis/issues/24)).
- Cookie-escalated YouTube downloads no longer force `player_client=android`/`ios` (yt-dlp skips those with cookies and then looks “outdated”). Cookies use the default web client ([#16](https://github.com/stuartromanek/louis/issues/16)).
- Desktop http(s) links (Report Issues, Settings, How To) open in the system browser instead of inside Louis ([#22](https://github.com/stuartromanek/louis/issues/22)).
- Update no longer shows raw `ENOSPC` / “no space left on device.” That is the Louis audio disk (Docker/HA `/data/audio` or the desktop disk), not the playlist tracks/time meters ([#23](https://github.com/stuartromanek/louis/issues/23)).
- Track trim could upload the full YouTube file when metadata duration was slightly longer than the probed audio — a tail cut looked like “keep everything.” Save now scales the keep-region onto the real file length.
- Uploaded playlist covers no longer punch in ~10% on art that is not exactly the cover frame. Min zoom shows the whole image (letterboxed); zoom in still fills the card.
- Saving playlist artwork no longer opens the Customize drawer.

## [1.2.5] - 2026-09-05

### Fixed
- Pack `desktop/*.mjs` (except tests) into the Electron asar so Windows/mac installers can import `js-runtime.mjs` / `nitro-health.mjs`. 1.2.4 crashed on launch: `Cannot find module …/app.asar/js-runtime.mjs`.

## [1.2.4] - 2026-09-05

### Fixed
- Windows desktop no longer fails to start with Nitro health 503 because the JS-runtime probe could not run `node.cmd` ([#26](https://github.com/stuartromanek/louis/issues/26)). Health uses the Electron-as-node process (and `Louis.exe` for yt-dlp); a degraded `/api/health` now shows the failed check in the startup dialog.

## [1.2.3] - 2026-09-05

### Changed
- YouTube bot checks retry anonymous player clients (android / ios / tv) before failing; search/import tries android once. Cookie escalate stays immediate when a cookies.txt is configured. Client switches after a bot check skip the 403/429 backoff.
- Desktop bundled yt-dlp pin `2026.07.04` → `2026.08.19`.

### Fixed
- Desktop YouTube preview/Update for challenge-heavy videos (e.g. some long storyteller uploads) when launched from Finder/Start Menu — Nitro now passes yt-dlp an absolute Electron-as-node shim (`LOUIS_YTDLP_JS_RUNTIME`) so EJS works without Homebrew/`node` on the GUI PATH.
- `/api/health` (and Settings → Advanced → Download tools) report `ytdlpJsRuntime` — missing Node/EJS runtime no longer hides behind “video unavailable.”
- YouTube bot-check errors no longer dump yt-dlp’s `--cookies-from-browser` wiki. Toasts point at Settings → Advanced, or ask to re-export cookies.txt if a jar already failed.

## [1.2.2] - 2026-09-02

### Fixed
- Splash **Louis!** wordmark uses `filter: drop-shadow` instead of `text-shadow` so Safari renders a solid offset shadow with `-webkit-text-stroke` (was hollow outline-only).
- Splash tagline chips are absolutely positioned under the Lottie stage box (explicit stage-sized visual + `top: calc(83% + 10px)`) so they track the player without a fragile negative-margin calc or sitting in a row beside it.

### Changed
- Splash tagline chips inflate 500ms earlier (`1200ms` after splash start).

## [1.2.1] - 2026-09-02

### Fixed
- Desktop app packs `shared/youtubeSafeSearch.mjs` into `extraResources` so the arm64/x64 Mac (and Windows) app can start (`configStore` import was missing from the installer).

## [1.2.0] - 2026-09-02

### Added
- **Homelab one-click hosting** — [docs/HOSTING.md](docs/HOSTING.md): Portainer app template ([`deploy/portainer/`](deploy/portainer/)); Coolify steps; best-effort CasaOS bundle ([`deploy/casaos/`](deploy/casaos/)).
- **Playlist artwork** — new playlists get a generated [DiceBear](https://www.dicebear.com/how-to-use/http-api/) cover on create. Playlist menu → **Artwork** opens a generate / upload / crop flyout (5×7 cover, session history; Yoto `metadata.cover.imageL`).
- **Track trim** — scissors on a YouTube track (or a split group) edits the **full** source. Trim keeps that region and re-splits if the keep is still over 55 minutes (or collapses to one slat if it is not). Waveform + preview in a flyout (desktop) or tray (phone).
- **Auto-split long tracks** — YouTube sources over 55 minutes expand into connected Part 1 / Part 2 / … playlist slats on add (move and delete as a group). Save downloads the audio once, then ffmpeg-splits into legal MYO chapters. Replaces the old “Enable long tracks?” confirm gate.
- **Track Art Editor** — per-track 16×16 Yoto icons from the playlist (desktop) and mobile playlist detail: Icons tab (Yoto public library + [yotoicons.com](https://yotoicons.com/) search + upload) and Draw tab (pixel canvas, palette, undo/redo).
- Instant icon patch for existing playlist tracks (`PATCH`-style content update) so art saves without a full playlist rewrite; new tracks stay local until Update.
- Server helpers for Yoto icon upload / public icons / URL import and yotoicons.com search.
- Modal polish: desktop pop / phone sheet motion, LED preview crossfade, Apply success beat, and keyboard/a11y pass (focus trap, tablist, listbox roving, focus rings).
- **Home Assistant add-on** under `homeassistant/` (Supervisor repo + options → `LOUIS_*`, port 4000, `/data/audio`) — [homeassistant/louis/DOCS.md](homeassistant/louis/DOCS.md).
- `LOUIS_COOKIE_SECURE` to override OAuth cookie `Secure` (plain HTTP / HA LAN).
- GHCR release images publish **linux/amd64 + linux/arm64** (required for typical HA hosts).
- Desktop **Use default client** prefills Louis's bundled public Yoto PKCE client ID (setup wizard + Settings → Advanced).
- **Add to Home** in the phone Menu and tablet/desktop status bar (browser install prompt, or a toast with Share / Install steps when the prompt is unavailable).
- Settings → Advanced: **Check for updates** / **Update yt-dlp** installs the official nightly into persistent app data (Docker `/data/audio/bin`, Electron userData). `npm run dev` is check-only.
- **Normalize new track levels?** at Update when this save will download YouTube audio — opt-in EBU R128 leveling (`I=-16`) for those extracts only. Existing playlist tracks stay unchanged.
- Phone Menu can Update every pending dirty playlist without opening a playlist first. Capacity and normalize prompts are answered once for the whole batch, in the playlist cover (desktop) or by taking over Menu Update (phone).
- Duplicate-track toast when adding a YouTube video that is already on the playlist (same playlist row or same YouTube id); it is not added again.
- Import YouTube into Search by pasting a URL: a video or Shorts link opens that track (ready to add); a public playlist or channel URL loads those videos, pre-checked for a group drag (desktop) or Add (phone).
- Playlist header menu (desktop panel + phone detail) to **Rename** or **Delete** a loaded playlist. Rename takes over the playlist body with the name form, prefilled. Delete asks for confirm in the same body cover as Normalize, then removes the playlist from Yoto.
- **New playlist** in the idle playlist empty state is a link that starts the New playlist flow.

### Changed
- **Faster playlist open** — Opening a playlist uses the saved card titles and Yoto track art. It no longer re-fetches YouTube video metadata (yt-dlp) on load. Public and mine icon catalogs cache for 5 minutes.
- **Faster library list** — `/content/mine` no longer N+1 GETs every playlist for track count. Count only if Yoto already included chapters; duration still comes from metadata.
- **Faster Update overlay** — Save overlaps download and leveling with Yoto’s transcode wait (one PUT at a time, up to two in-flight polls). The overlay appears on Confirm, the inner bar no longer restarts when the label changes, the overall percent uses tabular numbers, the client polls every 400ms during upload/transcode (1s otherwise), and the overlay dismisses when the job completes — card reload and library stats continue in the background. Near-instant completes still show for 450ms so they don’t flash.
- **Dual-registry Docker publish** — each `v*` release pushes the same multi-arch image to Docker Hub (`stuartromanek/louis`) and GHCR (`ghcr.io/stuartromanek/louis`). [`docker-compose.yml`](docker-compose.yml) defaults to Docker Hub.
- **`/api/youtube/*` hybrid discovery** — search, video details, playlist import, and channel browse use bundled/`PATH` yt-dlp when `LOUIS_YOUTUBE_API_KEY` is unset; Data API when a key is set. Quota or upstream failures (403/502/503) fall back to yt-dlp. Self-host, Docker, HA, and desktop share the same routes.
- **`LOUIS_YOUTUBE_API_KEY` is optional** — only `LOUIS_YOTO_CLIENT_ID` is required for self-host. Desktop first-run includes a skippable YouTube step (**Next** with a key, **Skip** for bundled yt-dlp). Search and import still work without a key; a Data API key is recommended (faster search, `safeSearch=moderate` on typed search) and can be added later in Settings → Advanced (desktop) or env / HA options.
- Artwork, Trim, Track Art, How To, and Settings share one **AppFlyout** (dismiss ×, compact footer commit).
- Confirming a new playlist name creates the playlist on Yoto immediately. Checked Search results (desktop New) or Add → New playlist (phone) upload with that create; otherwise the playlist starts empty and you add tracks, then Update.
- Update can save an empty playlist (clears chapters on Yoto). The create footer no longer says the playlist is not on Yoto yet.
- Track Art icon size preference persists on desktop; phone still uses 64 without overwriting that pref.
- After Update, the library fan shows the new track count and duration.
- How To uses a smaller flyout and more space between bullets.
- Trim **Play** is disabled while the waveform is loading; the times row shimmers until peaks arrive.
- Product copy uses Yoto’s playlist terms for library items (**Playlists**, **New playlist**, **Open a playlist**). Louis does not link physical MYO cards — that stays in the Yoto app.
- YouTube results have a maru checkbox next to the drag handle; checked rows import together when you drag (desktop) or tap Add (phone).
- Playlist URL results sit in a nested Playlist title box with a Select all / Deselect all toggle.
- Menu and the desktop status bar paint as soon as auth status is known; the library fetch no longer blocks chrome. Library / fan / add-to-playlist show placeholders while playlists load.
- Toasts default to bottom-end (top on iOS so they don't cover Safari's Share control).
- Yoto OAuth scopes now include `user:icons:manage` (reconnect if icon upload/patch is denied).
- Yoto Connect requests `offline_access` and reuses the access token until it expires (Yoto refresh tokens rotate; parallel refreshes are coalesced). Reconnect once so Louis can store a refresh token.
- Docker / GHCR: OAuth callback no longer defaults to `localhost` — unset `LOUIS_YOTO_REDIRECT_URI` uses the Host the browser actually opened (LAN / Portainer). Image and compose default `LOUIS_COOKIE_SECURE=false` for plain HTTP; set `true` behind TLS. Compose uses named volume `louis-audio`, and no longer requires a Git-tracked `env_file`.
- Yoto transcode waits scale with chapter size and duration (6-minute floor, 20-minute cap; ~30s wait per minute of audio so long split chapters actually reach the cap). Timeout copy names the part and last percent; the overlay keeps polling while the save job is still moving.
- Pixel editor draw sounds use `scribble-*` / `erase` / `clear` instead of the old `mdn-*` ticks.
- Auto-split shore-up: the overlay no longer fails a living save at 90 minutes (still-working hint only); lost-job copy asks you to check Yoto; ffmpeg split/loudnorm timeouts scale with duration; a partial re-extract cuts that chapter instead of uploading the full file; save re-plans when a probe would stretch a part past 60 minutes.

### Removed
- **Umbrel community store bundle** — removed [`deploy/umbrel/`](deploy/umbrel/) and all Umbrel hosting docs; use Portainer or plain Docker instead.
- `public.demoMode` / Settings demo-instance banner. yt-dlp Check and Update are no longer gated on a demo flag.

### Fixed
- Desktop Windows release fetch: refresh yt-dlp/FFmpeg-Builds pin (`autobuild-2026-09-02-17-51`) after the previous autobuild assets 404’d.
- Client save polling no longer treats a quiet 10 minutes as failure while the job is still running (false “Save stalled” after Yoto already accepted the upload). Overlay stays up; a still-working hint appears if progress has not moved. Jobs heartbeat during download / ffmpeg / PUT.
- Multi-track (split) Yoto uploads finish across retries: long chapters wait up to 20 minutes, completed parts keep their transcoded hashes so Update skips them, ffmpeg AAC-encodes cuts instead of GOP-unaligned copies, and a stall or Yoto failure re-PUTs once (a moving timeout does not). Extra-poll after a coalesced stall uses the chapter’s full wait budget.
- Split checkpoints key off the scaled cut and whether loudnorm actually ran; a changed source hash misses cache. Incomplete split drafts sanitize on restore.
- Desktop Yoto session: expired access without refresh forces reconnect; save/reuse-test read scope from cookie or `yoto-session.json`.
- Search, channel, and playlist listings omit untitled / “YouTube video” stubs and rows with no duration (the 0:00 cards).

## [1.1.2] - 2026-08-08

### Fixed
- macOS release builds both arm64 and x64 DMGs (`electron-builder --arm64 --x64`); CI verifies both artifacts before upload.

## [1.1.1] - 2026-08-08

### Fixed
- Release desktop jobs: electron-builder no longer tries to publish on tagged CI runs (`--publish never` / `publish: null`); Nitro LOUIS_* alias injection finds `nitro.mjs` on Windows (`chunks/_/` vs `chunks/nitro/`).

## [1.1.0] - 2026-08-08

### Added
- **Desktop app (Electron):** macOS DMG (arm64 + x64) and Windows NSIS installers built on `v*` tags and attached to the same GitHub Release as GHCR — [docs/DESKTOP.md](docs/DESKTOP.md), [docs/RELEASE.md](docs/RELEASE.md), checklist [docs/DESKTOP_SHIP.md](docs/DESKTOP_SHIP.md).
- Bundled yt-dlp + ffmpeg in the desktop package (`npm run desktop:fetch-binaries`); no Homebrew required for consumers.
- Desktop Settings → **Desktop API keys** (Yoto client ID, YouTube API key, optional cookies path) stored under Application Support `config.json` — not baked into the binary. Fixed OAuth redirect `http://127.0.0.1:4010/api/yoto/auth/callback`.
- **First-run setup wizard** on the splash background when required keys are missing: step-through Yoto / YouTube / redirect URI / “You’re ready”, with Back, step chip, FLIP layout motion, and **Let’s go** to save (and restart Nitro in the desktop app). Optional yt-dlp cookies stay in **Settings → Advanced** only.
- Shared `DesktopApiKeysFields` for Settings and the wizard (tooltips, yoto.dev / Google Cloud links, redirect URI **Copy**).
- Maintainer scripts: `desktop:spike`, `desktop:dir`, `desktop:build:host` / `:mac` / `:win` / `:build`.
- HMR preview: `?desktopPrefs=1` (and `&desktopSetup=1`) mocks desktop config in sessionStorage — [docs/DESKTOP.md](docs/DESKTOP.md).
- Phone editor IA below 600px: Search / Library tabs, nested card detail, Add-to-card drawer, Menu tray, toasts, and denser YouTube results (desktop/tablet two-column layout unchanged at `sm+`).
- Mobile Menu update affordances: Bell + light pink when a save is pending; IndexPointingUp + left-to-right pink progress fill while updating.
- How-to empty state and How To modal with beat art; Search Clear after submit; phone-friendly over-limit / long-track controls.
- Dirty MYO playlist draft persistence in `localStorage` (restored when the card is opened again).
- yt-dlp download outcome logs (`ok` / `fail` / `escalate` / `coalesce`) so Railway logs show whether cookies recovered after bot checks.
- In-process singleflight for concurrent downloads of the same YouTube id (preview stampede protection).

### Changed
- Desktop first-run wizard no longer asks for yt-dlp cookies; set that path later under **Settings → Advanced** ([docs/DESKTOP.md](docs/DESKTOP.md)).
- Auth gate: **Edit Settings** under Connect (and other primary actions) so a bad desktop client ID can be fixed without dismissing the gate.
- Desktop **Connect** opens Yoto OAuth in the system browser (`shell.openExternal`); tokens persist in `yoto-session.json` so Louis never navigates away (and password managers work).
- Renamed product UI copy from Preferences to **Settings**.
- Env contract is now **`LOUIS_*`** (e.g. `LOUIS_YOTO_CLIENT_ID`, `LOUIS_YOUTUBE_API_KEY`, `LOUIS_PUBLIC_DESKTOP`); legacy **`NUXT_*` / `NUXT_PUBLIC_*`** still accepted as a deprecated fallback (`LOUIS_*` wins when both are set). Nitro boot plugin applies custom names at container runtime.
- UI typeface is self-hosted **LT Saeada** (Regular / Medium / Bold) instead of Dongle.
- Desktop Settings: General / Advanced nav; Done saves dirty API keys (Save & restart in Electron); required Yoto client ID matches the wizard; YouTube API key is optional (Skip / empty uses bundled yt-dlp).
- Desktop config merge preserves omitted fields (e.g. client secret); `get-config` / setup gating use the same effective config (stored `config.json` with env filling blanks for spike/dev).
- After credential save, desktop reload opens setup again if required keys are still missing.
- Phone chrome polish for narrow viewports (densify ≤360px / ≤310px): tab icons, result art, howto mocks, tray open animation.
- Expected anon→cookies escalate and retries log at info level (not warn/error).
- Long API errors use h3 `message` instead of `statusMessage` (avoids future sanitization warnings).
- Release workflow uploads desktop installers alongside GHCR on each `v*` tag.

### Removed
- Public demo documentation and maintainer demo template (`docs/DEMO.md`, `.env.demo.example`, and related README / SECURITY notes).
- Bundled Dongle font files (replaced by LT Saeada).

## [1.0.0] - 2026-07-22

### Added
- Intro splash sequence (Lottie) on first visit per tab session, with a short delay before playback and a frame-synced Louis sound cue (`splashCue` / `louis.wav`).
- Splash debug mode via `?splash=debug` (loop, pause, frame HUD).
- Early splash cover so the main app does not flash before the intro.
- Post-auth welcome modal (`YotoConnectedModal`) after successful Yoto OAuth (`/?yoto=connected`), with feature list, TV frame UI, and celebration sound.
- Auth gate uses the Yoto-on SVG as a TV frame with Louis artwork; connect CTA plays `toggle_on`, gate open plays ringtone.
- `lottie-web` dependency for splash playback.

### Changed
- Successful OAuth callback redirects to `/?yoto=connected` instead of bare `/`.
- Auth gate copy updated to “Connect Louis to Yoto”; connect-gate and welcome typography/spacing tightened.
- YouTube result card channel meta uses tighter line-height.
- Docs and browser title brand the app as **Louis** (README banner, CONTRIBUTING, ROADMAP, DEMO, LICENSE).

### Removed
- Marketing page (`/marketing`) and `public/marketing/` assets (Louis/Yoto art lives under `public/images/`).
- Experimental muted `<video>` splash cue path (`louis.mp4`); splash audio uses the shared UI sound player only.

[Unreleased]: https://github.com/stuartromanek/louis/compare/v1.3.0...main
[1.3.0]: https://github.com/stuartromanek/louis/compare/v1.2.5...v1.3.0
[1.2.5]: https://github.com/stuartromanek/louis/compare/v1.2.4...v1.2.5
[1.2.4]: https://github.com/stuartromanek/louis/compare/v1.2.3...v1.2.4
[1.2.3]: https://github.com/stuartromanek/louis/compare/v1.2.2...v1.2.3
[1.2.2]: https://github.com/stuartromanek/louis/compare/v1.2.1...v1.2.2
[1.2.1]: https://github.com/stuartromanek/louis/compare/v1.2.0...v1.2.1
[1.2.0]: https://github.com/stuartromanek/louis/compare/v1.1.2...v1.2.0
[1.1.2]: https://github.com/stuartromanek/louis/compare/v1.1.1...v1.1.2
[1.1.1]: https://github.com/stuartromanek/louis/compare/v1.1.0...v1.1.1
[1.1.0]: https://github.com/stuartromanek/louis/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/stuartromanek/louis-/releases/tag/v1.0.0