From 45c0c177eb764e6417db287422cc86a7391ce556 Mon Sep 17 00:00:00 2001 From: Benjamin Hardy Date: Sat, 7 Mar 2026 22:32:34 +0100 Subject: [PATCH] fix: fixed issues related to missing wvd --- .env.example | 6 ++--- .gitignore | 3 ++- Dockerfile | 1 + app.py | 63 +++++++++++++++++++++++++++++++++++++++----- config/cookies.txt | 22 ---------------- docker-compose.yml | 4 +-- templates/index.html | 56 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 119 insertions(+), 36 deletions(-) delete mode 100644 config/cookies.txt diff --git a/.env.example b/.env.example index 956d92d..f5a481a 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,6 @@ PASSWORD= # Host port to expose the app on. PORT=5000 -# Host paths for persistent data. -DOWNLOADS_DIR=./downloads -CONFIG_DIR=./config +# Host paths for persistent data (used by docker-compose volumes only). +HOST_DOWNLOADS_DIR=./downloads +HOST_CONFIG_DIR=./config diff --git a/.gitignore b/.gitignore index 63f8e0d..c3b3c4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/config/ /downloads/ -.env \ No newline at end of file +.env diff --git a/Dockerfile b/Dockerfile index af483ad..4614a67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM python:3.12-slim RUN apt-get update && apt-get install -y --no-install-recommends \ ffmpeg \ + aria2 \ git \ curl \ unzip \ diff --git a/app.py b/app.py index 124daff..afd75d8 100644 --- a/app.py +++ b/app.py @@ -42,6 +42,7 @@ def require_login(): DOWNLOADS_DIR = Path(os.environ.get("DOWNLOADS_DIR", "/downloads")) COOKIES_PATH = Path(os.environ.get("COOKIES_PATH", "/config/cookies.txt")) CONFIG_DIR = Path(os.environ.get("CONFIG_DIR", "/config")) +WVD_PATH = Path(os.environ.get("WVD_PATH", "/config/device.wvd")) TEMP_DIR = Path("/tmp/votify") DOWNLOADS_DIR.mkdir(parents=True, exist_ok=True) @@ -93,6 +94,8 @@ def run_download(job_id: str, urls: list[str], options: dict): cmd.extend(["--cookies-path", str(COOKIES_PATH)]) cmd.extend(["--output-path", str(DOWNLOADS_DIR)]) cmd.extend(["--temp-path", str(TEMP_DIR)]) + if WVD_PATH.exists(): + cmd.extend(["--wvd-path", str(WVD_PATH)]) quality = options.get("audio_quality", "aac-medium") if quality: @@ -149,8 +152,14 @@ def run_download(job_id: str, urls: list[str], options: dict): text=True, bufsize=1, ) + with jobs_lock: + jobs[job_id]["process"] = process + output_lines = [] for line in process.stdout: + with jobs_lock: + if jobs[job_id]["status"] == "cancelled": + break line = line.rstrip("\n") output_lines.append(line) with jobs_lock: @@ -158,12 +167,18 @@ def run_download(job_id: str, urls: list[str], options: dict): process.wait() - if process.returncode == 0 and want_mp3: - convert_to_mp3(job_id, files_before) - with jobs_lock: - jobs[job_id]["status"] = "completed" if process.returncode == 0 else "failed" - jobs[job_id]["return_code"] = process.returncode + cancelled = jobs[job_id]["status"] == "cancelled" + + if cancelled: + with jobs_lock: + jobs[job_id]["output"] = jobs[job_id].get("output", []) + ["[cancelled] Job was cancelled by user."] + else: + if process.returncode == 0 and want_mp3: + convert_to_mp3(job_id, files_before) + with jobs_lock: + jobs[job_id]["status"] = "completed" if process.returncode == 0 else "failed" + jobs[job_id]["return_code"] = process.returncode except Exception as e: with jobs_lock: jobs[job_id]["status"] = "failed" @@ -244,10 +259,14 @@ def start_download(): return jsonify({"job_id": job_id}) +def job_to_dict(job): + return {k: v for k, v in job.items() if k != "process"} + + @app.route("/api/jobs") def list_jobs(): with jobs_lock: - return jsonify(list(jobs.values())) + return jsonify([job_to_dict(j) for j in jobs.values()]) @app.route("/api/jobs/") @@ -256,7 +275,22 @@ def get_job(job_id): job = jobs.get(job_id) if not job: return jsonify({"error": "Job not found"}), 404 - return jsonify(job) + return jsonify(job_to_dict(job)) + + +@app.route("/api/jobs//cancel", methods=["POST"]) +def cancel_job(job_id): + with jobs_lock: + job = jobs.get(job_id) + if not job: + return jsonify({"error": "Job not found"}), 404 + if job["status"] != "running": + return jsonify({"error": "Job is not running"}), 400 + job["status"] = "cancelled" + proc = job.get("process") + if proc: + proc.terminate() + return jsonify({"ok": True}) @app.route("/api/jobs/", methods=["DELETE"]) @@ -355,5 +389,20 @@ def upload_cookies(): return jsonify({"ok": True}) +@app.route("/api/wvd", methods=["GET"]) +def check_wvd(): + return jsonify({"exists": WVD_PATH.exists()}) + + +@app.route("/api/wvd", methods=["POST"]) +def upload_wvd(): + if "file" not in request.files: + return jsonify({"error": "No file uploaded"}), 400 + file = request.files["file"] + CONFIG_DIR.mkdir(parents=True, exist_ok=True) + file.save(WVD_PATH) + return jsonify({"ok": True}) + + if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=False) diff --git a/config/cookies.txt b/config/cookies.txt deleted file mode 100644 index 446a316..0000000 --- a/config/cookies.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Netscape HTTP Cookie File -# https://curl.haxx.se/rfc/cookie_spec.html -# This is a generated file! Do not edit. - -.spotify.com TRUE / TRUE 1804370105 sp_t 5090c304-1e4d-4352-aea1-16fe4ae9207c -.spotify.com TRUE / TRUE 1772920453 sp_new 1 -.spotify.com TRUE / TRUE 1772920453 sp_landing https%3A%2F%2Fopen.spotify.com%2F -.spotify.com TRUE / TRUE 1772920453 sp_landingref https%3A%2F%2Fwww.bing.com%2F -.spotify.com TRUE / FALSE 1804370056 OptanonAlertBoxClosed 2026-03-06T21:54:16.012Z -.spotify.com TRUE / FALSE 1804370056 eupubconsent-v2 CQgpG1gQgpG1gAcABBENCVFgAP_AAEOAAAYgJnABxC4URAFAaSIyAJIgMAAUgABAQAAQAAIBAAABCBgEQAQAkAAgBACABAACGAAAIAAAAAAACAAAAEAAAIAAJADAAAAEIAAAIAAAABAAAAAAAAAgEAAAAAAAgAAEAAAAiAAAAJIAEEAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAQAAQCgAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAQTOgUQAKAAsACoAFwAPAAgABIACgAGQANIAeAB6AD8AJwAXgA_ACcAFcAMoAc8A7gDvAH4AQgAiYBFgCSwFeAV8A4gB7YD9gP4Ah2BKoErALYAXYAvMBiwDGQGTAMsAgKBGYCZwARSAkAAsACoAIIAZABoADwAPwAygBzgDvAH4ARYAkoB7QEOgLYAXmAywCZxQAOABcAEgAnAB3AHbAYsAyY.f_gACHAAAAAA.IJnABxC4URAFAaSIyAJIgMAAUgABAQAAQAAIBAAABCBgEQAQAkAAgBACABAACGAAAIAAAAAAACAAAAEAAAIAAJADAAAAEIAAAIAAAADAAAAAAAAAgEAAAAAAAgAAEAABAiAAAAJIAEEAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAQAAQCgAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAQAAA -.spotify.com TRUE / TRUE 1772835856 _cs_mk_ga 0.3535578042278994_1772834056026 -open.spotify.com FALSE / FALSE 0 sss 1 -.spotify.com TRUE / FALSE 1772920505 _gid GA1.2.1328783107.1772834056 -.spotify.com TRUE / TRUE 1804370106 sp_adid ff3f915f-9589-4b4c-a755-2e1431a4cd63 -.spotify.com TRUE / TRUE 1772920461 sp_m nl -.spotify.com TRUE / FALSE 1807394104 _ga_S35RN5WNT2 GS2.1.s1772834061$o1$g1$t1772834103$j18$l0$h0 -.spotify.com TRUE / TRUE 1774043704 sp_dc AQAER3Lqcs1oCTqLdVMsph_dzJBd6ywwfIuFsTycqxoUrvj77I7KgQWTdnkOFZrw51GUURjj-wFXyAziah0ljXB3YSvXcugyoaz-N2ryPxh4e78XsDeJpJtPOGs9bncokmLK6W2RLbHFeTwcSawXf6LM6JixmRrH_f35MI0chLo-GbGxle9Jqf1-FmTGYt8dSXh-ryX2575p-IhWIg -.spotify.com TRUE / TRUE 1778018105 sp_gaid 0088fca431da2488813d802a0cd113719429847133955a017c6d57 -.spotify.com TRUE / FALSE 1807394106 _ga GA1.1.139841860.1772834056 -.spotify.com TRUE / FALSE 1807394106 _ga_ZWG1NSHWD8 GS2.1.s1772834056$o1$g1$t1772834105$j11$l0$h0 -.spotify.com TRUE / FALSE 1804370105 OptanonConsent isGpcEnabled=0&datestamp=Fri+Mar+06+2026+22%3A55%3A05+GMT%2B0100+(Central+European+Standard+Time)&version=202601.2.0&browserGpcFlag=0&isIABGlobal=false&hosts=&consentId=14d166da-7df6-43b2-bc25-f18987870a49&interactionCount=1&isAnonUser=1&prevHadToken=0&landingPath=NotLandingPage&groups=s00%3A1%2Cf00%3A1%2Cm00%3A1%2Ct00%3A1%2Cf11%3A1%2CID01%3A1%2Ci00%3A1%2CV2STACK3%3A1%2CV2STACK11%3A1%2CV2STACK20%3A1%2Cm03%3A1&intType=1&crTime=1772834056251&geolocation=NL%3BZH&AwaitingReconsent=false -.spotify.com TRUE / FALSE 1807394106 _ga_BMC5VGR8YS GS2.2.s1772834056$o1$g1$t1772834105$j11$l0$h0 diff --git a/docker-compose.yml b/docker-compose.yml index a86e314..41c3727 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,6 @@ services: env_file: - .env volumes: - - ${DOWNLOADS_DIR:-./downloads}:/downloads - - ${CONFIG_DIR:-./config}:/config + - ${HOST_DOWNLOADS_DIR:-./downloads}:/downloads + - ${HOST_CONFIG_DIR:-./config}:/config restart: unless-stopped diff --git a/templates/index.html b/templates/index.html index 7409dee..0115a97 100644 --- a/templates/index.html +++ b/templates/index.html @@ -66,6 +66,7 @@ .status-running { background: #1a3a2a; color: var(--accent); } .status-completed { background: #1a3a1a; color: #4caf50; } .status-failed { background: #3a1a1a; color: var(--danger); } + .status-cancelled { background: #3a3a1a; color: var(--warning); } .job-card { background: var(--surface); border-radius: var(--radius); padding: 16px; margin-bottom: 12px; } .job-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } @@ -218,6 +219,18 @@

+
+

Widevine Device

+ +
+ + +

+ +
@@ -235,7 +248,7 @@ if (name === 'jobs') loadJobs(); if (name === 'files') loadFiles(""); - if (name === 'settings') checkCookies(); + if (name === 'settings') { checkCookies(); checkWvd(); } if (jobPollInterval) clearInterval(jobPollInterval); if (name === 'jobs') jobPollInterval = setInterval(loadJobs, 3000); @@ -358,6 +371,7 @@ ${progressHtml} ${logHtml}
+ ${job.status === 'running' ? `` : ''} ${job.status !== 'running' ? `` : ''}
`; @@ -375,6 +389,11 @@ } } + async function cancelJob(id) { + await fetch('/api/jobs/' + id + '/cancel', { method: 'POST' }); + loadJobs(); + } + async function deleteJob(id) { expandedJobs.delete(id); await fetch('/api/jobs/' + id, { method: 'DELETE' }); @@ -485,6 +504,40 @@ } } + async function checkWvd() { + try { + const res = await fetch('/api/wvd'); + const data = await res.json(); + const el = document.getElementById('wvd-status'); + if (data.exists) { + el.innerHTML = '
device.wvd found'; + } else { + el.innerHTML = '
device.wvd not found'; + } + } catch (e) { + console.error(e); + } + } + + async function uploadWvd() { + const file = document.getElementById('wvd-file').files[0]; + if (!file) { alert('Select a file first'); return; } + const form = new FormData(); + form.append('file', file); + try { + const res = await fetch('/api/wvd', { method: 'POST', body: form }); + if (res.ok) { + alert('WVD file uploaded successfully'); + checkWvd(); + } else { + const data = await res.json(); + alert(data.error || 'Upload failed'); + } + } catch (e) { + alert('Error: ' + e.message); + } + } + function formatSize(bytes) { if (!bytes) return ''; const units = ['B', 'KB', 'MB', 'GB']; @@ -531,6 +584,7 @@ loadSettings(); checkCookies(); + checkWvd(); if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js')