---
key: sched.autotune-per-channel-overrides
title: '2026-07-17 — Auto-Tune per-channel overrides reuse the Channel Builder advanced-options DTO; weights + bug-colour logo split out to #425 (#385)'
status: active
since: '2026-07-17'
supersedes: none
superseded-by: none
rule: 'Auto-Tune per-channel overrides reuse the Channel Builder''s advanced-options DTO verbatim; per-source weights and bug-colour logo are deferred to #425.'
signals: 'Auto-Tune, per-channel overrides, advanced-options DTO reuse · paths: `CreateAutoTunedChannelsHandler`, `POST /api/v1/channels/auto-tune` · issues: #385, #383, #425, #283'
mechanics: '`CreateAutoTunedChannelsHandler.CreateOne`; `CreateChannelFromLineupAdvancedOptionsRequest`'
---

The Auto-Tune DetailPanel (#383) makes each proposed channel individually editable before bulk-create.
The backend for that (#385) split cleanly along a "structural cost" line, and only the additive half
shipped here; the rest is deliberately deferred rather than forced.

- **Shipped now (additive, over the frozen `/api/v1`):** `POST /api/v1/channels/auto-tune`'s
  `AutoTunedChannelRequest` gained three optional per-channel fields — `templateId`, `advanced`, and an
  uploaded `logo` image. Omitting each keeps PR1 behavior exactly (batch template, axis-derived playback
  order, on-the-fly fallback logo), so the change is backward-compatible and the older positional
  `{axis, value, name, number}` form still binds.
  - **`advanced` reuses the manual Channel Builder's `CreateChannelFromLineupAdvancedOptionsRequest`
    verbatim** — the same 24-field override set, the same `ToCommand()`, and the same
    `advanced.X ?? template.X` stamp-at-create contract that `POST /api/v1/channels/from-lineup` already
    honors. Auto-tune is now just a second caller of that wire contract; we did **not** mint a parallel
    24-field DTO. The one auto-tune-specific rule: the axis default (`SeasonEpisode` for a single show,
    `Shuffle` for a genre) fills `PlaybackOrder` only when the caller left it null, so an explicit
    per-channel order wins but an unset one is never dropped.
  - **`logo` is the uploaded channel image**, forwarded to `CreateChannelFromLineup.Logo` (persisted as an
    `ArtworkKind.Logo` artwork) and run through `ArtworkContentTypeModel.Sanitized()` at the request
    boundary — the same stored-XSS defense (#283) the Channel Builder uses; a hostile content type never
    reaches the command.
  - Per-channel independence is preserved: `templateId`/`advanced`/`logo` are resolved per channel inside
    `CreateAutoTunedChannelsHandler.CreateOne`, so one channel's bad override still yields a per-channel
    `Failed`/`Skipped` outcome without aborting the batch.

- **Deferred to #425 — per-source rotation weights + query corrections (exclude / add-untagged).** This is
  a structural redesign, not effort: #70's `WeightedShuffle` reads weights only off
  `MultiCollectionItem`/`MultiCollectionSmartItem` rows, but an auto-tuned channel is backed by a **single
  SmartCollection** (→ `GetFakeMultiCollectionCollections`, every fake group forced to `Weight = 1`), which
  gives fair-share but **cannot express differing per-source weights**. Honoring them requires auto-tune to
  build a **MultiCollection of per-source SmartCollections** (one query per weighted source), which
  contradicts the documented "one live query per channel" design and lands new structure in the scheduling
  subsystem. That is the `[PLAN-MODE]` design call the #385 body flagged; it belongs in its own issue with
  a recorded design decision, so it moved to **#425** (consumed by the SPA #386).

- **Deferred — bug initials + fallback colour generated logo.** The generated logo (`/iptv/logos/gen`) is a
  stateless on-the-fly render used by both the XMLTV `<icon>` and the FFmpeg `WatermarkSelector` bug, with
  **no persisted bug-initials/colour state on `Channel`**. Making it configurable needs either new `Channel`
  columns (a dual-provider migration) wired through the watermark pipeline, or create-time artwork-file
  materialization — neither is the additive plumbing this slice was scoped to, and it is a channel-wide
  capability rather than an auto-tune concern. Left for its own issue / the SPA branding work; the uploaded
  `logo` above already covers the on-screen bug for channels that supply an image.
