commit fe1ea4fe3ead525c11ca6e94c71c7652261d253c Author: hannes Date: Wed Apr 29 20:42:39 2026 +0000 Dateien nach „/“ hochladen diff --git a/README.md b/README.md new file mode 100644 index 0000000..27218a5 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# YT → MP3 Downloader + +Flask-App zum Herunterladen von YouTube-Videos als MP3 mit ID3-Tags. + +## Voraussetzungen + +- Python 3.8+ +- ffmpeg (muss im PATH sein) + +### ffmpeg installieren + +**Ubuntu/Debian:** +```bash +sudo apt install ffmpeg +``` + +**macOS:** +```bash +brew install ffmpeg +``` + +**Windows:** +Download von https://ffmpeg.org/download.html, dann zum PATH hinzufügen. + +## Setup + +```bash +# Abhängigkeiten installieren +pip install -r requirements.txt + +# App starten +python app.py +``` + +Dann im Browser öffnen: http://localhost:5000 + +## Funktionsweise + +1. YouTube-URL eingeben +2. Interpret, Titel und Album ausfüllen +3. „Download starten" klicken +4. Die MP3 wird im Ordner `downloads/` gespeichert + +## Projektstruktur + +``` +ytdl-app/ +├── app.py # Flask-Backend +├── requirements.txt # Python-Abhängigkeiten +├── downloads/ # Gespeicherte MP3-Dateien +└── templates/ + └── index.html # Web-Interface +``` diff --git a/app.py b/app.py new file mode 100644 index 0000000..fd4190e --- /dev/null +++ b/app.py @@ -0,0 +1,101 @@ +from flask import Flask, render_template, request, jsonify +import yt_dlp +import os +import threading + +app = Flask(__name__) + +DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "downloads") +os.makedirs(DOWNLOAD_DIR, exist_ok=True) + +download_status = {} + + +def do_download(task_id, url, artist, title, album): + try: + download_status[task_id] = {"status": "running", "message": "Download läuft..."} + + ydl_opts = { + "format": "bestaudio/best", + "outtmpl": os.path.join(DOWNLOAD_DIR, "%(title)s.%(ext)s"), + "postprocessors": [ + { + "key": "FFmpegExtractAudio", + "preferredcodec": "mp3", + "preferredquality": "192", + }, + { + "key": "FFmpegMetadata", + "add_metadata": True, + }, + ], + # Scoped to FFmpegMetadata so tags are set correctly + "postprocessor_args": { + "ffmpegmetadata": [ + "-metadata", f"artist={artist}", + "-metadata", f"title={title}", + "-metadata", f"album={album}", + ] + }, + "quiet": True, + "no_warnings": True, + } + + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + info = ydl.extract_info(url, download=True) + + # extract_info kann eine Playlist zurückgeben – dann erstes Entry nehmen + if info is None: + raise RuntimeError("yt-dlp hat keine Informationen zurückgegeben. Bitte URL prüfen.") + if "entries" in info: + info = info["entries"][0] + + filename = ydl.prepare_filename(info) + mp3_name = os.path.splitext(os.path.basename(filename))[0] + ".mp3" + + download_status[task_id] = { + "status": "done", + "message": f"Fertig! Gespeichert als: {mp3_name}", + "filename": mp3_name, + } + + except Exception as e: + download_status[task_id] = { + "status": "error", + "message": f"Fehler: {str(e)}", + } + + +@app.route("/") +def index(): + return render_template("index.html") + + +@app.route("/download", methods=["POST"]) +def download(): + data = request.get_json() + url = data.get("url", "").strip() + artist = data.get("artist", "").strip() + title = data.get("title", "").strip() + album = data.get("album", "").strip() + + if not url: + return jsonify({"error": "URL fehlt"}), 400 + + import uuid + task_id = str(uuid.uuid4()) + thread = threading.Thread(target=do_download, args=(task_id, url, artist, title, album)) + thread.daemon = True + thread.start() + + return jsonify({"task_id": task_id}) + + +@app.route("/status/") +def status(task_id): + result = download_status.get(task_id, {"status": "unknown", "message": "Unbekannte Task-ID"}) + return jsonify(result) + + +if __name__ == "__main__": + app.run(debug=True, port=5000) diff --git a/index.html b/index.html new file mode 100644 index 0000000..05b2732 --- /dev/null +++ b/index.html @@ -0,0 +1,307 @@ + + + + + + YT → MP3 + + + + + +
+
+

YT→MP3

+ YouTube Downloader +
+ +
+
+ + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ + +
+ + +
+ + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..55660d4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +flask>=3.0.0 +yt-dlp>=2024.1.0