docs(67): record the shared-preset decision, watermark seeding, and the additive DTO field
This commit is contained in:
@@ -289,6 +289,10 @@ returns `null` when the channel has no logo so the SPA falls back to its generat
|
||||
raw un-rooted `{path, contentType}` form is still used only by the channel **editor** DTO
|
||||
(`ChannelDetailResponseModel.Logo`), which round-trips it back on save.
|
||||
|
||||
`GET /api/v1/watermarks` returns picker-grade rows that carry `imageSource` alongside `id`/`name`
|
||||
(#67), so a client can find the seeded logo-driven `Channel Bug` preset without matching its
|
||||
user-editable name. The full geometry still requires `GET /api/v1/watermarks/{id}`.
|
||||
|
||||
### 4a. Artwork content type is sniffed, never client-supplied (issue #283)
|
||||
|
||||
The uploaded-artwork surfaces (channel logo, watermark) must never trust a client-declared content
|
||||
|
||||
+28
-2
@@ -139,6 +139,32 @@ The scheduling engine (`ErsatzTV.Core/Scheduling/`) resolves schedule items into
|
||||
|
||||
## Watermarks
|
||||
|
||||
`ChannelWatermark` supports modes: Permanent, Intermittent, OpacityExpression. Image sources: custom upload, channel logo, or built-in resource. Positioned with percentage-based margins and z-index.
|
||||
`ChannelWatermark` supports modes: Permanent, Intermittent, OpacityExpression. Image sources: custom
|
||||
upload, channel logo, or built-in resource. Positioned with percentage-based margins and z-index.
|
||||
|
||||
Note: `ChannelLogoGenerator.GenerateChannelLogoUrl()` hardcodes `localhost` for watermark logo fetching — see issue #1 for details.
|
||||
A watermark is a **shared, named entity** (unique `Name`), referenced by playout items, schedule
|
||||
items, block items and decos; a channel points at one via `Channel.WatermarkId`. It is not
|
||||
per-channel state.
|
||||
|
||||
`DbInitializer` seeds one shared preset named **`Channel Bug`** (`ImageSource = ChannelLogo`,
|
||||
Permanent, TopLeft, Scaled 5% width, 1%/1% margins, 80% opacity). Because `ChannelLogo` resolves
|
||||
each channel's own `ArtworkKind.Logo` artwork at render time, this single row makes every channel
|
||||
that points at it use its own logo as its on-screen bug — one uploaded image drives both the guide
|
||||
listing and the bug (#67). An existing `Channel Bug` row is adopted untouched, never overwritten, and
|
||||
a `ConfigElement` marker (`watermark.channel_bug_seeded`) makes the seed run once per database, so a
|
||||
deliberately deleted preset is not resurrected on the next restart.
|
||||
|
||||
Quick-add channel creation defaults to the preset, and the channel editor's Branding tab exposes it
|
||||
as a "Use logo as on-screen bug" toggle. On a **fresh** install the seed also stamps the preset onto
|
||||
the system channel templates it creates, so the library-to-lineup builder (which inherits
|
||||
`WatermarkId` from the selected template) gets the default too. On an existing install the templates
|
||||
are left alone, so builder-created and auto-tuned channels there inherit whatever the template
|
||||
already specifies.
|
||||
|
||||
**Limitation:** a logo set via **External logo URL** cannot drive the bug. `WatermarkSelector`
|
||||
resolves it to the URL and then `File.Exists`-checks it, which is never true, so the watermark is
|
||||
silently dropped — the URL wins for the guide listing but disables the on-screen bug. Tracked as
|
||||
**#502**; the editor does not offer a bug preview in that case.
|
||||
|
||||
Note: `ChannelLogoGenerator.GenerateChannelLogoUrl()` hardcodes `localhost` for watermark logo
|
||||
fetching — see issue #1 for details.
|
||||
|
||||
@@ -2361,3 +2361,43 @@ fixed here:
|
||||
sweep. This is *why* it cannot cross-delete in a mixed library, but it also means a music video removed
|
||||
from Jellyfin is never removed from ErsatzTV. Pre-existing and orthogonal to this change; it belongs with
|
||||
that scanner's other gaps (no `ItemId`/`Etag`, path-keyed identity — see #488).
|
||||
|
||||
## 2026-07-20 — One logo drives the bug via a shared ChannelLogo preset, not new schema (#67)
|
||||
|
||||
#67 asked that one uploaded image drive both the listing logo and the on-screen bug, separably
|
||||
overridable, with preview. Most of it already existed: `ChannelWatermarkImageSource.ChannelLogo`
|
||||
resolves the channel's own logo artwork at render time at all three watermark precedence levels, and
|
||||
`Custom` already provides the independent override. Production had already been running exactly this
|
||||
pattern by hand — 43 channels pointing at one hand-made `Channel Bug` preset.
|
||||
|
||||
**Decision: seed that preset rather than add per-channel bug columns.** A watermark is a shared named
|
||||
entity, so per-channel geometry would need either a dual-provider migration or one watermark row per
|
||||
channel (under a unique-name index). Since `ChannelLogo` resolves per channel at render time, a single
|
||||
shared row already delivers the user-visible behavior with no schema change.
|
||||
|
||||
- The seed **adopts** an existing `Channel Bug` row untouched, so an operator's tuned geometry is never
|
||||
overwritten, and a `ConfigElement` marker (`watermark.channel_bug_seeded`) makes it run once per
|
||||
database rather than once per name-absence — `ChannelWatermark` has no `IsSystem` flag and
|
||||
`DbInitializer.Initialize` runs at every startup, so a name-only guard would resurrect a deliberately
|
||||
deleted preset forever. Covered by `DbInitializerChannelBugWatermarkTests`.
|
||||
- `WatermarkResponseModel` gained `imageSource` (additive under the frozen `/api/v1`, #286) so clients
|
||||
identify logo-driven presets generically instead of matching a user-editable name. Note the limit:
|
||||
once a *second* logo-driven preset exists, `imageSource` identifies the class but not *the* default,
|
||||
so `findLogoBugWatermark` prefers the seeded name as a deterministic tiebreak.
|
||||
- The default is applied by the SPA's quick-add **creation** path, not by inferring "newness" in the
|
||||
editor — quick-add creates through the API and then navigates to the editor, so the editor only ever
|
||||
loads an existing row. The editor toggle reflects the **referenced** watermark's `imageSource`; an
|
||||
earlier draft searched the list instead, which (because `getWatermarks()` sorts by name) would have
|
||||
silently repointed channels bound to a non-first logo-driven preset. Caught in independent review
|
||||
and pinned by a regression test.
|
||||
- The builder flow is covered on **fresh installs only**, by stamping the preset onto the system
|
||||
channel templates the seed itself creates; existing installs' templates are never mutated.
|
||||
- Server-side create defaulting was rejected: making an omitted `watermarkId` mean "give me a
|
||||
watermark" would surprise machine clients of the frozen API. `POST /api/v1/channels/auto-tune` is
|
||||
therefore unchanged.
|
||||
- **Not fixed here:** external-URL logos never render a bug (`WatermarkSelector` `File.Exists`-checks a
|
||||
URL). Pre-existing, lands in the FFmpeg render path, tracked as **#502**. This change only stops the
|
||||
preview from promising it.
|
||||
|
||||
**Accepted trade-off:** every channel on the shared preset shares one geometry; per-channel tweaks mean
|
||||
creating a second preset on the Watermarks screen.
|
||||
|
||||
Reference in New Issue
Block a user