filesystem-based statuscheck, merged state fixes

feature/pipeline-finalization
peter.fong 3 days ago
parent 3a7cc7687c
commit 65842505b0

@ -358,28 +358,14 @@ from db.repository import get_book_state
@app.route("/inspect/statuscheck/<book_idx>", methods=["POST"])
@logcall
def inspect_statuscheck(book_idx):
try:
statuscheck_result = StatusCheckService.run(book_idx)
repo_state = get_book_state(book_idx)
return render_template(
"inspect/statuscheck_result.html",
result=statuscheck_result,
repo_state=repo_state,
)
StatusCheckService.run(book_idx)
return ("", 204) # background action, geen UI
except Exception as e:
log(f"[STATUSCHECK] ERROR book_idx={book_idx}: {e}")
return (
render_template(
"inspect/statuscheck_result.html",
error=str(e),
book_idx=book_idx,
),
500,
)
return jsonify({"error": str(e)}), 500
# =====================================================

@ -373,5 +373,18 @@ def get_book_state(book_idx):
state["downloaded"] = merged_downloaded
state["audio_done"] = merged_audio_done
state["chapters_total"] = chapters_total
# ----------------------------------------------------
# 4b. Derive status (READ-ONLY)
# ----------------------------------------------------
derived_status = sqlite_row.get("status") or "unknown"
if chapters_total > 0:
if merged_downloaded < chapters_total:
derived_status = "downloading"
elif merged_downloaded == chapters_total and merged_audio_done < chapters_total:
derived_status = "audio"
elif merged_audio_done == chapters_total:
derived_status = "done"
state["status"] = derived_status
return state

@ -5,7 +5,6 @@
import os
import stat
from logbus.publisher import log
from scraper.logger_decorators import logcall
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates")
@ -45,26 +44,40 @@ def detect_volumes(book_base: str):
# ------------------------------------------------------------
def build_merge_block(title: str, author: str, volumes):
lines = []
# --------------------------------------------------------
# Normalize input (defensive)
# --------------------------------------------------------
title = (title or "").strip()
author = (author or "").strip()
total_vols = len(volumes)
# Padding-regel:
# - altijd minimaal 2 (01, 02)
# - 3 bij >=100
if total_vols >= 100:
pad = 3
elif total_vols >= 10:
pad = 2
else:
pad = 0
pad = 2
for num, dirname in volumes:
if pad > 0:
vol_num = f"{num:0{pad}d}"
else:
vol_num = str(num)
vol_num = f"{num:0{pad}d}" # voor filename
series_part = f"{num:0{pad}d}" # voor series-part (string!)
line = (
f'm4b-tool merge --jobs=4 --writer="{author}" '
f'--albumartist="{author}" --album="{title}" '
f'--name="{title}" --output-file="{title}-{vol_num}.m4b" '
f"m4b-tool merge --jobs=4 "
f'--writer="{author}" '
f'--sortalbum="{title}" '
f'--albumartist="{author}" '
f'--album="{title}" '
f'--name="{title}" '
f'--series="{title}" '
f'--series-part="{series_part}" '
f'--output-file="{title}-{vol_num}.m4b" '
f'"{dirname}" -vvv'
)
lines.append(line)
if not lines:
@ -76,7 +89,14 @@ def build_merge_block(title: str, author: str, volumes):
# ------------------------------------------------------------
# Main generator
# ------------------------------------------------------------
@logcall
def generate_all_scripts(book_base: str, title: str, author: str):
# --------------------------------------------------------
# Defensive normalize
# --------------------------------------------------------
title = (title or "").strip()
author = (author or "").strip()
log(f"[SCRIPTGEN] Generating scripts in {book_base}")
# Load templates

@ -1,8 +1,11 @@
/* =======================================================================
File: static/css/bookcard.css
Purpose:
All styling for registered book cards (book-card) +
status colors + start/abort buttons + progress bars
Styling voor registered book cards:
- status kleuren
- badges
- start/abort/statuscheck
- progress bars
======================================================================= */
/* -----------------------------------------------------------------------
@ -17,7 +20,7 @@
}
/* -----------------------------------------------------------------------
BOOK CARD
BOOK CARD BASE
----------------------------------------------------------------------- */
.book-card {
@ -36,34 +39,28 @@
}
/* -----------------------------------------------------------------------
STATUS COLORS
STATUS COLORS (BOOK CARD BORDER)
----------------------------------------------------------------------- */
.book-card.processing {
border-color: #007aff;
box-shadow: 0 0 6px rgba(0, 122, 255, 0.35);
}
/* Downloading / actief bezig */
.book-card.downloading {
border-color: #ff9500;
box-shadow: 0 0 6px rgba(255, 149, 0, 0.35);
}
.book-card.parsing {
border-color: #ffcc00;
box-shadow: 0 0 6px rgba(255, 204, 0, 0.35);
}
/* Audio fase */
.book-card.audio {
border-color: #34c759;
box-shadow: 0 0 6px rgba(52, 199, 89, 0.35);
border-color: #ffca28;
box-shadow: 0 0 6px rgba(255, 202, 40, 0.35);
}
.book-card.completed {
border-color: #34c759;
box-shadow: 0 0 6px rgba(52, 199, 89, 0.35);
/* Volledig klaar */
.book-card.done {
border: 2px solid #4caf50;
box-shadow: 0 0 6px rgba(76, 175, 80, 0.35);
}
/* Afgebroken */
.book-card.aborted {
border-color: #ff3b30;
box-shadow: 0 0 6px rgba(255, 59, 48, 0.35);
@ -188,6 +185,21 @@
background: #555;
}
/* Statuscheck */
.statuscheck-btn {
background-color: #444;
color: #fff;
border: 1px solid #666;
margin-left: 4px;
padding: 4px 8px;
border-radius: 6px;
font-size: 12px;
cursor: pointer;
}
.statuscheck-btn:hover {
background-color: #333;
}
/* -----------------------------------------------------------------------
PROGRESS (FULL WIDTH)
----------------------------------------------------------------------- */
@ -201,7 +213,7 @@
}
.progress-row {
margin-bottom: 2px;
margin-bottom: 4px;
}
.progress-label {
@ -225,12 +237,12 @@
transition: width 0.4s ease;
}
/* Download = blauw */
/* Download */
.progressbar-fill.download {
background: #2196f3;
}
/* Audio = groen */
/* Audio */
.progressbar-fill.audio {
background: #4caf50;
}
@ -249,9 +261,50 @@
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
pointer-events: none;
}
.statuscheck-btn {
background-color: #444;
color: #fff;
border: 1px solid #666;
margin-left: 4px;
/* -----------------------------------------------------------------------
STATUS BADGE
----------------------------------------------------------------------- */
.status-badge {
display: inline-block;
margin-bottom: 6px;
padding: 2px 8px;
font-size: 11px;
font-weight: 600;
border-radius: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
cursor: default;
}
/* DONE */
.status-badge.status-done {
background-color: #e6f4ea;
color: #2e7d32;
border: 1px solid #4caf50;
}
/* AUDIO */
.status-badge.status-audio {
background-color: #fff8e1;
color: #8d6e00;
border: 1px solid #ffca28;
}
/* DOWNLOADING */
.status-badge.status-downloading {
background-color: #e3f2fd;
color: #1565c0;
border: 1px solid #42a5f5;
}
/* Statuscheck */
.icon-statuscheck {
background: #444;
}
.icon-statuscheck:hover {
background: #333;
transform: scale(1.05);
}

@ -52,6 +52,7 @@ function updateSingleBookCard(card, state) {
console.log("[BOOKCARD] updateSingleBookCard", state.book_idx);
updateStatus(card, state);
updateStatusBadge(card, state);
updateButtons(card, state);
updateProgress(card, state);
}
@ -64,6 +65,21 @@ function updateStatus(card, state) {
console.log("[BOOKCARD][STATUS]", state.book_idx, "→", state.status);
card.className = `book-card ${state.status || ""}`;
}
function updateStatusBadge(card, state) {
const badge = card.querySelector(".status-badge");
if (!badge) return;
const status = (state.status || "").toLowerCase();
badge.textContent = status.toUpperCase();
badge.className = `status-badge status-${status}`;
badge.title =
{
downloading: "Bezig met downloaden",
audio: "Downloads compleet, audio wordt gegenereerd",
done: "Alle chapters en audio zijn compleet",
}[status] || "";
}
/* ============================================================
BUTTONS

@ -31,6 +31,20 @@ component) ============================================================ #}
<!-- META -->
<div class="book-meta">
<!-- STATUS BADGE -->
{% if b.status %}
<span
class="status-badge status-{{ b.status }}"
title="
{% if b.status == 'done' %}Alle chapters en audio zijn compleet{% endif %}
{% if b.status == 'audio' %}Downloads compleet, audio wordt nog gegenereerd{% endif %}
{% if b.status == 'downloading' %}Bezig met downloaden{% endif %}
"
>
{{ b.status | upper }}
</span>
{% endif %}
<div class="book-title" data-field="title">{{ b.title }}</div>
<div class="book-author" data-field="author">{{ b.author }}</div>
<div class="book-created">
@ -62,10 +76,10 @@ component) ============================================================ #}
>
<button
type="submit"
class="statuscheck-btn"
class="icon-btn icon-statuscheck"
title="Herbereken status op basis van bestanden"
>
Statuscheck
<i class="fa-solid fa-magnifying-glass-chart"></i>
</button>
</form>
</div>

@ -22,25 +22,11 @@
<hr />
{% include "components/registered_books.html" %}
<hr />
<!-- ===========================================================
BOOK LIST
=========================================================== -->
<section class="dashboard-section">
<h2>Actieve boeken</h2>
{% if books and books|length > 0 %}
<div id="book-list" class="book-list">
{% for book in books %} {% include "components/book_list_item.html" %} {%
endfor %}
</div>
{% else %}
<div id="book-list" class="book-list-empty">Geen actieve boeken.</div>
{% endif %}
</section>
{% include "components/registered_books.html" %}
<hr />
<!-- ===========================================================

Loading…
Cancel
Save