0.102.0: Vortex Prime, Afterburn and Afterburn premiere idents
pauljd/davidson-ci: Davidson CI / quality (push) Failing after 2m27s
pauljd/davidson-ci: Davidson CI / ai-review (push) Failing after 1m29s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-04 18:44:21 +00:00
co-authored by Claude Opus 5.5
parent ed5322eff8
commit 44a7cc4978
14 changed files with 32 additions and 10 deletions
+2 -2
View File
@@ -9,8 +9,8 @@ android {
applicationId = "tv.orbit.classic"
minSdk = 26
targetSdk = 36
versionCode = 150
versionName = "0.101.0-new-arrivals"
versionCode = 151
versionName = "0.102.0-vortex-idents"
}
buildFeatures { compose = true }
compileOptions { sourceCompatibility = JavaVersion.VERSION_17; targetCompatibility = JavaVersion.VERSION_17 }
@@ -44,6 +44,10 @@ internal val BlockIdents: Map<String, BlockIdent> = mapOf(
"gold.sunday" to BlockIdent(R.raw.block_gold_sunday, R.raw.block_gold_sunday_sting),
"three.scottish" to BlockIdent(R.raw.block_three_scottish, R.raw.block_three_scottish_sting),
"vortex.movie" to BlockIdent(R.raw.block_vortex_movie, R.raw.block_vortex_movie_sting),
// Batch 6, approved by Paul on 4 November 2026: Vortex's afternoon and late blocks, and the Afterburn premiere.
"vortex.prime" to BlockIdent(R.raw.block_vortex_prime, R.raw.block_vortex_prime_sting),
"vortex.afterburn" to BlockIdent(R.raw.block_vortex_afterburn, R.raw.block_vortex_afterburn_sting),
"vortex.afterburn.premiere" to BlockIdent(R.raw.block_vortex_premiere, R.raw.block_vortex_premiere_sting),
"orbit2.retro" to BlockIdent(R.raw.block_orbit2_retro, R.raw.block_orbit2_retro_sting),
"orbit2.spooky" to BlockIdent(R.raw.block_orbit2_spooky, R.raw.block_orbit2_spooky_sting),
"ringx.ppv" to BlockIdent(R.raw.block_ringx_ppv, R.raw.block_ringx_ppv_sting, BlockOverlay.TonightsEvent),
@@ -155,7 +155,8 @@ internal val VortexDayParts get() = listOf(
// Mornings are the staples. Reviewed modern anime joins in the afternoon, so it complements the channel rather than leading it.
DayPart(6, 12, VortexCartoons + VortexFavourites + listOf("Cardcaptor Sakura", "Cardcaptors", "Sailor Moon", "Dragon Ball", "Zoids: Chaotic Century", "Zoids")),
DayPart(12, 16, VortexCartoons + VortexFavourites + VortexFamilyAnime + listOf("Dragon Ball Z", "Mobile Suit Gundam Wing", "Tenchi Muyo! Ryo-Ohki", "Yu Yu Hakusho", "Outlaw Star", "The Big O")),
DayPart(16, 18, VortexToonami, group = "toonami"), DayPart(18, 21, VortexToonami + VortexEvening, group = "night"), DayPart(21, 23, VortexToonami, group = "toonami"),
DayPart(16, 18, VortexToonami, group = "toonami", key = "vortex.prime"), DayPart(18, 21, VortexToonami + VortexEvening, group = "night"),
DayPart(21, 23, VortexToonami, group = "toonami", key = "vortex.afterburn"),
DayPart(23, 6, VortexLate, group = "day"))
/** Heroes: the recognisable superhero cartoons every day, Marvel on Saturday mornings as Fox Kids had it. */
@@ -66,7 +66,7 @@ internal class HttpsEmbyTransport(internal val deviceId: String = UUID.randomUUI
connection.connectTimeout = 10_000
connection.readTimeout = request.readTimeoutMs ?: if (request.path == "/Sessions/Logout") 5_000 else 15_000
connection.setRequestProperty("Accept", "application/json")
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.101.0\"")
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.102.0\"")
request.token?.let { connection.setRequestProperty("X-Emby-Token", it) }
if (request.method == "POST") {
val bytes = request.body.orEmpty().toByteArray(Charsets.UTF_8)
@@ -33,7 +33,7 @@ internal class SecureMediaDataSource(private val session: EmbySampleSession) : B
next.instanceFollowRedirects = false
next.connectTimeout = 10_000
next.readTimeout = 20_000
next.setRequestProperty("User-Agent", "OrbitTV/0.101.0")
next.setRequestProperty("User-Agent", "OrbitTV/0.102.0")
next.setRequestProperty("Accept-Encoding", "identity")
session.mediaHeaders(address).forEach { (key, value) -> next.setRequestProperty(key, value) }
if (dataSpec.position != 0L || dataSpec.length != C.LENGTH_UNSET.toLong()) {
Binary file not shown.
@@ -18,10 +18,9 @@ class PresentationTest {
@Test fun everyApprovedBlockKeyHasItsIdent() {
val keys = SmartChannels.flatMap { it.dayParts }.map { it.key }.filter { it.isNotEmpty() }.toSet()
// Riot 3, After Dark 3, Horror, Gold, Three, Vortex, Orbit 2 2, Ring X 7 (six nights and PPV), Terra, and the Vortex Afterburn premiere.
assertEquals(21, keys.size)
// The premiere's own ident isn't made yet: until it is, the standard Vortex ident plays (Stage D1).
assertEquals(setOf("vortex.afterburn.premiere"), keys - BlockIdents.keys)
// Riot 3, After Dark 3, Horror, Gold, Three, Vortex 4 (Movie Night, Prime, Afterburn and its premiere), Orbit 2 2, Ring X 7 (six nights and PPV), Terra.
assertEquals(23, keys.size)
assertEquals(emptySet<String>(), keys - BlockIdents.keys)
assertTrue(keys.all { PRESENTATION_KEY.matches(it) })
}
@@ -9,7 +9,8 @@ import java.io.File
* Four weeks of top-ups on every channel from copies of the box's committed plans and pools, run by hand:
* ORBIT_REPLAY_DIR=<copies of the box's files> ./gradlew :app:testDebugUnitTest --tests '*ReplayFingerprintTest*'
* Writes replay-fingerprints.txt there, one line per channel: a fingerprint of every programme booked and why. Run it
* on two versions of the app to show which channels a change touches. Needs 0.97.0 or later (weighted rotation).
* on two versions of the app to show which channels a change touches. ORBIT_FINGERPRINT_DUMP=<name> also writes each
* channel's full list, to see exactly what changed. Needs 0.97.0 or later (weighted rotation).
*/
class ReplayFingerprintTest {
private val dir = System.getenv("ORBIT_REPLAY_DIR")?.let(::File)
@@ -56,6 +57,7 @@ class ReplayFingerprintTest {
now += 6 * 60 * 60_000L
}
val text = all.joinToString("\n") { "${it.startMs} ${it.seriesId} ${it.mark} ${it.media.item.id} ${it.why} ${it.presentation}" }
System.getenv("ORBIT_FINGERPRINT_DUMP")?.let { File(dir, "$it-${programming.number}.txt").writeText(text) }
val digest = java.security.MessageDigest.getInstance("SHA-256").digest(text.toByteArray()).joinToString("") { "%02x".format(it) }.take(16)
val committedDigest = java.security.MessageDigest.getInstance("SHA-256").digest(all.take(committed.entries.size).joinToString("\n") { "${it.startMs} ${it.seriesId} ${it.mark} ${it.media.item.id} ${it.why} ${it.presentation}" }.toByteArray()).joinToString("") { "%02x".format(it) }.take(16)
out.appendLine("${programming.number} committed=$committedDigest kept=${all.take(committed.entries.size) == committed.entries} fourWeeks=$digest programmes=${all.size}")
+16
View File
@@ -0,0 +1,16 @@
# 0.102.0: Vortex Prime and Afterburn idents
Paul approved the Batch 6 previews on 4 November 2026 ("they all look amazing").
- **New presentation keys** on Vortex's day parts: `vortex.prime` (16:00 to 18:00) and `vortex.afterburn` (21:00 to 23:00).
- **Premiere:** `vortex.afterburn.premiere` now has its own clip; until now it fell back to the standard Vortex ident.
- **Assets:** `res/raw/block_vortex_prime|afterburn|premiere.mp4` and `_sting.m4a` (AAC 32 kbps). Stings play only with Special ident sound on.
## Checks
- 467 unit tests run, 0 failures; lint clean. PresentationTest now expects 23 keys, every one with its ident.
- **Four-week replay** from the box's plans and pools at 18:27 today (`ReplayFingerprintTest`, with the new `ORBIT_FINGERPRINT_DUMP` to write each channel's full list):
- Every channel's fingerprint is identical to 0.101.0 except Vortex.
- Vortex's 2,190 programmes are identical apart from the key: 145 now carry `vortex.prime` and 117 `vortex.afterburn`.
- Committed bookings are unchanged (`kept=true`). The first keyed booking is Wednesday 11 November at 16:10, because bookings already made keep no key.
- APK: `outputs/revision-0.102.0`, signed. Not installed.