142 lines
4.2 KiB
Dart
142 lines
4.2 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import '../models/song.dart';
|
|
import '../database/db_helper.dart';
|
|
|
|
class DownloadService {
|
|
static final DownloadService _instanz = DownloadService._();
|
|
factory DownloadService() => _instanz;
|
|
DownloadService._();
|
|
|
|
final YoutubeExplode _yt = YoutubeExplode();
|
|
final DbHelper _db = DbHelper();
|
|
|
|
bool _ladt = false;
|
|
double _fortschritt = 0;
|
|
String? _aktuellerTitel;
|
|
String? _fehler;
|
|
|
|
bool get ladt => _ladt;
|
|
double get fortschritt => _fortschritt;
|
|
String? get aktuellerTitel => _aktuellerTitel;
|
|
String? get fehler => _fehler;
|
|
|
|
void _resetStatus() {
|
|
_ladt = true;
|
|
_fortschritt = 0;
|
|
_aktuellerTitel = null;
|
|
_fehler = null;
|
|
}
|
|
|
|
Future<Song?> downloadVonUrl(String url) async {
|
|
_resetStatus();
|
|
|
|
try {
|
|
// URL-Validierung
|
|
if (!url.contains('youtube.com') && !url.contains('youtu.be')) {
|
|
_fehler = 'Keine gültige YouTube-URL';
|
|
_ladt = false;
|
|
return null;
|
|
}
|
|
|
|
String titel = '';
|
|
String kuenstler = '';
|
|
int dauer = 0;
|
|
|
|
// Video-Info mit Timeout
|
|
_aktuellerTitel = 'YouTube wird kontaktiert...';
|
|
try {
|
|
final video = await _yt.videos.get(url).timeout(const Duration(seconds: 15));
|
|
titel = video.title;
|
|
kuenstler = video.author;
|
|
dauer = video.duration?.inSeconds ?? 0;
|
|
_aktuellerTitel = 'Video gefunden: $titel';
|
|
} catch (e) {
|
|
_fehler = 'Timeout/Fehler bei Video-Info: ${e.toString()}';
|
|
debugPrint('Video-Info Fehler (vollständig): $e');
|
|
_ladt = false;
|
|
return null;
|
|
}
|
|
|
|
// Manifest abrufen
|
|
_aktuellerTitel = 'Audio-Stream wird ermittelt... (2/4)';
|
|
AudioStreamInfo? audio;
|
|
try {
|
|
final manifest = await _yt.videos.streamsClient
|
|
.getManifest(url).timeout(const Duration(seconds: 15));
|
|
final streams = manifest.audioOnly.toList();
|
|
if (streams.isEmpty) {
|
|
_fehler = 'Kein Audio-Stream verfügbar';
|
|
_ladt = false;
|
|
return null;
|
|
}
|
|
audio = streams.reduce((a, b) => a.bitrate.bitsPerSecond > b.bitrate.bitsPerSecond ? a : b);
|
|
} catch (e) {
|
|
_fehler = 'Stream-Fehler: ${e.toString()}';
|
|
debugPrint('Stream Manifest Fehler (vollständig): $e');
|
|
_ladt = false;
|
|
return null;
|
|
}
|
|
|
|
// Zielpfad
|
|
_aktuellerTitel = 'Speicher wird vorbereitet... (3/4)';
|
|
final dir = await getApplicationDocumentsDirectory();
|
|
final musikDir = Directory('${dir.path}/music');
|
|
if (!await musikDir.exists()) await musikDir.create(recursive: true);
|
|
final safeName = titel.replaceAll(RegExp(r'[^\w\s-]'), '').trim();
|
|
final dateiName = '${safeName.isEmpty ? "song" : safeName}.mp4';
|
|
final dateiPfad = '${musikDir.path}/$dateiName';
|
|
|
|
// Download
|
|
_aktuellerTitel = 'Lade herunter... (4/4)';
|
|
try {
|
|
final fileStream = _yt.videos.streamsClient.get(audio);
|
|
final file = File(dateiPfad);
|
|
final sink = file.openWrite();
|
|
int downloaded = 0;
|
|
final total = audio.size.totalBytes;
|
|
await for (final chunk in fileStream) {
|
|
sink.add(chunk);
|
|
downloaded += chunk.length;
|
|
_fortschritt = downloaded / total;
|
|
}
|
|
await sink.flush();
|
|
await sink.close();
|
|
} catch (e) {
|
|
_fehler = 'Download-Fehler: ${e.toString()}';
|
|
debugPrint('Download Stream Fehler (vollständig): $e');
|
|
_ladt = false;
|
|
return null;
|
|
}
|
|
|
|
// Song speichern
|
|
final song = Song(
|
|
titel: titel,
|
|
kuenstler: kuenstler,
|
|
album: 'YouTube',
|
|
dauerSekunden: dauer,
|
|
dateiPfad: dateiPfad,
|
|
groesseBytes: await File(dateiPfad).length(),
|
|
istHeruntergeladen: true,
|
|
downloadQuelle: 'youtube',
|
|
);
|
|
await _db.songEinfuegen(song);
|
|
|
|
_ladt = false;
|
|
_aktuellerTitel = '✅ ${song.titel} heruntergeladen';
|
|
return song;
|
|
} catch (e) {
|
|
_fehler = 'Fehler: ${e.toString()}';
|
|
debugPrint('Download allgemeiner Fehler (vollständig): $e');
|
|
_ladt = false;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
void dispose() {
|
|
_yt.close();
|
|
}
|
|
}
|