import os import re import uuid import json import time import threading import subprocess import sqlite3 from pathlib import Path from urllib.parse import urlparse, unquote from flask import Flask, request, jsonify, send_from_directory, render_template_string app = Flask(__name__) DOWNLOAD_DIR = Path("downloads") DOWNLOAD_DIR.mkdir(exist_ok=True) DB_FILE = Path("swiftload.db") # In-memory speed/eta cache (ephemeral, recomputed on restart for active tasks) _live_cache = {} _cache_lock = threading.Lock() # ─── Database ──────────────────────────────────────────────────────────────── def get_db(): conn = sqlite3.connect(str(DB_FILE)) conn.row_factory = sqlite3.Row return conn def init_db(): with get_db() as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS tasks ( task_id TEXT PRIMARY KEY, url TEXT NOT NULL, filename TEXT NOT NULL, save_path TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'queued', progress INTEGER DEFAULT 0, size TEXT DEFAULT '', error TEXT DEFAULT '', created_at TEXT NOT NULL, updated_at TEXT NOT NULL ) """) conn.commit() def db_upsert_task(task_id, url, filename, save_path, status="queued", progress=0, size="", error=""): now = time.strftime("%Y-%m-%d %H:%M:%S") with get_db() as conn: conn.execute(""" INSERT INTO tasks (task_id, url, filename, save_path, status, progress, size, error, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(task_id) DO UPDATE SET status=excluded.status, progress=excluded.progress, size=excluded.size, error=excluded.error, updated_at=excluded.updated_at """, (task_id, url, filename, str(save_path), status, progress, size, error, now, now)) conn.commit() def db_update_task(task_id, **kwargs): if not kwargs: return kwargs["updated_at"] = time.strftime("%Y-%m-%d %H:%M:%S") set_clause = ", ".join(f"{k}=?" for k in kwargs) values = list(kwargs.values()) + [task_id] with get_db() as conn: conn.execute(f"UPDATE tasks SET {set_clause} WHERE task_id=?", values) conn.commit() def db_get_task(task_id): with get_db() as conn: row = conn.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone() return dict(row) if row else None def db_all_tasks(): with get_db() as conn: rows = conn.execute("SELECT * FROM tasks ORDER BY created_at DESC").fetchall() return [dict(r) for r in rows] def db_resume_incomplete(): """On startup, mark any 'downloading' tasks as interrupted so user knows.""" with get_db() as conn: conn.execute(""" UPDATE tasks SET status='interrupted', error='Server restarted while downloading' WHERE status IN ('downloading', 'queued') """) conn.commit() # ─── Helpers ───────────────────────────────────────────────────────────────── def format_size(size_bytes): for unit in ["B", "KB", "MB", "GB", "TB"]: if size_bytes < 1024: return f"{size_bytes:.1f} {unit}" size_bytes /= 1024 return f"{size_bytes:.1f} PB" def get_filename_from_url(url): parsed = urlparse(url) name = unquote(parsed.path.split("/")[-1]) if not name or "." not in name: name = f"file_{uuid.uuid4().hex[:8]}" return name def unique_save_path(filename): save_path = DOWNLOAD_DIR / filename counter = 1 stem = save_path.stem suffix = save_path.suffix while save_path.exists(): save_path = DOWNLOAD_DIR / f"{stem}_{counter}{suffix}" counter += 1 return save_path # ─── Download Worker ───────────────────────────────────────────────────────── def download_with_wget(task_id, url, save_path): db_update_task(task_id, status="downloading", progress=0, error="") with _cache_lock: _live_cache[task_id] = {"speed": "connecting...", "eta": "–"} cmd = [ "wget", "--no-check-certificate", "--retry-connrefused", "--tries=5", "--timeout=30", "--waitretry=3", "--limit-rate=0", "--progress=dot:mega", "-c", "--content-disposition", "--trust-server-names", url ] try: process = subprocess.Popen( cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, bufsize=1, ) for line in process.stderr: line = line.strip() if not line: continue pct_match = re.search(r'(\d+)%', line) speed_match = re.search(r'([\d.]+\s*[KMG]B/s)', line) eta_match = re.search(r'(\d+[smhd]+)\s*$', line) if pct_match: pct = int(pct_match.group(1)) db_update_task(task_id, progress=pct) with _cache_lock: if speed_match: _live_cache[task_id]["speed"] = speed_match.group(1) if eta_match: _live_cache[task_id]["eta"] = eta_match.group(1) process.wait() if process.returncode == 0 and Path(save_path).exists(): size_str = format_size(Path(save_path).stat().st_size) db_update_task(task_id, status="done", progress=100, size=size_str, error="") with _cache_lock: _live_cache.pop(task_id, None) else: db_update_task(task_id, status="error", error=f"wget exited with code {process.returncode}") if Path(save_path).exists(): Path(save_path).unlink() except FileNotFoundError: db_update_task(task_id, status="error", error="wget not found. Install: sudo apt install wget") except Exception as e: db_update_task(task_id, status="error", error=str(e)) # ─── Routes ────────────────────────────────────────────────────────────────── @app.route("/") def index(): return render_template_string(HTML) @app.route("/download", methods=["POST"]) def start_download(): data = request.get_json() url = (data or {}).get("url", "").strip() if not url: return jsonify({"error": "No URL provided"}), 400 if not url.startswith(("http://", "https://", "ftp://")): return jsonify({"error": "Invalid URL"}), 400 task_id = uuid.uuid4().hex filename = get_filename_from_url(url) save_path = unique_save_path(filename) db_upsert_task(task_id, url, save_path.name, save_path, status="queued") thread = threading.Thread( target=download_with_wget, args=(task_id, url, save_path), daemon=True, ) thread.start() return jsonify({"task_id": task_id, "filename": save_path.name}) @app.route("/progress/") def get_progress(task_id): task = db_get_task(task_id) if not task: return jsonify({"status": "not_found"}), 404 with _cache_lock: live = _live_cache.get(task_id, {}) return jsonify({ "status": task["status"], "progress": task["progress"], "size": task["size"], "error": task["error"], "filename": task["filename"], "speed": live.get("speed", "–") if task["status"] == "downloading" else "–", "eta": live.get("eta", "–") if task["status"] == "downloading" else "–", }) @app.route("/tasks/all") def all_tasks(): """Return all tasks sorted newest first for full page restore.""" tasks = db_all_tasks() result = [] for t in tasks: with _cache_lock: live = _live_cache.get(t["task_id"], {}) result.append({ "task_id": t["task_id"], "url": t["url"], "filename": t["filename"], "status": t["status"], "progress": t["progress"], "size": t["size"], "error": t["error"], "created_at": t["created_at"], "speed": live.get("speed", "–") if t["status"] == "downloading" else "–", "eta": live.get("eta", "–") if t["status"] == "downloading" else "–", }) return jsonify(result) @app.route("/dl/") def download_file(task_id): task = db_get_task(task_id) if not task or task["status"] != "done": return jsonify({"error": "File not found or not ready"}), 404 p = Path(task["save_path"]) if not p.exists(): return jsonify({"error": "File missing from disk"}), 404 return send_from_directory(p.parent.resolve(), p.name, as_attachment=True) # ─── HTML ───────────────────────────────────────────────────────────────────── HTML = r""" ⚡ SwiftLoad
High-Speed File Downloader
Live Sync Active
0
📡
No active downloads

0
📂
No download history
""" # ─── Entry ──────────────────────────────────────────────────────────────────── if __name__ == "__main__": init_db() db_resume_incomplete() print("⚡ SwiftLoad running at http://localhost:7860") app.run(debug=False, host="0.0.0.0", port=7860, threaded=True)