Navidrome-Integration, Widgets (Recent + Tag-Statistik)
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
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<Song> songs = [];
|
||||
Set<String> aktiveTags = {};
|
||||
bool ladt = true;
|
||||
Set<int> favoritenIds = {};
|
||||
List<Map<String, String>> tags = [];
|
||||
List<Song> letzteSongs = [];
|
||||
Map<String, int> tagCounts = {};
|
||||
bool zeigeBotschaft = false;
|
||||
bool serverLadt = false;
|
||||
List<SubsonicAlbum> 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<Song> get gefilterteSongs {
|
||||
if (aktiveTags.isEmpty) return songs;
|
||||
return songs.where((s) =>
|
||||
aktiveTags.any((tag) =>
|
||||
s.titel.contains(tag) || s.kuenstler.contains(tag))
|
||||
).toList();
|
||||
}
|
||||
|
||||
Future<void> 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();
|
||||
tagCounts = _berechneTagCounts();
|
||||
letzteSongs = await db.letzteWiedergaben();
|
||||
await ladeTags();
|
||||
ladt = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Map<String, int> _berechneTagCounts() {
|
||||
final counts = <String, int>{};
|
||||
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<void> 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<void> 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<bool> 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<List<SubsonicSong>> ladeAlbumSongs(String albumId) async {
|
||||
return navidrome.getSongs(albumId);
|
||||
}
|
||||
|
||||
/// Song von Navidrome runterladen
|
||||
Future<bool> 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user