comicbox.online_estimate

source module comicbox.online_estimate

Estimate the API-request count and wall-clock for a batch online-tag run.

A batch caller (Codex) shows this to an operator before a “Tag Online” run and mirrors it in a live countdown. Keeping the model here — next to the search flow it describes and the rate limits it divides by — means the numbers move with comicbox’s behavior instead of drifting in a downstream copy.

The model is deliberately simple. Per comic, each source costs a characteristic number of API requests:

  • Metron resolves an issue with a single issues_list search plus the final issue fetch — a flat count (see v4.0.5 / PR #143; no series-discovery step since series ids land directly on issue-list results). Metron does not fan out, so effort does not move it.
  • Comic Vine discovers volumes, asks each surviving candidate for its issues, then fetches the issue it accepts. Only the middle step varies: Effort decides how many candidates are worth a call.

Match mode is not an input. It decides how a verdict is applied, never how many requests a search spends.

Under first-match-wins a comic stops at the first source that answers, so the run is billed the costliest single selected source; merging all sources (first_wins=False) queries every source per comic, so their per-comic costs are summed.

Wall-clock paces each source at its sustained throughput: Metron’s per-minute cap binds a bounded run directly. Comic Vine limits per resource pool — 200/hour for each endpoint (simyan keeps a separate bucket per endpoint, mirroring CV’s documented “200 requests per resource per hour”) — so a run is bound by its busiest single pool, not by the request total: discovery and the final fetches each land in their own pool while the per-volume issue lookups stack in one. first-match-wins means a comic isn’t done until the binding source answers, so the slowest source sets the pace; merging pays every source per comic, so their paces sum.

The projection prices a cold search for every comic. A real run batches by series, where one search answers for the whole series and the issues after it cost about one issue-list call each, so a library finishes ahead of this number. Each call counted here is one HTTP request: simyan 4.1.0 stops paginating on a short page, so a Comic Vine list call no longer spends two of its pool’s hourly budget (Simyan#309, see tasks/simyan-4-plan.md).

Classes

  • RunEstimate — The projected cost of a batch online-tag run.

Functions

source class RunEstimate()

The projected cost of a batch online-tag run.

Attributes

  • requests : int — Total API requests the run is expected to make.

  • seconds : float — Projected wall-clock duration in seconds.

source metron_requests_for_batch(comics: int, series: int) → int

Return what a Metron batch of comics over series runs costs.

The per-comic constant prices a comic that resolves its own series. That is the right number for a scattered batch and badly wrong for a library-scale one, where one series answers for a hundred comics: each series pays the cold price once and every other comic of it pays the warm price.

series is how many distinct series the batch spans — the CLI and OnlineSession.tag_many both count them to decide prefetching, so the caller has the number already. Clamped into range, since a batch cannot span more series than it has comics.

source source_rate_per_minute(source: str) → int

Return the pace to project a run at, preferring the server’s number.

SOURCE_RATE_PER_MINUTE holds documented starting points. Metron sends its real per-user burst limit on every response, and comicbox’s rate gate paces against that, so once anything has talked to Metron in this process the gate’s number is the one a projection should use — a self-hosted instance or a changed server setting makes the constant simply wrong.

source requests_per_comic(source: str, effort: str = Effort.BALANCED.value) → int

Return the API requests one comic costs against source at effort.

effort is an :class:~comicbox.config.online.settings.Effort or its value. Resolve it for the source it prices — resolve_effort(settings.online, "comicvine") — because a per-source override beats the global setting.

source estimate_run(comics: int, sources: Sequence[str], *, effort: str = Effort.BALANCED.value, merge_all_sources: bool = False) → RunEstimate

Project the request count and wall-clock seconds for a batch online-tag run.

comics is how many comics remain to look up and sources the enabled source names in priority order. effort is an :class:~comicbox.config.online.settings.Effort value ("balanced" etc.), resolved for the source it prices. merge_all_sources mirrors first_wins=False: every source is queried per comic and their per-comic costs summed, instead of stopping at the first match.

The projection assumes the effort it is handed. A CLI run can auto-engage a lower Comic Vine effort for a large unattended batch, which this cannot see.