fix: fixed issues related to missing wvd

This commit is contained in:
2026-03-07 22:32:34 +01:00
parent fc83bab9f7
commit 45c0c177eb
7 changed files with 119 additions and 36 deletions

View File

@@ -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

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/config/
/downloads/
.env

View File

@@ -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 \

55
app.py
View File

@@ -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,9 +167,15 @@ def run_download(job_id: str, urls: list[str], options: dict):
process.wait()
with jobs_lock:
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
@@ -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/<job_id>")
@@ -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/<job_id>/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/<job_id>", 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)

View File

@@ -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

View File

@@ -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

View File

@@ -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 @@
<br><br>
<button class="btn btn-sm btn-secondary" onclick="uploadCookies()">Upload</button>
</div>
<div class="card">
<h2>Widevine Device</h2>
<div class="cookie-status" id="wvd-status">
<div class="dot dot-red"></div>
<span>Checking...</span>
</div>
<br>
<label>Upload device.wvd (required for AAC quality)</label>
<input type="file" id="wvd-file" accept=".wvd" style="margin-top:8px">
<br><br>
<button class="btn btn-sm btn-secondary" onclick="uploadWvd()">Upload</button>
</div>
</div>
</div>
@@ -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}
<div class="job-actions">
${job.status === 'running' ? `<button class="btn btn-sm btn-danger" onclick="cancelJob('${job.id}')">Cancel</button>` : ''}
${job.status !== 'running' ? `<button class="btn btn-sm btn-danger" onclick="deleteJob('${job.id}')">Remove</button>` : ''}
</div>
</div>`;
@@ -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 = '<div class="dot dot-green"></div><span>device.wvd found</span>';
} else {
el.innerHTML = '<div class="dot dot-red"></div><span>device.wvd not found</span>';
}
} 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')