Initial commit

This commit is contained in:
Dustin-Mike Jens Hähnel
2026-07-23 14:31:28 +02:00
commit 94a4063070
152 changed files with 8500 additions and 0 deletions
+141
View File
@@ -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();
}
}