Commit Graph
31 Commits
Author SHA1 Message Date
Hermes (Server)andClaude Haiku 4.5 2163c83409 docs: Changelog für Settings-Tab und Abschluss des Tab-Restructure-Plans
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 18:32:03 +02:00
Hermes (Server)andClaude Haiku 4.5 23a1e7838d feat(settings): Settings-Tab mit Bibliotheks-Statistik, Berechtigungen, Über Melo
- Bibliotheks-Statistik: Song-Anzahl + Gesamtspieldauer via watchSongs()
  (reine Summier-/Format-Funktion in lib/settings/library_stats.dart,
  unabhängig von Flutter/Plattform testbar)
- Berechtigungen: Musik-Berechtigung öffnet Systemeinstellungen über das
  bereits vorhandene openMusicPermissionSettings()
- Über Melo: kurzer Name + Slogan, keine erfundene Versionsnummer
  (package_info_plus ist keine Dependency)
- Keine Platzhalter für Profil/Cloud-Sync/Geräte-Verwaltung, da es noch
  kein Account-/Cloud-System gibt (Phase 2)
- HomeShell nutzt SettingsScreen statt des Settings-Placeholders,
  toter _Placeholder-Widget-Code entfernt

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 18:31:30 +02:00
Hermes (Server)andClaude Haiku 4.5 3fc52327c9 docs: Changelog für Wiedergabeposition-Resume
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 18:17:51 +02:00
Hermes (Server)andClaude Haiku 4.5 edc93224b0 feat(player): Wiedergabeposition merken und beim Laden fortsetzen
- MeloAudioHandler bekommt MeloDb per Konstruktor injiziert
- Positions-Timer (alle 5s) schreibt db.recordPlayback(songId, positionMs)
  während aktiv abgespielt wird (songId aus mediaItem.extras['songId'])
- loadPlaylist liest db.lastPosition() für den Start-Song und seekt vor
  dem play() an die gespeicherte Position, statt immer von vorne zu starten
- shouldResumeAt() als reine, testbare Funktion extrahiert: kein Resume bei
  Position 0 oder innerhalb der letzten 3s einer bekannten Track-Dauer
- dispose() zum gefahrlosen Abbrechen des Positions-Timers in Tests ohne
  vollen App-Lifecycle (super.stop() lässt sich in Widget-Tests wegen der
  aktiven playbackState-Pipe nicht gefahrlos aufrufen)
- library_screen_test.dart: MeloAudioHandler-Konstruktion angepasst (db
  jetzt Pflichtparameter) + handler.dispose() im Teardown

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 18:17:26 +02:00
Hermes (Server)andClaude Haiku 4.5 09dd4a4367 docs: Changelog für Warteschlangen-Ansicht
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:55:18 +02:00
Hermes (Server)andClaude Haiku 4.5 d267bb9658 feat(player): Warteschlangen-Ansicht mit Springen, Entfernen, Umsortieren
- Neuer QueueScreen: zeigt handler.queue, hebt aktuell spielenden Titel
  hervor (fett/rot), Antippen springt per skipToQueueItem
- Entfernen und Umsortieren ohne Änderung an MeloAudioHandler: Queue wird
  clientseitig neu berechnet und per loadPlaylist neu geladen; Position
  wird nur wiederhergestellt, wenn der abgespielte Titel unverändert blieb
- Reine Queue-Logik (removeFromQueue/reorderQueue/newPlayingIndex) als
  eigenständige, ohne AudioHandler testbare Funktionen extrahiert
- Warteschlangen-Button in der Now-Playing-AppBar verlinkt den neuen Screen

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:55:01 +02:00
Hermes (Server)andClaude Haiku 4.5 ea6d8ab18e docs: Changelog für Künstler-/Alben-Browsing in der Bibliothek
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:35:10 +02:00
Hermes (Server)andClaude Haiku 4.5 58ceb42769 feat(library): add Artist and Album browsing tabs
- Library screen now has a TabBar (Alle / Künstler / Alben) inside a
  DefaultTabController; scan/permission/error banners stay above the
  TabBarView so they remain visible regardless of the active sub-tab
- New lib/library/song_grouping.dart: pure, unit-tested functions
  groupByArtist/groupByAlbum (null artist/album -> "Unbekannt" /
  "Unbekanntes Album", alphabetically sorted) plus albumArtistLabel
  ("Verschiedene Interpreten" when an album mixes artists)
- New lib/library/artist_list.dart + album_list.dart: StreamBuilder on
  MeloDb.watchSongs(), grouped client-side, tapping a row pushes a
  SongList screen for that artist/album; album rows show a CoverImage
  thumbnail from the first song's coverPath
- Existing "Alle" tab content (recently-added strip, full song list,
  empty state) preserved unchanged, moved into _AllSongsTab

Genre browsing is intentionally out of scope: the Songs table has no
genre column and neither scanner extracts genre metadata — needs a
schema migration + scanner work in a future task.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:34:41 +02:00
Hermes (Server)andClaude Haiku 4.5 4477370766 docs: Changelog für Playlist-Detailansicht
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:20:09 +02:00
Hermes (Server)andClaude Haiku 4.5 96d9f38a64 feat(playlists): Playlist-Detailansicht mit Drag&Drop-Umsortieren
PlaylistDetailScreen zeigt die Songs einer Playlist (watchPlaylistSongs),
erlaubt Entfernen einzelner Songs und Umsortieren per Drag & Drop
(ReorderableListView.builder). Tippen auf einen Song spielt die Playlist
ab dieser Stelle. Leer-Zustand mit Hinweistext.

Da bestehende position-Werte nach Einfügereihenfolge lückenhaft sein
können, schreibt das Umsortieren nicht nur den verschobenen Song um,
sondern vergibt für die gesamte Playlist neue lückenlose Positionen
(0..N-1): MeloDb.reorderAllPlaylistSongs (in einer Transaktion) +
PlaylistService.reorderAll als dünner Wrapper.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:19:56 +02:00
Hermes (Server)andClaude Haiku 4.5 4b5d77c196 docs: Changelog für Playlist-Hinzufügen-Button
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:19:42 +02:00
Hermes (Server)andClaude Haiku 4.5 fc9a6abdd7 feat(playlists): Songs per Bottom-Sheet zu Playlisten hinzufügen
Ohne Eintrittspunkt wären Playlisten unbenutzbar, da sie sonst nie Songs
erhalten könnten. SongList bekommt einen zusätzlichen Trailing-Button
(Icons.playlist_add) neben dem Favoriten-Herz, der ein Bottom-Sheet mit
allen Playlisten öffnet ("Neue Playlist" oben, dann bestehende). Antippen
hängt den Song ans Ende der gewählten Playlist an und zeigt eine SnackBar.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:19:29 +02:00
Hermes (Server)andClaude Haiku 4.5 822384b39f docs: Changelog für Playlisten-Tab
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:19:13 +02:00
Hermes (Server)andClaude Haiku 4.5 b2ac118b42 feat(playlists): add Playlisten-Tab mit Favoriten-Kurzeinstieg und Erstellen-Dialog
- PlaylistsScreen: Favoriten-ListTile oben (watchFavorites, pusht FavoritesListScreen),
  darunter alle Playlisten (watchPlaylists) mit Songanzahl, Löschen via Icon/Long-Press
  + Bestätigungsdialog
- CreatePlaylistDialog: Name-Eingabe, Erstellen-Button erst aktiv bei nicht-leerem Text,
  gibt die neue Playlist-ID zurück
- Leer-Zustand "Noch keine Playlisten" nach Vorbild von library_screen.dart
- main.dart: Playlisten-Platzhalter durch PlaylistsScreen ersetzt

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:18:56 +02:00
Hermes (Server)andClaude Haiku 4.5 492d3f19f9 docs: Changelog für finales 5-Tab-Layout
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:03:39 +02:00
Hermes (Server)andClaude Haiku 4.5 3a040b4661 feat(ui): finales 5-Tab-Layout (Player/Bibliothek/Playlisten/Suche/Settings)
- "Zuletzt hinzugefügt" aus dem alten HomeScreen in die Bibliothek verschoben:
  horizontal scrollende Leiste über der vollen Songliste, nur sichtbar wenn
  nicht leer und nicht gerade gescannt wird
- home_screen.dart entfernt (Funktionalität jetzt Teil von LibraryScreen)
- HomeShell-Tabs auf die finale Struktur umgestellt: NowPlayingScreen an
  Position 0, Playlisten- und Settings-Platzhalter an ihren finalen
  Positionen (werden in späteren Tasks durch echte Screens ersetzt)
- BottomNavigationBar-Icons/Labels an die neue Reihenfolge angepasst
- MiniPlayer wird ausgeblendet, solange der Now-Playing-Tab aktiv ist
  (redundant, da der Tab selbst schon die volle Wiedergabe-Ansicht zeigt)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:03:24 +02:00
Hermes (Server)andClaude Haiku 4.5 be1248d7ba docs: Changelog für Now-Playing-Tab und MediaItem-songId
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:01:47 +02:00
Hermes (Server)andClaude Haiku 4.5 2712ace665 feat(player): Now Playing als Tab statt Push-Screen, Favoriten-Herz
- songToMediaItem trägt jetzt die Song-UUID in extras['songId'], damit
  Downstream-Code (Favoriten-Button, später Playback-History) den echten
  Song wiederfinden kann statt nur die file-URI zu haben
- NowPlayingScreen: Schließen-Button entfernt (AppBar-leading), da der
  Screen jetzt als eigener Tab läuft statt gepusht zu werden
- FavoriteButton für den aktuellen Song in die AppBar-Actions eingebaut,
  reaktiv über einen verschachtelten StreamBuilder<MediaItem?>; ohne
  gültige songId (z.B. nichts spielt) wird kein Button gerendert

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:01:31 +02:00
Hermes (Server) 62c65e8eca docs: update changelog for favorite button 2026-08-18 15:30:02 +02:00
Hermes (Server)andClaude Haiku 4.5 db99e2b1f7 feat(ui): add favorite heart toggle, wire into song list
- Filled heart when favorited, outline when not (reactive via watchIsFavorite)
- Wired into every song list tile (library, search, playlists)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 15:29:51 +02:00
Hermes (Server) 5c8796d353 docs: update changelog for PlaylistService 2026-08-18 15:29:01 +02:00
Hermes (Server)andClaude Haiku 4.5 92f565b1d4 feat(services): add PlaylistService, wire into app providers
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 15:28:24 +02:00
Hermes (Server)andClaude Haiku 4.5 449fe4ce76 docs: update changelog for playlist schema
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 15:14:20 +02:00
Hermes (Server)andClaude Haiku 4.5 7478beeb11 fix(test): remove unused material import in sleep_timer_test
Pre-commit analyze gate requires zero issues; this pre-existing
warning was blocking the schema commit.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 15:14:00 +02:00
Hermes (Server)andClaude Haiku 4.5 d09a677315 feat(database): add Playlists, Favorites, PlaybackHistory tables
- Playlists + PlaylistSongs tables with position-ordered join query
- Favorites table with toggle + watch (reactive heart icon support)
- PlaybackHistory table for resume-position tracking
- Schema v2 with migration from v1
- Follows existing Songs/Folders sync-ready pattern (uuid id, updatedAtMs, deleted)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 15:13:44 +02:00
dustin bc8f3bfe77 Merge pull request 'Add sleep timer feature' (#1) from feature/sleep-timer into main
Reviewed-on: dustin/Mello#1
2026-08-16 18:20:50 +00:00
Dustin-Mike Jens HähnelandClaude Opus 4.8 51c2229148 Add sleep timer feature
Timer stops playback (pause) after 5/10/15/30/60 min. Wall-clock
deadline is self-correcting against OS throttling; 1Hz tick drives
the countdown UI. Cancelled in stop() so no timer resurrects a paused
player after teardown.

- sleep_timer.dart: isolated time logic, injectable clock for testing
- audio_handler.dart: sleepTimer field + cancel() in stop()
- now_playing_screen.dart: AppBar button + bottom sheet
- 10 unit tests via mocked clock

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 20:06:08 +02:00
Dustin-Mike Jens Hähnel 78f4b55e68 Add CLAUDE.md mit Projektregeln 2026-08-16 19:51:23 +02:00
Dustin-Mike Jens HähnelandClaude Haiku 4.5 d04783073f Fix critical blocker issues and improve tests
## Critical Blockers Fixed
- Remove playbackState.addError() which broke audio_service permanently
  - Move error handling from audio_handler to song_list where UI context exists
  - Display error in SnackBar instead of destroying playback stream

- Add escapeChar parameter to LIKE queries for correct wildcard handling
  - Fixes broken search for titles with _ or % characters

- Add dismissScanError() method to allow closing error banner
  - Previously banner couldn't be dismissed (missing notifyListeners())

## Improvements
- Remove dead livePaths list, use companions.isNotEmpty instead
- Add initial progress update to show total files when scan starts
- Add tooltip to now_playing_screen Zurück button
- Add proper error handling in song_list with async/await

## Testing
- Add regression test for CRITICAL-1: empty scan doesn't delete songs
- Add regression test for CRITICAL-2: tombstoned files restore on re-scan
- All 11 tests pass

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-16 19:09:00 +02:00
Dustin-Mike Jens HähnelandClaude Haiku 4.5 4fa8d12fa0 Fix critical data loss bugs and improve error handling
## Critical Fixes
- Fix CRITICAL-1: Empty scans no longer delete entire library
  - Use timestamp-based tombstone logic instead of path-based
  - Only mark missing if livePaths is not empty

- Fix CRITICAL-2: Re-scanned files now return from deleted state
  - Add deleted=false to SongsCompanion.insert in both scanners

## High Priority Fixes
- HIGH-1: Fix SQLite variable limit crash on large libraries
  - Replace isNotIn() with timestamp comparison (O(1) not O(n))

- HIGH-2: Fix UI freezing during scan
  - Throttle progress updates (every 50 files or end of scan)

- HIGH-3: Add error handling for scan failures
  - Wrap scanFolders/scanAndroidMediaStore in try/catch
  - Display scanError banner in LibraryScreen

- HIGH-4: Add error handling for playback failures
  - Wrap loadPlaylist in try/catch

## Medium Priority Fixes
- MEDIUM-1: Replace ! with ?? to handle unknown ProcessingState
- MEDIUM-2: Escape LIKE wildcards in search queries
- MEDIUM-4: Display StreamBuilder errors instead of treating as empty
- MEDIUM-8: Add tooltips to all IconButtons for accessibility
- Add doc comments to public database APIs

## Testing
- Add regression tests for CRITICAL-1 and CRITICAL-2
- All 9 existing tests pass

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-16 19:04:54 +02:00
Dustin-Mike Jens Hähnel b5ab7bd9dd Initial commit 2026-08-16 18:46:06 +02:00