Files

147 lines
4.0 KiB
Dart

import 'dart:io';
import 'package:just_audio/just_audio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import '../models/song.dart';
import '../database/db_helper.dart';
class MusikScanner {
static final MusikScanner _instanz = MusikScanner._();
factory MusikScanner() => _instanz;
MusikScanner._();
final DbHelper _db = DbHelper();
int _neueSongs = 0;
int get anzahlNeueSongs => _neueSongs;
Future<bool> frageSpeicherZugriff() async {
final status = await Permission.audio.request();
return status.isGranted;
}
Future<List<Song>> scanneMusikOrdner() async {
_neueSongs = 0;
final gefunden = <Song>[];
final pfade = await _sammleMusikPfade();
for (final pfad in pfade) {
try {
final file = File(pfad);
if (!await file.exists()) continue;
final stat = await file.stat();
gefunden.add(Song(
titel: _dateiNameOhneEndung(pfad),
kuenstler: 'Unbekannt',
album: '',
dauerSekunden: await _ermittleDauer(pfad),
dateiPfad: pfad,
coverPfad: null,
groesseBytes: stat.size,
istHeruntergeladen: true,
downloadQuelle: 'local',
));
} catch (_) {
// Datei nicht lesbar → überspringen
}
}
// In DB speichern
final vorhandene = await _db.alleSongs();
final vorhandenePfade = vorhandene.map((s) => s.dateiPfad).toSet();
final neue = gefunden.where((s) => !vorhandenePfade.contains(s.dateiPfad)).toList();
if (neue.isNotEmpty) {
await _db.songsEinfuegen(neue);
_neueSongs = neue.length;
}
return gefunden;
}
Future<List<String>> _sammleMusikPfade() async {
final pfade = <String>{};
// Typische Musik-Ordner auf Android
final ordner = [
'/storage/emulated/0/Music',
'/storage/emulated/0/Download',
'/storage/emulated/0/Musik',
'/storage/emulated/0/Downloads',
'/sdcard/Music',
'/sdcard/Download',
'/sdcard/Musik',
];
// Externe SD-Karte (falls vorhanden)
try {
final extern = await getExternalStorageDirectory();
if (extern != null) {
ordner.add(extern.path);
}
} catch (_) {}
// Android Media Store (bessere Methode)
try {
final pfadeVonMediaStore = await _scanneViaMediaStore();
pfade.addAll(pfadeVonMediaStore);
} catch (_) {}
// Fallback: Dateisystem durchsuchen
for (final ord in ordner) {
try {
final dir = Directory(ord);
if (await dir.exists()) {
await _durchsucheOrdner(dir, pfade);
}
} catch (_) {}
}
return pfade.toList();
}
Future<List<String>> _scanneViaMediaStore() async {
// Nutzt Android's MediaStore Query
// Wird über Method Channel in native Android implementiert
// Für v1: Fallback auf Dateisystem-Suche
return [];
}
Future<void> _durchsucheOrdner(Directory dir, Set<String> pfade, {int tiefe = 0}) async {
if (tiefe > 4) return;
try {
await for (final entity in dir.list(followLinks: false)) {
if (entity is File) {
final ext = entity.path.toLowerCase();
if (ext.endsWith('.mp3') || ext.endsWith('.m4a') ||
ext.endsWith('.flac') || ext.endsWith('.wav') ||
ext.endsWith('.aac') || ext.endsWith('.ogg')) {
pfade.add(entity.path);
}
} else if (entity is Directory) {
await _durchsucheOrdner(entity, pfade, tiefe: tiefe + 1);
}
}
} catch (_) {}
}
Future<int> _ermittleDauer(String pfad) async {
try {
final player = AudioPlayer();
await player.setFilePath(pfad);
final dauer = player.duration;
await player.dispose();
return dauer?.inSeconds ?? 0;
} catch (_) {
return 0;
}
}
String _dateiNameOhneEndung(String pfad) {
final name = pfad.split('/').last;
final dot = name.lastIndexOf('.');
return dot > 0 ? name.substring(0, dot) : name;
}
}