0.108.3: Black Mirror on Paradox after 21:00; After Dark anthology strand; linked episodes; Tales from the Crypt off Orbit 2 (built, not installed)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Claude
co-authored by
Claude Opus 5.5
parent
80d315481b
commit
a57d0dfbe1
@@ -36,7 +36,7 @@ private val Orbit2Panel = listOf("QI", "Have I Got News for You", "Would I Lie t
|
||||
/** Retro Action, with Orbit 2's cult guests as late surprises. Paradox is their preferred home. */
|
||||
private val Orbit2RetroAction = listOf("Knight Rider", "The A-Team", "Airwolf", "MacGyver",
|
||||
"Street Hawk", "The Fall Guy", "Magnum, P.I.", "Miami Vice", "CHiPs", "The Dukes of Hazzard", "Blue Thunder",
|
||||
"Quantum Leap", "Tales from the Crypt", "The Twilight Zone", "The Outer Limits", "Sliders", "Twin Peaks")
|
||||
"Quantum Leap", "The Twilight Zone", "The Outer Limits", "Sliders", "Twin Peaks") // Tales from the Crypt moved to After Dark, 4 November 2026
|
||||
private val Orbit2Spooky = listOf("Goosebumps", "Are You Afraid of the Dark?", "Eerie, Indiana")
|
||||
internal val Orbit2Shows get() = (Orbit2Daytime + Orbit2Teatime + Orbit2GameShows + Orbit2Panel + Orbit2RetroAction + Orbit2Spooky).distinct()
|
||||
internal val Orbit2DayParts get() = listOf(
|
||||
@@ -338,6 +338,16 @@ internal val AfterDarkEvents get() = listOf(DayPart(21, 1, BMovieThrowbacks, day
|
||||
DayPart(21, 1, listOf(LAST_DRIVE_IN), days = setOf(DayOfWeek.SATURDAY), onTime = true, key = "afterdark.drivein"),
|
||||
DayPart(21, 1, CultClassics, days = setOf(DayOfWeek.SUNDAY), onTime = true, key = "afterdark.cult"))
|
||||
|
||||
/**
|
||||
* After Dark's late strand of anthology (approved by Paul on 4 November 2026; docs/anthology-awareness-investigation.md): after
|
||||
* the night's film, at 01:00 on Monday to Thursday nights, two Tales from the Crypt on Monday and Wednesday and three Love,
|
||||
* Death & Robots shorts on Tuesday and Thursday, each in a fixed mixed order with linked episodes kept together.
|
||||
*/
|
||||
internal val AfterDarkAnthologies get() = listOf("Tales from the Crypt", "Love, Death & Robots")
|
||||
internal val AfterDarkStrand get() = listOf(
|
||||
DayPart(1, 2, listOf("Tales from the Crypt"), days = setOf(DayOfWeek.TUESDAY, DayOfWeek.THURSDAY), onTime = true, run = 2, shuffled = true),
|
||||
DayPart(1, 2, listOf("Love, Death & Robots"), days = setOf(DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY), onTime = true, run = 3, shuffled = true))
|
||||
|
||||
/** Orbit Junior: British children's TV for about 5 to 11, with the classics at breakfast. Tots is the preferred home of modern preschool. */
|
||||
private val JuniorBreakfast = listOf("Thomas the Tank Engine & Friends", "Thomas & Friends", "Postman Pat", "Brum", "Rosie and Jim", "Pingu", "Pingu in the City", "Button Moon",
|
||||
"Tots TV", "Bagpuss", "Camberwick Green", "Trumpton", "The Clangers", "Tweenies", "Rainbow", "The Wombles", "Noddy", "Noddy's Toyland Adventures", "Balamory", "Fimbles",
|
||||
@@ -407,7 +417,7 @@ internal val Orbit1Tiers: Map<String, Double> get() =
|
||||
/** Orbit 2's tiers, from docs/channels/orbit-2.md (approved 4 November 2026): core 3, surprises 1, everything else 2. */
|
||||
internal val Orbit2Tiers: Map<String, Double> get() =
|
||||
listOf("Airwolf", "Countdown", "Gladiators", "Have I Got News for You", "Knight Rider", "MacGyver", "Mastermind", "Pointless", "QI", "Robot Wars", "Takeshi's Castle", "The A-Team", "The Crystal Maze", "University Challenge", "Would I Lie to You?").associate { seriesKey(it) to TIER_CORE } +
|
||||
listOf("Are You Afraid of the Dark?", "Eerie, Indiana", "Goosebumps", "Quantum Leap", "Tales from the Crypt").associate { seriesKey(it) to TIER_SURPRISE }
|
||||
listOf("Are You Afraid of the Dark?", "Eerie, Indiana", "Goosebumps", "Quantum Leap").associate { seriesKey(it) to TIER_SURPRISE }
|
||||
/** Gold's tiers, from docs/channels/orbit-gold.md (approved 4 November 2026): core 3, surprises 1, everything else 2. */
|
||||
internal val GoldTiers: Map<String, Double> get() =
|
||||
listOf("Blackadder", "Dad's Army", "Father Ted", "Fawlty Towers", "Keeping Up Appearances", "One Foot in the Grave", "Only Fools and Horses", "Open All Hours", "Porridge", "The Vicar of Dibley").associate { seriesKey(it) to TIER_CORE } +
|
||||
|
||||
@@ -27,7 +27,9 @@ internal data class DayPart(val fromHour: Int, val toHour: Int, val titles: List
|
||||
/** Set to true for a block only on the channel's premiere nights, false for one only on its other nights. Null is every night. */
|
||||
val premiereNights: Boolean? = null,
|
||||
/** An appointment's own allowance for starting late, instead of [ON_TIME_GRACE_MS]. */
|
||||
val graceMs: Long? = null) {
|
||||
val graceMs: Long? = null,
|
||||
/** On a film channel, a series event's episodes a night (a strand), and whether they go round a mixed deck. */
|
||||
val run: Int = 1, val shuffled: Boolean = false) {
|
||||
fun covers(hour: Int) = if (fromHour <= toHour) hour in fromHour until toHour else hour >= fromHour || hour < toHour
|
||||
/** An event that runs past midnight belongs to the night it started, so its small hours count from the day before. */
|
||||
fun covers(hour: Int, day: java.time.DayOfWeek) = covers(hour) && (days.isEmpty() || (if (fromHour > toHour && hour < toHour) day.minus(1) else day) in days)
|
||||
@@ -92,8 +94,11 @@ internal data class ChannelProgramming(val number: Int, val name: String, val se
|
||||
private val ParadoxStandalones = listOf("Doctor Who (2005)", "Doctor Who (2023)", "Star Trek: The Next Generation", "The X-Files", "The Twilight Zone", "Stargate SG-1", "Sliders", "Warehouse 13", "Eureka")
|
||||
private val ParadoxPrimeTime = listOf("Buffy the Vampire Slayer", "Angel", "Supernatural", "Fringe")
|
||||
private val ParadoxLate = listOf("Twin Peaks", "The Outer Limits", "The Outer Limits (1995)", "Torchwood", "Farscape", "The X-Files")
|
||||
private val ParadoxShows get() = (ParadoxStandalones + ParadoxPrimeTime + ParadoxLate).distinct()
|
||||
private val ParadoxDayParts get() = listOf(DayPart(6, 18, ParadoxStandalones), DayPart(18, 23, ParadoxPrimeTime), DayPart(23, 6, ParadoxLate))
|
||||
/** Adult anthology for the late evening only (approved by Paul on 4 November 2026): standalone stories, in a mixed order. */
|
||||
private val ParadoxAdult = listOf("Black Mirror")
|
||||
private val ParadoxShows get() = (ParadoxStandalones + ParadoxPrimeTime + ParadoxLate + ParadoxAdult).distinct()
|
||||
private val ParadoxDayParts get() = listOf(DayPart(6, 18, ParadoxStandalones), DayPart(18, 21, ParadoxPrimeTime), DayPart(21, 23, ParadoxPrimeTime + ParadoxAdult),
|
||||
DayPart(23, 6, ParadoxLate + ParadoxAdult))
|
||||
|
||||
private val NotCultCinema = setOf("Family", "Animation", "Documentary", "Music")
|
||||
|
||||
@@ -170,9 +175,9 @@ private val AfterDarkFilms = listOf("The Thing (1982)", "The Evil Dead (1983)",
|
||||
"Army of Darkness", "House (1977)", "The Holy Mountain", "Repo! The Genetic Opera")
|
||||
|
||||
internal val SmartChannels = listOf(
|
||||
ChannelProgramming(AFTER_DARK_NUMBER, "After Dark", AfterDarkFilms, seriesCount = AfterDarkFilms.size, filmSeries = listOf(LAST_DRIVE_IN), filmRestMs = 7 * 24 * 60 * 60_000L, adaptiveFilmRest = true,
|
||||
ChannelProgramming(AFTER_DARK_NUMBER, "After Dark", AfterDarkFilms, seriesCount = AfterDarkFilms.size, filmSeries = listOf(LAST_DRIVE_IN) + AfterDarkAnthologies, filmRestMs = 7 * 24 * 60 * 60_000L, adaptiveFilmRest = true,
|
||||
withdrawn = AfterDarkWithdrawn,
|
||||
dayParts = AfterDarkEvents + listOf(DayPart(21, 5), DayPart(5, 21, listOf("The 'Burbs", "'Til Death Do Us Part (2017)", "Tremors (1990)", "The Lost Boys (1987)", "Fright Night (1985)", "Poltergeist (1982)", "Scream (1996)",
|
||||
dayParts = AfterDarkEvents + AfterDarkStrand + listOf(DayPart(21, 5), DayPart(5, 21, listOf("The 'Burbs", "'Til Death Do Us Part (2017)", "Tremors (1990)", "The Lost Boys (1987)", "Fright Night (1985)", "Poltergeist (1982)", "Scream (1996)",
|
||||
"Escape from New York", "They Live", "Repo Man", "Donnie Darko", "From Dusk Till Dawn", "Creepshow", "Near Dark", "Halloween (1978)", "The Fog (1980)", "Blue Velvet",
|
||||
"The Wicker Man (1973)", "An American Werewolf in London", "Prince of Darkness") + AfterDarkDaytimeCrossover, group = "day")),
|
||||
suggestionRules = listOf(
|
||||
@@ -192,7 +197,7 @@ internal val SmartChannels = listOf(
|
||||
ChannelProgramming(201, "Terra", TerraShows, seriesCount = TerraShows.size, finishTogether = true, exactTitles = true, dayParts = TerraDayParts),
|
||||
ChannelProgramming(TOTS_NUMBER, "Tots", TotsShows, seriesCount = 40, finishTogether = true, exactTitles = true, dayParts = TotsDayParts, shuffleEpisodes = true,
|
||||
tierWeights = TotsTiers, catalogueScaleHours = CATALOGUE_SCALE_HOURS),
|
||||
ChannelProgramming(106, "Paradox", ParadoxShows, seriesCount = 20, finishTogether = true, exactTitles = true, dayParts = ParadoxDayParts, shuffleTitles = ParadoxStandalones.toSet(),
|
||||
ChannelProgramming(106, "Paradox", ParadoxShows, seriesCount = 20, finishTogether = true, exactTitles = true, dayParts = ParadoxDayParts, shuffleTitles = (ParadoxStandalones + ParadoxAdult).toSet(),
|
||||
tierWeights = ParadoxTiers, storyGrouping = true)) +
|
||||
ChannelPresets.filter { it.smartOnly }.map { ChannelProgramming(it.number, it.name, it.searches, seriesCount = if (it.films) it.searches.size else 60, finishTogether = !it.films, exactTitles = true,
|
||||
filmRestMs = if (it.films) 7 * 24 * 60 * 60_000L else null,
|
||||
@@ -447,13 +452,23 @@ internal fun extendFilmSchedule(pool: List<PoolSeries>, cursor: ScheduleCursor,
|
||||
// An event naming only a series plays its next whole episode, once a night. Everywhere else is films only.
|
||||
val series = available.singleOrNull()?.takeIf { !it.isFilm && block.startsWith("event ") }
|
||||
if (series != null && shown[series.id]?.let { start - it < SERIES_NIGHT_MS } != true) {
|
||||
val episode = nextEpisode(series, marks[series.id])
|
||||
entries += ScheduleEntry(series.id, episode.mark, ScheduledMedia(scheduledItem(series.title, episode), episode.sourceId, episode.durationMs), start,
|
||||
"$block; next episode in order, played whole", part?.key.orEmpty())
|
||||
marks[series.id] = episode.mark
|
||||
// A strand ([DayPart.run]) plays several episodes back to back; a shuffled one goes round a fixed mixed deck in
|
||||
// which linked episodes and stories stay together and in order ([storiesOf]).
|
||||
val deck = if (part?.shuffled == true) shuffledStories(series, storiesOf(series)) else null
|
||||
var mark = marks[series.id]
|
||||
val stories = storiesOf(series)
|
||||
var played = 0
|
||||
while (played < (part?.run ?: 1) || (mark != null && stories.of(series.episodes.first { it.mark == mark })?.let { story -> story.parts.last().mark != mark } == true)) {
|
||||
val episode = deck?.let { d -> d[(d.indexOfFirst { it.mark == mark } + 1) % d.size] } ?: nextEpisode(series, mark)
|
||||
entries += ScheduleEntry(series.id, episode.mark, ScheduledMedia(scheduledItem(series.title, episode), episode.sourceId, episode.durationMs), start,
|
||||
"$block; " + (if (deck != null) "next in its mixed order" else "next episode in order") + ", played whole", part?.key.orEmpty())
|
||||
mark = episode.mark
|
||||
start += episode.durationMs
|
||||
played++
|
||||
}
|
||||
marks[series.id] = mark!!
|
||||
shown[series.id] = start
|
||||
last = series.id
|
||||
start += episode.durationMs
|
||||
continue
|
||||
}
|
||||
val films = available.filter { it.isFilm }.ifEmpty { pool.filter { it.isFilm } }.ifEmpty { available }
|
||||
|
||||
@@ -31,8 +31,23 @@ internal fun partNumber(title: String): Int? = PartNumber.find(title)?.let { mat
|
||||
number.toIntOrNull() ?: PartWords[number.lowercase()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Linked episodes the numbering doesn't show, found by the anthology cast check (4 November 2026): a sequel that shares its
|
||||
* original's title and three or more of its leads. Each is kept with its original, in order, as a story is.
|
||||
*/
|
||||
internal val LinkedEpisodes: Map<String, List<List<EpisodeMark>>> get() = mapOf(
|
||||
seriesKey("Black Mirror") to listOf(listOf(EpisodeMark(4, 1), EpisodeMark(7, 6))), // USS Callister, then Into Infinity
|
||||
seriesKey("Love, Death & Robots") to listOf(listOf(EpisodeMark(1, 2), EpisodeMark(3, 1)))) // Three Robots, then Exit Strategies
|
||||
|
||||
internal fun storiesOf(show: PoolSeries): ShowStories {
|
||||
if (show.isFilm) return ShowStories(emptyList())
|
||||
val linked = LinkedEpisodes[seriesKey(show.title)].orEmpty().mapNotNull { marks ->
|
||||
marks.map { mark -> show.episodes.firstOrNull { it.mark == mark } ?: return@mapNotNull null }.let(::Story) }
|
||||
if (linked.isNotEmpty()) return ShowStories(numberedStories(show) + linked)
|
||||
return ShowStories(numberedStories(show))
|
||||
}
|
||||
|
||||
private fun numberedStories(show: PoolSeries): List<Story> {
|
||||
val episodes = show.episodes.sortedBy { it.mark }
|
||||
val lastOfSeason = episodes.groupBy { it.mark.season }.mapValues { (_, it) -> it.maxOf { e -> e.mark.episode } }
|
||||
fun follows(before: PoolEpisode, after: PoolEpisode) = (after.mark.season == before.mark.season && after.mark.episode == before.mark.episode + 1) ||
|
||||
@@ -46,7 +61,7 @@ internal fun storiesOf(show: PoolSeries): ShowStories {
|
||||
if (run.size in 2..MAX_STORY_PARTS) stories += Story(run)
|
||||
i += run.size
|
||||
}
|
||||
return ShowStories(stories)
|
||||
return stories
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package tv.orbit.classic
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/** Anthologies (approved by Paul on 4 November 2026): standalone stories shuffle, linked episodes stay together and in order. */
|
||||
class AnthologyTest {
|
||||
private val london = ZoneId.of("Europe/London")
|
||||
private val minute = 60_000L
|
||||
private fun at(d: Int, h: Int) = ZonedDateTime.of(2026, 11, d, h, 0, 0, 0, london).toInstant().toEpochMilli()
|
||||
private fun show(id: String, title: String, marks: List<Pair<Int, Int>>, minutes: Long) =
|
||||
PoolSeries(id, title, marks.map { (s, e) -> PoolEpisode("$id-$s-$e", "src", EpisodeMark(s, e), "E$s.$e", minutes * minute) })
|
||||
private fun film(n: Int) = "f$n".let { id -> PoolSeries(id, "Film $n", listOf(PoolEpisode(id, "s$id", EpisodeMark(1, 1), "Film $n", (90L + n % 30) * minute))) }
|
||||
|
||||
private val mirror = show("bm", "Black Mirror", (1..7).flatMap { s -> (1..6).map { s to it } }, 60)
|
||||
private val robots = show("ldr", "Love, Death & Robots", (1..4).flatMap { s -> (1..9).map { s to it } }, 14)
|
||||
private val crypt = show("tftc", "Tales from the Crypt", (1..7).flatMap { s -> (1..13).map { s to it } }, 26)
|
||||
|
||||
@Test fun linkedEpisodesAreAStoryInOrder() {
|
||||
val callister = storiesOf(mirror).of(mirror.episodes.first { it.mark == EpisodeMark(4, 1) })!!
|
||||
assertEquals(listOf(EpisodeMark(4, 1), EpisodeMark(7, 6)), callister.parts.map { it.mark })
|
||||
val deck = shuffledStories(mirror, storiesOf(mirror))
|
||||
val i = deck.indexOfFirst { it.mark == EpisodeMark(4, 1) }
|
||||
assertEquals(EpisodeMark(7, 6), deck[i + 1].mark)
|
||||
assertEquals(mirror.episodes.size, deck.size)
|
||||
// Anything else has no links.
|
||||
assertTrue(storiesOf(crypt).stories.isEmpty())
|
||||
}
|
||||
|
||||
@Test fun afterDarksStrandPlaysItsMixedRunAfterTheFilm() {
|
||||
val pool = (1..40).map(::film) + crypt + robots
|
||||
val parts = AfterDarkStrand + listOf(DayPart(21, 5), DayPart(5, 21))
|
||||
val plan = extendFilmSchedule(pool, ScheduleCursor(), at(9, 12), at(16, 12), 2 * 24 * 60 * minute, parts, london)
|
||||
val strand = plan.entries.filter { !it.media.item.title.startsWith("Film") }
|
||||
// Monday and Wednesday nights: two Crypts; Tuesday and Thursday nights: three shorts; nothing at the weekend.
|
||||
val byNight = strand.groupBy { Instant.ofEpochMilli(it.startMs).atZone(london).toLocalDate() }
|
||||
assertEquals(setOf(java.time.DayOfWeek.TUESDAY, java.time.DayOfWeek.WEDNESDAY, java.time.DayOfWeek.THURSDAY, java.time.DayOfWeek.FRIDAY), byNight.keys.map { it.dayOfWeek }.toSet())
|
||||
byNight.forEach { (day, entries) ->
|
||||
val crypts = entries.count { it.seriesId == "tftc" }; val shorts = entries.count { it.seriesId == "ldr" }
|
||||
if (day.dayOfWeek in setOf(java.time.DayOfWeek.TUESDAY, java.time.DayOfWeek.THURSDAY)) assertEquals(2, crypts) else assertTrue(shorts >= 3)
|
||||
entries.zipWithNext { a, b -> assertEquals(a.endMs, b.startMs) }
|
||||
assertTrue(Instant.ofEpochMilli(entries.first().startMs).atZone(london).hour in 1..1)
|
||||
}
|
||||
// A mixed order, not season order.
|
||||
val crypts = strand.filter { it.seriesId == "tftc" }.map { it.mark }
|
||||
assertNotEquals(crypts.sortedWith(compareBy({ it.season }, { it.episode })), crypts)
|
||||
plan.entries.zipWithNext { a, b -> assertEquals(a.endMs, b.startMs) }
|
||||
}
|
||||
|
||||
@Test fun threeRobotsNeverAirsExitStrategiesFirst() {
|
||||
val pool = (1..40).map(::film) + robots
|
||||
val parts = AfterDarkStrand + listOf(DayPart(21, 5), DayPart(5, 21))
|
||||
val plan = extendFilmSchedule(pool, ScheduleCursor(), at(2, 12), at(30, 12) + 120L * 24 * 60 * minute, 2 * 24 * 60 * minute, parts, london)
|
||||
val shorts = plan.entries.filter { it.seriesId == "ldr" }
|
||||
val first = shorts.indexOfFirst { it.mark == EpisodeMark(1, 2) }; val sequel = shorts.indexOfFirst { it.mark == EpisodeMark(3, 1) }
|
||||
assertTrue(first >= 0 && sequel == first + 1)
|
||||
}
|
||||
|
||||
@Test fun theChannelsAreAsApproved() {
|
||||
val paradox = SmartChannels.first { it.number == 106 }
|
||||
assertTrue("Black Mirror" in paradox.searches)
|
||||
assertTrue(paradox.mixes(mirror))
|
||||
// Only from 21:00: not in the 18:00 to 21:00 block or the day.
|
||||
assertTrue(paradox.dayParts.filter { "Black Mirror" in it.titles }.all { it.fromHour >= 21 })
|
||||
val afterDark = SmartChannels.first { it.number == AFTER_DARK_NUMBER }
|
||||
assertTrue(afterDark.filmSeries.containsAll(listOf("Tales from the Crypt", "Love, Death & Robots")))
|
||||
assertTrue("Tales from the Crypt" !in SmartChannels.first { it.number == 102 }.searches)
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,6 @@ class PpvSundayTest {
|
||||
assertTrue(ringX.signature.contains("#films:"))
|
||||
assertTrue(ringX.signature.contains(PpvSunday.first()))
|
||||
val afterDark = SmartChannels.first { it.number == AFTER_DARK_NUMBER }
|
||||
assertTrue(afterDark.signature.contains("#series:$LAST_DRIVE_IN#"))
|
||||
assertTrue(afterDark.signature.contains("#series:${(listOf(LAST_DRIVE_IN) + AfterDarkAnthologies).joinToString("|")}#"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# 0.108.3: anthologies on Paradox and After Dark, built on the installed 0.108.2, NOT installed
|
||||
|
||||
Paul approved proposals 1 to 3 on 4 November 2026 at 22:01 (docs/anthology-awareness-investigation.md). This release is built on 0.108.2, the version on the box, so Advent and The Naughty List aren't in it. The same change is on main.
|
||||
|
||||
## What it does
|
||||
|
||||
**Black Mirror on Paradox (106), from 21:00 only.**
|
||||
- **Blocks:** Paradox's 18:00 to 23:00 block is split into 18:00 to 21:00 (unchanged shows) and 21:00 to 23:00 (the same shows plus Black Mirror). Black Mirror also joins 23:00 to 06:00.
|
||||
- **Order:** it's shuffled as standalones.
|
||||
- **Copy:** the pool read keeps the copy with the most episodes, 7536213, which has 32 regular episodes.
|
||||
- **Specials:** season 0 is never read, so Bandersnatch (interactive) and the White Christmas special are left out.
|
||||
|
||||
**After Dark (305) late strand.**
|
||||
- **What and when:** Tales from the Crypt and Love, Death & Robots, read whole as After Dark's Joe Bob series is (`filmSeries`). New `AfterDarkStrand` appointments at 01:00:
|
||||
- Monday and Wednesday nights: two Tales from the Crypt;
|
||||
- Tuesday and Thursday nights: three Love, Death & Robots shorts.
|
||||
- **How:** `DayPart.run` and `DayPart.shuffled` let a series event on a film channel play several episodes from a fixed mixed deck. The films before it finish in time, as for any appointment.
|
||||
|
||||
**Linked episodes** (`LinkedEpisodes` in StoryGroups.kt, from the cast check) are kept together and in order, as stories are:
|
||||
- Black Mirror: USS Callister, then Into Infinity.
|
||||
- Love, Death & Robots: Three Robots, then Exit Strategies.
|
||||
|
||||
**Tales from the Crypt leaves Orbit 2's** Retro Action list and tiers. Orbit 2's pool drops it at its next read, and its existing bookings stay as booked.
|
||||
|
||||
## Checks
|
||||
|
||||
- 486 unit tests run, 0 failures; lint has no errors. `AnthologyTest` adds 4 tests:
|
||||
- linked episodes form a story and sit together in the deck;
|
||||
- the strand's nights, counts, mixed order and on-time run;
|
||||
- Three Robots never airs its sequel first;
|
||||
- the channels are as approved.
|
||||
- **Four-week replay** from the box's plans at 22:29 today (`ReplayFingerprintTest`). The pools were the box's own, with Black Mirror, Tales from the Crypt and Love, Death & Robots added from the on-box inspections, and Tales from the Crypt removed from Orbit 2.
|
||||
- **Fingerprints:** every channel's is identical except Orbit 2, Paradox and After Dark. All committed bookings are kept (`kept=true`).
|
||||
- **Paradox:** Black Mirror airs about 37 times in four weeks, all at 21:00 or later. USS Callister is followed directly by Into Infinity. The order is mixed. The X-Files has 123 → 115 airings, Supernatural 60 → 56 and Buffy 56 → 52.
|
||||
- **After Dark:** 16 strand nights in the four weeks. Each starts between 01:02 and 01:17, after that night's film. It totals 16 Crypts and 24 shorts.
|
||||
- **Orbit 2:** only small reshuffles once Tales from the Crypt is gone, of one or two programmes per show.
|
||||
|
||||
**Black Mirror's share on Paradox:** it runs at the normal tier, which gives it the 37 airings above. Making it a "surprise" tier would roughly halve that. That's Paul's call.
|
||||
|
||||
APK: `outputs/revision-0.108.3`, signed, versionCode 163. **Not installed.**
|
||||
@@ -0,0 +1,30 @@
|
||||
101 committed=af5c16550fe392e8 kept=true fourWeeks=c7b3ba6191dccdc4 programmes=2147
|
||||
102 committed=922490b3996c41fb kept=true fourWeeks=b1a9013c24381155 programmes=1296
|
||||
103 committed=0918ee2c03e716e8 kept=true fourWeeks=f2ac211e4a4b9072 programmes=1795
|
||||
104 committed=6f6a2f491703e112 kept=true fourWeeks=d292d863a41e7bbc programmes=1710
|
||||
105 committed=17abed5208c9d1ad kept=true fourWeeks=dbe25703104fda57 programmes=2525
|
||||
106 committed=3efc0cedba402fca kept=true fourWeeks=9beec7545969be6f programmes=1124
|
||||
107 committed=f4059d1aae164bde kept=true fourWeeks=0d72aeaa11f91b10 programmes=1000
|
||||
201 committed=ca5a8865812f7909 kept=true fourWeeks=e58367ea15bdb46b programmes=949
|
||||
202 committed=f047a503df58c243 kept=true fourWeeks=7c10bff8f112f0b8 programmes=1117
|
||||
203 committed=43d85b99498c3647 kept=true fourWeeks=75f120d14806d8b2 programmes=1122
|
||||
204 committed=75426228a60bd5db kept=true fourWeeks=275feb59680e538e programmes=1246
|
||||
301 committed=a2be939a7727fbe4 kept=true fourWeeks=a3e54e71690c7b7e programmes=357
|
||||
302 committed=42447fbb1928fe84 kept=true fourWeeks=9b1776c0ae9f350d programmes=436
|
||||
303 committed=341c373541eb1f9a kept=true fourWeeks=519214275cc45da2 programmes=413
|
||||
304 committed=36a9cc7637a97c62 kept=true fourWeeks=1e3ed510c5da5fa4 programmes=486
|
||||
305 committed=bce04ffed4ae362a kept=true fourWeeks=16ffb5a3b5a63c2a programmes=533
|
||||
306 committed=4769d0a01972d4da kept=true fourWeeks=6da09324c161095f programmes=486
|
||||
307 committed=759fcec471f60b24 kept=true fourWeeks=a4b8d8aefdf199a0 programmes=438
|
||||
308 committed=42036cefce120ef5 kept=true fourWeeks=4a9e31fdb6e1d23a programmes=510
|
||||
309 committed=bea0bff60803200f kept=true fourWeeks=7c60691856057a56 programmes=491
|
||||
401 committed=13f8347e5c01ec30 kept=true fourWeeks=ce30ecd9bc0832fb programmes=867
|
||||
601 committed=3300a4019fd1b6aa kept=true fourWeeks=b84da138b7d5bd83 programmes=2174
|
||||
602 committed=31b7055fe7d3b96e kept=true fourWeeks=f1c3f82f461e3719 programmes=2304
|
||||
603 committed=23a6259ff689581f kept=true fourWeeks=c60e59f786858f46 programmes=2284
|
||||
604 committed=b443664b922658f4 kept=true fourWeeks=e2f280386111cd4f programmes=3215
|
||||
605 committed=82e0823ef1c7bbf2 kept=true fourWeeks=ca1fb1f642e4491a programmes=4457
|
||||
611 committed=ad55af51bab2e926 kept=true fourWeeks=ff15635df75eb739 programmes=2701
|
||||
612 committed=a9b6d7ea8d9511fe kept=true fourWeeks=fae95ada5f64df8e programmes=572
|
||||
613 committed=df565f8d4f207103 kept=true fourWeeks=415e69170d79544f programmes=514
|
||||
614 committed=e36973393d11a9ba kept=true fourWeeks=c9a50f8128ce6ee8 programmes=3795
|
||||
Reference in New Issue
Block a user