import 'package:flutter/foundation.dart'; import '../database/db_helper.dart'; import '../services/player_service.dart'; import '../services/musik_scanner.dart'; import '../services/favoriten_service.dart'; import '../services/download_service.dart'; import '../services/navidrome_service.dart'; import '../services/playlist_service.dart'; import '../models/song.dart'; import '../models/tag.dart'; class MeloHomeViewModel extends ChangeNotifier { final PlayerService player = PlayerService(); final DbHelper db = DbHelper(); final FavoritenService favoriten = FavoritenService(); final MusikScanner scanner = MusikScanner(); final DownloadService downloader = DownloadService(); final NavidromeService navidrome = NavidromeService(); final PlaylistService playlists = PlaylistService(); List songs = []; Set aktiveTags = {}; bool ladt = true; Set favoritenIds = {}; List> tags = []; List letzteSongs = []; Map tagCounts = {}; bool zeigeBotschaft = false; bool serverLadt = false; List navidromeAlben = []; int _playCount = 0; static const int _botschaftSchwellwert = 10; static const _defaultTags = [ {'name': 'Alle', 'icon': ''}, {'name': '❤️ Für uns', 'icon': '❤️'}, {'name': 'Nightcore', 'icon': '⚡'}, {'name': 'Traurig', 'icon': '😢'}, {'name': 'Party', 'icon': '🎉'}, {'name': 'Mitsingen', 'icon': '🎤'}, {'name': '2000er', 'icon': '📀'}, ]; List get gefilterteSongs { if (aktiveTags.isEmpty) return songs; return songs.where((s) => aktiveTags.any((tag) => s.titel.contains(tag) || s.kuenstler.contains(tag)) ).toList(); } Future ladeSongs() async { ladt = true; notifyListeners(); var alle = await db.alleSongs(); if (alle.isEmpty) { final beispiele = [ Song(titel: 'Leichtes Gepäck', kuenstler: 'Silbermond', album: 'Schritte', dauerSekunden: 232, dateiPfad: '', groesseBytes: 5242880, istHeruntergeladen: true), Song(titel: 'Auf uns', kuenstler: 'Andreas Bourani', album: 'Hey', dauerSekunden: 241, dateiPfad: '', groesseBytes: 4819200, istHeruntergeladen: true), Song(titel: '80 Millionen', kuenstler: 'Max Giesinger', album: 'Der Junge', dauerSekunden: 225, dateiPfad: '', groesseBytes: 5107200, istHeruntergeladen: true), Song(titel: 'Tage wie diese', kuenstler: 'Die Toten Hosen', album: 'Ballast', dauerSekunden: 252, dateiPfad: '', groesseBytes: 4300800, istHeruntergeladen: true), Song(titel: 'Atlantis', kuenstler: 'Frida Gold', album: 'Liebe', dauerSekunden: 228, dateiPfad: '', groesseBytes: 3987200, istHeruntergeladen: true), ]; await db.songsEinfuegen(beispiele); alle = await db.alleSongs(); } songs = alle; favoritenIds = await favoriten.favoritenIds(); letzteSongs = await db.letzteWiedergaben(); await ladeTags(); ladt = false; notifyListeners(); } Map _berechneTagCounts() { final counts = {}; for (final s in songs) { for (final t in tags) { final name = t['name']!; if (name == 'Alle') continue; if (s.titel.contains(name) || s.kuenstler.contains(name)) { counts[name] = (counts[name] ?? 0) + 1; } } } return counts; } Future ladeTags() async { var dbTags = await db.alleTags(); if (dbTags.isEmpty) { for (final t in _defaultTags) { if (t['name'] != 'Alle') { await db.tagErstellen(t['name']!, icon: t['icon']); } } dbTags = await db.alleTags(); } tags = [{'name': 'Alle', 'icon': ''}, ...dbTags.map((t) => t.toDisplay())]; // Tag-Counts nachladen wenn Tags geladen tagCounts = _berechneTagCounts(); } void spieleSong(Song song) { player.setWarteschlange(songs, startIndex: songs.indexWhere((s) => s.id == song.id)); player.spiele(song); // In Verlauf speichern if (song.id != null) { db.positionAktualisieren(song.id!, 0); } // Play-Counter für Hidden Message _playCount++; if (_playCount >= _botschaftSchwellwert && !zeigeBotschaft) { zeigeBotschaft = true; notifyListeners(); } // Letzte Songs aktualisieren db.letzteWiedergaben().then((liste) { letzteSongs = liste; notifyListeners(); }); } /// Manuell die Botschaft "Seit 2008" anzeigen void botschaftAnzeigen() { zeigeBotschaft = true; notifyListeners(); } /// Botschaft ausblenden void botschaftAusblenden() { zeigeBotschaft = false; notifyListeners(); } void toggleTag(String tag) { if (tag == 'Alle') { aktiveTags.clear(); } else if (aktiveTags.contains(tag)) { aktiveTags.remove(tag); } else { aktiveTags.add(tag); } notifyListeners(); } Future favoritenUmschalten(Song song) async { if (song.id == null) return; await favoriten.umschalten(song.id!); favoritenIds = await favoriten.favoritenIds(); notifyListeners(); } // ─── Navidrome Server-Sync ───────────────────────── /// Navidrome-Zugangsdaten setzen void verbindeNavidrome(String url, String user, String password) { navidrome.setCredentials(url, user, password); } /// Verbindung testen und Alben laden Future ladeNavidromeAlben() async { if (!navidrome.istVerbunden) return false; serverLadt = true; notifyListeners(); final ok = await navidrome.ping(); if (ok) { navidromeAlben = await navidrome.getAlben(); } serverLadt = false; notifyListeners(); return ok; } /// Songs eines Albums laden Future> ladeAlbumSongs(String albumId) async { return navidrome.getSongs(albumId); } /// Song von Navidrome runterladen Future downloadNavidromeSong(SubsonicSong s) async { final song = await navidrome.downloadSong(s); if (song != null) { await ladeSongs(); return true; } return false; } @override void dispose() { player.dispose(); super.dispose(); } }