gallery_server_client_s190js/test_fn/fix_hls_cast.py
2026-10-26 11:00:14 +01:00

200 lines
6.2 KiB
Python

#!/usr/bin/env python3
from pathlib import Path
from datetime import datetime
import re
import shutil
import sys
ROOT = Path("/home/nvme/dockerdata/prove/s152js")
MANAGER = ROOT / "helpers/hlsManager.js"
ROUTE = ROOT / "routes/hls.js"
def replace_once(text, pattern, replacement, label, flags=0):
new_text, count = re.subn(
pattern, replacement, text, count=1, flags=flags
)
if count != 1:
raise RuntimeError(
f"{label}: attesa 1 sostituzione, trovate {count}. "
"File non modificati."
)
print(f"[OK] {label}")
return new_text
def main():
if not MANAGER.is_file() or not ROUTE.is_file():
sys.exit(f"ERRORE: file non trovati sotto {ROOT}")
manager = MANAGER.read_text(encoding="utf-8")
route = ROUTE.read_text(encoding="utf-8")
# 1. Stato esplicito del job.
manager = replace_once(
manager,
r"notified:\s*false,",
"notified: false,\n"
" ready: false,\n"
" status: 'processing',",
"Stato iniziale del job",
)
# 2. Il poller dichiara il job pronto solo alla soglia richiesta.
manager = replace_once(
manager,
r"if\s*\(\s*tsCount\s*>=\s*job\.segmentsNeeded\s*\)"
r"\s*\{\s*job\.notified\s*=\s*true;",
"if (tsCount >= job.segmentsNeeded) {\n"
" job.ready = true;\n"
" job.status = 'ready';\n"
" job.notified = true;",
"Soglia dei segmenti HLS",
)
# 3. Registra esplicitamente il timeout.
timeout_start = manager.find("// Timeout")
exit_start = manager.find("// FFmpeg exit", timeout_start)
if timeout_start < 0 or exit_start < 0:
raise RuntimeError("Blocco timeout non trovato. Nessun file scritto.")
timeout_block = manager[timeout_start:exit_start]
timeout_block = replace_once(
timeout_block,
r"job\.notified\s*=\s*true;",
"job.notified = true;\n job.status = 'timeout';",
"Stato timeout",
)
manager = (
manager[:timeout_start]
+ timeout_block
+ manager[exit_start:]
)
# 4. All'uscita di FFmpeg, non confondere fine conversione
# con buffer sufficiente per iniziare il casting.
manager = replace_once(
manager,
r"let status;\s*"
r"if\s*\(code === 0 && masterExists\)\s*\{\s*"
r"status = 'finished';\s*"
r"\}\s*else\s*\{\s*"
r"status = 'error';\s*"
r"\}\s*"
r"job\.notified\s*=\s*true;",
"const hasMinimumBuffer =\n"
" code === 0 &&\n"
" masterExists &&\n"
" tsCount >= job.segmentsNeeded;\n"
"\n"
" if (hasMinimumBuffer) {\n"
" job.ready = true;\n"
" }\n"
"\n"
" job.status = job.ready\n"
" ? 'ready'\n"
" : (code === 0 && masterExists\n"
" ? 'finished_insufficient_segments'\n"
" : 'error');\n"
"\n"
" status = job.status;\n"
" job.notified = true;",
"Controllo finale all'uscita di FFmpeg",
)
# 5. Conserva il job per due minuti dopo l'uscita di FFmpeg.
manager = replace_once(
manager,
r"finally\s*\{\s*jobs\.delete\(\s*jobId\s*\);\s*\}",
"finally {\n"
" job.finishedAt = Date.now();\n"
" setTimeout(() => {\n"
" jobs.delete(jobId);\n"
" }, 2 * 60 * 1000).unref();\n"
" }",
"Conservazione temporanea del job",
)
# 6. Aggiunge il conteggio locale dei segmenti .ts alla route.
route = replace_once(
route,
r"const router = express\.Router\(\);",
"const router = express.Router();\n"
"\n"
"function countTsSegments(outDir) {\n"
" try {\n"
" return fs.readdirSync(outDir).filter(\n"
" name => name.toLowerCase().endsWith('.ts')\n"
" ).length;\n"
" } catch (err) {\n"
" return 0;\n"
" }\n"
"}",
"Conteggio segmenti nella route",
)
# 7. L'endpoint URL richiede almeno segmentsNeeded segmenti.
route = replace_once(
route,
r"if\s*\(\s*"
r"!job\.notified\s*\|\|\s*"
r"!fs\.existsSync\(masterPath\)\s*"
r"\)\s*\{\s*"
r"return res\.status\(404\)\.json\(\{\s*"
r"error:\s*'Playlist not yet ready'\s*"
r"\}\);\s*"
r"\}",
"const tsCount = countTsSegments(job.outDir);\n"
"\n"
" if (\n"
" !job.ready ||\n"
" !fs.existsSync(masterPath) ||\n"
" tsCount < job.segmentsNeeded\n"
" ) {\n"
" if (job.exitCode !== null) {\n"
" return res.status(409).json({\n"
" error: 'HLS finished before minimum buffer was ready',\n"
" status: job.status,\n"
" ready_segments: tsCount,\n"
" required_segments: job.segmentsNeeded\n"
" });\n"
" }\n"
"\n"
" return res.status(404).json({\n"
" error: 'Playlist not yet ready',\n"
" ready_segments: tsCount,\n"
" required_segments: job.segmentsNeeded\n"
" });\n"
" }",
"Controllo della soglia nell'endpoint URL",
flags=re.S,
)
# Tutte le sostituzioni sono state verificate prima di scrivere.
# Crea backup e poi salva entrambi i file.
stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_manager = MANAGER.with_name(
f"hlsManager.js.bak_{stamp}"
)
backup_route = ROUTE.with_name(
f"hls.js.bak_{stamp}"
)
shutil.copy2(MANAGER, backup_manager)
shutil.copy2(ROUTE, backup_route)
MANAGER.write_text(manager, encoding="utf-8")
ROUTE.write_text(route, encoding="utf-8")
print("\nModifiche applicate.")
print(f"Backup: {backup_manager}")
print(f"Backup: {backup_route}")
print("\nVerifica ora la sintassi JavaScript con:")
print("node --check helpers/hlsManager.js")
print("node --check routes/hls.js")
if __name__ == "__main__":
main()