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