Initial commit
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user