From 3068bbdabb927e3502975285fa85ce5d16df37d9 Mon Sep 17 00:00:00 2001 From: "Hermes (Server)" Date: Wed, 19 Aug 2026 19:07:05 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A4=20Feature=20B:=20Artist-Browse=20U?= =?UTF-8?q?I=20(Phase=202,=20Part=203/3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Toggle-Buttons in Navidrome-Tab: "📀 Alben" ↔ "🎤 Künstler" - Artist-Liste zeigt Namen pro Künstler, Tap lädt alle Songs des Künstlers - _playArtist() Methode lädt Künstler-Songs via getArtistSongs() API - Albums/Artists sind Lazy-Loaded (nur beim Toggle laden) Co-Authored-By: Claude Haiku 4.5 Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru --- lib/library/library_screen.dart | 138 +++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 18 deletions(-) diff --git a/lib/library/library_screen.dart b/lib/library/library_screen.dart index 89eaf5b..bdf698e 100644 --- a/lib/library/library_screen.dart +++ b/lib/library/library_screen.dart @@ -253,7 +253,9 @@ class _NavidromeTab extends StatefulWidget { class _NavidromeTabState extends State<_NavidromeTab> { late final NavidromeService _nav = NavidromeService(); List _albums = []; + List _artists = []; bool _loading = false; + bool _showArtists = false; @override void initState() { @@ -286,6 +288,23 @@ class _NavidromeTabState extends State<_NavidromeTab> { } } + Future _loadArtists() async { + if (!_nav.istVerbunden) return; + setState(() => _loading = true); + try { + final artists = await _nav.getArtists(); + if (mounted) { + setState(() { + _artists = artists; + _loading = false; + }); + } + } catch (e) { + if (mounted) setState(() => _loading = false); + debugPrint('Fehler beim Laden der Künstler: $e'); + } + } + @override Widget build(BuildContext context) { if (!_nav.istVerbunden) { @@ -331,25 +350,79 @@ class _NavidromeTabState extends State<_NavidromeTab> { ); } - return ListView.builder( - padding: const EdgeInsets.all(12), - itemCount: _albums.length, - itemBuilder: (context, i) { - final album = _albums[i]; - return Card( - margin: const EdgeInsets.only(bottom: 8), - child: ListTile( - leading: Icon( - Icons.album, - size: 48, - color: Colors.redAccent.withValues(alpha: 0.5), - ), - title: Text(album.name), - subtitle: Text('${album.songCount} Songs'), - onTap: () => _playAlbum(album), + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(12), + child: Row( + children: [ + Expanded( + child: FilledButton( + onPressed: () => setState(() { + _showArtists = false; + if (_albums.isEmpty) _loadAlbums(); + }), + style: FilledButton.styleFrom( + backgroundColor: !_showArtists ? Colors.redAccent : Colors.grey.shade700, + ), + child: const Text('📀 Alben'), + ), + ), + const SizedBox(width: 8), + Expanded( + child: FilledButton( + onPressed: () => setState(() { + _showArtists = true; + if (_artists.isEmpty) _loadArtists(); + }), + style: FilledButton.styleFrom( + backgroundColor: _showArtists ? Colors.redAccent : Colors.grey.shade700, + ), + child: const Text('🎤 Künstler'), + ), + ), + ], ), - ); - }, + ), + Expanded( + child: _showArtists + ? ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 12), + itemCount: _artists.length, + itemBuilder: (context, i) { + final artist = _artists[i]; + return Card( + margin: const EdgeInsets.only(bottom: 8), + child: ListTile( + leading: const Icon(Icons.person, size: 48), + title: Text(artist.name), + onTap: () => _playArtist(artist), + ), + ); + }, + ) + : ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 12), + itemCount: _albums.length, + itemBuilder: (context, i) { + final album = _albums[i]; + return Card( + margin: const EdgeInsets.only(bottom: 8), + child: ListTile( + leading: Icon( + Icons.album, + size: 48, + color: Colors.redAccent.withValues(alpha: 0.5), + ), + title: Text(album.name), + subtitle: Text('${album.songCount} Songs'), + onTap: () => _playAlbum(album), + ), + ); + }, + ), + ), + ], ); } @@ -387,4 +460,33 @@ class _NavidromeTabState extends State<_NavidromeTab> { ); } } + + Future _playArtist(SubsonicArtist artist) async { + final handler = context.read(); + try { + final songs = await _nav.getArtistSongs(artist.id); + if (songs.isEmpty) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Künstler hat keine Songs')), + ); + return; + } + final items = songs + .map((s) => MediaItem( + id: _nav.streamUrl(s.id).toString(), + title: s.titel, + artist: s.kuenstler, + album: s.album, + duration: Duration(seconds: s.dauerSekunden), + )) + .toList(); + await handler.loadPlaylist(items); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Fehler: $e')), + ); + } + } }