Skip to content

Module API

Domekologe edited this page Jul 28, 2026 · 3 revisions

Module API

🌐 English · Deutsch

Everything a module can register with MediaForge, in one place. The full reference — with a runnable example module per entry point — lives in .examples/thirdparties/README.md in the repository; this page is the map.

A module is a folder under web/thirdparties/<name>/ with a register(app) function. Everything below is called from there.

Registration functions

Function Module What it adds
register_thirdparty web.thirdparties.registry The module itself: menu entry, settings card, dashboard widget, extra_settings fields, enable toggle
register_provider providers A content source (site) with its own series/season/episode classes
register_search_source search An additional source for the global search
register_hoster extractors A hoster/extractor that resolves an embed URL to a stream
register_site_mirrors mirrors Mirror domains for a site, with automatic failover
register_cineinfo_source web.cineinfo.registry A metadata source next to TMDB
register_monitor_site web.uptime_monitor A card on the UpTime dashboard, probed like the built-in sites
register_notification_channel web.thirdparties.registry A notification channel next to Telegram/Pushover/Discord/ntfy
register_event_hook web.thirdparties.registry A callback on app events (download finished, queue item failed, …)
register_background_worker web.thirdparties.registry A worker thread started and stopped with the module
register_backup_category web.backup Own data in the Full/Selective backup
register_sensitive_keys web.db Settings keys that are encrypted at rest
register_ui_pref_key web.db A per-account UI preference

register_restart_handler (web.restart) is not part of this API — it is core-internal and only called by web/app.py.

Secrets

A "secret" field, a key in MODULE_SENSITIVE_SETTINGS and anything passed to register_sensitive_keys() is encrypted in the database, and the generic settings API returns a mask (registry.SECRET_MASK) instead of the value — a PUT carrying the mask back means "unchanged". This holds for every key MediaForge knows to be sensitive, not just for fields declared as "secret", so a secret rendered as a plain text field is masked too. Inside the module nothing changes: get_setting() decrypts as always. If you render such a value on a page of your own, do the same — never put a stored secret into the HTML.

Frontend building blocks

Reuse the existing design vocabulary rather than shipping your own CSS:

  • Form controls (forms.css): .chb-main for checkboxes, .toggle for on/off switches, .mf-segmented, .mf-multiselect, .mf-chip.
  • Layout and content (mf_components.css, loaded globally): .mf-search, .mf-toolbar, .mf-poster-grid, .mf-timeline, .mf-progress, .mf-empty, .mf-pagination-bar.
  • Colours (variables.css): never hardcode a hex value. Status pills use the pair --success + --success-bg (same for --warning, --error, --info); both halves are defined per theme.
  • Escaping (mf_escape.js, loaded on every page): window.mfEscape() for anything that goes into HTML — it is quote-safe, so it covers attributes too — and window.mfSafeUrl() for href/src. Do not write your own escaper.
  • Polling (mf_poll.js): window.mfPoll(fn, ms) instead of setInterval, so your timer pauses while the tab is hidden.

Translations

Module templates use {{ _('...') }} like the core does; a module brings its own catalogue under <module>/translations/<locale>/LC_MESSAGES/. After editing a .po you must run pybabel compile — without it the change has no effect at runtime.

Clone this wiki locally