0.105.0: story grouping on Vortex
pauljd/davidson-ci: Davidson CI / quality (push) Failing after 2m7s
pauljd/davidson-ci: Davidson CI / ai-review (push) Failing after 1m43s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-04 19:15:58 +00:00
co-authored by Claude Opus 5.5
parent a67b3fbcec
commit dfbc865810
6 changed files with 40 additions and 8 deletions
+2 -2
View File
@@ -9,8 +9,8 @@ android {
applicationId = "tv.orbit.classic"
minSdk = 26
targetSdk = 36
versionCode = 153
versionName = "0.104.0-orbit-x-stories"
versionCode = 154
versionName = "0.105.0-vortex-stories"
}
buildFeatures { compose = true }
compileOptions { sourceCompatibility = JavaVersion.VERSION_17; targetCompatibility = JavaVersion.VERSION_17 }
@@ -182,7 +182,7 @@ internal val SmartChannels = listOf(
SuggestionRule("day", "Daytime", "Movie", listOf("Thriller", "Mystery"), 1975..2005, 6.5f, setOf("NC-17", "X"), 30, NotCultCinema + "Science Fiction", 70, 7.9f),
SuggestionRule("late", "Late night", "Movie", listOf("Horror"), 1970..2010, 6f, emptySet(), 90, NotCultCinema, 70))),
ChannelProgramming(VORTEX_NUMBER, "Vortex", VortexShows, seriesCount = 40, finishTogether = true, exactTitles = true, englishAudio = false, tierWeights = VortexTiers, catalogueScaleHours = CATALOGUE_SCALE_HOURS,
dayParts = VortexDayParts, withdrawn = setOf(BEERUS_OTHER_COPY),
dayParts = VortexDayParts, withdrawn = setOf(BEERUS_OTHER_COPY), storyGrouping = true,
suggestionRules = listOf(
SuggestionRule("movie", "Movie Night", "Movie", listOf("Animation"), 1985..2025, 5f, setOf("NC-17", "X"), 30, setOf("Documentary"), 30, titleContains = "Dragon Ball"),
SuggestionRule("movie", "Movie Night", "Movie", listOf("Animation", "Action"), 1985..2015, 7f, setOf("NC-17", "X"), 30, setOf("Documentary", "Family"), 60),
@@ -201,7 +201,7 @@ internal val SmartChannels = listOf(
catalogueScaleHours = if (it.number in CatalogueScaledChannels) CATALOGUE_SCALE_HOURS else null,
catalogueScaleFloor = CatalogueScaleFloors[it.number] ?: MIN_CATALOGUE_SCALE, storyGrouping = it.number in StoryGroupingChannels) }
/** Living Network Stage E: story grouping beyond Paradox, one channel at a time. Heroes first (4 November 2026): 119 story parts a week, none booked together; then Orbit X. */
/** Living Network Stage E: story grouping beyond Paradox, one channel at a time. Heroes first (4 November 2026): 119 story parts a week, none booked together; then Orbit X. Vortex sets its own, above. */
private val StoryGroupingChannels get() = setOf(602, 603)
/**
@@ -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.104.0\"")
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.105.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.104.0")
next.setRequestProperty("User-Agent", "OrbitTV/0.105.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()) {
@@ -129,8 +129,8 @@ class StoryGroupingTest {
}
@Test fun onlyApprovedChannelsGroupStories() {
// Paradox (Stage A), then Heroes and Orbit X (Stage E, one channel at a time). Grouping works through weighted rotation.
assertEquals(listOf(106, 602, 603), SmartChannels.filter { it.storyGrouping }.map { it.number }.sorted())
// Paradox (Stage A), then Heroes, Orbit X and Vortex (Stage E, one channel at a time). Grouping works through weighted rotation.
assertEquals(listOf(106, 601, 602, 603), SmartChannels.filter { it.storyGrouping }.map { it.number }.sorted())
assertTrue(SmartChannels.filter { it.storyGrouping }.all { it.tierWeights != null })
}
}
+32
View File
@@ -0,0 +1,32 @@
# 0.105.0: story grouping on Vortex (Stage E, third channel)
Vortex's `ChannelProgramming` now has `storyGrouping = true`. Heroes and Orbit X stay on `StoryGroupingChannels`.
## Checks
- 467 unit tests run, 0 failures; lint clean.
- **Four-week replay** from the box's plans and pools at 18:27 today. Every fingerprint is identical to 0.104.0 except Vortex, and Vortex's committed bookings are unchanged.
- **Vortex over the four weeks:**
| | 0.104.0 | 0.105.0 |
|---|---|---|
| Programmes | 1,707 | 1,705 |
| Story openers followed by part 2 | 0 | 57 |
| Openers left hanging | 58 | 2 |
| Later parts aired alone | 76 | 11 |
| Same episode again within 7 days | 1 | 0 |
- **Afterburn premiere:** still on time, episodes 1 and 2 on 21 November and 3 and 4 on 28 November.
| Night | 0.104.0 | 0.105.0 |
|---|---|---|
| 21 November | 21:08 | 21:02 |
| 28 November | 21:00 | 21:07 |
The 28 November start is within the premiere's 10-minute grace. Dragon Ball Movie Night bookings: 20 before, 21 after.
**What's left apart:**
- **Attack on Titan:** most of the 11 later parts are its Struggle for Trost, which runs 9 parts. That's past the 6-part limit, so it isn't grouped. It plays in order across days, as a serial, which is the case Paul's "serial slots" idea covers.
- **Two others:** a Yu-Gi-Oh! Zexal opener and a Gravity Falls part 2 whose part 1 isn't numbered as one.
APK: `outputs/revision-0.105.0`, signed. Not installed.