152 for (
size_t i = 0U; i < (l->
count / 2U); ++i) {
174 for (
size_t i = 0U; i < l->
count; ++i) {
175 double ignored = 0.0;
180 for (
size_t i = 0U; i + 1U < l->
count; ++i) {
182 for (
size_t j = i + 1U; j < l->
count; ++j) {
216 "mdl: WARNING: at least one chapter has no numeric "
217 "identifier; preserving site document order\n");
237 const size_t plen =
strlen(prefix);
239 for (
size_t r = 0U; r < l->
count; ++r) {
266 static bool s_warned =
false;
271 "mdl: WARNING: no contact configured; pass --contact "
272 "<email|url> so a site operator can reach you before banning\n");
303 const char* selector;
319 for (
size_t i = 0U; i < (
sizeof(optional) /
sizeof(optional[0])); ++i) {
320 if (optional[i].selector[0] ==
'\0') {
329 " exceeds the bounded metadata field\n");
330 return (output_error ==
k_ra8_ok) ? rc : output_error;
333 optional[i].out[0] =
'\0';
338 optional[i].selector);
341 "' matched no bounded value\n");
383 return (output_error ==
k_ra8_ok) ? rc : output_error;
392 "mdl: series cover URL could not be resolved safely\n");
427 const char* series_url,
440 if ((language_len < 0) ||
458 "mdl: series title exceeds the bounded metadata field\n");
459 return (output_error ==
k_ra8_ok) ? rc : output_error;
467 "' failed; using URL leaf '");
501 const char* series_url)
505 const int prefix_len = snprintf(prefix,
sizeof(prefix),
"%s", site->
chapter_url_prefix);
506 if ((prefix_len < 0) || ((
size_t)prefix_len >=
sizeof(prefix))) {
510 const int prefix_len = snprintf(prefix,
sizeof(prefix),
"%s", series_url);
511 if ((prefix_len < 0) || ((
size_t)prefix_len >=
sizeof(prefix))) {
514 size_t pl =
strlen(prefix);
515 if ((pl > 0U) && (prefix[pl - 1U] !=
'/') && (pl + 1U <
sizeof(prefix))) {
517 prefix[pl + 1U] =
'\0';
548 const char* series_url,
552 if ((cache ==
nullptr) ||
558 .timeout_ms = timeout};
614 const char* cfg_contact,
618 const char* contact = opts->
contact;
619 if ((contact ==
nullptr) && (cfg_contact !=
nullptr) && (cfg_contact[0] !=
'\0')) {
620 contact = cfg_contact;
658 const char* series_url,
@ 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.
Module-private contract shared by the application-mode translation units.
@ k_dir_path_bytes
Directory-path buffer.
bool priv_mdl_app_prepare_series_dir(const char *out_dir, const char *series_url, char *slug, size_t slug_cap, char *abs_dir)
Create and resolve a series directory beneath an output root.
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 a series page and build its ordered chapter list.
static ra8_err_t internal_extract_series_metadata(const mdl_site_t *site, const char *series_url, const char *html, size_t len)
Extract descriptor-driven series metadata from one fetched page.
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 the shared network session identity for one run.
ra8_err_t priv_mdl_app_report_cache(const char *url, const mdl_cache_result_t *result)
Report verified cache reuse with its pre-request staleness.
static ra8_err_t internal_extract_cover(const mdl_site_t *site, const char *series_url, const char *html, size_t len)
Extract and resolve the configured required cover URL.
static ra8_err_t internal_prepare_filter_chapters(const mdl_site_t *site, const char *series_url)
Retain only chapters belonging to the configured series.
static ra8_err_t internal_prepare_diag3(const char *first, const char *second, const char *third)
Append three borrowed preparation diagnostics.
static ra8_err_t internal_prepare_cache_fetch(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)
Dispatch one cache request directly through the active network seam.
static void internal_filter_prefix(mdl_url_list_t *l, const char *prefix)
Keep only list entries whose URL starts with a prefix.
static ra8_err_t internal_warn_no_contact(void)
Print the missing-operator-contact warning once.
static ra8_err_t internal_extract_optional_metadata(const mdl_site_t *site, const char *html, size_t len)
Extract the optional summary and creator fields.
static bool internal_sort_by_chapter_num(mdl_url_list_t *l)
Sort a URL list by parsed chapter number.
static ra8_err_t internal_apply_order(mdl_url_list_t *l, mdl_chapter_order_t order)
Apply the configured chapter ordering in place.
static void internal_swap_rows(mdl_url_list_t *l, size_t a, size_t b)
Swap two rows of a URL list.
static void internal_reverse_list(mdl_url_list_t *l)
Reverse a URL list in place.
static char s_rowtmp[k_mdl_url_max]
Scratch row used while reordering the prepared chapter list.
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_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.
mdl_chapter_order_t
How to order chapters extracted from a series page.
@ k_mdl_order_asc
Sort by the number parsed from the URL.
@ k_mdl_order_reverse
Reverse document order (newest-first -> old).
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).
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.
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.
void mdl_session_init(mdl_session_t *session, mdl_net_iface_t *net, const char *user_agent, ra8_io_stream_t *diagnostic, bool honor_robots)
Initialise a session over a network backend.
bool mdl_session_build_ua(const char *contact, char *out, size_t cap)
Build the truthful default User-Agent into out.
@ k_mdl_state_read_ltr
Left-to-right page progression.
@ k_mdl_state_read_rtl
Right-to-left page progression.
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.
double mdl_urlname_chapter_value(const char *url)
Parse a chapter URL as a possibly-decimal chapter value.
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_no_data
No application data available (e.g.
@ k_ra8_fail
Generic unspecified failure.
@ 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_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.
int strncmp(const char *s1, const char *s2, size_t n)
Compare two strings up to a specified length.
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.
mdl_series_metadata_t series_metadata
Current scraped metadata.
mdl_session_t session
Network policy session.
mdl_storage_t storage
Injected filesystem facade.
ra8_io_stream_t * diagnostic
Borrowed diagnostic sink.
Observable outcome of one cache lookup.
bool body_reused
Returned bytes came from verified storage.
bool index_rebuilt
A corrupt index was discarded first.
int64_t age_seconds
Age before this operation, or -1 if unknown.
uint16_t observed_status
Network or retained status.
One non-reentrant cache binding over caller storage.
A network backend handle: an immutable method table plus its state.
Per-request session parameters.
Per-transfer response metadata surfaced to the politeness governor.
Cross-cutting policy and injected transport threaded into every mode.
const char * contact
Operator contact override, or NULL.
bool honor_robots
False when robots.txt is ignored.
const char * user_agent
Full UA header (caller-owned).
One site's scraping rules (all data, no logic).
char series_author_selector[k_mdl_match_max]
Series writer selector.
char reading_direction[k_mdl_kind_max]
ltr or rtl.
char series_cover_selector[k_mdl_match_max]
Series cover-URL selector.
char chapter_url_contains[k_mdl_match_max]
href must contain this.
char language[k_mdl_kind_max]
BCP-47 language tag.
char series_summary_selector[k_mdl_match_max]
Series synopsis selector.
mdl_chapter_order_t chapter_order
Ordering to apply.
char series_artist_selector[k_mdl_match_max]
Series artist selector.
char chapter_url_prefix[k_mdl_search_url_max]
Absolute series-chapter prefix.
char series_title_selector[k_mdl_match_max]
Series display-title selector.
One non-reentrant downloader filesystem dependency bundle.
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.
#define min(x, y)
Untyped minimum shim used by the SOUP's buffer clamping.