ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_app_series.c
Go to the documentation of this file.
1
10#include "mdl_app_internal.h"
12
15
18
37 const char* first,
38 const char* second,
39 const char* third)
40{
41 ra8_err_t error = priv_mdl_stream_text(k_ra8_ok, stream, first);
42 error = priv_mdl_stream_text(error, stream, second);
43 return priv_mdl_stream_text(error, stream, third);
44}
45
62RA8_INTERNAL static bool internal_list_range(const mdl_url_list_t* l, double* lo, double* hi)
63{
64 *lo = 0.0;
65 *hi = 0.0;
66 if (l->count == 0U) {
67 return false;
68 }
69 if (!mdl_urlname_chapter_parse(l->urls[0], lo)) {
70 return false;
71 }
72 *hi = *lo;
73 for (size_t i = 1U; i < l->count; ++i) {
74 double num = 0.0;
75 if (!mdl_urlname_chapter_parse(l->urls[i], &num)) {
76 return false;
77 }
78 *lo = (num < *lo) ? num : *lo;
79 *hi = (num > *hi) ? num : *hi;
80 }
81 return true;
82}
83
100RA8_INTERNAL static void
101internal_select_window(bool from_present, double from_num, size_t count, mdl_url_list_t* out)
102{
103 size_t start = 0U;
104 if (from_present) {
105 while (start < priv_mdl_app_context()->chapters.count) {
106 double value = 0.0;
107 if (mdl_urlname_chapter_parse(priv_mdl_app_context()->chapters.urls[start], &value) &&
108 (value >= from_num)) {
109 break;
110 }
111 ++start;
112 }
113 }
114 out->count = 0U;
115 for (size_t i = start; (i < priv_mdl_app_context()->chapters.count) && (out->count < count);
116 ++i) {
117 memcpy(out->urls[out->count], priv_mdl_app_context()->chapters.urls[i], k_mdl_url_max);
118 out->count += 1U;
119 }
120}
121
138RA8_PRIV bool priv_mdl_app_state_path_of(const char* abs_dir, char* out, size_t cap)
139{
140 const int n = snprintf(out, cap, "%s/.mdl_state", abs_dir);
141 if (n < 0) {
142 return false;
143 }
144 return (size_t)n < cap;
145}
146
168RA8_INTERNAL static bool internal_copy_metadata_text(char* dst, size_t cap, const char* src)
169{
170 if ((dst == nullptr) || (cap == 0U) || (src == nullptr)) {
171 return false;
172 }
173 const int n = snprintf(dst, cap, "%s", src);
174 if ((n < 0) || ((size_t)n >= cap)) {
175 dst[0] = '\0';
176 return false;
177 }
178 return true;
179}
180
206RA8_INTERNAL static bool internal_build_export_metadata(const char* abs_dir,
207 const mdl_chapter_rec_t* rec,
208 mdl_export_meta_t* meta)
209{
210 mdl_meta_init(meta);
212 sizeof(meta->series_title),
213 priv_mdl_app_context()->state.series_title) ||
215 sizeof(meta->summary),
216 priv_mdl_app_context()->state.summary) ||
218 sizeof(meta->writer),
219 priv_mdl_app_context()->state.writer) ||
221 sizeof(meta->artist),
222 priv_mdl_app_context()->state.artist) ||
224 sizeof(meta->source_url),
225 priv_mdl_app_context()->state.series_url) ||
227 sizeof(meta->language),
228 priv_mdl_app_context()->state.language)) {
229 return false;
230 }
231 meta->reading_direction =
234 if (priv_mdl_app_context()->state.cover_path[0] != '\0') {
235 if (!mdl_path_join(abs_dir,
236 priv_mdl_app_context()->state.cover_path,
237 meta->cover_path,
238 sizeof(meta->cover_path))) {
239 return false;
240 }
241 }
242 uint64_t identity = mdl_hash_str(priv_mdl_app_context()->state.series_url);
243 if (rec != nullptr) {
245 sizeof(meta->chapter_title),
246 rec->title)) {
247 return false;
248 }
249 meta->chapter_number = rec->number_known ? rec->number : 0.0;
250 identity = mdl_hash_bytes_seed(rec->source_url, strlen(rec->source_url), identity);
251 }
252 const int id_len =
253 snprintf(meta->identifier, sizeof(meta->identifier), "urn:media-dl:%016" PRIx64, identity);
254 return (id_len >= 0) && ((size_t)id_len < sizeof(meta->identifier));
255}
256
277 const char* abs_dir,
278 const mdl_url_list_t* sel,
279 int64_t run_start)
280{
281 size_t fails = 0U;
282 for (size_t i = 0U; i < sel->count; ++i) {
283 char id[k_mdl_chapter_id_max];
284 mdl_urlname_last_segment(sel->urls[i], id, sizeof(id));
286 if ((rec != nullptr) && rec->complete && (rec->fetched_at >= run_start)) {
288 if (!internal_build_export_metadata(abs_dir, rec, &meta)) {
290 " export metadata for ",
291 id,
292 " exceeded a bounded field\n");
293 fails += 1U;
294 } else {
295 fails += mdl_pack_one_meta(&priv_mdl_app_context()->storage,
296 format,
297 abs_dir,
298 id,
299 &meta,
300 &priv_mdl_app_context()->export_ws,
301 priv_mdl_app_context()->output,
302 priv_mdl_app_context()->diagnostic);
303 }
304 }
305 }
306 return fails;
307}
308
326 const mdl_fetch_stats_t* st)
327{
329 ra8_err_t error = internal_series_text3(output, "done into ", abs_dir, "/: ");
330 error = priv_mdl_stream_u64(error, output, st->chapters_completed);
331 error = priv_mdl_stream_text(error, output, " chapter(s) fetched, ");
332 error = priv_mdl_stream_u64(error, output, st->chapters_skipped);
333 error = priv_mdl_stream_text(error, output, " skipped, ");
334 error = priv_mdl_stream_u64(error, output, st->chapters_failed);
335 error = priv_mdl_stream_text(error, output, " failed; pages ");
336 error = priv_mdl_stream_u64(error, output, st->pages_fetched);
337 error = priv_mdl_stream_text(error, output, " new / ");
338 error = priv_mdl_stream_u64(error, output, st->pages_reused);
339 error = priv_mdl_stream_text(error, output, " reused / ");
340 error = priv_mdl_stream_u64(error, output, st->pages_failed);
341 return priv_mdl_stream_text(error, output, " failed\n");
342}
343
364 const mdl_url_list_t* sel,
365 char* combined_rel,
366 size_t cap,
367 const char* slug)
368{
369 combined_rel[0] = '\0';
370 if (r->update || !r->combine || (r->format == k_mdl_format_loose)) {
372 }
373 double lo = 0.0;
374 double hi = 0.0;
375 if (internal_list_range(sel, &lo, &hi)) {
376 (void)snprintf(combined_rel, cap, "%s-%.10g-%.10g", slug, lo, hi);
377 } else {
378 (void)snprintf(combined_rel, cap, "%s-%zu-chapters", slug, sel->count);
379 }
381}
382
404 const mdl_site_t* site,
405 const char* abs_dir,
406 const char* state_path,
407 mdl_governor_t* gov,
408 mdl_cache_t* cache)
409{
410 return (mdl_fetch_ctx_t){.session = &priv_mdl_app_context()->session,
411 .storage = &priv_mdl_app_context()->storage,
412 .state = &priv_mdl_app_context()->state,
413 .cache = cache,
414 .state_path = state_path,
415 .series_abs_dir = abs_dir,
416 .series_url = r->series_url,
417 .site = site,
418 .gov = gov,
419 .timeout_ms = r->timeout,
420 .page_buf = priv_mdl_app_context()->page,
421 .page_cap = sizeof(priv_mdl_app_context()->page),
422 .images = &priv_mdl_app_context()->images,
423 .update_only = r->update,
424 .refetch = r->opts->refetch,
425 .faillog = &s_faillog,
426 .diagnostic = priv_mdl_app_context()->diagnostic,
427 .progress_fn =
429 .progress_ctx = priv_mdl_app_context()->output,
430 .progress_error = k_ra8_ok};
431}
432
456 const char* abs_dir,
457 mdl_fetch_layout_t layout,
458 const char* combined_rel,
459 const mdl_url_list_t* sel,
460 const mdl_fetch_stats_t* stats,
461 int64_t run_start)
462{
463 if (r->format == k_mdl_format_loose) {
464 return 0U;
465 }
466 if (layout == k_mdl_layout_combined) {
468 if (!internal_build_export_metadata(abs_dir, nullptr, &meta)) {
470 priv_mdl_app_context()->diagnostic,
471 " combined export metadata exceeded a bounded field\n");
472 return 1U;
473 }
475 r->format,
477 abs_dir,
478 combined_rel,
479 stats,
480 &meta,
481 &priv_mdl_app_context()->export_ws,
482 priv_mdl_app_context()->output,
483 priv_mdl_app_context()->diagnostic);
484 }
485 return internal_export_fresh_separate(r->format, abs_dir, sel, run_start);
486}
487
506 const char* failure)
507{
508 bool published = false;
509 const ra8_err_t error = mdl_state_save(&priv_mdl_app_context()->storage,
510 state_path,
511 &priv_mdl_app_context()->state,
512 &published);
513 if (error != k_ra8_ok) {
514 (void)internal_series_text3(priv_mdl_app_context()->diagnostic, failure, state_path, "'\n");
515 }
516 return error;
517}
518
531{
533 priv_mdl_app_context()->diagnostic,
534 "mdl: series cover acquisition failed "
535 "(err 0x");
536 output_error =
537 priv_mdl_stream_hex(output_error, priv_mdl_app_context()->diagnostic, (uint32_t)error);
538 (void)priv_mdl_stream_text(output_error, priv_mdl_app_context()->diagnostic, ")\n");
539}
540
559 const mdl_url_list_t** out_sel)
560{
561 *out_sel = &priv_mdl_app_context()->chapters;
562 if (!r->update) {
564 *out_sel = &s_selected;
565 if ((*out_sel)->count == 0U) {
567 priv_mdl_app_context()->diagnostic,
568 "mdl: no chapters match the requested range\n");
570 }
571 }
572 return k_ra8_ok;
573}
574
595 const mdl_site_t* site,
596 const char* abs_dir,
597 const char* state_path,
598 mdl_cache_t* cache)
599{
600 const mdl_url_list_t* sel = nullptr;
601 if (internal_select_run_window(r, &sel) != k_ra8_ok) {
602 return 1;
603 }
604 char display_slug[k_slug_bytes];
605 (void)mdl_sanitize_segment(priv_mdl_app_context()->state.series_title,
606 display_slug,
607 sizeof(display_slug));
608 char combined_rel[k_leaf_name_bytes];
609 mdl_fetch_layout_t layout =
610 internal_choose_layout(r, sel, combined_rel, sizeof(combined_rel), display_slug);
611
612 mdl_governor_t gov;
613 const mdl_gov_cfg_t cfg = mdl_config_gov_cfg(site);
614 mdl_governor_init(&gov, &cfg, r->seed);
615 memset(&s_faillog, 0, sizeof(s_faillog));
616 mdl_fetch_ctx_t ctx = internal_make_ctx(r, site, abs_dir, state_path, &gov, cache);
617 const ra8_err_t crc = priv_mdl_app_ensure_series_cover(&ctx, abs_dir);
618 if (crc != k_ra8_ok) {
620 return 1;
621 }
622 if (internal_save_series_state(state_path, "mdl: failed to checkpoint series metadata '") !=
623 k_ra8_ok) {
624 return 1;
625 }
626 const int64_t run_start = (int64_t)time(nullptr);
627 mdl_fetch_stats_t stats;
628 const ra8_err_t frc = mdl_fetch_run(&ctx,
629 sel,
630 layout,
631 (layout == k_mdl_layout_combined) ? combined_rel : nullptr,
632 &stats);
633 if (ctx.progress_error != k_ra8_ok) {
634 return 1;
635 }
636 const ra8_err_t src =
637 internal_save_series_state(state_path, "mdl: failed to save library state '");
638 if (internal_report_stats(abs_dir, &stats) != k_ra8_ok) {
639 return 1;
640 }
642 return 1;
643 }
644 const size_t efail =
645 internal_export_after(r, abs_dir, layout, combined_rel, sel, &stats, run_start);
646 return ((frc == k_ra8_ok) && (src == k_ra8_ok) && (efail == 0U)) ? 0 : 1;
647}
648
657
672{
674 (void)snprintf(prior->title, sizeof(prior->title), "%s", app->state.series_title);
675 (void)snprintf(prior->summary, sizeof(prior->summary), "%s", app->state.summary);
676 (void)snprintf(prior->writer, sizeof(prior->writer), "%s", app->state.writer);
677 (void)snprintf(prior->artist, sizeof(prior->artist), "%s", app->state.artist);
678 (void)snprintf(prior->cover_path,
679 sizeof(prior->cover_path),
680 "%s",
682 ? app->state.cover_path
683 : "");
684}
685
705 const mdl_site_t* site,
706 const series_prior_metadata_t* prior)
707{
709 const char* title = app->series_metadata.title;
710 if (!app->series_metadata.title_selected && (prior->title[0] != '\0')) {
711 title = prior->title;
713 priv_mdl_app_context()->diagnostic,
714 "mdl: WARNING: preserving recorded series title after "
715 "selector failure\n") != k_ra8_ok) {
716 return false;
717 }
718 }
719 const char* summary =
720 (app->series_metadata.summary[0] != '\0') ? app->series_metadata.summary : prior->summary;
721 const char* writer =
722 (app->series_metadata.writer[0] != '\0') ? app->series_metadata.writer : prior->writer;
723 const char* artist =
724 (app->series_metadata.artist[0] != '\0') ? app->series_metadata.artist : prior->artist;
725 mdl_state_set_series(&app->state, run->series_url, title, site->name, site->host, run->cfg_path);
727 summary,
728 writer,
729 artist,
731 prior->cover_path,
734}
735
754RA8_INTERNAL static int
756{
757 char slug[k_slug_bytes];
758 char abs_dir[k_fw_fs_path_cap];
759 char state_path[k_fw_fs_path_cap];
760 if (!priv_mdl_app_prepare_series_dir(run->out_dir,
761 run->series_url,
762 slug,
763 sizeof(slug),
764 abs_dir) ||
765 !priv_mdl_app_state_path_of(abs_dir, state_path, sizeof(state_path))) {
766 return 1;
767 }
768 const ra8_err_t load_rc =
769 mdl_state_load(&priv_mdl_app_context()->storage, state_path, &priv_mdl_app_context()->state);
770 if (load_rc != k_ra8_ok) {
771 ra8_err_t output_error = internal_series_text3(priv_mdl_app_context()->diagnostic,
772 "mdl: refusing to overwrite unreadable "
773 "state '",
774 state_path,
775 "' (err 0x");
776 output_error =
777 priv_mdl_stream_hex(output_error, priv_mdl_app_context()->diagnostic, (uint32_t)load_rc);
778 (void)priv_mdl_stream_text(output_error, priv_mdl_app_context()->diagnostic, ")\n");
779 return 1;
780 }
783 if (!internal_reconcile_series_state(run, site, &prior)) {
785 priv_mdl_app_context()->diagnostic,
786 "mdl: scraped series metadata exceeded state bounds\n");
787 return 1;
788 }
789 return internal_run_prepared(run, site, abs_dir, state_path, cache);
790}
791
810{
811 mdl_net_iface_t net = {};
812 if (mdl_net_provider_open(run->opts->net, &run->opts->policy, &net) != k_ra8_ok) {
814 priv_mdl_app_context()->diagnostic,
815 "mdl: network init failed\n");
816 return 1;
817 }
818 char ua[k_mdl_ua_max];
819 if (priv_mdl_app_start_session(&net, run->opts, site->contact, ua, sizeof(ua)) != k_ra8_ok) {
820 mdl_net_destroy(&net);
821 return 1;
822 }
823
824 mdl_cache_t cache = {.storage = &priv_mdl_app_context()->storage,
826 .root = run->cache_dir,
827 .diagnostic = priv_mdl_app_context()->diagnostic,
828 .refetch = run->opts->refetch};
829 int rc = 1;
830 if (priv_mdl_app_prepare_chapters(site, run->series_url, run->timeout, &cache) != k_ra8_ok) {
832 priv_mdl_app_context()->diagnostic,
833 "mdl: series fetch failed\n");
834 } else if (priv_mdl_app_context()->chapters.count == 0U) {
836 priv_mdl_app_context()->diagnostic,
837 "mdl: no chapters (check chapter_url_contains)\n");
838 } else {
839 ra8_err_t output_error =
840 priv_mdl_stream_text(k_ra8_ok, priv_mdl_app_context()->output, "chapters found: ");
841 output_error = priv_mdl_stream_u64(output_error,
842 priv_mdl_app_context()->output,
843 priv_mdl_app_context()->chapters.count);
844 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, "\n");
845 if (output_error == k_ra8_ok) {
846 rc = internal_run_series_paths(run, site, &cache);
847 }
848 }
849 mdl_net_destroy(&net);
850 return rc;
851}
852
854{
855 if ((r == nullptr) || (r->cache_dir == nullptr) ||
857 k_ra8_ok)) {
858 return 1;
859 }
860 mdl_site_t site;
861 if (mdl_config_load(&priv_mdl_app_context()->storage, r->cfg_path, &site) != k_ra8_ok) {
862 return 1;
863 }
864 if (r->opts->polite) {
866 }
867 ra8_err_t output_error =
868 internal_series_text3(priv_mdl_app_context()->output, "site: ", site.name, " (host ");
869 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, site.host);
870 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, ", kind ");
871 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, site.kind);
872 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, ")\n");
873 if (output_error != k_ra8_ok) {
874 return 1;
875 }
876
877 return internal_run_series_network(r, &site);
878}
@ 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.
Definition mdl_app.c:33
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.
Definition mdl_config.c:557
mdl_gov_cfg_t mdl_config_gov_cfg(const mdl_site_t *site)
Build the politeness-governor tunables for a loaded site descriptor.
Definition mdl_config.c:594
void mdl_config_apply_polite(mdl_site_t *site)
Raise a descriptor's per-request delay floors for --polite.
Definition mdl_config.c:625
@ k_mdl_read_rtl
Right-to-left page progression.
Definition mdl_export.h:89
@ k_mdl_read_ltr
Left-to-right page progression.
Definition mdl_export.h:88
void mdl_meta_init(mdl_export_meta_t *meta)
Initialise a metadata struct to empty/default values.
@ k_mdl_url_max
Max bytes per URL, including the NUL.
Definition mdl_extract.h:25
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.
Definition mdl_fetch.c:947
mdl_fetch_layout_t
Output directory layout the orchestrator writes.
Definition mdl_fetch.h:51
@ k_mdl_layout_combined
All chapters -> one dir, continuous numbering.
Definition mdl_fetch.h:52
@ k_mdl_layout_separate
Each chapter -> own dir, per-chapter numbering.
Definition mdl_fetch.h:53
Portable single-transaction response-body sink for media fetches.
mdl_format_t
Artifact format selected by a media-download composition root.
Definition mdl_format.h:35
@ k_mdl_format_loose
Leave loose page images.
Definition mdl_format.h:36
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.
Definition mdl_hash.c:21
uint64_t mdl_hash_str(const char *s)
FNV-1a 64 hash of a NUL-terminated string (excluding the NUL).
Definition mdl_hash.c:40
void mdl_net_destroy(mdl_net_iface_t *net)
Deinitialise a caller-owned network interface.
Definition mdl_net.c:120
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.
Definition mdl_net.c:64
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.
Definition mdl_pack.c:386
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.
Definition mdl_pack.c:182
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.
Definition mdl_report.c:119
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.
Definition mdl_report.c:169
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.
Definition mdl_report.c:214
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.
Definition mdl_session.h:27
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.
Definition mdl_state.c:186
mdl_chapter_rec_t * mdl_state_find_chapter(mdl_state_t *st, const char *id)
Find a chapter record by its stable identifier.
Definition mdl_state.c:243
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.
Definition mdl_state.h:110
@ k_mdl_person_max
Writer/artist bytes.
Definition mdl_state.h:100
@ k_mdl_summary_max
Series summary bytes.
Definition mdl_state.h:99
@ k_mdl_chapter_id_max
Chapter identifier bytes (sanitised).
Definition mdl_state.h:97
@ k_mdl_title_max
Series title bytes.
Definition mdl_state.h:98
@ k_mdl_relpath_max
Page path relative to the series dir.
Definition mdl_state.h:102
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.
Definition mdl_state.c:226
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.
Definition mdl_stream.c:26
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.
Definition mdl_stream.c:16
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.
Definition mdl_stream.c:21
void mdl_urlname_last_segment(const char *url, char *out, size_t cap)
Sanitised last non-empty path segment of a URL.
Definition mdl_urlname.c:75
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.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
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.
Definition mdl_app.h:162
mdl_series_metadata_t series_metadata
Current scraped metadata.
Definition mdl_app.h:179
mdl_session_t session
Network policy session.
Definition mdl_app.h:163
ra8_io_stream_t * output
Borrowed command output sink.
Definition mdl_app.h:166
mdl_url_list_t images
Prepared page image URLs.
Definition mdl_app.h:171
mdl_cache_index_t cache_index
One loaded per-host index.
Definition mdl_app.h:173
mdl_storage_t storage
Injected filesystem facade.
Definition mdl_app.h:165
ra8_io_stream_t * diagnostic
Borrowed diagnostic sink.
Definition mdl_app.h:167
mdl_url_list_t chapters
Prepared chapter URLs.
Definition mdl_app.h:170
char page[k_page_buf_bytes]
Whole-page HTML buffer.
Definition mdl_app.h:169
mdl_state_t state
Persistent tracked state.
Definition mdl_app.h:172
One non-reentrant cache binding over caller storage.
Definition mdl_cache.h:92
One chapter's coverage in library state.
Definition mdl_state.h:128
double number
Parsed number; 0 may be valid.
Definition mdl_state.h:132
int64_t fetched_at
Completion time (epoch s).
Definition mdl_state.h:137
char source_url[k_mdl_url_max]
Chapter page URL.
Definition mdl_state.h:130
bool number_known
Whether number is known.
Definition mdl_state.h:133
char title[k_mdl_title_max]
Display title, or empty.
Definition mdl_state.h:131
bool complete
All pages present + verified.
Definition mdl_state.h:136
Rich series and chapter metadata for export containers (ComicInfo.xml, EPUB OPF).
Definition mdl_export.h:96
char chapter_title[k_mdl_meta_title_max]
Chapter title.
Definition mdl_export.h:101
mdl_reading_direction_t reading_direction
Fixed-layout page progression.
Definition mdl_export.h:107
double chapter_number
Chapter number (0.0 if unnumbered).
Definition mdl_export.h:102
char identifier[k_mdl_meta_id_max]
Stable identifier; derived when empty.
Definition mdl_export.h:108
char language[k_mdl_meta_lang_max]
BCP-47 language tag (default "en").
Definition mdl_export.h:106
char cover_path[k_mdl_meta_path_max]
Trusted verified local cover path/name.
Definition mdl_export.h:104
char writer[k_mdl_meta_name_max]
Writer / Author.
Definition mdl_export.h:99
char artist[k_mdl_meta_name_max]
Artist / Illustrator.
Definition mdl_export.h:100
char source_url[k_mdl_meta_url_max]
Validated absolute HTTP(S) source URL.
Definition mdl_export.h:103
char summary[k_mdl_meta_summary_max]
Summary / description.
Definition mdl_export.h:98
char series_title[k_mdl_meta_title_max]
Series title.
Definition mdl_export.h:97
Everything mdl_fetch_run needs, injected for testability.
Definition mdl_fetch.h:164
ra8_err_t progress_error
First callback failure from this run.
Definition mdl_fetch.h:184
Bounded record of every failure in a run (declare at file scope).
Definition mdl_fetch.h:108
Tallies a fetch run reports back (all zero-initialised by the run).
Definition mdl_fetch.h:65
size_t chapters_completed
Chapters finished (all pages present) this run.
Definition mdl_fetch.h:66
size_t chapters_skipped
Chapters skipped as already complete (–update).
Definition mdl_fetch.h:67
size_t pages_fetched
Pages transferred over the network.
Definition mdl_fetch.h:69
size_t pages_reused
Pages served from an already-held verified file.
Definition mdl_fetch.h:70
size_t chapters_failed
Chapters left partial by a page failure.
Definition mdl_fetch.h:68
size_t pages_failed
Page fetches that failed or robots refused.
Definition mdl_fetch.h:71
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.
Definition mdl_app.h:78
bool progress
True to draw a progress bar.
Definition mdl_app.h:80
bool refetch
True to bypass verified page reuse.
Definition mdl_app.h:81
bool allow_incomplete
True to package a run with failures.
Definition mdl_app.h:79
char language[k_mdl_language_max]
BCP-47 language tag.
Definition mdl_app.h:99
char title[k_mdl_title_max]
Display title.
Definition mdl_app.h:94
char artist[k_mdl_person_max]
Artist/illustrator.
Definition mdl_app.h:97
mdl_state_reading_direction_t direction
Page progression.
Definition mdl_app.h:100
char cover_url[k_mdl_url_max]
Resolved remote cover.
Definition mdl_app.h:98
bool title_selected
Title selector succeeded.
Definition mdl_app.h:101
char summary[k_mdl_summary_max]
Synopsis/description.
Definition mdl_app.h:95
char writer[k_mdl_person_max]
Writer/author.
Definition mdl_app.h:96
Parameters of one series download, as values rather than arguments.
Definition mdl_app.h:114
bool combine
Combine into one archive.
Definition mdl_app.h:120
uint64_t seed
Politeness jitter seed.
Definition mdl_app.h:125
bool update
Incremental (skip complete).
Definition mdl_app.h:121
const char * out_dir
Output library root.
Definition mdl_app.h:117
mdl_format_t format
Output container.
Definition mdl_app.h:119
double from_num
First chapter number to fetch.
Definition mdl_app.h:123
bool from_present
Whether from_num applies.
Definition mdl_app.h:122
const char * cfg_path
Site descriptor path.
Definition mdl_app.h:115
uint32_t timeout
Per-request budget, ms.
Definition mdl_app.h:126
size_t chapters
Max chapters (window mode).
Definition mdl_app.h:124
const char * series_url
Series page URL.
Definition mdl_app.h:116
const char * cache_dir
Per-host persistent cache root.
Definition mdl_app.h:118
const mdl_run_opts_t * opts
Identity/security knobs.
Definition mdl_app.h:127
One site's scraping rules (all data, no logic).
Definition mdl_config.h:46
char kind[k_mdl_kind_max]
Content kind hint (reader mode).
Definition mdl_config.h:49
char name[k_mdl_name_max]
Human-readable site name.
Definition mdl_config.h:47
char host[k_mdl_host_max]
Site host, e.g.
Definition mdl_config.h:48
char contact[k_mdl_contact_max]
Operator contact for the UA.
Definition mdl_config.h:50
char summary[k_mdl_summary_max]
Series synopsis.
Definition mdl_state.h:182
char cover_path[k_mdl_relpath_max]
Local cover path.
Definition mdl_state.h:186
char series_title[k_mdl_title_max]
Series title.
Definition mdl_state.h:178
mdl_state_reading_direction_t reading_direction
Page progression.
Definition mdl_state.h:189
char artist[k_mdl_person_max]
Artist/illustrator.
Definition mdl_state.h:184
char cover_url[k_mdl_url_max]
Remote cover URL.
Definition mdl_state.h:185
char writer[k_mdl_person_max]
Writer/author.
Definition mdl_state.h:183
Bounded list of absolute URLs found on a page.
Definition mdl_extract.h:29
char urls[k_mdl_max_urls][k_mdl_url_max]
Absolute, NUL-terminated.
Definition mdl_extract.h:30
size_t count
Number of valid entries.
Definition mdl_extract.h:31
Caller-allocated byte-stream handle binding a sink to its context.
Persisted descriptive fields retained across selector misses.
char cover_path[k_mdl_relpath_max]
Reusable verified cover leaf.
char writer[k_mdl_person_max]
Previously recorded writer.
char artist[k_mdl_person_max]
Previously recorded artist.
char title[k_mdl_title_max]
Previously recorded title.
char summary[k_mdl_summary_max]
Previously recorded synopsis.