Skip to content

Modules

chodeus edited this page Jun 18, 2026 · 26 revisions

🏠 Home › Modules

Modules

CHUB ships fifteen modules. Each is a scheduled chore you can also run on demand, with its own section in config.yml and its own page under Settings → Modules.

On this page: What every module supports · Module summary · then one section per module (see jump-list below).

Jump to a module:

  • 🖼️ poster_renamerr — rename posters to match your library
  • 🪧 asset_renamerr — apply logos, square art, and backgrounds
  • 🎨 border_replacerr — apply brand or holiday borders
  • 🎬 cl2k_maker — generate CL2K-style posters from TMDB/fanart art
  • 🧹 poster_cleanarr — sweep Plex Metadata bloat, orphan asset files, and stale-duplicate asset folders
  • 🏷️ labelarr — mirror ARR tags into Plex labels
  • 🔍 jduparr — find duplicate media files
  • 🔗 nohl — find non-hardlinked media
  • unmatched_assets — report media without posters
  • ⬆️ upgradinatorr — trigger ARR upgrade searches
  • ✏️ renameinatorr — apply ARR naming to existing files
  • 🩺 health_checkarr — surface ARR health problems
  • 🪺 nestarr — detect mismatches and nested paths
  • ☁️ sync_gdrive — pull poster assets from Google Drive
  • 🧼 plex_maintenance — run Plex-side cleanup (trash, bundles, DB, photo cache)

What every module supports

  • Dry rundry_run: true logs what the module would do without making changes. Use it the first time. (unmatched_assets and nestarr are report-only and have no dry_run flag.)
  • Log leveldebug / info / warning / error, per module. Default info.
  • Cancel from the UI — Settings → Jobs → click the running job → Cancel. Most modules stop on the next iteration. Exceptions: border_replacerr runs to completion; plex_maintenance is partial (see its section).
  • Run history — Settings → Jobs, with full log output.

Module summary

Poster & artwork

Module What it does Cancellable Report-only
poster_renamerr Match posters to your library and rename/apply them Yes No
asset_renamerr Apply logos, backgrounds, and square art Yes No
border_replacerr Apply brand or holiday borders No No
cl2k_maker Generate CL2K-style posters (develop-only) Yes No
poster_cleanarr Sweep Plex Metadata bloat, orphan asset files, and stale-duplicate asset folders Yes No (has report mode)
sync_gdrive Pull poster assets from Google Drive Yes No
unmatched_assets Report media missing a poster Yes Yes

Media library

Module What it does Cancellable Report-only
labelarr Mirror ARR tags into Plex labels Yes No
jduparr Find duplicate media files by hash Yes No
nohl Find non-hardlinked media Yes No (has scan mode)
upgradinatorr Trigger ARR upgrade searches Yes No
renameinatorr Apply ARR naming to existing files Yes No
health_checkarr Delete items removed from TMDB/TVDB Yes No (has dry_run)
nestarr Report ARR↔Plex mismatches and nested paths Yes Yes

Maintenance

Module What it does Cancellable Report-only
plex_maintenance Plex housekeeping (trash, bundles, DB, photo cache) Partial No (has dry_run)

🖼️ poster_renamerr

Matches poster files against your Radarr/Sonarr/Plex libraries, renames them, and either copies/moves/hardlinks them into a destination tree (Kometa) or uploads them straight to Plex.

Run modes: Cancellable: yes.

Setup

  1. Set apply_method: kometa (write renamed files to destination_dir) or plex (upload to Plex). It does one or the other, not both.
  2. List your poster source_dirs. They process top→bottom and later entries win — put your highest-priority source last.
  3. List instances. For the plex path, add add_posters: true on each Plex instance you want uploads pushed to.
  4. Optionally chain post-hooks: run_border_replacerr, run_asset_renamerr, clean_orphan_assets, report_unmatched_assets.

Gotcha: if nothing moves, check dry_run is off, destination_dir is writable by your PUID/PGID, and action_type: hardlink isn't crossing filesystems.

How matching works. CHUB matches each poster against the metadata your *arr already holds (title, year, TVDb/TMDb/IMDb id, season) — pulled live from the API, not parsed from your media folder names. ID match (TVDb → TMDb → IMDb) is most reliable and fires only when the poster filename also carries the id; otherwise it falls back to title+year+season parsed from the poster filename.

TL;DR — Poster matching reads your *arr's API, not your disk. Name your library however TRaSH recommends; to improve matching, focus on the poster set's filenames and id tags.

See Kometa Integration for end-to-end setup.

Config

poster_renamerr:
  dry_run: false
  log_level: info
  apply_method: kometa                  # kometa (write to destination_dir) | plex (upload to Plex); legacy "direct" = plex
  action_type: copy                     # copy | move | hardlink | symlink (kometa path)
  asset_folders: true                   # expect Kometa-style per-item folders (kometa path)
  sync_posters: false
  print_only_renames: false
  run_border_replacerr: false           # chain border_replacerr after rename
  run_asset_renamerr: false             # chain asset_renamerr (logos/art) after rename
  clean_orphan_assets: false            # chain orphan-asset cleanup (mode from poster_cleanarr.orphan_assets_mode)
  report_unmatched_assets: false        # chain unmatched_assets report
  upload_delay_ms: 0                    # optional pause (ms) after each Plex upload
  source_dirs:
    - /kometa
  destination_dir: /posters
  # Music / Lidarr art (only when a Lidarr instance is configured):
  music_source_dirs: []                 # dirs of custom music art (artist posters / album covers)
  music_lock_artist_art: false          # plex path: lock Plex thumb/art after an artist-poster upload
  music_lma_sidecars: false             # also write cover.jpg / artist-poster.jpg / background.jpg sidecars
  instances:
    - radarr_main
    - sonarr_main
    - plex_main:
        library_names: ["Movies", "TV Shows"]
        add_posters: true               # plex apply_method only: upload to this instance
Field Default Purpose
apply_method kometa kometa writes to destination_dir; plex uploads to Plex. Legacy direct = plex.
action_type copy copy / move / hardlink / symlink (kometa path).
music_source_dirs [] Custom artist/album art dirs (Lidarr only).
music_lock_artist_art false Lock Plex art after upload so the music agent can't re-derive it (Plex path).
music_lma_sidecars false Write image-only sidecars into Plex music folders for refresh-proof art.

🪧 asset_renamerr

Applies the non-poster artwork types — logo, square art, and background/fanart — to matched media, reusing poster_renamerr's matching engine.

Run modes: Cancellable: yes. Idempotent — unchanged assets (same file mtime / same fanart.tv URL) are skipped.

Setup

  1. Set sources (ordered, first hit wins): local (files from source_dirs) and/or fanart (live from fanart.tv).
  2. For fanart, add your personal fanart.tv API key under Settings → fanart.tv (free; cuts new-art delay 7→2 days). Without it, fanart is skipped.
  3. Set asset_types (default [logo, background] — the two that work on both apply paths).
  4. Set apply_method (kometa or plex) and action_type.
  5. Run it chained (set poster_renamerr.run_asset_renamerr: true — reuses that run's sync/scan/snapshot) or standalone (its own sync_assets + scan).

Gotcha: on the plex path, art uploads to every library the item lives in (e.g. both a 1080p and a 4K library). On the kometa path, asset_folders must match your Kometa config.

local file naming: same convention as posters — Title (Year) {tmdb-123}plus a type tag: - Logo.png, - Background.png, - SquareArt.png. The tag must be separated by - or _ (no delimiter = treated as a poster); case-insensitive. Banner is recognized but never applied.

Apply capability (which types work on each path):

apply_method logo background squareart
plex (upload via API; legacy direct = plex)
kometa (rename into destination_dir) logo.ext background.ext ❌ not read by Kometa

Banner is unsupported on both paths. On the kometa path, season art uses Kometa's Season##_logo.ext / Season##_background.ext naming.

Media-level support:

Level logo background squareart
Movie ✅ plex / ✅ kometa ✅ plex / ✅ kometa ✅ plex only
Show ✅ plex / ✅ kometa ✅ plex / ✅ kometa ✅ plex only
Season ❌ not expected ✅ plex / ✅ kometa (Season##_background) ✅ plex only
Collection ✅ plex / ✅ kometa ✅ plex / ✅ kometa ✅ plex only

Seasons never expect a logo — no provider publishes per-season logos, so CHUB doesn't attempt or count them.

Config

asset_renamerr:
  dry_run: false
  log_level: info
  print_only_renames: false             # true = only list applied assets in the log
  sources: [local, fanart]              # ordered; first hit wins (local | fanart)
  asset_types: [logo, background]       # + squareart (plex only); banner unsupported
  apply_method: kometa                  # kometa | plex (legacy "direct" = plex)
  action_type: copy                     # copy | move | hardlink | symlink (kometa path)
  asset_folders: false                  # match your Kometa asset_folders setting
  destination_dir: /posters             # kometa path: where renamed art lands
  source_dirs:
    - /kometa                           # local source (same convention as posters)
  music_source_dirs: []                 # music art source dirs (artist logos/backgrounds; Lidarr)
  sync_assets: false                    # run sync_gdrive first (standalone only)
  tmdb_language: [en]                   # preferred image languages; string or list (coerced to list)
  instances:
    - radarr_main
    - sonarr_main
    - plex_main:
        library_names: ["Movies", "TV Shows"]
        add_posters: true               # plex apply_method only: upload to this instance
Field Default Purpose
sources [local, fanart] Ordered; first source with an image wins.
asset_types [logo, background] Add squareart for the plex path only.
apply_method kometa kometa or plex; legacy direct = plex.
music_source_dirs [] Artist logo/background source dirs (Lidarr).
tmdb_language [en] Preferred fanart.tv image languages; single string or list.

🎨 border_replacerr

Re-applies a brand or holiday border to every matched poster.

Run modes: Cancellable: no — it runs to completion. Restart the container to stop a big run.

Two modes:

  • Color mode (default): crops border_width px off all four edges and re-paints a flat border from a cycling color in border_colors. Holiday windows substitute that holiday's colors.
  • Image mode: composites a 1000×1500 decorative PNG over the poster (transparent center shows the artwork). Activates when a holiday's borders: field is populated; border_width is ignored.

The crop step trusts the TPDB convention of a 26 px white border.

Gotcha (color mode): border_width must match the actual border on your source posters. The default 26 is right for MediUX/TPDB art; non-standard art loses 26 px of real content per edge. If two holidays overlap, the one listed first wins.

Where settings live: Module Settings → Border Replacerr holds border_width, exclusion_list, ignore_folders, log_level, dry_run, and the holiday list (name + schedule). The Border Replacerr page (/poster/border-replacerr) holds border_colors, per-holiday colors/borders, the thumbnail picker, and the live preview. The YAML below is the source of truth either way.

Bundled themed borders

CHUB ships 56 SVG borders across 13 holidays. Each variant is a complete decorative ring with a transparent center.

Holiday Variants Schedule (default preset)
🎆 New Year's Day v1v4 12/30 – 01/02
🧧 Lunar New Year v1v4 01/20 – 02/20
💘 Valentine's Day v1v5 02/05 – 02/15
🍀 St. Patrick's Day v1v4 03/14 – 03/18
🐣 Easter v1v4 03/31 – 04/02
🌸 Mother's Day v1v4 05/10 – 05/15
👨‍👧‍👦 Father's Day v1v4 06/15 – 06/20
🏳️‍🌈 Pride v1v5 06/01 – 06/30
🗽 Independence Day v1v4 07/01 – 07/05
🧹 Labor Day v1v4 09/01 – 09/07
🎃 Halloween v1v5 10/01 – 10/31
🦃 Thanksgiving v1v4 11/01 – 11/30
🎄 Christmas v1v5 12/01 – 12/31

Christmas v1 Halloween v1 Valentine's v1 Pride v1

Browse the full set in backend/assets/borders/.

Custom borders

Drop PNGs into /config/borders/<holiday-folder>/ to extend or override the bundled set. User files win over bundled files of the same name.

PNG requirements:

Requirement Value
Dimensions 1000 × 1500 (2:3)
Format PNG with alpha channel (RGBA)
Inner transparent area (60, 60) to (940, 1440) must be fully transparent (poster shows through)
Outer ring The 60 px band around all edges should be opaque — your decoration zone
Corner ornaments May extend ~120 px inward but not into the transparent center
Content No text, faces, or copyrighted IP — appears on every poster

Off-spec dimensions are resized to 1000×1500 on the fly (lossy). A file with no transparent center hides the artwork behind it.

Holiday folder slugs: newyear, lunarnewyear, valentines, stpatricks, easter, mothersday, fathersday, pride, independence, labor, halloween, thanksgiving, christmas. Custom holiday names get a slug auto-generated by the same rule (alphanumeric, lowercase, no spaces).

Add a custom border:

  1. Design and export a PNG at exactly 1000×1500 with the inner (60,60)(940,1440) area transparent. Start from a bundled SVG if you want a template.
  2. Save it to /config/borders/<holiday-folder>/<your-name>.png.
  3. On the Border Replacerr page, expand the holiday card and click the file in the Custom thumbnail row (or edit the borders: list in config.yml directly).
  4. Hit Save changes. The next run picks it up — no image rebuild needed.

borders: config notes: the list is the rotation order; entries are filenames without .png (added automatically — v1 and v1.png both work). Resolution is case-sensitive. A name that resolves to nothing is skipped with a log warning; the rest still cycle.

Config

border_replacerr:
  dry_run: false
  log_level: info
  source_dirs:
    - /posters
  destination_dir: /posters
  border_width: 26                      # integer 0–200; matches the TPDB white-border standard (color mode only)
  border_workers: null                  # advanced: re-encode thread pool; null = min(8, CPU cores)
  border_colors:                        # color-mode fallback when no holiday active
    - "#ff7300"
  ignore_folders: []                    # source folder names to skip
  exclusion_list: null                  # media titles to leave alone
  holidays:
    # Color-mode holiday — cycles through colors
    - name: 🎃 Halloween
      schedule: "range(10/01-10/31)"
      colors: ["#FF6600", "#000000"]

    # Image-mode holiday — borders win over colors when set
    - name: 🎄 Christmas
      schedule: "range(12/01-12/26)"
      colors: ["#C8102E", "#00843D"]    # fallback if a border PNG is missing
      borders: [v1, v2, v3, v4, v5]

    # Mixed example with a custom border
    - name: 💘 Valentine's Day
      schedule: "range(02/05-02/15)"
      colors: ["#D41F3A", "#FFC0CB"]
      borders:
        - v1
        - v2
        - custom-roses                  # /config/borders/valentines/custom-roses.png
Field Default Purpose
border_width 26 Pixels cropped per edge (color mode). Integer 0–200.
border_workers null Re-encode thread pool size; null = min(8, CPU cores).
border_colors ["#ff7300"] Color-mode fallback when no holiday is active.

🎬 cl2k_maker

Generates CL2K-style posters (contain-fit backdrop, black-fill bottom, template gradient, logo or typeset wordmark) from TMDB/fanart.tv art, saves them to output_dir, and records them in the poster cache for poster_renamerr to apply. Develop-only extension.

Run modes: Cancellable: yes (batch runs).

Setup

  1. Set enabled: true and an output_dir — add that dir to poster_renamerr.source_dirs.
  2. Tune logo_max_width (CL2K standard 600, max 800), whiten_logo, and text_logo_fallback.
  3. Use the Posters → CL2K Maker page for search, art picking, framing, preview, season batches, and PSD export.

Gotcha: scheduled batch runs cover movies and shows only. Collections and seasons are supported but only on demand from the page/API.

Config

cl2k_maker:
  log_level: info
  enabled: false
  output_dir: /posters/cl2k             # add to poster_renamerr.source_dirs
  language: en                          # preferred logo/backdrop language
  logo_max_width: 600                   # px; CL2K standard 600, max 800
  whiten_logo: true                     # recolor dark logos to white
  text_logo_fallback: true              # synth a typeset wordmark when no logo exists
  skip_existing: true                   # batch runs skip items that already have a CL2K poster
  style: CL2K                           # poster_cache style tag
  priority: 0
  # Optional Google Drive upload (rclone copy; uses the sync_gdrive OAuth token).
  upload_to_gdrive: false
  gdrive_folder_id: ""
  # Optional AI text removal for backdrops with baked-in titles.
  ai_provider: none                     # none | lama_sidecar | openai | huggingface
  ai_endpoint: ""                       # lama sidecar URL, or HF inference URL
  ai_api_key: ""                        # openai / huggingface token
  ai_model: ""                          # openai model id (default gpt-image-1) / HF model id
  ai_timeout: 120

The API lives under /api/cl2k-maker/* (search, resolve, images, season-images, fanart-images, external-ids, preview, generate, generate-seasons, generated, upload-generate, upload-poster, retext, psd-export, upload-status).


🧹 poster_cleanarr

Three independent cleanup operations in one module. All three run on every scheduled (or manual) Poster Cleanarr job.

Run modes: Cancellable: yes. All passes default to report.

  1. Bloat-image cleanup — sweeps Plex's Metadata/ folder for poster variants Plex no longer references. Driven by mode.
  2. Orphan-asset cleanup — walks asset_dirs and acts on poster files whose title matches no media in the configured instances. Comparison set comes from CHUB's cached media_cache/collections_cache (no live API calls). Driven by orphan_assets_enabled + orphan_assets_mode.
  3. Stale-duplicate cleanup — detects Kometa asset folders whose {tmdb-N}/{tvdb-N} id matches a live library item but whose folder name no longer matches the item's canonical folder (e.g. after a Sonarr/Radarr folder rename). The canonical folder is always kept; only the misnamed duplicate is reported/moved/removed. A folder with no matching live id is left to the orphan pass. Move/remove are skipped when the canonical destination is not yet present on disk (never deletes the only copy). Driven by stale_duplicates_enabled + stale_duplicates_mode. Uses the same instance comparison set as orphan cleanup (orphan_instances, falling back to instances).

Not the same as unmatched_assets. Unmatched Assets reports media missing a poster (media → asset). Orphan cleanup acts on posters missing a media (asset → media). Opposite directions.

Gotcha: plex_path must be a filesystem path (e.g. …/Plex Media Server/Metadata), not a URL.

Bloat-image modes (mode): report (lists), move (to a Poster Cleanarr Restore folder), remove (deletes), restore (moves restore items back), clear (deletes the restore folder), nothing (skips bloat so orphan/stale can still run). Promote reportmoveremove.

Orphan-asset modes (orphan_assets_mode): report (log only), move (to a hidden .chub_orphan_restore subdir — recoverable), remove (permanent). Leave on report for one run before promoting.

Stale-duplicate modes (stale_duplicates_mode): report (log only), move (to .chub_orphan_restore inside the asset dir — recoverable, same restore dir as orphan), remove (permanent). Leave on report for one run before promoting.

Overlays-only mode: with overlays_only: true, each bloat candidate is checked for Kometa's EXIF marker; files without it (your own uploads) are skipped. Small CPU cost; bloat pass only.

Config

poster_cleanarr:
  log_level: info
  mode: report                          # bloat: report | move | remove | restore | clear | nothing
  plex_path: "/plex-config/Library/Application Support/Plex Media Server/Metadata"
  local_db: false                       # clone Plex DB before scanning (safer on a running server)
  use_existing_db: false                # reuse the last cloned DB
  ignore_running: false                 # skip when Plex is active
  overlays_only: false                  # only sweep Kometa-tagged overlays; preserve custom uploads
  timeout: 600                          # seconds to wait for Plex tasks
  instances:
    - plex_main                         # Plex for bloat; add Radarr/Sonarr for orphan comparison
  # Orphan-asset cleanup (default off)
  orphan_assets_enabled: false
  orphan_assets_mode: report            # report | move | remove
  orphan_instances: []                  # comparison-set instances; empty = use `instances`
  orphan_ignore_titles: []              # media titles to exclude from orphan matching
  asset_dirs: []                        # walked recursively; .chub_orphan_restore is skipped
  include_collections: true             # treat Plex collection titles as part of the comparison set
  # Stale-duplicate cleanup (default off)
  stale_duplicates_enabled: false
  stale_duplicates_mode: report         # report | move | remove

🏷️ labelarr

Mirrors Radarr/Sonarr tags into Plex labels — tag an item favorite in Sonarr and it shows up with the favorite label in the mapped Plex library.

Run modes: Cancellable: yes.

Setup

  1. Add a mapping per ARR instance: app_instance, labels, and the target plex_instances + library_names.
  2. labels may be a YAML list or a comma-separated string.

Gotcha: label updates are batched — untagging many items reflects in Plex on the next run, not instantly.

Config

labelarr:
  dry_run: false
  log_level: info
  mappings:
    - app_instance: sonarr_main
      labels: [watched, favorite]       # list OR comma-separated string ("watched, favorite")
      plex_instances:
        - instance: plex_main
          library_names: ["TV Shows"]
    - app_instance: radarr_main
      labels: [favorite]
      plex_instances:
        - instance: plex_main
          library_names: ["Movies"]

🔍 jduparr

Finds duplicate files across your media tree by content hash, persisting hashes so repeat runs are incremental.

Run modes: Cancellable: yes.

Setup

  1. Point source_dirs at your media trees.
  2. Set hash_database to a writable path.

Gotcha: the first run on a large library takes hours; later runs are fast. hash_database can't contain null bytes or start with -, and its parent dir must exist (a safety check) — see Troubleshooting if you hit it.

Config

jduparr:
  dry_run: false
  log_level: info
  hash_database: /config/jduparr.db
  source_dirs:
    - /media/movies
    - /media/tv

🔗 nohl

Finds media files not hardlinked to your downloader's completed directory (usually a broken rename or a non-hardlinked re-import) and optionally re-queues an ARR upgrade search.

Run modes: Cancellable: yes. Per-path scan mode is report-only.

Setup

  1. List source_dirs, each with a mode: resolve (default — match to ARR and queue re-searches) or scan (report only, no ARR processing).
  2. Set searches (re-searches per run) and any exclude_* lists.
  3. List ARR instances.

Config

nohl:
  dry_run: false
  log_level: info
  searches: 10                          # re-searches to queue per run
  print_files: false                    # log the full list of non-hardlinked files
  source_dirs:
    - path: /media/movies
      mode: resolve                     # scan | resolve (default: resolve)
    - path: /media/tv
      mode: resolve
  exclude_profiles: []                  # ARR quality-profile names to skip
  exclude_movies: []                    # movie titles to skip
  exclude_series: []                    # series titles to skip
  instances:
    - radarr_main
    - sonarr_main

unmatched_assets

Reports media items that don't have a matching poster in your renamed tree. Report-only — never changes anything.

Run modes: Cancellable: yes. Report-only (no dry_run).

Setup

  1. List instances. String form includes the whole instance; dict form (with library_names) limits which Plex libraries count.
  2. Tune the ignore_* filters.
  3. Run standalone or chain it via poster_renamerr.report_unmatched_assets: true.

What gets flagged (all must hold): cache matched=0; status not in {announced, tba, upcoming, deleted}; content actually exists (Radarr hasFile; Sonarr episodeFileCount > 0); passes your ignore_* filters. ignore_unmonitored honors the per-season monitored flag.

Where to act: open Assets → Statistics (/poster/statistics). Each unmatched row has a Request button that copies a Discord-ready block (title + TMDb link; series add Missing main poster / Missing seasons: …, with a direct season link when exactly one is missing).

Config

unmatched_assets:
  log_level: info
  ignore_folders: []                    # folders to skip while scanning posters
  ignore_profiles: []                   # ARR quality profiles to ignore
  ignore_titles: []                     # media titles to ignore
  ignore_tags: []                       # ARR tags to ignore
  ignore_collections: []                # Plex collection names to ignore
  ignore_unmonitored: false             # skip unmonitored items (per-season aware)
  instances:
    - radarr_main
    - sonarr_main
    - plex_main:                        # dict form narrows to specific libraries
        library_names: ["Movies", "TV Shows"]

⬆️ upgradinatorr

Picks a fixed number of items per ARR instance that haven't been searched recently, fires an upgrade search, and tags them so they aren't re-picked right away. Lidarr fully supported.

Run modes: Cancellable: yes.

Setup

  1. Add each instance under instances_list with a count, tag_name, ignore_tag, and search_mode (upgrade | missing | cutoff).
  2. For Sonarr/Lidarr, choose count_mode (below).

count_mode (Sonarr + Lidarr only):

Mode What count caps Tagging
series_artist (default) Series / artists processed per run (all monitored children searched) Parent tagged after one pass
season_album Individual SeasonSearch / AlbumSearch calls per run Parent tagged only when all children searched (progress persisted, resumes across runs)

Switching modes is safe — leftover progress rows clear automatically when a parent next gets tagged.

Config

upgradinatorr:
  dry_run: false
  log_level: info
  instances_list:
    - instance: radarr_main
      count: 10                         # items to search per run
      tag_name: chub-upgradinatorr      # tag applied after search
      ignore_tag: ignore                # skip items carrying this tag
      unattended: false
      search_mode: upgrade              # upgrade | missing | cutoff
    - instance: sonarr_main
      count: 5                          # interpreted by count_mode
      count_mode: season_album          # series_artist (default) | season_album
      tag_name: chub-upgradinatorr
      ignore_tag: ignore
      season_monitored_threshold: 0.5   # Sonarr: require ≥ this fraction of monitored seasons
      search_mode: upgrade
    - instance: lidarr_main
      count: 5
      count_mode: season_album          # 5 album searches/run, resumes mid-artist
      tag_name: chub-upgradinatorr
      ignore_tag: ignore
      search_mode: upgrade

✏️ renameinatorr

Walks Radarr/Sonarr and applies the ARR's own naming scheme to existing files — useful after changing a naming template.

Run modes: Cancellable: yes.

Setup

  1. Set count (total items per run) — or use radarr_count/sonarr_count to override per type. count accepts an integer or an empty string "" meaning "all items".
  2. Set ignore_tags (comma-separated ARR tag names) to skip items.
  3. Set refresh_before_rename: true to trigger a metadata refresh in the ARR before reading the rename list (skipped on dry runs).

Config

renameinatorr:
  dry_run: false
  log_level: info
  rename_folders: true
  refresh_before_rename: false          # refresh ARR metadata before reading the rename list (skipped on dry run)
  count: 100                            # total items per run; integer OR "" = all items
  radarr_count: 0                       # override per type (used when count drives the rest)
  sonarr_count: 0
  tag_name: chub-renameinatorr
  ignore_tags: ""                       # comma-separated ARR tag names; items carrying any are skipped
  enable_batching: false                # batch API calls for speed
  instances:
    - radarr_main
    - sonarr_main
Field Default Purpose
refresh_before_rename false Refresh ARR metadata first so renamed-on-TVDB/TMDB items pick up new names. Skipped on dry runs.
count 100 Total items per run. Integer, or "" for all items.

🩺 health_checkarr

Checks each ARR's health feed for items removed from TMDB/TVDB (Radarr RemovedMovieCheck, Sonarr RemovedSeriesCheck) and deletes them from the ARR. Radarr and Sonarr only.

Run modes: Cancellable: yes. Use dry_run: true to preview deletions.

Config

health_checkarr:
  dry_run: false
  log_level: info
  instances:
    - radarr_main
    - sonarr_main

🪺 nestarr

Scans for two library problems and reports them — never moves or deletes. (1) Compares your ARR cache against Plex and flags mismatches (in ARR but not Plex, in Plex but tracked by no ARR). (2) Detects nested paths (tracked media inside another tracked item's folder, usually a botched import).

Run modes: Cancellable: yes. Report-only.

Setup

  1. Add library_mappings to scope ARR↔Plex library pairs.
  2. Add path_mapping (arr_path / local_path) when ARR and CHUB see media at different paths.

Config

nestarr:
  log_level: info
  library_mappings:                     # scope to specific ARR↔Plex library pairs
    - arr_instance: radarr_main
      plex_instances:
        - instance: plex_main
          library_names: ["Movies"]
    - arr_instance: sonarr_main
      plex_instances:
        - instance: plex_main
          library_names: ["TV Shows"]
  path_mapping:                         # translate ARR paths → local paths when volumes differ
    - arr_path: /media/movies
      local_path: /data/movies

☁️ sync_gdrive

Pulls poster assets from Google Drive folders into a local directory using rclone. Supports OAuth tokens or a service-account JSON file (SA preferred).

Run modes: Cancellable: yes.

Setup

  1. Provide auth: gdrive_sa_location (preferred) or the OAuth client_id / client_secret / token triple.
  2. Add each Drive folder under gdrive_list with an id, location, and name.

Gotcha: sync_location, gdrive_sa_location, and folder IDs can't contain null bytes or start with - (a safety check against rclone-flag injection).

For service-account creation and the rclone OAuth flow, see the DAPS wiki's rclone configuration guide — CHUB uses the same backend.

Config

sync_gdrive:
  dry_run: false
  log_level: info
  verbose: false                        # log individual file actions (Copied/Deleted/Updated/Renamed)
  gdrive_sa_location: /config/gdrive-sa.json   # preferred — service account JSON
  # OR the OAuth client triple (alternative to gdrive_sa_location):
  # client_id: "<oauth-client-id>"
  # client_secret: "<oauth-client-secret>"
  # token: "<rclone-token-json>"
  gdrive_list:
    - id: "<google-drive-folder-id>"
      location: /posters/gdrive-pull
      name: "Community poster mirror"

🧼 plex_maintenance

Runs Plex-side housekeeping on its own schedule (split from poster_cleanarr so heavy chores run less often). Four independent toggles:

  • empty_trashlibrary.emptyTrash(), permanently removes items marked for deletion.
  • clean_bundleslibrary.cleanBundles(), drops orphaned .bundle dirs.
  • optimize_dblibrary.optimize(), VACUUMs Plex's metadata DB. Longest-running; run monthly.
  • photo_transcoder — deletes $PLEX/Cache/PhotoTranscoder/ files directly; runs even if Plex is unreachable.

Run modes: Cancellable: partial. photo_transcoder checks the cancel flag per file; the three Plex-API tasks run to completion (Plex's API has no interrupt). Restart the container to kill one mid-run.

Gotcha: photo_transcoder needs plex_path pointed at the Plex application-support dir (containing Cache/PhotoTranscoder/). Empty plex_path = photo cleanup silently skipped.

Config

plex_maintenance:
  log_level: info
  dry_run: false                # log what each task would do without changing anything
  empty_trash: true
  clean_bundles: true
  optimize_db: false            # heavier — run on a slower cron
  photo_transcoder: true
  plex_path: /plex              # Plex application-support dir (for photo cache cleanup)
  sleep: 60                     # seconds to pause between Plex-API tasks
  timeout: 600                  # seconds to wait for the Plex connection
  instances:
    - plex_main

Next: Configuration · Related: Kometa Integration, UI Guide

Clone this wiki locally