24typedef enum : uint16_t {
79 return (a > b) ? a : b;
114 "mdl: this site descriptor has no browse_url; browsing is ",
120 if ((n < 0) || ((
size_t)n >= cap)) {
129 "mdl: this site descriptor has no search_url; searching is ",
134 if ((req->
term ==
nullptr) || (req->
term[0] ==
'\0')) {
142 "mdl: search term is too long to encode\n",
157 " placeholder (or is too long)\n");
192 const char* host =
mdl_url_host(url, hostbuf,
sizeof(hostbuf)) ? hostbuf :
nullptr;
209 *out_status = resp.
status;
263 for (
size_t i = 0U; i < hits->
count; ++i) {
293 :
"(none configured)";
301 " link(s) but none matched the result pattern '");
305 "' (a genuine no-match, or the result markup changed)\n");
316 " link(s) but none matched the result pattern '");
341 "mdl: could not read a results page from ",
343 " -- the response carried no links, so its markup may have changed or the "
344 "request was blocked\n");
371 const int n = snprintf(out_url, out_cap,
"%s", chosen->
url);
372 if ((n < 0) || ((
size_t)n >= out_cap)) {
375 "mdl: selected URL is too long to hand off\n",
428 " (add --pick N to download ",
430 " N, e.g. --pick 1 --format cbz)\n");
444 if ((out_url ==
nullptr) || (out_cap == 0U)) {
447 "mdl: --pick needs an output buffer to hand off the selection\n",
460 if (pick > hits->
count) {
473 if ((out_url !=
nullptr) && (out_cap > 0U)) {
476 if ((req ==
nullptr) || (req->
session ==
nullptr) || (req->
gov ==
nullptr) ||
477 (req->
site ==
nullptr) || (req->
page_buf ==
nullptr) || (req->
hits ==
nullptr) ||
479 if ((req !=
nullptr) && (req->
diagnostic !=
nullptr)) {
482 "mdl: discovery called with missing dependencies\n");
501 " request failed -- ");
512 " results exceeded the bounded hit table\n");
static void internal_print_header(const mdl_discover_req_t *req, size_t n)
Print the numbered results header for the run's mode.
static void internal_print_markup_changed(const mdl_discover_req_t *req)
The honest "the markup changed" message, distinct from no results.
static const char * internal_mode_word(mdl_discover_mode_t m)
The human word for a discovery mode, for diagnostics.
static ra8_err_t internal_discover_fetch(const mdl_discover_req_t *req, const char *url, size_t *out_len, long *out_status)
Robots-gate and governed-fetch the results page into req->page_buf.
int mdl_discover_run(const mdl_discover_req_t *req, size_t pick, char *out_url, size_t out_cap)
Run one search/browse discovery: fetch, parse, present, and select.
static void internal_print_hits(const mdl_discover_req_t *req)
Print every hit as [N] title over its series URL.
static int internal_present_and_pick(const mdl_discover_req_t *req, mdl_search_outcome_t outcome, size_t pick, char *out_url, size_t out_cap)
Present the outcome and, when pick != 0, resolve the selection.
static void internal_print_zero(const mdl_discover_req_t *req)
The honest "no results" message, distinct from a broken page.
static void internal_discover_text3(const mdl_discover_req_t *req, ra8_io_stream_t *stream, const char *first, const char *second, const char *third)
Append three discovery fragments and latch the first sink failure.
mdl_discover_size_t
Local buffer sizes and the bounded governed-retry budget.
@ k_disc_url_max
Composed results-URL buffer bytes.
@ k_disc_max_attempts
1 initial + up to 3 governed retries.
@ k_disc_term_max
Percent-encoded term buffer bytes.
static uint32_t internal_max_u32(uint32_t a, uint32_t b)
Larger of two unsigned values.
static bool internal_discover_url(const mdl_discover_req_t *req, char *out, size_t cap, int *rc)
Build the results URL for this run; false + message on a config gap.
static bool internal_select_hit(const mdl_discover_req_t *req, const mdl_hit_t *chosen, size_t pick, char *out_url, size_t out_cap)
Copy the picked hit's URL out and announce it; false + message on overrun.
Search / browse discovery: governed fetch of a results page, parse, honest presentation,...
mdl_discover_mode_t
Which discovery endpoint of a site descriptor to query.
@ k_mdl_discover_browse
browse_url latest-updates page, no term.
@ k_mdl_discover_search
search_url with a {q} term substitution.
bool priv_mdl_fetch_is_retryable(ra8_err_t rc)
Whether a failed transfer result is worth retrying.
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.
Module-private fetch-loop decisions promoted for host unit tests and the CLI's end-of-run reporting.
@ k_mdl_reason_max
Failure-reason string buffer bytes.
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).
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.
Pure search/discovery policy: query encoding, URL templating, and the honest zero-vs-broken result cl...
const char * mdl_search_placeholder(void)
The {q} placeholder a search-URL template must contain.
mdl_search_outcome_t
The honest outcome of a parsed search/browse results page.
@ k_mdl_search_markup_changed
No links at all: the page could not be read.
@ k_mdl_search_have_results
One or more hits matched; print them.
@ k_mdl_search_zero_results
Links present, none matched: a real no-hit.
size_t mdl_search_filter_series_hits(mdl_hit_list_t *hits, const char *chapter_marker)
Remove chapter links and duplicate series URLs from discovery hits.
bool mdl_query_encode(const char *term, char *out, size_t cap)
Percent-encode a raw query term for safe inclusion in a URL.
mdl_search_outcome_t mdl_search_classify(const mdl_hit_list_t *hits)
Classify a parsed results page into an honest search outcome.
bool mdl_search_build_url(const char *tmpl, const char *encoded_term, char *out, size_t cap)
Expand a query-URL template by substituting the encoded term for {q}.
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.
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.
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.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_would_block
Non-blocking operation would have blocked.
@ k_ra8_fail
Generic unspecified failure.
@ 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.
Injected dependencies and inputs for one discovery run.
mdl_hit_list_t * hits
Receives the parsed hits (borrowed).
ra8_io_stream_t * output
Borrowed normal-output byte sink.
mdl_session_t * session
Initialised session (net + UA + robots).
const char * term
Raw search term; ignored/NULL for browse.
ra8_err_t * io_error
Caller-owned first sink failure latch.
mdl_governor_t * gov
Initialised per-host politeness governor.
const mdl_site_t * site
Descriptor holding the discovery keys.
uint32_t timeout_ms
Per-request time budget, milliseconds.
mdl_discover_mode_t mode
Search or browse.
size_t page_cap
Capacity of page_buf in bytes.
ra8_io_stream_t * diagnostic
Borrowed diagnostic byte sink.
char * page_buf
Results-page fetch scratch (borrowed).
Bounded list of titled hits plus the raw anchor tally.
mdl_hit_t hits[k_mdl_max_hits]
Deduplicated titled hits.
size_t count
Number of valid hits.
size_t anchors_seen
Total resolvable <a href> anchors.
One discovery hit: a human-facing title paired with a series URL.
char title[k_mdl_hit_title_max]
Display title, NUL-terminated.
char url[k_mdl_url_max]
Absolute series URL, NUL-terminated.
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.
mdl_net_iface_t * net
Backend used for robots.txt fetches.
const char * user_agent
Full UA header (caller-owned).
uint32_t chapter_delay_min
Inter-chapter spacing floor.
char browse_url[k_mdl_search_url_max]
Latest-updates page (no {q}).
char search_url[k_mdl_search_url_max]
Query template with a {q} slot.
uint32_t chapter_delay_max
Inter-chapter spacing ceiling.
char host[k_mdl_host_max]
Site host, e.g.
char chapter_url_contains[k_mdl_match_max]
href must contain this.
char search_result_contains[k_mdl_match_max]
Series-link substring on results.
Caller-allocated byte-stream handle binding a sink to its context.