Commit Graph

21 Commits

Author SHA1 Message Date
fd6283718c Analyse tracks in a pool of worker processes
A first pass over an unanalysed library is hours of librosa, and there was no
reason for a desktop to spend them one core at a time. Analysis now runs in a
process pool sized by `general.library.analysis_workers`, defaulting to one per
core bar one (capped at 8) when the key is absent.

Two consequences worth knowing before touching this: whatever an `Analyzer`
returns has to be picklable, and an analyzer that keeps state on its own
instance - a test double counting calls - only behaves as written with
`analysis_workers=1`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 18:04:01 +02:00
75b0eed080 Remove the Quallenalarm (jellyfish) typing minigame
Letters-round lessons only had bubbles and jellyfish as eligible arcade
modes; with jellyfish gone, bubbles is now always used instead of the
two alternating - no lessons need to be dropped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 00:38:19 +02:00
b8f9f6d537 Give the tab rail its own glass bar and reserve space for it
The three tab buttons now share one frosted glass pill instead of each
floating separately, and BrowseView/RoomView reserve TAB_RAIL_CLEARANCE
of right-side padding so a full-width row or grid never renders under
it - verified against the real library, where dense rows (e.g. Conni's
82 audiobooks) previously butted right up against the rail.

Also gitignore the real (non-.example) tippen-curriculum.yml and
tippen-progress.json, matching config.yml's own privacy treatment,
since the curriculum now contains real reward paths into a personal
library.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 00:16:42 +02:00
7f5e2733c2 Merge the typing game into the music player as a tab, with lock/unlock UI
Moves tippen from a standalone app into web/ as a third tab (audio player /
smarthome / typing), replacing the old single room-toggle corner button with a
vertical icon tab rail. Curriculum and progress now come from the backend
(musicmouse/tippen/*) instead of a build-time YAML import and localStorage.

Adds reward-driven lock rendering: Cover/BrowseView/AlbumModal show a question
mark for locked albums/tracks with a hint on what unlocks them, and
ResultSheet gets a new unlock-animation block alongside the existing
lesson-unlock and aquarium-creature celebrations.

CSS from the two apps is merged carefully: identical rules (bubble/card/
key-cap/view-enter/backdrop-enter and their keyframes) are shared as-is,
while rules that bake in each app's own hue are kept separate under a
`tp-` prefix and scoped to the typing tab's own .tp-stage wrapper, so
neither app's look bleeds into the other's.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 21:25:42 +02:00
f7a5d24d8d Add backend support for the typing game: curriculum, progress, and reward unlocks
Moves the typing app's lesson plan and progress from client-side YAML/localStorage
into the backend, and adds a reward system that ties passing a lesson to unlocking
part of the music library. Lock state is always recomputed live from curriculum x
progress x the live library, never persisted separately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 21:03:45 +02:00
a5210fead2 Typing lessons like duolingo & musicmouse cleanup 2026-09-12 18:58:02 +02:00
498243af46 Anglicize tippen's codebase and finish pending UI/curriculum cleanup
- Rename all German identifiers, types, mode ids, file names, CSS classes
  and data-attributes to English throughout tippen/src; only user-facing
  text (lesson titles, word lists, labels, spoken praise) stays German.
- Add a word/nonsense-word list to the "Übung: die Grundstellung" home-row
  lesson in the curriculum.
- Remove the unused HandHint component and speech.ts, and carry forward
  the in-progress App.tsx/component/generator/progress edits from other
  sessions.
- Refresh the regenerated music-library cache index.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 00:07:18 +02:00
fbf03a9847 UI cleanup, visual and keyboard navigation 2026-09-11 13:32:07 +02:00
df89acd9a8 Fix podcast episode downloads: ffmpeg muxer bug and dead-URL retry storm
_extract_audio wrote transcoded audio to a .tmp temp path, so ffmpeg
couldn't guess a muxer from the filename and aborted on every video
episode whose source was still reachable. Pass -f mp3 explicitly instead
of relying on the extension.

Separately, a permanently 404'd episode was retried on every startup and
every 6-hour poll forever, since nothing remembered past failures. Track
failed downloads per show folder with a 7-day backoff before retrying.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 12:30:42 +02:00
69119bb72a Add per-episode podcast cover art and fix nested album grouping
- podcast_feeds.py: fetch per-episode cover art from a feed's itunes:image
  (when it's genuinely distinct from the channel image) or, failing that,
  from the og:image on the episode's own linked page; extract audio from
  video-only enclosures via ffmpeg; match podcast-dl's filename convention
  (illegal characters become "_" instead of being dropped) so enabling
  feed.txt on an already-downloaded show doesn't re-download its back catalog
- scanner.py: _cover_for_episode now checks for a same-stem sidecar cover
  image before falling back to embedded ID3 art and the shared folder cover
- scanner.py/__init__.py: fixed a bug where an album folder nested one level
  deeper than usual (an age-range grouping folder, say) was mistaken for an
  empty album and skipped; added periodic progress logging for long scans
  and analysis passes

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 09:54:33 +02:00
747c390303 Add IR remote control (LIRC) with a number-key content mapping
Adds a TCP client for lircd's classic protocol: play/pause/next/prev/
volume/mute map to the same intents every other front-end already
emits, and number keys 0-9 play an assigned album/audiobook from the
start or a podcast show's newest episode, resolved fresh on every
press. The mapping is configured in config.yml and editable from the
frontend: a small "Taste zuweisen" button on the play screen (or the
A+digit keyboard shortcut) opens a 10-key picker to assign whatever is
currently playing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 08:31:28 +02:00
57afc32f4a Auto-download new podcast episodes from an RSS feed
A show folder under Kinderpodcasts/ opts in by containing a feed.txt marker
naming its RSS feed. A new PodcastFeedService polls every such feed every 6
hours (and once at startup), downloads any episode not already on disk using
the existing YYYYMMDD - Title.ext convention, and triggers the same
rescan-and-broadcast sequence "Bibliothek neu einlesen" already uses - now
shared via App.rescan_library() instead of duplicated. A show with no
feed.txt is untouched, so there is no new config section for this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 22:43:50 +02:00
2e0e6ad199 Add librosa beat/mood analysis and an Ambience background driven by it
The background worker now runs a real librosa analyzer (tempo, beat grid,
per-second energy/valence curves) instead of only the null baseline, kept
behind build_analyzer() so a plain checkout without the analysis extra still
runs fine. The web player reads that per-track analysis and drives a new
animated "Ambience" background (bubbles, colour, current) that reacts to the
beat and the mood curve as the track plays, plus a debug overlay for tuning
it. Also adds a one-off script to backfill podcast cover art from iTunes.
2026-09-10 22:42:51 +02:00
8aed3b022b updates 2026-08-27 23:46:19 +02:00
a8ed350aec Add "Mein Zimmer" room-control page (Home Assistant, proxied through the backend)
Implements the room-control page from the design mockup: a scenes row above cards
for shutters, color lamps, and brightness-only lamps, all driven by a new
`general.ha` config section (server URL, token, ordered device/scene lists).

The backend proxies every Home Assistant call server-side (GET/POST /api/ha/...)
rather than the browser calling Home Assistant directly, so the long-lived token
never leaves the LAN device and Home Assistant's own CORS settings don't need to
know about musicmouse at all. Card kind (shutter/color/brightness-only) is
inferred at runtime from what Home Assistant reports about each entity, not
configured explicitly.

Also stops tracking python-backend/config.yml, which had drifted into the repo
despite its own header saying it shouldn't be - it now carries real credentials
locally and needs to stay untracked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 23:45:29 +02:00
edb6e5e027 Web frontend 2026-08-27 12:32:20 +02:00
d44c24ec97 Full rearchitecture using Claude
- event bus systen
- all components are independent
- preparation for web frontend
2026-08-26 13:22:28 +02:00
55bcc9448c color parsing cleanup 2026-08-26 11:28:48 +02:00
1cb3387fbe Fix: lights now should turn off correctly when playlist ends 2026-08-26 10:54:40 +02:00
7aa7fe4693 Claude cleanup 2026-08-26 10:38:56 +02:00
bd8925a278 Cleaned up repository
- only moving files around
2026-08-25 17:06:53 +02:00