Shorts: fuller slots and variety; guide and slot-details design for approval
pauljd/davidson-ci: Davidson CI / quality (push) Failing after 1m59s
pauljd/davidson-ci: Davidson CI / ai-review (push) Failing after 1m35s

Ported from the release line; design/shorts holds the proposed guide cell, slot details and on-air banner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-06 04:33:34 +00:00
co-authored by Claude Opus 5.5
parent 27b6cfc3c1
commit 59221e05f8
8 changed files with 173 additions and 21 deletions
@@ -850,7 +850,8 @@ internal fun extendWeighted(pool: List<PoolSeries>, cursor: ScheduleCursor, from
}
val open = unclashed.ifEmpty { available }.let { shows -> if (stories) shows.filter(::storyFits).ifEmpty { shows } else shows }
// Inside a shorts slot only the shorts that fit it take part; none fits, and the slot closes where it is.
val inSlot = slot?.let { s -> unclashed.filter { it.id != last && s.takes(start, following(it).durationMs) } }.orEmpty()
fun slotShows(s: ShortsSlot) = unclashed.filter { it.id != last && s.takes(start, following(it).durationMs) }
val inSlot = slot?.let { s -> slotShows(s).ifEmpty { if (s.stretchIfShort(start)) slotShows(s) else emptyList() } }.orEmpty()
if (inSlot.isEmpty()) slot = null
val clear = if (continuing == null && inSlot.isNotEmpty()) inSlot else open
val rules = listOf<(PoolSeries) -> Boolean>(
@@ -861,7 +862,8 @@ internal fun extendWeighted(pool: List<PoolSeries>, cursor: ScheduleCursor, from
val low = eligible.minOf { tally[it.id]!! }
clear.filter { it.id != last && it !in eligible }.forEach { tally[it.id] = maxOf(tally[it.id]!!, low) }
val byTally = compareBy<PoolSeries> { tally[it.id]!! }.thenBy { pool.indexOf(it) }
val preferred = eligible.minWith(byTally)
// In a shorts slot, a show it hasn't had yet goes first when it's close behind in turn.
val preferred = slot?.takeIf { continuing == null && inSlot.isNotEmpty() }?.varied(eligible.sortedWith(byTally)) { it.id } ?: eligible.minWith(byTally)
// Before an appointment, the eligible show that leaves it on time, then any clear one, in tally order.
val appointment = if (part?.onTime == true) null else nextAppointment(start, zone, partAt = ::partAt)
val fitted = if (continuing != null) null else appointment?.let { at -> fun ends(show: PoolSeries) = start + lengthOf(show)
@@ -999,7 +1001,8 @@ internal fun extendSchedule(pool: List<PoolSeries>, cursor: ScheduleCursor, from
// A block carries on from its own last show. Starting from the channel's last show would begin each block at the same place every day.
val after = if (blockRotation) blockLast[block] ?: last else last
// Inside a shorts slot, the next short in turn that fits it; none fits, and the slot closes where it is.
val inSlot = slot?.let { open -> turnOrder(pool, after).firstOrNull { show -> show.id != last && allowed(show) && open.takes(start, following(show).durationMs) && !avoid(show, start, start + following(show).durationMs) } }
fun slotPick(open: ShortsSlot) = open.varied(turnOrder(pool, after).filter { show -> show.id != last && allowed(show) && open.takes(start, following(show).durationMs) && !avoid(show, start, start + following(show).durationMs) }) { it.id }
val inSlot = slot?.let { open -> slotPick(open) ?: if (open.stretchIfShort(start)) slotPick(open) else null }
if (inSlot == null) slot = null
val picked = inSlot ?: turnOrder(pool, after).firstOrNull { show ->
when {
@@ -24,6 +24,8 @@ import java.time.ZoneId
internal const val SHORTS_MAX_MS = 8 * 60_000L
internal const val SHORTS_SLOT_MIN_MS = 15 * 60_000L
internal const val SHORTS_OVERRUN_MS = 2 * 60_000L
/** How far down the turn order a slot looks for a show it hasn't had yet, before taking a show back. */
internal const val SHORTS_VARIETY_TURNS = 1
/** A channel's shorts slots: what each is called at a given time. */
internal class ShortsRule(val label: (startMs: Long, zone: ZoneId) -> String)
@@ -35,8 +37,21 @@ internal val ToonZShorts = ShortsRule { _, _ -> "Cartoon Shorts" }
internal fun isShort(durationMs: Long) = durationMs in 1..SHORTS_MAX_MS
/** One slot being filled: when it aims to end, and the tag its shorts carry. */
internal class ShortsSlot(val startMs: Long, val targetMs: Long, val label: String) {
internal class ShortsSlot(val startMs: Long, var targetMs: Long, val label: String) {
val tag get() = shortsTag(startMs, label)
/** The shows already in this slot: a show comes back only when no other short fits (Paul, 6 November 2026: variety first). */
val shows = mutableSetOf<String>()
/**
* Variety without jumping the queue: [first] in turn order stands, unless it's already in the slot and a show that isn't
* is close behind it (within [SHORTS_VARIETY_TURNS] places of [ordered]).
*/
fun <T> varied(ordered: List<T>, id: (T) -> String): T? = ordered.firstOrNull()?.let { first ->
if (id(first) !in shows) first else ordered.take(SHORTS_VARIETY_TURNS + 1).firstOrNull { id(it) !in shows } ?: first }
/**
* A slot still under [SHORTS_SLOT_MIN_MS] that can't fit its next short by its target aims for the half hour after
* instead, rather than closing as a fragment with another slot straight after it.
*/
fun stretchIfShort(atMs: Long): Boolean = (atMs - startMs < SHORTS_SLOT_MIN_MS).also { if (it) targetMs += 30 * 60_000L }
/** Whether a short of [durationMs] starting at [atMs] belongs in this slot. */
fun takes(atMs: Long, durationMs: Long) = isShort(durationMs) && atMs < targetMs && atMs + durationMs <= targetMs + SHORTS_OVERRUN_MS
}
@@ -80,6 +95,7 @@ internal fun shortsBlocks(entries: List<ScheduleEntry>): List<ShortsBlock> = bui
internal fun shortsTagFor(rule: ShortsRule?, slot: ShortsSlot?, series: PoolSeries, episode: PoolEpisode, inBlock: Boolean, startMs: Long, zone: ZoneId): Pair<String, ShortsSlot?> {
if (rule == null || series.isFilm || !inBlock || !isShort(episode.durationMs)) return "" to null
val open = slot?.takeIf { it.takes(startMs, episode.durationMs) } ?: openShortsSlot(rule, startMs, zone)
open.shows += series.id
return open.tag to open
}
@@ -89,12 +89,19 @@ class ShortsReplayTest {
val loose = after.count { isShort(it.media.durationMs) && it.group.isEmpty() }
val lengths = blocks.map { it.endMs - it.startMs }.sorted()
val repeats = blocks.sumOf { b -> b.entries.zipWithNext().count { (a, c) -> a.seriesId == c.seriesId } }
assertEquals("${programming.name}: no show twice in a row in a slot", 0, repeats)
val returns = blocks.sumOf { b -> b.entries.size - b.entries.map { it.seriesId }.distinct().size }
val couldVary = blocks.count { b -> b.entries.size > b.entries.map { it.seriesId }.distinct().size }
val shortSlots = blocks.filter { it.endMs - it.startMs < SHORTS_SLOT_MIN_MS }
out.append("\n${programming.name} ($number)\n")
out.append(" Programmes booked: ${before.size} before, ${after.size} after; shorts among them: $shortsBefore before, $shortsAfter after\n")
out.append(" Shorts slots: ${blocks.size}, holding ${blocks.sumOf { it.entries.size }} shorts; $loose short(s) left on their own (a slot of one, or the block changed)\n")
if (lengths.isNotEmpty()) out.append(" Slot length: shortest ${mins(lengths.first())} min, median ${mins(lengths[lengths.size / 2])}, longest ${mins(lengths.last())}; " +
"${blocks.count { onHalfHour(it.endMs) }} of ${blocks.size} end within 2 min of :00 or :30\n")
out.append(" Shows per slot: average ${"%.1f".format(blocks.map { it.shows.size }.average())}; the same show twice in a row inside a slot: $repeats time(s)\n")
out.append(" Shows per slot: average ${"%.1f".format(blocks.map { it.shows.size }.average())}; the same show twice in a row inside a slot: $repeats time(s); a show back later in the same slot: $returns time(s), in $couldVary slot(s)\n")
out.append(" Slots under 15 minutes: ${shortSlots.size}${if (shortSlots.isEmpty()) "" else ": " + shortSlots.take(6).joinToString { blk ->
val next = after.firstOrNull { it.startMs == blk.endMs }
"${clock(blk.startMs)} ${mins(blk.endMs - blk.startMs)} min, then ${next?.let(::name) ?: "?"} (${next?.let { mins(it.media.durationMs) } ?: "?"} min)" }}\n")
out.append(" Programmes starting within 2 min of :00 or :30: ${before.count { onHalfHour(it.startMs) }} of ${before.size} before, ${after.count { onHalfHour(it.startMs) }} of ${after.size} after\n")
out.append(" Labels: ${blocks.groupingBy { it.label }.eachCount()}\n")
val airtime = { run: List<ScheduleEntry> -> run.groupBy(::name).mapValues { (_, v) -> v.sumOf { it.media.durationMs } / 60_000 } }
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""Shorts slots in the guide, design proposal (Paul, 6 November 2026): how a slot of shorts shows in the TV Guide, what OK on
it opens, and Tots Story Time as the evening's identity for single bedtime stories. Every short is still its own programme;
the slot is only how they're shown. Times and titles from the seven-day replay of the box's own plans. For approval only."""
import ast
from pathlib import Path
HERE = Path(__file__).resolve().parent
SRC = HERE.parent / 'profiles' / 'build-profiles.py'
tree = ast.parse(SRC.read_text())
keep = []
for n in tree.body:
if isinstance(n, ast.Expr) and isinstance(n.value, ast.Constant): continue
if isinstance(n, ast.Expr) and isinstance(n.value, ast.Call) and getattr(n.value.func, 'id', '') == 'screen': break
keep.append(n)
ns = {'__file__': str(SRC)}
exec(compile(ast.Module(keep, []), str(SRC), 'exec'), ns)
globals().update({k: v for k, v in ns.items() if k not in ('HERE', '__file__', 'save')})
def save(name): ns['im'].resize((W, H), Image.LANCZOS).save(HERE / name); print(name)
def D(): return ns['d']
from PIL import Image
GREEN = (110, 220, 170); LATER = (15, 44, 92); PINK = (255, 122, 198); LUNA = (167, 123, 255)
def stack(x, y, s=18, col=YELLOW):
"""The shorts mark: three short bars, a slot of several programmes."""
for k in range(3): D().rounded_rectangle((S(x), S(y + k * s * .38), S(x + s * (1 - k * .18)), S(y + k * s * .38 + s * .24)), S(3), fill=col)
def moon(x, y, s=16, col=LUNA):
D().ellipse((S(x), S(y), S(x + s), S(y + s)), fill=col); D().ellipse((S(x + s * .35), S(y - s * .1), S(x + s * 1.2), S(y + s * .8)), fill=PANEL)
def guide_header(times, y=150, x0=420, step=290, day='Thursday'):
box(60, y, 1800, 48, fill=BLUE, r=0); text((80, y + 8), day, semi(28), WHITE)
for i, t in enumerate(times): text((x0 + i * step, y + 8), t, semi(28), WHITE)
def fit(s, f, w):
"""Cut to fit [w] pixels, with an ellipsis, as the guide does."""
if D().textlength(s, font=f) / SS <= w: return s
while s and D().textlength(s + '…', font=f) / SS > w: s = s[:-1]
return (s.rstrip() + '…') if s else ''
def cell(x, w, y, title, sub=None, fill=PANEL, focus=False, mark=None, h=100):
box(x, y, w - 6, h, fill=NAVY if focus else fill, r=4, outline=YELLOW if focus else None, width=4)
tx = x + 18
if mark == 'stack': stack(tx, y + 22); tx += 30
if mark == 'moon': moon(tx, y + 24); tx += 30
text((tx, y + 18), fit(title, semi(26), x + w - 24 - tx), semi(26), WHITE)
if sub: text((x + 18, y + 58), fit(sub, reg(21), w - 36), reg(21), MUTED)
def mins(m, step=290): return m / 30 * step
# 1. The guide: a ToonZ slot, a Tots slot, Tots' evening stories one by one, and a slot cut short by a block change.
screen()
text((80, 70), 'TV GUIDE', anton(44), WHITE); text((300, 84), 'A slot of shorts is one cell; the shorts in it are still each their own programme.', reg(26), MUTED)
guide_header(['13:30', '14:00', '14:30', '15:00', '15:30'])
x0 = 410; t0 = 13 * 60 + 30
def X(hm): return x0 + mins(hm - t0)
rows = [('605', 'TOONZ', [(13*60+30, 13*60+51, 'The Flintstones', 'S3 E12', None), (13*60+51, 14*60+31, 'Cartoon Shorts', 'Tom and Jerry, Yogi Bear, Popeye and 3 more · 40 min', 'stack'),
(14*60+31, 14*60+55, 'Looney Tunes', 'The Looney Tunes Show', None), (14*60+55, 15*60+58, 'Wacky Races', 'S1 E4', None)]),
('614', 'TOTS', [(13*60+30, 13*60+41, 'Fireman Sam', 'S9 E3', None), (13*60+41, 14*60+50, 'Andy\'s Wild Adventures', 'S2 E8', None),
(14*60+50, 15*60+28, 'Tots Little Mix', 'Bluey, Numberblocks, Peppa Pig and 3 more · 38 min', 'stack'), (15*60+28, 16*60, 'Andy\'s Wild Adventures', 'S2 E9', None)])]
for r, (num, name, progs) in enumerate(rows):
y = 210 + r * 120
box(60, y, 340, 100, fill=PANEL, r=0); text((80, y + 30), num, semi(30), YELLOW); text((170, y + 30), name, anton(34), WHITE)
for a, b, title, sub, mark in progs:
a2, b2 = max(a, t0), min(b, t0 + 150)
if b2 <= a2: continue
cell(X(a2), mins(b2 - a2), y, title, sub, focus=(mark == 'stack' and r == 0), mark=mark)
# Evening: single bedtime stories carry Tots Story Time; a slot cut short by a block change shows its real length.
guide_header(['18:00', '18:30', '19:00', '19:30', '20:00'], y=470)
y = 530; box(60, y, 340, 100, fill=PANEL, r=0); text((80, y + 30), '614', semi(30), YELLOW); text((170, y + 30), 'TOTS', anton(34), WHITE)
t1 = 18 * 60
def X1(hm): return x0 + mins(hm - t1)
for a, b, title, sub, mark in [(18*60, 18*60+5, 'Story', None, 'moon'), (18*60+5, 18*60+25, 'In The Night Garden', 'S1 E14', None), (18*60+25, 18*60+31, 'Story', None, 'moon'),
(18*60+31, 18*60+50, 'Teletubbies', 'S2 E40', None), (18*60+50, 19*60+2, 'BeddyByes', 'S1 E9', None), (19*60+2, 19*60+7, 'Story', None, 'moon'),
(19*60+7, 19*60+30, 'Clangers', 'S2 E3', None), (19*60+30, 20*60+30, 'In The Night Garden', 'S1 E15 · Tots Dreamtime from 19:00', None)]:
cell(X1(a), mins(b - a), y, title if title != 'Story' else '', sub, mark=mark)
box(420, 650, 1440, 110, fill=(10, 30, 66), r=10)
moon(446, 676, 20)
text((482, 668), 'Tots Story Time: each evening bedtime story is its own short programme, with the moon and the story\'s name when focused,', reg(23), MUTED)
text((482, 700), 'for example "Tots Story Time: Kipper\'s Snowy Day, 5 min". No evening block: In The Night Garden and the wind-down keep their turns.', reg(23), MUTED)
guide_header(['21:30', '22:00', '22:30', '23:00', '23:30'], y=790)
y = 850; box(60, y, 340, 100, fill=PANEL, r=0); text((80, y + 30), '605', semi(30), YELLOW); text((170, y + 30), 'TOONZ', anton(34), WHITE)
t2 = 21 * 60 + 30
def X2(hm): return x0 + mins(hm - t2)
for a, b, title, sub, mark, fill in [(21*60+30, 21*60+45, 'Danger Mouse', 'S4 E2', None, PANEL), (21*60+45, 22*60, 'Cartoon Shorts', '21:45 to 22:00 · 15 min', 'stack', PANEL),
(22*60, 22*60+22, 'Futurama', 'ToonZ Late', None, (60, 30, 70)), (22*60+22, 23*60, 'King of the Hill', 'ToonZ Late', None, (60, 30, 70))]:
cell(X2(a), mins(b - a), y, title, sub, fill=fill, mark=mark)
text((80, 962), 'A slot that ends early (here at 22:00, when ToonZ Late takes over and has no shorts) is drawn at its real length, with its real times.', reg(24), MUTED)
save('shorts-1-guide.png')
# 2. OK on the slot: every short, its times, and what's watchable.
screen()
for k in range(6): box(60, 160 + k * 120, 1800, 100, fill=(10, 27, 60), r=0)
overlay = Image.new('RGBA', ns['im'].size, (7, 22, 50, 170)); ns['im'].paste(Image.alpha_composite(ns['im'].convert('RGBA'), overlay).convert('RGB')); ns['d'] = __import__('PIL.ImageDraw', fromlist=['x']).Draw(ns['im'])
box(360, 120, 1200, 820, fill=DEEP, r=16, outline=BLUE, width=3)
stack(400, 160, 26); text((446, 150), 'CARTOON SHORTS', anton(44), WHITE)
text((400, 214), '605 ToonZ · Thursday 13:51 to 14:31 · 40 min · 6 shorts', semi(28), MUTED)
shorts = [('13:51', 'Tom and Jerry', 'Mouse Trouble', '6 min', 'aired'), ('13:57', 'The Yogi Bear Show', 'Pie Pirates', '7 min', 'aired'),
('14:04', 'Popeye the Sailor', 'Sea Sick Sailors', '6 min', 'now'), ('14:10', 'The Huckleberry Hound Show', 'Lion Hearted Huck', '7 min', 'next'),
('14:18', 'The Pink Panther', 'Pink Pajamas', '6 min', 'next'), ('14:24', 'Looney Tunes', 'Rabbit Seasoning', '7 min', 'next')]
for i, (t, show, ep, ln, state) in enumerate(shorts):
y = 280 + i * 92; focus = i == 1
box(400, y, 1120, 80, fill=NAVY if focus else PANEL, r=8, outline=YELLOW if focus else None, width=3)
text((424, y + 22), t, semi(30), YELLOW)
text((540, y + 10), show, semi(30), WHITE); text((540, y + 46), ep, reg(22), MUTED)
text((1180, y + 22), ln, reg(26), MUTED)
if state == 'aired': box(1290, y + 20, 200, 40, fill=GREEN, r=6); text((1390, y + 26), 'CATCH UP', semi(22), NAVY, 'ma')
if state == 'now': box(1290, y + 20, 200, 40, fill=YELLOW, r=6); text((1390, y + 26), 'ON NOW', semi(22), NAVY, 'ma')
text((400, 846), 'Each short is its own programme: Catch Up, where you\'ve got to, and Kids rules work short by short.', reg(24), MUTED)
text((400, 880), 'The list is what\'s actually booked, so the title, times and shows always match what airs.', reg(24), MUTED)
keys([('OK', 'Watch from this short'), ('▲▼', 'Shorts'), ('Back', 'Guide')])
save('shorts-2-details.png')
# 3. While it's on: the info banner names the short and where it sits in the slot.
screen()
box(0, 0, W, H, fill=(30, 30, 30), r=0)
for k in range(18): box(80 + k * 100, 120 + (k % 4) * 60, 60, 40, fill=(45, 45, 45), r=6)
box(0, 780, W, 300, fill=(7, 22, 50), r=0)
text((80, 810), '605', semi(36), YELLOW); text((170, 806), 'TOONZ', anton(40), WHITE)
stack(80, 880, 22); text((118, 872), 'Cartoon Shorts · 3 of 6', semi(28), MUTED)
text((80, 920), 'Popeye the Sailor: Sea Sick Sailors', semi(40), WHITE)
text((80, 975), '14:04 to 14:10 · next: The Huckleberry Hound Show 14:10', reg(26), MUTED)
box(1100, 900, 700, 10, fill=(40, 60, 100), r=5); box(1100, 900, 300, 10, fill=YELLOW, r=5)
text((1100, 930), 'The short\'s own progress, not the slot\'s', reg(24), MUTED)
save('shorts-3-banner.png')
Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

+18 -16
View File
@@ -1,13 +1,14 @@
Shorts grouping: seven days of new bookings after each committed plan, from the box's files (2026-11-06).
Tots (614)
Programmes booked: 762 before, 767 after; shorts among them: 255 before, 263 after
Shorts slots: 35, holding 156 shorts; 107 short(s) left on their own (a slot of one, or the block changed)
Slot length: shortest 10.0 min, median 25.0, longest 42.1; 22 of 35 end within 2 min of :00 or :30
Shows per slot: average 3.0; the same show twice in a row inside a slot: 0 time(s)
Programmes starting within 2 min of :00 or :30: 136 of 762 before, 133 of 767 after
Labels: {Tots Little Mix=35}
Biggest airtime changes over the week (minutes): Teletubbies -21, Something Special -20, Bluey (2018) +15, Ben and Holly's Little Kingdom -11, Numberblocks +10, CBeebies Bedtime Stories +9, Hey Duggee +7, Peppa Pig +5
Programmes booked: 762 before, 770 after; shorts among them: 255 before, 265 after
Shorts slots: 32, holding 159 shorts; 106 short(s) left on their own (a slot of one, or the block changed)
Slot length: shortest 17.0 min, median 29.7, longest 44.2; 22 of 32 end within 2 min of :00 or :30
Shows per slot: average 3.3; the same show twice in a row inside a slot: 0 time(s); a show back later in the same slot: 52 time(s), in 26 slot(s)
Slots under 15 minutes: 0
Programmes starting within 2 min of :00 or :30: 136 of 762 before, 117 of 770 after
Labels: {Tots Little Mix=32}
Biggest airtime changes over the week (minutes): In The Night Garden -28, Bluey (2018) +22, Teletubbies -21, Something Special -20, Numberblocks +15, Andy's Wild Adventures -15, Floogals +11, Bob the Builder +10
Example slot Thu 14:50 to 15:28 "Tots Little Mix: Bluey (2018), Numberblocks, Peppa Pig and more"
14:50 Bluey (2018) (7.3 min)
14:58 Numberblocks (5.0 min)
@@ -32,13 +33,14 @@ Tots (614)
After that first slot: 15:28 Andy's Wild Adventures, straight after the last short
ToonZ (605)
Programmes booked: 757 before, 758 after; shorts among them: 416 before, 419 after
Shorts slots: 94, holding 419 shorts; 0 short(s) left on their own (a slot of one, or the block changed)
Slot length: shortest 12.6 min, median 32.3, longest 46.4; 58 of 94 end within 2 min of :00 or :30
Shows per slot: average 3.6; the same show twice in a row inside a slot: 0 time(s)
Programmes starting within 2 min of :00 or :30: 121 of 757 before, 136 of 758 after
Labels: {Cartoon Shorts=94}
Biggest airtime changes over the week (minutes): Duckman +22, Hong Kong Phooey -21, Wacky Races -12, Looney Tunes -9, Tom and Jerry +7, The Yogi Bear Show +7, Popeye the Sailor +7, The Pink Panther -6
Programmes booked: 757 before, 754 after; shorts among them: 416 before, 413 after
Shorts slots: 87, holding 413 shorts; 0 short(s) left on their own (a slot of one, or the block changed)
Slot length: shortest 13.1 min, median 33.7, longest 49.0; 66 of 87 end within 2 min of :00 or :30
Shows per slot: average 3.9; the same show twice in a row inside a slot: 0 time(s); a show back later in the same slot: 70 time(s), in 48 slot(s)
Slots under 15 minutes: 3: Sat 21:45 14.9 min, then Futurama (21.7 min), Mon 08:24 13.9 min, then Looney Tunes (23.8 min), Tue 21:48 13.1 min, then Aqua Teen Hunger Force (11.5 min)
Programmes starting within 2 min of :00 or :30: 121 of 757 before, 139 of 754 after
Labels: {Cartoon Shorts=87}
Biggest airtime changes over the week (minutes): The Jetsons -48, The Looney Tunes Show +44, The Flintstones -26, Danger Mouse +24, The Critic +23, Duckman +22, Tom and Jerry -14, Popeye the Sailor -13
Example slot Thu 13:51 to 14:31 "Cartoon Shorts: Tom and Jerry, The Yogi Bear Show, Popeye the Sailor and more"
13:51 Tom and Jerry (6.4 min)
13:57 The Yogi Bear Show (7.1 min)
@@ -50,8 +52,8 @@ ToonZ (605)
16:08 Looney Tunes (6.7 min)
16:15 Tom and Jerry (6.4 min)
16:21 The Yogi Bear Show (7.2 min)
Example slot Thu 17:42 to 17:59 "Cartoon Shorts: Popeye the Sailor, Bananaman"
Example slot Thu 17:42 to 18:00 "Cartoon Shorts: Popeye the Sailor, Bananaman, Looney Tunes"
17:42 Popeye the Sailor (6.2 min)
17:48 Bananaman (4.6 min)
17:53 Popeye the Sailor (6.7 min)
17:53 Looney Tunes (7.8 min)
After that first slot: 14:31 Looney Tunes, straight after the last short