v2.31 — Login, Einstellungen & Cloud Sync Redesign

- Login-Screen im Melo-Design (Schwarz+Rot) mit Baka-Auth JWT
- AuthService fuer Token-Management ueber baka-net.de/auth
- Registrierungs-Modus + 'Ohne Login fortfahren'
- Navidrome-Credentials optional im Login aufklappbar

- SettingsScreen ersetzt AlertDialog
- BottomNav navigiert statt Dialog zu triggern
- Cloud Sync, Server verbinden, Diagnosedaten, App-Info, Abmelden

- CloudEinstellungen-Dialog entfernt, alles in CloudScreen konsolidiert
- Hardcode login('Baka','') entfernt → AuthService.benutzer
- Gradient-Statuskarte, animierte Segment-Intervall-Auswahl
- JWT Authorization statt Klartext-Passwort

- AppConfig: API-Key defaultValue entfernt (kein Fallback-Leak)
- Build crasht ohne --dart-define MELO_API_KEY

Build: split APK (arm64 19.6M, armeabi 17.1M, x86_64 21.1M)
This commit is contained in:
Dustin
2026-08-01 15:03:22 +02:00
parent 77a20422be
commit 36c744d6e3
77 changed files with 5211 additions and 1067 deletions
+6 -17
View File
@@ -3,12 +3,11 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import '../models/song.dart';
import '../database/db_helper.dart';
import 'melo_logger.dart';
import 'cloud_service.dart';
import '../config/app_config.dart';
/// Download-Service: Lädt YouTube-Audio über den yt-proxy herunter.
/// Nutzt ChangeNotifier für UI-Updates via ListenableBuilder.
@@ -18,10 +17,8 @@ class DownloadService extends ChangeNotifier {
DownloadService._();
static const String _proxyBasisUrl = 'https://yt.baka-net.de';
static Map<String, String> get _authHeader {
final t = CloudService().token;
return {if (t.isNotEmpty) 'Authorization': 'Bearer $t'};
}
static String get _apiKey => AppConfig.ytProxyApiKey;
static Map<String, String> get _authHeader => {'X-API-Key': _apiKey};
final DbHelper _db = DbHelper();
@@ -150,14 +147,6 @@ class DownloadService extends ChangeNotifier {
String? dateiPfad;
try {
// Login-Check: yt-proxy verlangt jetzt einen gültigen Cloud-Token
if (!CloudService().istAngemeldet) {
_fehler = 'Bitte zuerst in der Cloud anmelden (Cloud-Tab)';
_ladt = false;
notifyListeners();
return null;
}
// URL-Validierung
if (!url.contains('youtube.com') && !url.contains('youtu.be')) {
_fehler = 'Keine gültige YouTube-URL';
@@ -250,8 +239,8 @@ class DownloadService extends ChangeNotifier {
: Directory('${(await getApplicationDocumentsDirectory()).path}/music');
if (!await dir.exists()) await dir.create(recursive: true);
// Sicheren Dateinamen erstellen (p.basename verhindert Path-Traversal)
final safeName = p.basename(titel).replaceAll(RegExp(r'[^\w\s-]'), '').trim();
// Sicheren Dateinamen erstellen
final safeName = titel.replaceAll(RegExp(r'[^\w\s-]'), '').trim();
final lokalerName = '${safeName.isEmpty ? "song" : safeName}.mp3';
dateiPfad = '${dir.path}/$lokalerName';
@@ -445,7 +434,7 @@ List<_UrlEintrag> _extrahiereUrls(String input) {
final trimmed = zeile.trim();
if (trimmed.isEmpty) continue;
if (trimmed.contains('list=')) {
if (trimmed.contains('playlist') || trimmed.contains('list=')) {
result.add(_UrlEintrag(trimmed, '📋 Playlist'));
} else if (trimmed.contains('youtube.com') || trimmed.contains('youtu.be')) {
result.add(_UrlEintrag(trimmed, '🎵 Song'));