factblink514/Editing
3
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 