diff --git a/src/MatchesWebApp.py b/src/MatchesWebApp.py
index 2674998..5ed47f7 100644
--- a/src/MatchesWebApp.py
+++ b/src/MatchesWebApp.py
@@ -70,6 +70,7 @@ _INDEX_HTML = """
+
@@ -317,6 +318,23 @@ document.getElementById("build").addEventListener("click", async () => {
setStatus("Build failed: " + err.message);
}
});
+document.getElementById("move").addEventListener("click", async () => {
+ if (!confirm("Start move operation? This will process all series and may take a long time.")) return;
+ const btn = document.getElementById("move");
+ btn.disabled = true;
+ setStatus("Moving… (running on the server)");
+ try {
+ const r = await fetch("/api/move", { method: "POST" });
+ if (!r.ok) throw new Error(await r.text());
+ const data = await r.json();
+ const total = Object.keys(data.results || {}).length;
+ setStatus("Move finished — " + total + " series processed");
+ } catch (err) {
+ setStatus("Move failed: " + err.message);
+ } finally {
+ btn.disabled = false;
+ }
+});
for (const th of document.querySelectorAll("th.sortable")) {
th.addEventListener("click", () => {
const col = th.dataset.col;
@@ -350,6 +368,7 @@ class MatchesWebApp:
self._host = host
self._port = port
self._build_lock = threading.Lock()
+ self._move_lock = threading.Lock()
self._app = Flask(__name__)
self._thread: "threading.Thread | None" = None
self._register_routes()
@@ -459,3 +478,17 @@ class MatchesWebApp:
finally:
self._build_lock.release()
return jsonify(result)
+
+ @app.post("/api/move")
+ def api_move():
+ if self._mover is None:
+ return Response("no mover configured", status=503)
+ if not self._move_lock.acquire(blocking=False):
+ return Response("move already running", status=409)
+ try:
+ results = self._mover.process_all()
+ except Exception as exc:
+ return Response(f"move failed: {exc}", status=500)
+ finally:
+ self._move_lock.release()
+ return jsonify({"results": results})