carm5333 commited on
Commit
4e5e54b
ยท
verified ยท
1 Parent(s): 4f69b8b

S146-imp: job persistence, bg bootstrap, /wake, log trim, 32MB upload

Browse files
Files changed (1) hide show
  1. app.py +67 -2
app.py CHANGED
@@ -15,7 +15,7 @@ from flask_cors import CORS
15
 
16
  app = Flask(__name__)
17
  CORS(app)
18
- app.config["MAX_CONTENT_LENGTH"] = 6 * 1024 * 1024
19
 
20
  JOBS = {}
21
  HISTORY = []
@@ -23,6 +23,62 @@ JOBS_LOCK = threading.Lock()
23
  HISTORY_LOCK = threading.Lock()
24
  SEMAPHORE = threading.Semaphore(3)
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  TMP = Path(tempfile.gettempdir())
27
  UPLOAD_DIR = TMP / "tilawa_uploads"
28
  CHUNK_DIR = TMP / "tilawa_chunks"
@@ -159,6 +215,12 @@ def health():
159
  def ping():
160
  return jsonify({"ok": True, "t": time.time()})
161
 
 
 
 
 
 
 
162
  @app.route("/queue")
163
  def queue_status():
164
  with JOBS_LOCK:
@@ -383,11 +445,14 @@ def _run_engine(job_id):
383
  with JOBS_LOCK:
384
  job["status"] = "error"; job["error"] = str(e)
385
  job["label"] = f"ูุดู„: {e}"
 
386
  return
387
  with JOBS_LOCK:
388
  job["status"] = "done"; job["progress"] = 100; job["label"] = "ุงูƒุชู…ู„ุช โœ“"
389
  if not job.get("score"): job["score"] = 90
 
390
  _add_history(job)
 
391
  try: Path(job["in_path"]).unlink(missing_ok=True)
392
  except Exception: pass
393
  _prune_jobs()
@@ -487,4 +552,4 @@ def _bootstrap_engines():
487
  except Exception as ex:
488
  print(f"[bootstrap] failed {e}: {ex}")
489
 
490
- _bootstrap_engines()
 
15
 
16
  app = Flask(__name__)
17
  CORS(app)
18
+ app.config["MAX_CONTENT_LENGTH"] = 32 * 1024 * 1024 # Imp-5: was 6MB
19
 
20
  JOBS = {}
21
  HISTORY = []
 
23
  HISTORY_LOCK = threading.Lock()
24
  SEMAPHORE = threading.Semaphore(3)
25
 
26
+ # โ”€โ”€ Imp-1: Job persistence โ€” survives space restart/sleep โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
27
+ JOBS_FILE = TMP / "tilawa_jobs.json"
28
+
29
+ def _save_jobs():
30
+ """Persist active jobs to disk (excludes locks and file handles)."""
31
+ import json
32
+ _SKIP = {"_lock"}
33
+ try:
34
+ with JOBS_LOCK:
35
+ snapshot = {}
36
+ for jid, j in JOBS.items():
37
+ if j.get("status") in ("done", "error"):
38
+ continue # completed jobs don't need persistence
39
+ row = {}
40
+ for k, v in j.items():
41
+ if k in _SKIP:
42
+ continue
43
+ if isinstance(v, set):
44
+ row[k] = list(v)
45
+ else:
46
+ row[k] = v
47
+ snapshot[jid] = row
48
+ with open(JOBS_FILE, "w") as f:
49
+ json.dump(snapshot, f)
50
+ except Exception as e:
51
+ print(f"[persist] save failed: {e}")
52
+
53
+ def _load_jobs():
54
+ """Reload jobs from disk on startup. Mark running/queued as error."""
55
+ import json
56
+ if not JOBS_FILE.exists():
57
+ return
58
+ try:
59
+ with open(JOBS_FILE) as f:
60
+ snapshot = json.load(f)
61
+ loaded = 0
62
+ with JOBS_LOCK:
63
+ for jid, j in snapshot.items():
64
+ # recreate threading.Lock
65
+ j["_lock"] = threading.Lock()
66
+ # restore set
67
+ if "received_set" in j:
68
+ j["received_set"] = set(j["received_set"])
69
+ # jobs that were mid-run can't resume โ€” mark as error
70
+ if j.get("status") in ("running", "queued", "merging", "uploading"):
71
+ j["status"] = "error"
72
+ j["label"] = "ูุดู„: ุฃูุนูŠุฏ ุชุดุบูŠู„ ุงู„ุฎุงุฏู… โ€” ุฃุฑุณู„ ุงู„ู…ู„ู ู…ุฌุฏุฏุงู‹"
73
+ j["error"] = "Server restarted โ€” please resubmit"
74
+ JOBS[jid] = j
75
+ loaded += 1
76
+ print(f"[persist] loaded {loaded} job(s) from disk")
77
+ except Exception as e:
78
+ print(f"[persist] load failed: {e}")
79
+
80
+ _load_jobs()
81
+
82
  TMP = Path(tempfile.gettempdir())
83
  UPLOAD_DIR = TMP / "tilawa_uploads"
84
  CHUNK_DIR = TMP / "tilawa_chunks"
 
215
  def ping():
216
  return jsonify({"ok": True, "t": time.time()})
217
 
218
+ @app.route("/wake") # Imp-3: Flutter pre-warm endpoint
219
+ def wake():
220
+ engines = {k: v.exists() for k, v in ENGINE_SCRIPTS.items()}
221
+ return jsonify({"ok": True, "t": time.time(),
222
+ "engines": engines, "running": _count_running()})
223
+
224
  @app.route("/queue")
225
  def queue_status():
226
  with JOBS_LOCK:
 
445
  with JOBS_LOCK:
446
  job["status"] = "error"; job["error"] = str(e)
447
  job["label"] = f"ูุดู„: {e}"
448
+ _save_jobs() # Imp-1: persist error state
449
  return
450
  with JOBS_LOCK:
451
  job["status"] = "done"; job["progress"] = 100; job["label"] = "ุงูƒุชู…ู„ุช โœ“"
452
  if not job.get("score"): job["score"] = 90
453
+ job.pop("engine_log", None) # Imp-4: trim log after done โ€” save RAM
454
  _add_history(job)
455
+ _save_jobs() # Imp-1: persist
456
  try: Path(job["in_path"]).unlink(missing_ok=True)
457
  except Exception: pass
458
  _prune_jobs()
 
552
  except Exception as ex:
553
  print(f"[bootstrap] failed {e}: {ex}")
554
 
555
+ threading.Thread(target=_bootstrap_engines, daemon=True).start() # Imp-2: non-blocking startup