The initialization page computed its ETA by regex-parsing the English
details strings ("Found N", "Processing x/y") and the progress bar
advanced per file, so one 7 GB LoRA and one 100 KB embedding moved it
by the same amount.
The scanner progress callbacks now carry the collected file sizes end
to end: process_models/count_models broadcasts include structured
processed/total/bytes_done/bytes_total fields, and the bar percentage
is weighted by bytes (falling back to file counts when sizes are
unavailable). initialization.js consumes the structured fields
directly - no more string parsing - and computes the remaining-time
estimate per byte, keeping the file-count path as a fallback.
Two initialization-page defects made failures look like progress:
- A cache read exception while rendering a model/recipe page forced
is_initializing=True, showing an endless spinner; worse, the polling
fallback then reported the idle scanner as "complete", reloading the
page straight back into the same exception. Page handlers now pass
init_error to the initialization template, which renders the error
state (with retry) server-side, and initialization.js stays on it
instead of starting WebSocket/polling.
- The WebSocket path reloaded once the page's own scanner finished,
while the polling fallback waited for every scanner. The polling
fallback now mirrors the per-page semantics: /api/lm/init-status
accepts ?page=<pageType> (aggregate behavior preserved without it),
and ModelScanner records _init_error so a failed initial scan is
reported as "error" instead of masquerading as "complete". Polling
stops on error instead of reloading into the failure.