CoolFace
Apppublic

factblink514/Editing

sourceHugging Faceupdated 10d agoView on Hugging Face
3likes
app.py197 linesDownload Raw Back to root
1import os2import re3import uuid4import shutil5import tempfile6import threading7import time8 9from flask import Flask, request, jsonify, send_file, render_template, after_this_request10import yt_dlp11 12app = Flask(__name__)13 14DOWNLOAD_DIR = os.path.join(tempfile.gettempdir(), "videodl_downloads")15os.makedirs(DOWNLOAD_DIR, exist_ok=True)16 17# ---- housekeeping: delete files older than 1 hour ----18def cleanup_loop():19    while True:20        now = time.time()21        for fname in os.listdir(DOWNLOAD_DIR):22            fpath = os.path.join(DOWNLOAD_DIR, fname)23            try:24                if os.path.isfile(fpath) and now - os.path.getmtime(fpath) > 3600:25                    os.remove(fpath)26            except OSError:27                pass28        time.sleep(600)29 30threading.Thread(target=cleanup_loop, daemon=True).start()31 32 33def safe_filename(name: str) -> str:34    name = re.sub(r"[^\w\s\-\.]", "", name).strip()35    return re.sub(r"\s+", "_", name)[:100] or "download"36 37 38COMMON_OPTS = {39    "quiet": True,40    "no_warnings": True,41    "noplaylist": True,42    "nocheckcertificate": True,43    "geo_bypass": True,44}45 46 47@app.route("/")48def index():49    return render_template("index.html")50 51 52@app.route("/api/info", methods=["POST"])53def get_info():54    """Fetch title, thumbnail, description, duration, and available formats."""55    data = request.get_json(force=True)56    url = (data or {}).get("url", "").strip()57    if not url:58        return jsonify({"error": "No URL provided"}), 40059 60    ydl_opts = dict(COMMON_OPTS)61    try:62        with yt_dlp.YoutubeDL(ydl_opts) as ydl:63            info = ydl.extract_info(url, download=False)64    except Exception as e:65        return jsonify({"error": f"Could not fetch video info: {str(e)}"}), 40066 67    formats_out = []68    seen = set()69    for f in info.get("formats", []):70        if not f.get("url"):71            continue72        height = f.get("height")73        ext = f.get("ext")74        vcodec = f.get("vcodec", "none")75        acodec = f.get("acodec", "none")76 77        if vcodec != "none":78            label = f"{height}p" if height else f.get("format_note", f.get("format_id"))79            key = ("video", label, ext)80            if key in seen:81                continue82            seen.add(key)83            formats_out.append({84                "format_id": f["format_id"],85                "label": f"{label} · {ext} " + ("(video+audio)" if acodec != "none" else "(video only)"),86                "ext": ext,87                "type": "video",88                "filesize": f.get("filesize") or f.get("filesize_approx"),89                "height": height or 0,90            })91        elif acodec != "none":92            abr = f.get("abr")93            key = ("audio", abr, ext)94            if key in seen:95                continue96            seen.add(key)97            formats_out.append({98                "format_id": f["format_id"],99                "label": f"Audio · {int(abr) if abr else '?'}kbps · {ext}",100                "ext": ext,101                "type": "audio",102                "filesize": f.get("filesize") or f.get("filesize_approx"),103                "height": 0,104            })105 106    formats_out.sort(key=lambda x: (x["type"] != "video", -x["height"]))107 108    return jsonify({109        "title": info.get("title"),110        "description": info.get("description"),111        "thumbnail": info.get("thumbnail"),112        "duration": info.get("duration"),113        "uploader": info.get("uploader"),114        "webpage_url": info.get("webpage_url"),115        "extractor": info.get("extractor_key"),116        "formats": formats_out,117    })118 119 120@app.route("/api/download", methods=["POST"])121def download():122    """Download the selected format (or best audio->mp3) and serve the file."""123    data = request.get_json(force=True)124    url = (data or {}).get("url", "").strip()125    format_id = (data or {}).get("format_id", "").strip()126    as_mp3 = bool((data or {}).get("mp3", False))127 128    if not url:129        return jsonify({"error": "No URL provided"}), 400130 131    job_id = uuid.uuid4().hex132    out_template = os.path.join(DOWNLOAD_DIR, f"{job_id}.%(ext)s")133 134    ydl_opts = dict(COMMON_OPTS)135    ydl_opts["outtmpl"] = out_template136 137    if as_mp3:138        ydl_opts["format"] = "bestaudio/best"139        ydl_opts["postprocessors"] = [{140            "key": "FFmpegExtractAudio",141            "preferredcodec": "mp3",142            "preferredquality": "192",143        }]144    else:145        if format_id:146            # ensure audio is muxed in for video-only formats147            ydl_opts["format"] = f"{format_id}+bestaudio/best" if "+" not in format_id else format_id148        else:149            ydl_opts["format"] = "best"150        ydl_opts["merge_output_format"] = "mp4"151 152    try:153        with yt_dlp.YoutubeDL(ydl_opts) as ydl:154            info = ydl.extract_info(url, download=True)155            final_path = ydl.prepare_filename(info)156            if as_mp3:157                base, _ = os.path.splitext(final_path)158                final_path = base + ".mp3"159            elif not os.path.exists(final_path):160                # merged output may have a different extension161                base, _ = os.path.splitext(final_path)162                candidate = base + ".mp4"163                if os.path.exists(candidate):164                    final_path = candidate165    except Exception as e:166        return jsonify({"error": f"Download failed: {str(e)}"}), 400167 168    if not os.path.exists(final_path):169        return jsonify({"error": "File not found after processing"}), 500170 171    title = safe_filename(info.get("title", "download"))172    ext = os.path.splitext(final_path)[1]173    download_name = f"{title}{ext}"174 175    @after_this_request176    def schedule_cleanup(response):177        def _delete_later(path):178            time.sleep(30)179            try:180                os.remove(path)181            except OSError:182                pass183        threading.Thread(target=_delete_later, args=(final_path,), daemon=True).start()184        return response185 186    return send_file(final_path, as_attachment=True, download_name=download_name)187 188 189@app.route("/health")190def health():191    return jsonify({"status": "ok"})192 193 194if __name__ == "__main__":195    port = int(os.environ.get("PORT", 7860))196    app.run(host="0.0.0.0", port=port)197