73 for (
size_t i = 1U; i < l->
count; ++i) {
78 *lo = (num < *lo) ? num : *lo;
79 *hi = (num > *hi) ? num : *hi;
108 (value >= from_num)) {
140 const int n = snprintf(out, cap,
"%s/.mdl_state", abs_dir);
144 return (
size_t)n < cap;
170 if ((dst ==
nullptr) || (cap == 0U) || (src ==
nullptr)) {
173 const int n = snprintf(dst, cap,
"%s", src);
174 if ((n < 0) || ((
size_t)n >= cap)) {
243 if (rec !=
nullptr) {
254 return (id_len >= 0) && ((size_t)id_len <
sizeof(meta->
identifier));
282 for (
size_t i = 0U; i < sel->
count; ++i) {
290 " export metadata for ",
292 " exceeded a bounded field\n");
369 combined_rel[0] =
'\0';
376 (void)snprintf(combined_rel, cap,
"%s-%.10g-%.10g", slug, lo, hi);
378 (void)snprintf(combined_rel, cap,
"%s-%zu-chapters", slug, sel->
count);
406 const char* state_path,
414 .state_path = state_path,
415 .series_abs_dir = abs_dir,
458 const char* combined_rel,
471 " combined export metadata exceeded a bounded field\n");
508 bool published =
false;
534 "mdl: series cover acquisition failed "
565 if ((*out_sel)->count == 0U) {
568 "mdl: no chapters match the requested range\n");
597 const char* state_path,
607 sizeof(display_slug));
626 const int64_t run_start = (int64_t)time(
nullptr);
711 title = prior->
title;
714 "mdl: WARNING: preserving recorded series title after "
715 "selector failure\n") !=
k_ra8_ok) {
719 const char* summary =
772 "mdl: refusing to overwrite unreadable "
786 "mdl: scraped series metadata exceeded state bounds\n");
815 "mdl: network init failed\n");
826 .root = run->cache_dir,
828 .refetch = run->opts->refetch};
833 "mdl: series fetch failed\n");
837 "mdl: no chapters (check chapter_url_contains)\n");
855 if ((r ==
nullptr) || (r->
cache_dir ==
nullptr) ||
@ k_fw_fs_path_cap
Largest portable path including its NUL.
mdl_app_context_t * priv_mdl_app_context(void)
The working set the composition root bound with mdl_app_bind.
ra8_err_t priv_mdl_app_ensure_series_cover(mdl_fetch_ctx_t *ctx, const char *series_directory)
Ensure series state names one verified cached or freshly fetched cover.
Module-private contract shared by the application-mode translation units.
bool priv_mdl_app_prepare_series_dir(const char *out_dir, const char *series_url, char *slug, size_t slug_cap, char *abs_dir)
Prepare a contained canonical directory for one series.
ra8_err_t priv_mdl_app_prepare_chapters(const mdl_site_t *site, const char *series_url, uint32_t timeout, mdl_cache_t *cache)
Fetch and extract a series' bounded chapter list.
ra8_err_t priv_mdl_app_start_session(mdl_net_iface_t *net, const mdl_run_opts_t *opts, const char *cfg_contact, char *ua, size_t ua_cap)
Initialize one bounded network-policy session.
@ k_leaf_name_bytes
Composed archive/dir leaf name.
@ k_slug_bytes
Series slug buffer.
bool priv_mdl_app_state_path_of(const char *abs_dir, char *out, size_t cap)
Compose the state-journal path within a series directory.
static ra8_err_t internal_series_text3(ra8_io_stream_t *stream, const char *first, const char *second, const char *third)
Append three borrowed series-report fragments to one stream.
static mdl_fetch_layout_t internal_choose_layout(const mdl_series_run_t *r, const mdl_url_list_t *sel, char *combined_rel, size_t cap, const char *slug)
Choose output layout and its combined directory name.
static size_t internal_export_after(const mdl_series_run_t *r, const char *abs_dir, mdl_fetch_layout_t layout, const char *combined_rel, const mdl_url_list_t *sel, const mdl_fetch_stats_t *stats, int64_t run_start)
Package freshly downloaded output according to the selected layout.
static ra8_err_t internal_report_stats(const char *abs_dir, const mdl_fetch_stats_t *st)
Print the per-run download tally.
static ra8_err_t internal_save_series_state(const char *state_path, const char *failure)
Save the shared series state and report a contextual failure.
static bool internal_copy_metadata_text(char *dst, size_t cap, const char *src)
Copy one complete string into a fixed metadata field.
static bool internal_reconcile_series_state(const mdl_series_run_t *run, const mdl_site_t *site, const series_prior_metadata_t *prior)
Reconcile freshly scraped metadata with validated persisted state.
static mdl_fetch_ctx_t internal_make_ctx(const mdl_series_run_t *r, const mdl_site_t *site, const char *abs_dir, const char *state_path, mdl_governor_t *gov, mdl_cache_t *cache)
Assemble a fetch context over the shared bounded buffers.
static void internal_snapshot_prior_metadata(series_prior_metadata_t *prior)
Snapshot metadata that may survive a temporary selector miss.
static ra8_err_t internal_select_run_window(const mdl_series_run_t *r, const mdl_url_list_t **out_sel)
Resolve the chapter-selection window for one prepared run.
static void internal_select_window(bool from_present, double from_num, size_t count, mdl_url_list_t *out)
Select a bounded chapter window from the prepared list.
static bool internal_list_range(const mdl_url_list_t *l, double *lo, double *hi)
Determine the inclusive numeric range of a chapter list.
static int internal_run_prepared(const mdl_series_run_t *r, const mdl_site_t *site, const char *abs_dir, const char *state_path, mdl_cache_t *cache)
Drive one prepared series through fetch, checkpoint, and export.
static mdl_url_list_t s_selected
Selected chapter window for non-update downloads.
static mdl_fetch_faillog_t s_faillog
Bounded failure log for the active series run.
static size_t internal_export_fresh_separate(mdl_format_t format, const char *abs_dir, const mdl_url_list_t *sel, int64_t run_start)
Export every newly completed selected chapter separately.
int mdl_app_run_series(const mdl_series_run_t *r)
Execute one prepared series download.
static int internal_run_series_network(const mdl_series_run_t *run, mdl_site_t *site)
Run one prepared descriptor through the network-backed series flow.
static void internal_report_cover_failure(ra8_err_t error)
Report a failed series-cover acquisition with its exact error code.
static bool internal_build_export_metadata(const char *abs_dir, const mdl_chapter_rec_t *rec, mdl_export_meta_t *meta)
Convert persisted series/chapter state into exporter metadata.
bool priv_mdl_app_state_path_of(const char *abs_dir, char *out, size_t cap)
Compose the absolute .mdl_state path for a series.
static int internal_run_series_paths(const mdl_series_run_t *run, const mdl_site_t *site, mdl_cache_t *cache)
Prepare paths, load state, reconcile metadata, and run one series.
ra8_err_t priv_mdl_app_storage_ensure_directory(mdl_storage_t *storage, const char *path)
Ensure one canonical portable path names a real directory.
ra8_err_t mdl_config_load(mdl_storage_t *storage, const char *path, mdl_site_t *out)
Load a site descriptor from a flat key=value file.
mdl_gov_cfg_t mdl_config_gov_cfg(const mdl_site_t *site)
Build the politeness-governor tunables for a loaded site descriptor.
void mdl_config_apply_polite(mdl_site_t *site)
Raise a descriptor's per-request delay floors for --polite.
@ k_mdl_read_rtl
Right-to-left page progression.
@ k_mdl_read_ltr
Left-to-right page progression.
void mdl_meta_init(mdl_export_meta_t *meta)
Initialise a metadata struct to empty/default values.
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.
mdl_fetch_layout_t
Output directory layout the orchestrator writes.
@ k_mdl_layout_combined
All chapters -> one dir, continuous numbering.
@ k_mdl_layout_separate
Each chapter -> own dir, per-chapter numbering.
Portable single-transaction response-body sink for media fetches.
uint64_t mdl_hash_bytes_seed(const void *data, size_t len, uint64_t seed)
Continue an FNV-1a 64 fold over a byte range from a running state.
uint64_t mdl_hash_str(const char *s)
FNV-1a 64 hash of a NUL-terminated string (excluding the NUL).
void mdl_net_destroy(mdl_net_iface_t *net)
Deinitialise a caller-owned network interface.
ra8_err_t mdl_net_provider_open(const mdl_net_provider_t *provider, const mdl_net_policy_t *policy, mdl_net_iface_t *out_net)
Open a transport through an injected provider.
size_t mdl_pack_combined_meta(mdl_storage_t *storage, mdl_format_t format, bool allow_incomplete, const char *series_dir, const char *combined_rel, const mdl_fetch_stats_t *stats, const mdl_export_meta_t *meta, mdl_export_workspace_t *ws, ra8_io_stream_t *output, ra8_io_stream_t *diagnostic)
Package a combined page folder into format with rich metadata.
size_t mdl_pack_one_meta(mdl_storage_t *storage, mdl_format_t format, const char *series_dir, const char *chap_id, const mdl_export_meta_t *meta, mdl_export_workspace_t *ws, ra8_io_stream_t *output, ra8_io_stream_t *diagnostic)
Package one downloaded chapter folder into format with rich metadata.
void mdl_governor_init(mdl_governor_t *g, const mdl_gov_cfg_t *cfg, uint64_t seed)
Initialise a governor on the real host clock and blocking sleep.
ra8_err_t mdl_report_progress(void *ctx, const mdl_fetch_progress_t *ev)
Per-page progress sink: print one redirect-safe line per page.
ra8_err_t mdl_report_progress_bar(void *ctx, const mdl_fetch_progress_t *ev)
Per-page progress bar sink: render an in-place terminal progress bar.
ra8_err_t mdl_report_failures(ra8_io_stream_t *diagnostic, const mdl_fetch_faillog_t *log)
End-of-run summary: list every failure with its URL and reason.
bool mdl_path_join(const char *parent, const char *seg, char *out, size_t cap)
Join one safe child segment under a parent directory path.
bool mdl_sanitize_segment(const char *raw, char *out, size_t cap)
Rewrite an untrusted path segment into a safe single filename.
@ k_mdl_ua_max
Built User-Agent buffer bytes.
bool mdl_state_set_series_metadata(mdl_state_t *st, const char *summary, const char *writer, const char *artist, const char *cover_url, const char *cover_path, const char *language, mdl_state_reading_direction_t direction)
Set the optional rich metadata persisted for a series.
mdl_chapter_rec_t * mdl_state_find_chapter(mdl_state_t *st, const char *id)
Find a chapter record by its stable identifier.
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.
@ k_mdl_state_read_rtl
Right-to-left page progression.
@ k_mdl_person_max
Writer/artist bytes.
@ k_mdl_summary_max
Series summary bytes.
@ k_mdl_chapter_id_max
Chapter identifier bytes (sanitised).
@ k_mdl_title_max
Series title bytes.
@ k_mdl_relpath_max
Page path relative to the series dir.
ra8_err_t mdl_state_load(mdl_storage_t *storage, const char *path, mdl_state_t *st)
Load a series' newest valid state through injected portable storage.
void mdl_state_set_series(mdl_state_t *st, const char *url, const char *title, const char *site_name, const char *site_host, const char *config_path)
Record the series identity and the descriptor used.
ra8_err_t priv_mdl_stream_hex(ra8_err_t prior, ra8_io_stream_t *stream, uint32_t value)
Append one hexadecimal error/status value after prior success.
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.
ra8_err_t priv_mdl_stream_u64(ra8_err_t prior, ra8_io_stream_t *stream, uint64_t value)
Append one unsigned integer unless an earlier append failed.
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.
#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).
@ k_ra8_err_invalid_arg
Invalid function argument.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
The one bounded working set every application mode shares.
mdl_series_metadata_t series_metadata
Current scraped metadata.
mdl_session_t session
Network policy session.
ra8_io_stream_t * output
Borrowed command output sink.
mdl_url_list_t images
Prepared page image URLs.
mdl_cache_index_t cache_index
One loaded per-host index.
mdl_storage_t storage
Injected filesystem facade.
ra8_io_stream_t * diagnostic
Borrowed diagnostic sink.
mdl_url_list_t chapters
Prepared chapter URLs.
char page[k_page_buf_bytes]
Whole-page HTML buffer.
mdl_state_t state
Persistent tracked state.
One non-reentrant cache binding over caller storage.
One chapter's coverage in library state.
double number
Parsed number; 0 may be valid.
int64_t fetched_at
Completion time (epoch s).
char source_url[k_mdl_url_max]
Chapter page URL.
bool number_known
Whether number is known.
char title[k_mdl_title_max]
Display title, or empty.
bool complete
All pages present + verified.
Everything mdl_fetch_run needs, injected for testability.
ra8_err_t progress_error
First callback failure from this run.
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 pages_fetched
Pages transferred over the network.
size_t pages_reused
Pages served from an already-held verified file.
size_t chapters_failed
Chapters left partial by a page failure.
size_t pages_failed
Page fetches that failed or robots refused.
Per-site politeness tunables the governor is initialised with.
Closed-loop per-host politeness governor (rate + backoff + concurrency).
A network backend handle: an immutable method table plus its state.
bool polite
True to raise per-host delays.
bool progress
True to draw a progress bar.
bool refetch
True to bypass verified page reuse.
bool allow_incomplete
True to package a run with failures.
Parameters of one series download, as values rather than arguments.
bool combine
Combine into one archive.
uint64_t seed
Politeness jitter seed.
bool update
Incremental (skip complete).
const char * out_dir
Output library root.
mdl_format_t format
Output container.
double from_num
First chapter number to fetch.
bool from_present
Whether from_num applies.
const char * cfg_path
Site descriptor path.
uint32_t timeout
Per-request budget, ms.
size_t chapters
Max chapters (window mode).
const char * series_url
Series page URL.
const char * cache_dir
Per-host persistent cache root.
const mdl_run_opts_t * opts
Identity/security knobs.
One site's scraping rules (all data, no logic).
char kind[k_mdl_kind_max]
Content kind hint (reader mode).
char name[k_mdl_name_max]
Human-readable site name.
char host[k_mdl_host_max]
Site host, e.g.
char contact[k_mdl_contact_max]
Operator contact for the UA.
char summary[k_mdl_summary_max]
Series synopsis.
char cover_path[k_mdl_relpath_max]
Local cover path.
char series_title[k_mdl_title_max]
Series title.
mdl_state_reading_direction_t reading_direction
Page progression.
char artist[k_mdl_person_max]
Artist/illustrator.
char cover_url[k_mdl_url_max]
Remote cover URL.
char writer[k_mdl_person_max]
Writer/author.
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.
Caller-allocated byte-stream handle binding a sink to its context.