## Server (melo_cloud.py v3.0.0) - Neue DB-Tabellen: user_playlists, user_playlist_songs, user_favorites, user_history, sync_meta - Playlist-Endpunkte: GET/POST /api/cloud/playlists, GET /api/cloud/playlists/<id>, POST songs, DELETE songs, PUT positions - Favoriten-Endpunkte: GET/POST /api/cloud/favorites, POST /api/cloud/favorites/toggle - History-Endpunkte: GET/POST /api/cloud/history (mit Dedup) - Rename-Endpunkt: POST /api/cloud/rename (custom_title/custom_artist) - Sync-Endpunkte: POST /api/cloud/sync-all, GET /api/cloud/sync-status - delete-all erweitert um Playlists + Favorites + History ## App - db_helper.dart: Migration v4→v5 (cloud_id, cloud_title, cloud_artist, server_favorites, sync_metadata) - auth_service.dart: Token-Validierung beim Start (online check + Offline-Fallback), Persistent Login - cloud_service.dart: Neue Methoden für Playlists (CRUD), Favorites (get/sync/toggle), Rename, History, syncAll/syncStatus - cloud_screen.dart: Komplett-Rewrite mit Sync-Modus (Manuell/Auto), Intervall-Auswahl, Sync-Fortschritt-Anzeige, Playlist-Verwaltung, Favoriten-Anzeige + Umbenennen, Sync-Info-Karte, nächster Sync - main.dart: Auto-Sync beim App-Start (nur wenn konfiguriert + fällig) ## Flutter Analyze - 0 neue Issues — nur 7 pre-existing
107 lines
3.1 KiB
Dart
107 lines
3.1 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:audio_service/audio_service.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
import 'database/db_helper.dart';
|
||
import 'services/favoriten_service.dart';
|
||
import 'services/auth_service.dart';
|
||
import 'services/cloud_service.dart';
|
||
import 'services/melo_logger.dart';
|
||
import 'services/audio_handler.dart';
|
||
import 'utils/farb_theme.dart';
|
||
import 'screens/home_screen.dart';
|
||
import 'screens/login_screen.dart';
|
||
|
||
void main() async {
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
|
||
// Logger startet sofort – zeichnet ALLES auf
|
||
MeloLogger().init('2.35');
|
||
|
||
try {
|
||
await DbHelper().db;
|
||
await FavoritenService().init();
|
||
// Hintergrund-Audio-Service starten
|
||
await AudioService.init(
|
||
builder: () => MeloAudioHandler(),
|
||
config: const AudioServiceConfig(
|
||
androidNotificationChannelId: 'de.baka.melo.audio',
|
||
androidNotificationChannelName: 'Melo Wiedergabe',
|
||
androidNotificationOngoing: true,
|
||
),
|
||
);
|
||
MeloLogger().zustand('start_ok', {'db': 'ok', 'audio': 'ok'});
|
||
} catch (e, stack) {
|
||
MeloLogger().fehler('App-Start', e, stack);
|
||
}
|
||
|
||
// Auth initialisieren (Token aus SharedPreferences laden + online validieren)
|
||
await AuthService().initialisieren();
|
||
|
||
// Auto-Sync beim Start (wenn eingeloggt und Auto-Sync aktiv)
|
||
_starteAutoSyncFallsNoetig();
|
||
|
||
runApp(const MeloApp());
|
||
}
|
||
|
||
/// Führt Cloud-Auto-Sync beim App-Start aus, falls konfiguriert
|
||
Future<void> _starteAutoSyncFallsNoetig() async {
|
||
try {
|
||
final auth = AuthService();
|
||
if (!auth.istEingeloggt) return;
|
||
|
||
// Prüfen, ob Auto-Sync aktiviert ist
|
||
final SharedPreferences prefs =
|
||
await SharedPreferences.getInstance();
|
||
final autoSync = prefs.getBool('cloud_auto') ?? true;
|
||
if (!autoSync) return;
|
||
|
||
final cloud = CloudService();
|
||
final ok = await cloud.login(auth.benutzer);
|
||
if (!ok) return;
|
||
|
||
// Letzten Sync prüfen — nur syncen wenn nötig
|
||
final lastSync = prefs.getString('cloud_last_sync_ts');
|
||
final now = DateTime.now();
|
||
|
||
if (lastSync != null) {
|
||
final last = DateTime.tryParse(lastSync);
|
||
if (last != null) {
|
||
final interval = prefs.getInt('cloud_interval') ?? 6;
|
||
if (now.difference(last).inHours < interval) {
|
||
return; // Noch nicht fällig
|
||
}
|
||
}
|
||
}
|
||
|
||
// Sync ausführen
|
||
MeloLogger().aktion('auto_sync_start', {});
|
||
final st = await cloud.status();
|
||
if (st != null) {
|
||
final serverSongs = await cloud.listSongs();
|
||
MeloLogger().aktion('auto_sync_done',
|
||
{'songs': serverSongs.length});
|
||
}
|
||
await prefs.setString('cloud_last_sync_ts', now.toIso8601String());
|
||
} catch (e) {
|
||
MeloLogger().fehler('auto_sync_init', e);
|
||
}
|
||
}
|
||
|
||
class MeloApp extends StatelessWidget {
|
||
const MeloApp({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final auth = AuthService();
|
||
|
||
return MaterialApp(
|
||
title: 'Melo',
|
||
debugShowCheckedModeBanner: false,
|
||
theme: MeloTheme.theme,
|
||
home: auth.istEingeloggt
|
||
? const Scaffold(body: MeloHome())
|
||
: const LoginScreen(),
|
||
);
|
||
}
|
||
}
|