29typedef enum : uint16_t {
34typedef enum : uint8_t {
39typedef enum : uint8_t {
107 return (a > b) ? a : b;
117 if (stats ==
nullptr) {
132 if (buf ==
nullptr) {
138 const char* base =
nullptr;
144 base =
"request timed out";
147 base =
"rate limited";
153 base =
"response exceeded size cap";
156 base =
"still failing after retries";
166 (void)snprintf(buf, cap,
"%s (HTTP %ld)", base, status);
168 (void)snprintf(buf, cap,
"%s", base);
186 (void)snprintf(item->
url,
sizeof(item->
url),
"%s", (url !=
nullptr) ? url :
"");
198 bool published =
false;
253 if (out_resp !=
nullptr) {
256 if (out_bytes !=
nullptr) {
323 const char* target_abs,
335 if (out_resp !=
nullptr) {
350 if (out_bytes !=
nullptr) {
358 const char* target_abs,
363 if (out_resp !=
nullptr) {
366 if (out_bytes !=
nullptr) {
369 if ((ctx ==
nullptr) || (ctx->
session ==
nullptr) || (ctx->
session->
net ==
nullptr) ||
370 (ctx->
site ==
nullptr) || (url ==
nullptr) || (url[0] ==
'\0') || (target_abs ==
nullptr) ||
371 (target_abs[0] !=
'/')) {
382 if (host ==
nullptr) {
390 .if_none_match =
nullptr,
391 .if_modified_since =
nullptr,
558 const char* combined_abs,
559 const char* combined_rel,
563 const char** dest_abs,
564 const char** dest_rel,
568 *dest_abs = combined_abs;
569 *dest_rel = combined_rel;
576 *dest_abs = chap_abs;
626 const char* chapter_url,
629 work->title = work->rec->title;
631 if (work->rec->title[0] ==
'\0') {
632 work->title = work->id;
639 work->extracted_title,
640 sizeof(work->extracted_title));
642 work->title = work->extracted_title;
647 "mdl: chapter title for '",
649 "' exceeds the persistent field; refusing truncation\n");
653 const bool preserving = work->rec->title[0] !=
'\0';
655 "mdl: chapter title selector '",
663 preserving ?
"preserving recorded title\n" :
"using chapter id\n");
669 work->title = work->id;
691 const char* chapter_url,
705 work->number_known =
true;
709 "mdl: chapter number selector '",
711 "' returned invalid value '");
724 "mdl: chapter number for '",
726 "' exceeds the bounded selector field\n");
731 "mdl: chapter number selector '",
739 work->number_known ?
"recorded number\n" :
"unknown number\n");
771 const char* chapter_url,
773 const char* combined_abs,
774 const char* combined_rel,
777 size_t chapter_index,
778 size_t chapter_total,
784 work->rec->page_count = (uint16_t)ctx->
images->
count;
785 if (work->rec->pages_done > work->rec->page_count) {
786 work->rec->pages_done = 0U;
788 char chap_abs[PATH_MAX];
789 const char* dest_abs =
nullptr;
790 const char* dest_rel =
nullptr;
806 .chapter_total = chapter_total,
807 .chapter_id = work->id};
845 const char* chapter_url,
847 const char* combined_abs,
848 const char* combined_rel,
851 size_t chapter_index,
852 size_t chapter_total)
862 if (work.rec ==
nullptr) {
865 work.number = work.rec->number;
866 work.number_known = work.rec->number_known;
868 *global_no += work.rec->page_count;
871 work.rec->complete =
false;
872 work.rec->fetched_at = 0;
939 if (ctx ==
nullptr) {
942 return (ctx->
session !=
nullptr) && (ctx->
storage !=
nullptr) && (ctx->
state !=
nullptr) &&
950 const char* combined_dir_rel,
953 if ((ctx ==
nullptr) || (chapters ==
nullptr) || (stats ==
nullptr)) {
959 memset(stats, 0,
sizeof(*stats));
962 char combined_abs[PATH_MAX];
968 sizeof(combined_abs))) {
972 combined_abs[0] =
'\0';
975 size_t global_no = 0U;
976 bool any_fail =
false;
977 for (
size_t i = 0U; i < chapters->
count; ++i) {
ra8_err_t mdl_cache_get_buf(mdl_cache_t *cache, const char *url, const mdl_net_req_t *base_request, mdl_cache_fetch_fn fetch, void *fetch_context, char *buffer, size_t capacity, size_t *out_length, mdl_net_resp_t *response, mdl_cache_result_t *result)
Fetch one document through the per-host persistent cache.
static bool internal_mdl_fetch_select_chapter_number(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_chapter_work_t *work)
Select and strictly parse optional descriptor-driven chapter numbering.
static mdl_chap_status_t internal_mdl_fetch_process_chapter(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_fetch_layout_t layout, const char *combined_abs, const char *combined_rel, size_t *global_no, mdl_fetch_stats_t *stats, size_t chapter_index, size_t chapter_total)
Download one chapter (or skip it), returning its outcome.
ra8_err_t priv_mdl_fetch_checkpoint(const mdl_fetch_ctx_t *ctx)
Persist state atomically when a checkpoint target is configured.
static bool internal_mdl_fetch_tally(mdl_chap_status_t s, mdl_fetch_stats_t *stats)
Fold one chapter outcome into run statistics.
static ra8_err_t internal_mdl_fetch_diag3(mdl_fetch_ctx_t *ctx, const char *first, const char *second, const char *third)
Emit three fetch diagnostics and latch a sink failure.
mdl_fetch_retry_t
Bounded attempts per request: the initial try plus backoff retries.
@ k_fetch_max_attempts
One initial request plus at most three retries.
static bool internal_mdl_fetch_ctx_ready(const mdl_fetch_ctx_t *ctx)
Validate that every required fetch dependency is present.
static const char * internal_mdl_fetch_fail_reason(long status)
Prose for a k_ra8_fail: a 5xx server error versus a transport error.
bool priv_mdl_fetch_run_incomplete(const mdl_fetch_stats_t *stats)
Whether a finished run left anything unfetched (chapter or page).
bool priv_mdl_fetch_is_retryable(ra8_err_t rc)
Whether a failed transfer result is worth retrying.
static bool internal_mdl_fetch_resolve_dest(mdl_fetch_ctx_t *ctx, mdl_fetch_layout_t layout, const char *id, const char *combined_abs, const char *combined_rel, size_t global_no, char *chap_abs, size_t chap_cap, const char **dest_abs, const char **dest_rel, size_t *base)
Resolve the output directory and starting page number for one chapter.
ra8_err_t mdl_fetch_run(mdl_fetch_ctx_t *ctx, const mdl_url_list_t *chapters, mdl_fetch_layout_t layout, const char *combined_dir_rel, mdl_fetch_stats_t *stats)
Download a series' chapters incrementally, resuming and deduping.
ra8_err_t mdl_fetch_asset(mdl_fetch_ctx_t *ctx, const char *url, const char *target_abs, const char *referer, mdl_net_resp_t *out_resp, size_t *out_bytes)
Fetch one policy-governed asset to an absolute file path.
static uint32_t internal_mdl_fetch_max_u32(uint32_t a, uint32_t b)
Perform the max u32 step.
mdl_chap_status_t
Per-chapter outcome reported to the run loop.
@ k_ch_skipped
Already complete (–update skip).
@ k_ch_failed
Left partial by a page failure.
@ k_ch_completed
Every page present and verified.
ra8_err_t priv_mdl_fetch_with_retry(mdl_fetch_ctx_t *ctx, const char *host, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, uint32_t jmin, uint32_t jmax, mdl_net_resp_t *out_resp, size_t *out_bytes)
Bounded, governed retry of one image transfer; last status latched.
static void internal_mdl_fetch_mark_complete(mdl_fetch_ctx_t *ctx, mdl_fetch_layout_t layout, mdl_chapter_rec_t *rec, size_t *global_no)
Mark a chapter complete and advance combined numbering.
static bool internal_mdl_fetch_select_chapter_title(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_chapter_work_t *work)
Select one bounded persistent chapter title.
void priv_mdl_fetch_record_fail(const mdl_fetch_ctx_t *ctx, const char *url, long status, ra8_err_t err)
Append one failure to the run's log (when set); always tally total.
void priv_mdl_fetch_reason(ra8_err_t err, long status, char *buf, size_t cap)
Render a human-readable reason for a transfer result and HTTP status.
mdl_fetch_http_t
HTTP-status boundary used when rendering a failure reason.
@ k_http_server_err
First status that is a server error.
static ra8_err_t internal_mdl_fetch_chapter_html(mdl_fetch_ctx_t *ctx, const char *chapter_url, size_t *out_len)
Fetch one chapter HTML document and extract its page URLs.
ra8_err_t priv_mdl_fetch_cache_get_buf(void *context, const char *url, const mdl_net_req_t *request, char *buffer, size_t capacity, size_t *out_length, mdl_net_resp_t *response)
Fetch one cache attempt through governor and bounded retry policy.
static ra8_err_t internal_fetch_asset_execute(mdl_fetch_ctx_t *ctx, const char *host, const char *url, const mdl_net_req_t *req, uint32_t jmin, uint32_t jmax, const char *target_abs, mdl_net_resp_t *out_resp, size_t *out_bytes)
Fetch one asset body and commit it to permanent storage.
static const char * internal_mdl_fetch_page_host(const char *url, char *buf, size_t cap)
Governor host key for url, or NULL when it cannot be parsed.
static ra8_err_t internal_mdl_fetch_governed_get_body(mdl_fetch_ctx_t *ctx, const char *host, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, uint32_t jmin, uint32_t jmax, mdl_net_resp_t *out_resp, size_t *out_bytes)
One governed body transfer: pace, fetch, feed the outcome back.
static mdl_chap_status_t internal_mdl_fetch_chapter_pages_and_checkpoint(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_fetch_layout_t layout, const char *combined_abs, const char *combined_rel, size_t *global_no, mdl_fetch_stats_t *stats, size_t chapter_index, size_t chapter_total, mdl_chapter_work_t *work)
Persist metadata, fetch pages, and checkpoint one prepared chapter.
State-aware chapter/page download orchestrator for the media downloader.
@ k_mdl_fetch_fail_max
Failures stored before the log saturates.
mdl_fetch_layout_t
Output directory layout the orchestrator writes.
@ k_mdl_layout_combined
All chapters -> one dir, continuous numbering.
ra8_err_t priv_mdl_fetch_body_commit(mdl_fetch_body_t *body)
Validate exact size/hash and publish one prepared body.
ra8_err_t priv_mdl_fetch_body_init_exact(mdl_fetch_body_t *body, mdl_storage_t *storage, const char *target_abs)
Initialize an exact-path transactional body sink.
mdl_net_body_sink_t priv_mdl_fetch_body_sink(mdl_fetch_body_t *body)
Return the network sink view of one body state.
ra8_err_t priv_mdl_fetch_body_prepare(mdl_fetch_body_t *body)
Finish prefix classification and make a nonempty stage commit-ready.
ra8_err_t priv_mdl_fetch_body_abort(mdl_fetch_body_t *body)
Abort any private stage owned by a body sink.
Portable single-transaction response-body sink for media fetches.
Module-private fetch-loop decisions promoted for host unit tests and the CLI's end-of-run reporting.
ra8_err_t priv_mdl_fetch_chapter_pages(mdl_fetch_ctx_t *ctx, const char *chapter_url, const char *dest_abs, const char *dest_rel, size_t base, mdl_chapter_rec_t *rec, mdl_fetch_stats_t *stats, const mdl_run_pos_t *pos)
Fetch and checkpoint every extracted page in one chapter.
struct mdl_fetch_cache_request mdl_fetch_cache_request_t
Caller-owned governor parameters for one cached buffer fetch.
Streaming HTTP(S) GET seam (a real function-pointer vtable) for the host manga downloader (v0).
ra8_err_t mdl_net_get_buf(mdl_net_iface_t *net, const char *url, const mdl_net_req_t *req, char *buf, size_t cap, size_t *out_len, mdl_net_resp_t *resp)
GET url fully into a caller buffer (used for HTML pages).
ra8_err_t mdl_net_get_body(mdl_net_iface_t *net, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, size_t *out_len, mdl_net_resp_t *resp)
GET url and stream its body through a caller-owned sink.
Guarded filesystem directory joins for the media downloader.
bool mdl_join_dir_under(mdl_storage_t *storage, const char *parent_abs, const char *seg, char *out, size_t cap)
Join seg under parent_abs, create it, and verify it stays inside.
void mdl_governor_observe(mdl_governor_t *g, const char *host, long status, const char *retry_after)
Feed a completed request's outcome back into the host's governor state.
@ k_mdl_gov_host_max
Host-key buffer bytes (matches config).
ra8_err_t mdl_governor_acquire(mdl_governor_t *g, const char *host, uint32_t jitter_min_ms, uint32_t jitter_max_ms)
Reserve an in-flight slot for a request to host, pacing as required.
void mdl_governor_release(mdl_governor_t *g, const char *host)
Release the in-flight slot reserved by a matching mdl_governor_acquire.
bool mdl_session_url_allowed(mdl_session_t *session, const char *url, uint32_t *crawl_delay_ms)
Decide whether url may be fetched under robots.txt, and its delay.
bool mdl_state_set_chapter_metadata(mdl_chapter_rec_t *chapter, const char *title, double number, bool number_known)
Set a chapter's display title and explicit parsed number.
ra8_err_t mdl_state_save(mdl_storage_t *storage, const char *path, const mdl_state_t *st, bool *out_published)
Publish a checksummed successor without sacrificing the newest state.
mdl_chapter_rec_t * mdl_state_add_chapter_numbered(mdl_state_t *st, const char *id, const char *url, double number, bool number_known)
Find or append a chapter with explicit parsed-number presence.
@ k_mdl_chapter_id_max
Chapter identifier bytes (sanitised).
@ k_mdl_title_max
Series title bytes.
Injected portable storage resources for downloader domain code.
ra8_err_t priv_mdl_stream_text(ra8_err_t prior, ra8_io_stream_t *stream, const char *text)
Append text unless an earlier operation already failed.
Private bounded text helpers over the portable byte-stream contract.
Pure URL / address safety predicates for the libcurl backend.
bool mdl_url_host(const char *url, char *out, size_t cap)
Extract the authority (host and optional port) from an http(s) URL.
Bounded URL naming and portable image-classification helpers.
bool mdl_urlname_chapter_text_parse(const char *text, double *out)
Parse a selector result as one complete bounded chapter number.
void mdl_urlname_last_segment(const char *url, char *out, size_t cap)
Sanitised last non-empty path segment of a URL.
bool mdl_urlname_chapter_parse(const char *url, double *out)
Parse an explicitly-marked integral or decimal chapter value.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_retry_limit
Retry budget exhausted – operation still failing after all attempts.
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
@ k_ra8_err_busy
Resource busy – blocking operation cannot proceed.
@ k_ra8_err_no_data
No application data available (e.g.
@ k_ra8_err_would_block
Non-blocking operation would have blocked.
@ k_ra8_fail
Generic unspecified failure.
@ k_ra8_err_invalid_arg
Invalid function argument.
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
@ k_ra8_err_timeout
Operation exceeded its time budget.
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
Observable outcome of one cache lookup.
One chapter's coverage in library state.
int64_t fetched_at
Completion time (epoch s).
uint16_t pages_done
Pages fetched and verified.
bool complete
All pages present + verified.
Bounded metadata and state accumulated while processing one chapter.
bool number_known
Explicit number presence.
mdl_chapter_rec_t * rec
Persistent chapter record.
char extracted_title[k_mdl_title_max]
Selector title scratch.
double number
Parsed chapter number.
size_t html_len
Retained chapter HTML.
const char * title
Selected persistent title.
Caller-owned state for one retryable streamed fetch.
mdl_fetch_ctx_t * ctx
Active fetch dependencies.
char host[k_mdl_gov_host_max]
Governor host key.
uint32_t jmin
Minimum request delay.
uint32_t jmax
Maximum request delay.
Everything mdl_fetch_run needs, injected for testability.
mdl_state_t * state
Persistent state, read and updated.
mdl_url_list_t * images
Extracted-image scratch (caller-owned).
const mdl_site_t * site
Selectors + politeness bounds.
mdl_governor_t * gov
Per-host rate/backoff governor, or NULL.
const char * state_path
Atomic checkpoint target, or NULL.
bool update_only
Skip chapters already recorded complete.
mdl_storage_t * storage
Portable storage binding + workspaces.
char * page_buf
Chapter-HTML scratch (caller-owned).
mdl_cache_t * cache
Per-host HTML cache binding.
const char * series_abs_dir
Absolute series dir (paths resolve here).
mdl_fetch_faillog_t * faillog
Caller-cleared failure log, or NULL.
ra8_io_stream_t * diagnostic
Borrowed diagnostic sink.
mdl_session_t * session
Identity + robots + net backend.
const char * series_url
Series URL, sent as the chapter Referer.
size_t page_cap
Capacity of page_buf.
uint32_t timeout_ms
Per-request time budget.
ra8_err_t progress_error
First callback failure from this run.
One recorded page/chapter failure: what failed, and with what status.
ra8_err_t err
Classified failure code (rendered on demand).
char url[k_mdl_url_max]
Failing page or chapter URL (truncation-safe).
long status
HTTP status observed, 0 when none.
Bounded record of every failure in a run (declare at file scope).
Tallies a fetch run reports back (all zero-initialised by the run).
size_t chapters_completed
Chapters finished (all pages present) this run.
size_t chapters_skipped
Chapters skipped as already complete (–update).
size_t chapters_failed
Chapters left partial by a page failure.
size_t pages_failed
Page fetches that failed or robots refused.
Caller-owned lifecycle and write seam for one response body.
Per-request session parameters.
Per-transfer response metadata surfaced to the politeness governor.
long status
Finished transfer's HTTP status, 0 if none.
char retry_after[k_mdl_retry_after_max]
Raw Retry-After value, "" when absent.
Stable chapter position carried into per-page progress events.
mdl_net_iface_t * net
Backend used for robots.txt fetches.
const char * user_agent
Full UA header (caller-owned).
char chapter_number_selector[k_mdl_match_max]
Per-chapter number selector.
uint32_t chapter_delay_min
Inter-chapter spacing floor.
uint32_t img_delay_max
Per-image spacing ceiling.
uint32_t chapter_delay_max
Inter-chapter spacing ceiling.
char chapter_title_selector[k_mdl_match_max]
Per-chapter title selector.
char page_img_url_contains[k_mdl_match_max]
Keep only URLs with this.
char page_img_attr[k_mdl_attr_max]
Preferred attr (fallback other).
uint32_t img_delay_min
Per-image spacing floor.
Bounded list of absolute URLs found on a page.
char urls[k_mdl_max_urls][k_mdl_url_max]
Absolute, NUL-terminated.
size_t count
Number of valid entries.