Seasonal / date-conditional channels & schedule rules #73

Closed
opened 2026-07-01 23:44:23 +02:00 by timothy · 3 comments
Owner

Concept borrowed from PseudoTV (holiday/seasonal channels).

Goal

Channels or schedule items that surface content based on the date — e.g. a holiday channel that only airs (or prioritizes) seasonal content in December.

Scope

  • A date/season predicate on schedule items (or a Deco-like modifier): "active only between dates," "prioritize collection X during date range."
  • Optionally: auto-surface a seasonal channel in the lineup during its window.

Notes

  • ErsatzTV has no native date-conditional scheduling today; you'd hand-build it seasonally.
  • Backend: date predicate in the playout builder; UI: date-range control on schedule items.

Related: #59.

Concept borrowed from **PseudoTV** (holiday/seasonal channels). ## Goal Channels or schedule items that surface content based on the date — e.g. a holiday channel that only airs (or prioritizes) seasonal content in December. ## Scope - A date/season predicate on schedule items (or a Deco-like modifier): "active only between dates," "prioritize collection X during date range." - Optionally: auto-surface a seasonal channel in the lineup during its window. ## Notes - ErsatzTV has no native date-conditional scheduling today; you'd hand-build it seasonally. - Backend: date predicate in the playout builder; UI: date-range control on schedule items. Related: #59.
timothy added the enhancementcontentpriority: low labels 2026-07-01 23:44:23 +02:00
timothy added this to the Scheduling: refactor + distribution milestone 2026-07-17 00:25:07 +02:00
timothy added the in-progress label 2026-07-17 09:13:35 +02:00
Author
Owner

🔵 Claiming (Claude Code / Opus 4.8 orchestrator session).

How selection landed here — backlog-mode cascade, live state:

  • Tier 1 (OPEN issues in an OPEN milestone) is not empty, so selection never reached the flat priority: backlog.
  • Open milestones: Auto-Tune DetailPanel (11), Scheduling: refactor + distribution (10), Design system (6).
  • The whole Auto-Tune milestone is unavailable: #383 is an umbrella container (its Done-when is just "the child endpoint issues are shipped"), #384 is in-progress (claimed 08:49 today), #385 is blocked-by open #70, #386 is blocked-by open #385 + #384.
  • Design system tier-1 member #59 is likewise an epic container.
  • Remaining eligible tier-1: #73, #176 (both priority: low, no open deps; #395 is blocked-by open #381). Tie on priority → lowest issue number → #73.

Coordination note (shared surface, not a duplicate pickup): #70 (Weighted / fair-share content distribution) was claimed at 09:06 today and is actively reworking playout-builder selection logic across the ~5 PlaybackOrder switch sites — the same surface a date predicate in the playout builder would touch. Claiming #73 prevents duplicate pickup, not overlapping edits, so I'm scoping this to avoid colliding with that in-flight work and will re-check #70's landed shape before touching any shared scheduling file.

First step (before any design): per the standing "recon whether the feature already exists" rule, this issue's premise — "ErsatzTV has no native date-conditional scheduling today" — is exactly the kind of claim this fork has repeatedly falsified (the fork imported the full upstream codebase; #77's clock-boundary padding turned out to be fully present via FillerMode.Pad, and re-scoped to verify+document). Upstream has a Deco / DecoTemplate concept that I believe already carries date-range gating. Recon is running now against the Deco entities, the playout builders/scheduling engines, the schedule/block item entities, and the search-query date fields.

Outcome of that recon decides the shape of this issue: a genuine build, or a re-scope to verify + document (+ a possible SPA-reachability gap). I'll post the verdict here before proposing anything.

Also flagging: #73 currently has no ## Done-when section, so the H6/H10 merge gate would fall back to ask. I'll add one once recon fixes the real scope.

🔵 **Claiming** (Claude Code / Opus 4.8 orchestrator session). **How selection landed here** — backlog-mode cascade, live state: - Tier 1 (OPEN issues in an OPEN milestone) is **not** empty, so selection never reached the flat `priority:` backlog. - Open milestones: `Auto-Tune DetailPanel` (11), `Scheduling: refactor + distribution` (10), `Design system` (6). - The whole Auto-Tune milestone is unavailable: **#383 is an umbrella container** (its Done-when is just "the child endpoint issues are shipped"), **#384 is `in-progress`** (claimed 08:49 today), **#385 is blocked-by open #70**, **#386 is blocked-by open #385 + #384**. - `Design system` tier-1 member #59 is likewise an epic container. - Remaining eligible tier-1: #73, #176 (both `priority: low`, no open deps; #395 is blocked-by open #381). Tie on priority → **lowest issue number → #73**. **Coordination note (shared surface, not a duplicate pickup):** #70 (*Weighted / fair-share content distribution*) was claimed at 09:06 today and is actively reworking playout-builder selection logic across the ~5 `PlaybackOrder` switch sites — the same surface a date predicate in the playout builder would touch. Claiming #73 prevents duplicate *pickup*, not overlapping *edits*, so I'm scoping this to avoid colliding with that in-flight work and will re-check #70's landed shape before touching any shared scheduling file. **First step (before any design):** per the standing "recon whether the feature already exists" rule, this issue's premise — *"ErsatzTV has no native date-conditional scheduling today"* — is exactly the kind of claim this fork has repeatedly falsified (the fork imported the full upstream codebase; #77's clock-boundary padding turned out to be fully present via `FillerMode.Pad`, and re-scoped to verify+document). Upstream has a **Deco / DecoTemplate** concept that I believe already carries date-range gating. Recon is running now against the Deco entities, the playout builders/scheduling engines, the schedule/block item entities, and the search-query date fields. Outcome of that recon decides the shape of this issue: a genuine build, or a re-scope to **verify + document** (+ a possible SPA-reachability gap). I'll post the verdict here before proposing anything. Also flagging: #73 currently has **no `## Done-when` section**, so the H6/H10 merge gate would fall back to `ask`. I'll add one once recon fixes the real scope.
Author
Owner

🔎 Recon verdict: this issue's premise is false — date-conditional scheduling FULLY EXISTS.

The body says "ErsatzTV has no native date-conditional scheduling today; you'd hand-build it seasonally." That is not the case. It is a first-class, shipped, SPA-reachable feature on both scheduling engines — and it's already documented in this fork. Same pattern as #77 (clock-boundary padding turned out to be fully present via FillerMode.Pad).

The predicate

ErsatzTV.Core/Domain/Scheduling/IAlternateScheduleItem.cs:3-16:
DaysOfWeek, DaysOfMonth, MonthsOfYear, LimitToDateRange, StartMonth/StartDay/StartYear?, EndMonth/EndDay/EndYear?.

Nullable StartYear/EndYear is the seasonal switch: null years = repeats every year (the holiday-channel case); set years = a one-off absolute window.

Two implementers, one per engine:

  • ProgramScheduleAlternate.cs — gates which ProgramSchedule a classic playout uses on a given date.
  • Scheduling/PlayoutTemplate.cs — gates which Template (+ optional DecoTemplate) a block playout uses on a given date.

The evaluator

ErsatzTV.Core/Scheduling/AlternateScheduleSelector.cs:22-108 — GetScheduleForDate<T>, first-match-wins in Index order, broadest/unconditional row last = catch-all default. It already handles both hard cases: year-wrapping ranges (Nov 1 → Feb 1) and invalid/leap dates (Feb 31 clamped, not thrown).

Wired into every engine: PlayoutBuilder.cs:572,:956 (classic, incl. anchor replay), BlockScheduling/EffectiveBlock.cs:19 (block), BlockPlayoutFillerBuilder.cs:486 (filler), FFmpeg/DecoSelector.cs:24 (deco at stream time).

Reachable from the SPA today — yes

web/src/screens/PlayoutScheduleEditors.tsx:267-300 ships a "Limit to date range" checkbox + start/end month/day/year selects for both editors; playoutTemplateCalendar.ts:54 renders a per-date calendar preview (wrap-around covered by playoutTemplateCalendar.test.ts:59-100). Routes /app/playouts/{id}/alternate-schedules + /templates; API GET|PUT /api/v1/playouts/{id}/alternate-schedules.

Already documented: docs/domain-model.md:57-58,64-65 and docs/channels.md:74-76 — the latter literally says "Useful for seasonal programming."

Correcting the issue body's Deco hypothesis

The body suggests "a Deco-like modifier". Decos do not carry dates: DecoTemplateItem.cs:10-11 is TimeSpan StartTime/EndTime — time-of-day, not date. Deco, DecoTemplate, Template, Block, BlockItem, Playlist, ProgramSchedule, ProgramScheduleItem carry no date field at all. The date dimension lives exclusively on the PlayoutTemplate / ProgramScheduleAlternate join row. Composition is PlayoutTemplate (date range) → Template (time-of-day grid) → Block (content). Upstream provenance: migrations 20240403150903_PlayoutTemplate_OptionalDateRange, 20240403180938_Rework_PlayoutTemplate_ActiveDateRange.

Residual gaps vs. what #73 asked

  1. Granularity — #73 wants the predicate on individual schedule items; the shipped design swaps the whole schedule/template per date. Functionally equivalent for the seasonal use case (clone a schedule, date-gate it, order it above the catch-all), but it means duplicating a schedule rather than flagging one row.
  2. "Prioritize collection X during a date range" — no weighting/priority primitive exists; selection is binary first-match-wins, not a soft boost. ⚠️ This looks like #70's problem space, not #73's — and #70 (Weighted / fair-share content distribution) is claimed and mid-flight right now. Building a second weighting primitive here would be a collision.
  3. Deco date-gating is only indirect (via the PlayoutTemplate row's DecoTemplateId); a Playout.DecoId default deco is not date-gated at all.
  4. Discoverability — the feature is named "Alternate Schedules" / "Playout Templates", which doesn't read as "seasonal". This is plausibly the whole reason #73 was filed.

Where this is heading

My read is that #73 is mostly already done, and the honest residue is a docs/UX-discoverability problem plus a #70-owned weighting ask — not a scheduling-engine build. Because the evidence contradicts the issue body, I've escalated the disposition (close vs. re-scope-to-verify+document vs. narrow build) to a frontier judge rather than deciding it unilaterally. Verdict to follow here; no code will be written before it lands.

🔎 **Recon verdict: this issue's premise is false — date-conditional scheduling FULLY EXISTS.** The body says *"ErsatzTV has no native date-conditional scheduling today; you'd hand-build it seasonally."* That is not the case. It is a first-class, shipped, SPA-reachable feature on **both** scheduling engines — and it's already documented in this fork. Same pattern as #77 (clock-boundary padding turned out to be fully present via `FillerMode.Pad`). ### The predicate `ErsatzTV.Core/Domain/Scheduling/IAlternateScheduleItem.cs:3-16`: `DaysOfWeek`, `DaysOfMonth`, `MonthsOfYear`, `LimitToDateRange`, `StartMonth/StartDay/StartYear?`, `EndMonth/EndDay/EndYear?`. **Nullable `StartYear`/`EndYear` is the seasonal switch**: null years = *repeats every year* (the holiday-channel case); set years = a one-off absolute window. Two implementers, one per engine: - `ProgramScheduleAlternate.cs` — gates which `ProgramSchedule` a **classic** playout uses on a given date. - `Scheduling/PlayoutTemplate.cs` — gates which `Template` (+ optional `DecoTemplate`) a **block** playout uses on a given date. ### The evaluator `ErsatzTV.Core/Scheduling/AlternateScheduleSelector.cs:22-108` — `GetScheduleForDate<T>`, first-match-wins in `Index` order, broadest/unconditional row last = catch-all default. It already handles both hard cases: **year-wrapping ranges** (Nov 1 → Feb 1) and **invalid/leap dates** (Feb 31 clamped, not thrown). Wired into every engine: `PlayoutBuilder.cs:572,:956` (classic, incl. anchor replay), `BlockScheduling/EffectiveBlock.cs:19` (block), `BlockPlayoutFillerBuilder.cs:486` (filler), `FFmpeg/DecoSelector.cs:24` (deco at stream time). ### Reachable from the SPA today — yes `web/src/screens/PlayoutScheduleEditors.tsx:267-300` ships a **"Limit to date range"** checkbox + start/end month/day/year selects for both editors; `playoutTemplateCalendar.ts:54` renders a per-date calendar preview (wrap-around covered by `playoutTemplateCalendar.test.ts:59-100`). Routes `/app/playouts/{id}/alternate-schedules` + `/templates`; API `GET|PUT /api/v1/playouts/{id}/alternate-schedules`. Already documented: `docs/domain-model.md:57-58,64-65` and `docs/channels.md:74-76` — the latter literally says *"Useful for seasonal programming."* ### Correcting the issue body's Deco hypothesis The body suggests *"a Deco-like modifier"*. Decos do **not** carry dates: `DecoTemplateItem.cs:10-11` is `TimeSpan StartTime/EndTime` — **time-of-day**, not date. `Deco`, `DecoTemplate`, `Template`, `Block`, `BlockItem`, `Playlist`, `ProgramSchedule`, `ProgramScheduleItem` carry no date field at all. The date dimension lives *exclusively* on the `PlayoutTemplate` / `ProgramScheduleAlternate` join row. Composition is **PlayoutTemplate (date range) → Template (time-of-day grid) → Block (content)**. Upstream provenance: migrations `20240403150903_PlayoutTemplate_OptionalDateRange`, `20240403180938_Rework_PlayoutTemplate_ActiveDateRange`. ### Residual gaps vs. what #73 asked 1. **Granularity** — #73 wants the predicate on individual *schedule items*; the shipped design swaps the *whole schedule/template* per date. Functionally equivalent for the seasonal use case (clone a schedule, date-gate it, order it above the catch-all), but it means duplicating a schedule rather than flagging one row. 2. **"Prioritize collection X during a date range"** — no weighting/priority primitive exists; selection is binary first-match-wins, not a soft boost. ⚠️ **This looks like #70's problem space, not #73's** — and #70 (*Weighted / fair-share content distribution*) is claimed and mid-flight right now. Building a second weighting primitive here would be a collision. 3. **Deco date-gating** is only indirect (via the `PlayoutTemplate` row's `DecoTemplateId`); a `Playout.DecoId` default deco is not date-gated at all. 4. **Discoverability** — the feature is named *"Alternate Schedules"* / *"Playout Templates"*, which doesn't read as "seasonal". This is plausibly the whole reason #73 was filed. ### Where this is heading My read is that #73 is **mostly already done**, and the honest residue is a **docs/UX-discoverability problem plus a #70-owned weighting ask** — not a scheduling-engine build. Because the evidence contradicts the issue body, I've escalated the disposition (close vs. re-scope-to-verify+document vs. narrow build) to a frontier judge rather than deciding it unilaterally. Verdict to follow here; no code will be written before it lands.
Author
Owner

✅ Closing — already implemented. Frontier verdict is in and executed. Docs landed on main at 62c15da4.

What was done

No production code. The feature #73 asked for already exists; the only real gap was discoverability, and it was a docs gap — the mechanism was documented as a mechanism, never as a task. Fixed with a task-shaped recipe.

Root cause of the issue (why #73 existed at all)

The feature is named "Alternate Schedules" (classic) / "Playout Templates" (block). A user thinking "holiday channel" has no reason to look there, and on finding channels.md:76 ("useful for seasonal programming") gets one clause and no worked steps. So the capability read as absent and got filed as a build. Same class as #77 (clock-boundary padding was fully present via FillerMode.Pad).

Issue-ask → shipped mechanism

#73 asked for Shipped as SPA Doc
"active only between dates" IAlternateScheduleItem.LimitToDateRange + StartMonth/Day/Year? / EndMonth/Day/Year? → ProgramScheduleAlternate (classic), PlayoutTemplate (block); evaluated by AlternateScheduleSelector.GetScheduleForDate (first-match by Index, catch-all last) /app/playouts/{id}/alternate-schedules, /app/playouts/{id}/templates channels.md recipe
holiday channel that "only airs in December" date-gated alternate schedule/template, years left blank → repeats every year same channels.md gotchas
"a Deco-like modifier" ❌ premise wrong — decos carry no dates (DecoTemplateItem.StartTime/EndTime = time-of-day). Date-gate a deco via the PlayoutTemplate row's DecoTemplateId /app/playouts/{id}/templates domain-model.md row
"prioritize collection X during date range" ⚠️ routed to #70 — see below — decisions.md
"auto-surface a seasonal channel in the lineup" ❌ known non-goal — see below — decisions.md

The load-bearing undocumented fact (AlternateScheduleSelector.cs:32-40, verified by reading it, not assumed): years default to the queried date's year, so blank years = repeats every year — the "set once, works every December" switch. The override branch requires both StartYear and EndYear (setting only one silently stays yearly), and explicit years force reverse = false, disabling wrap-around detection. That appeared in no doc anywhere and is now in all three.

Why closed, not #77-style "re-scope to verify + document"

#77 had two real gaps: undocumented and untested. #73 has neither — AlternateScheduleSelectorTests.cs (year-wrap + Feb-31 clamping) and playoutTemplateCalendar.test.ts already existed on main, as did domain-model.md:57-58 / channels.md:74-76. Even #77's re-scope target was already met. Under the close-don't-park convention (decisions.md 2026-07-17), holding #73 open for a hypothetical future build is exactly the parking that convention forbids. Reopen instantly if revived.

Deferred / rejected (deliberate, not overlooked)

  • "Prioritize collection X during a date range" → #70. Decomposes as date scoping (shipped) × soft prioritization (no primitive today — precisely what #70 is building across the PlaybackOrder switch sites). Once #70 lands it's pure composition. Building a second weighting primitive here while #70 is claimed and mid-flight in the same milestone would have been a direct collision. Composition note posted on #70 (the one caveat: weighting must live on the schedule item/block, not the playout, or the date-gate can't vary it).
  • Per-schedule-item date predicate (#73's literal ask) — rejected as-designed. The shipped design swaps the whole schedule per date; functionally equivalent for every seasonal case here, at a cost of "clone a schedule". Moving the predicate onto ProgramScheduleItem rows touches the classic playout builder's core loop for zero new expressible behaviour, with regression risk in the exact engine #163/#380 just built golden nets around.
  • Date-gating the default Playout.DecoId — rejected: the gated path exists (PlayoutTemplate.DecoTemplateId); a date-conditional fallback is a contradiction in terms.
  • UI rename ("Alternate Schedules" → something seasonal-sounding) — rejected: inherited upstream vocabulary baked into routes, API paths and parity docs; churn for marginal gain. Docs fix this instead.
  • Known non-goal — "auto-surface a channel in the lineup during its window": genuinely absent (M3U/XMLTV lineup visibility is not date-conditional), but speculative with no concrete use case (equivalent today: air a date-gated schedule year-round, or toggle ShowInEpg). Per close-don't-park, no placeholder issue filed — refile if a real need appears.

Files changed

  • docs/channels.md — "Recipe: seasonal / holiday programming" (both engines, ~5 steps each + 5 gotchas)
  • docs/domain-model.md — glossary row (mirrors the #77 "already exists" row)
  • docs/decisions.md — verdict entry + index link

Verification

No live-E2E — no production code changed, so the write-path/500 justification doesn't apply (same reasoning as #77). The mechanism stays pinned by the existing AlternateScheduleSelectorTests + playoutTemplateCalendar.test.ts. The one claim worth verifying was the yearly-repeat semantics, and that was confirmed by reading AlternateScheduleSelector.cs:32-40 directly before writing it into the docs. Independent review: skipped under the stated rubric (docs-only, no server-state effect) — stating the exemption rather than staying silent.

Follow-ups created

None, deliberately (see close-don't-park above).

✅ **Closing — already implemented.** Frontier verdict is in and executed. Docs landed on `main` at **`62c15da4`**. ## What was done No production code. The feature #73 asked for already exists; the only real gap was **discoverability**, and it was a docs gap — the mechanism was documented as a *mechanism*, never as a *task*. Fixed with a task-shaped recipe. ## Root cause of the issue (why #73 existed at all) The feature is named **"Alternate Schedules"** (classic) / **"Playout Templates"** (block). A user thinking *"holiday channel"* has no reason to look there, and on finding `channels.md:76` ("useful for seasonal programming") gets one clause and no worked steps. So the capability read as absent and got filed as a build. Same class as #77 (clock-boundary padding was fully present via `FillerMode.Pad`). ## Issue-ask → shipped mechanism | #73 asked for | Shipped as | SPA | Doc | |---|---|---|---| | "active only between dates" | `IAlternateScheduleItem.LimitToDateRange` + `StartMonth/Day/Year?` / `EndMonth/Day/Year?` → `ProgramScheduleAlternate` (classic), `PlayoutTemplate` (block); evaluated by `AlternateScheduleSelector.GetScheduleForDate` (first-match by `Index`, catch-all last) | `/app/playouts/{id}/alternate-schedules`, `/app/playouts/{id}/templates` | `channels.md` recipe | | holiday channel that "only airs in December" | date-gated alternate schedule/template, **years left blank** → repeats every year | same | `channels.md` gotchas | | "a Deco-like modifier" | ❌ premise wrong — decos carry no dates (`DecoTemplateItem.StartTime/EndTime` = time-of-day). Date-gate a deco via the `PlayoutTemplate` row's `DecoTemplateId` | `/app/playouts/{id}/templates` | `domain-model.md` row | | "prioritize collection X during date range" | ⚠️ **routed to #70** — see below | — | `decisions.md` | | "auto-surface a seasonal channel in the lineup" | ❌ **known non-goal** — see below | — | `decisions.md` | **The load-bearing undocumented fact** (`AlternateScheduleSelector.cs:32-40`, verified by reading it, not assumed): years default to the *queried date's* year, so **blank years = repeats every year** — the "set once, works every December" switch. The override branch requires **both** `StartYear` and `EndYear` (setting only one silently stays yearly), and explicit years force `reverse = false`, disabling wrap-around detection. That appeared in no doc anywhere and is now in all three. ## Why closed, not #77-style "re-scope to verify + document" #77 had two real gaps: undocumented **and** untested. #73 has **neither** — `AlternateScheduleSelectorTests.cs` (year-wrap + Feb-31 clamping) and `playoutTemplateCalendar.test.ts` already existed on main, as did `domain-model.md:57-58` / `channels.md:74-76`. Even #77's re-scope *target* was already met. Under the **close-don't-park** convention (`decisions.md` 2026-07-17), holding #73 open for a hypothetical future build is exactly the parking that convention forbids. Reopen instantly if revived. ## Deferred / rejected (deliberate, not overlooked) - **"Prioritize collection X during a date range" → #70.** Decomposes as *date scoping* (shipped) × *soft prioritization* (no primitive today — precisely what #70 is building across the `PlaybackOrder` switch sites). Once #70 lands it's pure composition. Building a second weighting primitive here while #70 is claimed and mid-flight in the same milestone would have been a direct collision. **Composition note posted on #70** (the one caveat: weighting must live on the schedule item/block, not the playout, or the date-gate can't vary it). - **Per-schedule-item date predicate** (#73's literal ask) — rejected as-designed. The shipped design swaps the whole schedule per date; functionally equivalent for every seasonal case here, at a cost of "clone a schedule". Moving the predicate onto `ProgramScheduleItem` rows touches the classic playout builder's core loop for **zero new expressible behaviour**, with regression risk in the exact engine #163/#380 just built golden nets around. - **Date-gating the default `Playout.DecoId`** — rejected: the gated path exists (`PlayoutTemplate.DecoTemplateId`); a date-conditional *fallback* is a contradiction in terms. - **UI rename** ("Alternate Schedules" → something seasonal-sounding) — rejected: inherited upstream vocabulary baked into routes, API paths and parity docs; churn for marginal gain. Docs fix this instead. - **Known non-goal — "auto-surface a channel in the lineup during its window"**: genuinely absent (M3U/XMLTV lineup visibility is not date-conditional), but speculative with no concrete use case (equivalent today: air a date-gated schedule year-round, or toggle `ShowInEpg`). Per close-don't-park, **no placeholder issue filed** — refile if a real need appears. ## Files changed - `docs/channels.md` — "Recipe: seasonal / holiday programming" (both engines, ~5 steps each + 5 gotchas) - `docs/domain-model.md` — glossary row (mirrors the #77 "already exists" row) - `docs/decisions.md` — verdict entry + index link ## Verification No live-E2E — no production code changed, so the write-path/500 justification doesn't apply (same reasoning as #77). The mechanism stays pinned by the existing `AlternateScheduleSelectorTests` + `playoutTemplateCalendar.test.ts`. The one claim worth verifying was the yearly-repeat semantics, and that was confirmed by reading `AlternateScheduleSelector.cs:32-40` directly before writing it into the docs. Independent review: skipped under the stated rubric (docs-only, no server-state effect) — **stating the exemption rather than staying silent**. ## Follow-ups created None, deliberately (see close-don't-park above).
timothy removed the in-progress label 2026-07-17 09:21:54 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: timothy/ersatztv#73