0.81.4: Drive-In days turn over at midnight, lineup soonest first, changeovers in playback notes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# Orbit TV 0.81.4: Drive-In days and order
|
||||
|
||||
- Drive-In day names turn over at midnight. After 00:00, today's 20:00 bill says Tonight, not Tomorrow. A late show still belongs to the evening it starts on.
|
||||
- The Drive-In lineup lists events soonest first, so both of tonight's come before Friday and Saturday.
|
||||
- Playback notes now record each changeover, and whether an ident was shown, so a missed ident can be explained.
|
||||
- Unit tests: 309 passed, 0 failed. Lint passed.
|
||||
|
||||
Not yet checked on the box. It refused adb this morning, so wireless debugging is probably off after a restart.
|
||||
@@ -9,8 +9,8 @@ android {
|
||||
applicationId = "tv.orbit.classic"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 94
|
||||
versionName = "0.81.3-audio-choice"
|
||||
versionCode = 95
|
||||
versionName = "0.81.4-drive-in-days"
|
||||
}
|
||||
buildFeatures { compose = true }
|
||||
compileOptions { sourceCompatibility = JavaVersion.VERSION_17; targetCompatibility = JavaVersion.VERSION_17 }
|
||||
|
||||
@@ -165,16 +165,17 @@ internal fun driveInBills(plan: ChannelPlan, zone: ZoneId = ZoneId.systemDefault
|
||||
}
|
||||
}
|
||||
|
||||
/** The next of each kind, for the list on screen. */
|
||||
/** The next of each kind, for the list on screen, soonest first. */
|
||||
internal fun driveInLineup(bills: List<DriveInBill>, nowMs: Long): List<DriveInBill> =
|
||||
DriveInKind.values().mapNotNull { kind -> bills.firstOrNull { it.kind == kind && it.endMs > nowMs } }
|
||||
DriveInKind.values().mapNotNull { kind -> bills.firstOrNull { it.kind == kind && it.endMs > nowMs } }.sortedBy { it.startMs }
|
||||
|
||||
internal fun driveInAiring(plan: ChannelPlan, nowMs: Long) = plan.entries.any { it.startMs <= nowMs && nowMs < it.endMs }
|
||||
|
||||
internal fun driveInDay(bill: DriveInBill, nowMs: Long, zone: ZoneId = ZoneId.systemDefault()): String {
|
||||
if (bill.startMs <= nowMs) return "Now showing"
|
||||
// A late show belongs to the evening it starts on. Today turns over at midnight, as it does on a clock.
|
||||
val date = eveningOf(bill.startMs, zone)
|
||||
val today = eveningOf(nowMs, zone)
|
||||
val today = Instant.ofEpochMilli(nowMs).atZone(zone).toLocalDate()
|
||||
return when (date) {
|
||||
today -> "Tonight"
|
||||
today.plusDays(1) -> "Tomorrow"
|
||||
|
||||
@@ -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.81.3\"")
|
||||
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.81.4\"")
|
||||
request.token?.let { connection.setRequestProperty("X-Emby-Token", it) }
|
||||
if (request.method == "POST") {
|
||||
val bytes = request.body.orEmpty().toByteArray(Charsets.UTF_8)
|
||||
|
||||
@@ -57,9 +57,17 @@ internal fun ScheduledChannelPlayer(session: EmbySampleSession, channel: SampleC
|
||||
val identsOn = remember { OrbitSettings(settingsContext).idents }
|
||||
val identsHere = channel is ScheduledChannel && clockOffsetMs == 0L && channel.number != DRIVE_IN_NUMBER && next.startMs == current.endMs && (identsOn || channel.number == TOTS_NUMBER)
|
||||
val phase = if (identsHere) identPhase(current.startMs, current.endMs, now, identMs) else IdentPhase.None
|
||||
// Each changeover in the playback notes, so a missing ident can be explained afterwards.
|
||||
val notes = remember { java.io.File(settingsContext.noBackupFilesDir, "schedule") }
|
||||
LaunchedEffect(channel, current.startMs) {
|
||||
PlaybackNotes.add(notes, "channel=${channel.number} programme start=${current.startMs} lengthMs=${current.endMs - current.startMs} nextFollows=${next.startMs == current.endMs} scheduled=${channel is ScheduledChannel} offset=$clockOffsetMs identsOn=$identsOn")
|
||||
}
|
||||
val playerContext = androidx.compose.ui.platform.LocalContext.current
|
||||
var loaded by remember(channel) { mutableStateOf<LoadedNext?>(null) }
|
||||
DisposableEffect(channel) { onDispose { loaded?.discard(session) } }
|
||||
LaunchedEffect(channel, current.startMs, phase == IdentPhase.Showing) {
|
||||
if (phase == IdentPhase.Showing) PlaybackNotes.add(notes, "channel=${channel.number} ident shown next=${next.startMs} loaded=${loaded?.startMs == next.startMs}")
|
||||
}
|
||||
// A problem here only means the next show tunes in the ordinary way.
|
||||
LaunchedEffect(channel, current.startMs, phase == IdentPhase.None) {
|
||||
if (phase == IdentPhase.None || loaded?.startMs == next.startMs) return@LaunchedEffect
|
||||
|
||||
@@ -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.81.3")
|
||||
next.setRequestProperty("User-Agent", "OrbitTV/0.81.4")
|
||||
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()) {
|
||||
|
||||
@@ -78,7 +78,15 @@ class DriveInTest {
|
||||
@Test fun theLineupIsTheNextOfEachKind() {
|
||||
val plan = planDriveIn(null, big, late, bills, at(2, 10), london)!!
|
||||
val lineup = driveInLineup(driveInBills(plan, london), at(2, 10))
|
||||
assertEquals(DriveInKind.values().toList(), lineup.map { it.kind })
|
||||
assertEquals(DriveInKind.values().toSet(), lineup.map { it.kind }.toSet())
|
||||
assertEquals(lineup.sortedBy { it.startMs }, lineup)
|
||||
}
|
||||
|
||||
@Test fun afterMidnightTheNewDayIsTonight() {
|
||||
val plan = planDriveIn(null, big, late, bills, at(2, 10), london)!!
|
||||
val tuesday = driveInBills(plan, london).first { it.startMs >= at(3, 6) }
|
||||
assertEquals("Tonight", driveInDay(tuesday, at(3, 0, 30), london))
|
||||
assertEquals("Tomorrow", driveInDay(tuesday, at(2, 23, 30), london))
|
||||
}
|
||||
|
||||
@Test fun dayNamesCountTheSmallHoursAsTheNightBefore() {
|
||||
|
||||
Reference in New Issue
Block a user