Dwnldr / app.py
Offex's picture
Update app.py
d490a0a verified
Raw
History Blame Contribute Delete
7.46 kB
from flask import Flask, request, jsonify, send_from_directory, send_file
import subprocess
import json
import os
import threading
import uuid
import re
app = Flask(__name__)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DOWNLOAD_DIR = os.path.join(BASE_DIR, 'downloads')
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
download_status = {}
@app.route('/')
def index():
return send_from_directory(BASE_DIR, 'deepu_downloader.html')
@app.route('/formats', methods=['POST'])
def get_formats():
data = request.json
url = data.get('url', '').strip()
if not url:
return jsonify({'error': 'URL do bhai!'}), 400
try:
result = subprocess.run(
['yt-dlp', '-J', '--no-playlist', url],
capture_output=True, text=True, timeout=30
)
if result.returncode != 0:
return jsonify({'error': 'URL sahi nahi hai ya video private hai'}), 400
info = json.loads(result.stdout)
formats = []
seen = set()
for f in info.get('formats', []):
fid = f.get('format_id', '')
height = f.get('height')
acodec = f.get('acodec', 'none')
vcodec = f.get('vcodec', 'none')
ext = f.get('ext', '')
filesize = f.get('filesize') or f.get('filesize_approx') or 0
if vcodec != 'none' and height:
ftype = 'video'
label = f'{height}p'
elif acodec != 'none' and vcodec == 'none':
ftype = 'audio'
abr = f.get('abr', 0)
label = f'Audio {int(abr)}kbps' if abr else 'Audio'
elif vcodec != 'none' and acodec != 'none':
ftype = 'both'
label = f'{height}p (V+A)' if height else 'Video+Audio'
else:
continue
key = f'{label}-{ext}-{ftype}'
if key in seen:
continue
seen.add(key)
size_str = ''
if filesize:
mb = filesize / (1024*1024)
size_str = f'{mb:.1f} MB'
formats.append({
'id': fid,
'label': label,
'ext': ext,
'type': ftype,
'size': size_str,
'height': height or 0
})
formats.sort(key=lambda x: (x['height'] or 0), reverse=True)
return jsonify({
'title': info.get('title', 'Unknown'),
'thumbnail': info.get('thumbnail', ''),
'duration': info.get('duration_string', ''),
'formats': formats
})
except subprocess.TimeoutExpired:
return jsonify({'error': 'Timeout ho gaya, dobara try karo'}), 500
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/download', methods=['POST'])
def start_download():
data = request.json
url = data.get('url', '').strip()
format_id = data.get('format_id', 'bestvideo+bestaudio')
mode = data.get('mode', 'normal')
dl_id = str(uuid.uuid4())[:8]
download_status[dl_id] = {'status': 'starting', 'progress': 0, 'speed': '', 'log': [], 'filename': ''}
# Speed boost flags
SPEED_FLAGS = [
'--concurrent-fragments', '8',
'--buffer-size', '16K',
'--http-chunk-size', '10M',
]
def run_download():
try:
if mode == 'live':
cmd = ['yt-dlp', '--live-from-start', '-f', 'best'] + SPEED_FLAGS + \
['-o', os.path.join(DOWNLOAD_DIR, '%(title)s.%(ext)s'), url]
elif mode == 'audio':
cmd = ['yt-dlp', '-x', '--audio-format', 'mp3'] + SPEED_FLAGS + \
['-o', os.path.join(DOWNLOAD_DIR, '%(title)s.%(ext)s'), url]
else:
if format_id in ['best', 'bestvideo+bestaudio']:
fmt = 'bestvideo+bestaudio/best'
else:
fmt = f'{format_id}+140/best'
cmd = ['yt-dlp', '-f', fmt, '--merge-output-format', 'mp4'] + SPEED_FLAGS + \
['-o', os.path.join(DOWNLOAD_DIR, '%(title)s.%(ext)s'), url]
process = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, bufsize=1
)
download_status[dl_id]['status'] = 'downloading'
for line in process.stdout:
line = line.strip()
if not line:
continue
download_status[dl_id]['log'].append(line)
pct_match = re.search(r'(\d+\.\d+)%', line)
speed_match = re.search(r'at\s+([\d.]+\s*\w+/s)', line)
dest_match = re.search(r'Destination:\s+(.+)', line)
merge_match = re.search(r'Merging formats into "(.+)"', line)
if pct_match:
download_status[dl_id]['progress'] = float(pct_match.group(1))
if speed_match:
download_status[dl_id]['speed'] = speed_match.group(1)
if dest_match:
download_status[dl_id]['filename'] = os.path.basename(dest_match.group(1).strip())
if merge_match:
download_status[dl_id]['filename'] = os.path.basename(merge_match.group(1).strip())
process.wait()
if process.returncode == 0:
download_status[dl_id]['status'] = 'done'
download_status[dl_id]['progress'] = 100
files = os.listdir(DOWNLOAD_DIR)
if files:
latest = max([os.path.join(DOWNLOAD_DIR, f) for f in files], key=os.path.getmtime)
download_status[dl_id]['filename'] = os.path.basename(latest)
else:
download_status[dl_id]['status'] = 'error'
except Exception as e:
download_status[dl_id]['status'] = 'error'
download_status[dl_id]['log'].append(str(e))
t = threading.Thread(target=run_download)
t.daemon = True
t.start()
return jsonify({'dl_id': dl_id})
@app.route('/progress/<dl_id>')
def get_progress(dl_id):
return jsonify(download_status.get(dl_id, {'status': 'not found'}))
@app.route('/get-file/<path:filename>')
def get_file(filename):
try:
filepath = os.path.join(DOWNLOAD_DIR, filename)
if not os.path.exists(filepath):
return jsonify({'error': 'File nahi mili'}), 404
return send_file(filepath, as_attachment=True, download_name=filename)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/files')
def list_files():
files = []
for f in os.listdir(DOWNLOAD_DIR):
fp = os.path.join(DOWNLOAD_DIR, f)
size = os.path.getsize(fp)
files.append({'name': f, 'size': f'{size/(1024*1024):.1f} MB'})
files.sort(key=lambda x: os.path.getmtime(os.path.join(DOWNLOAD_DIR, x['name'])), reverse=True)
return jsonify(files)
@app.route('/delete-file', methods=['POST'])
def delete_file():
data = request.json
filename = data.get('filename', '')
filepath = os.path.join(DOWNLOAD_DIR, filename)
if os.path.exists(filepath):
os.remove(filepath)
return jsonify({'success': True})
return jsonify({'error': 'File nahi mili'}), 404
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
app.run(host="0.0.0.0", port=port)