0.108.14: Drive-In event ideas, phase 1, read only (built, not installed)
On this line the competition check knows the season's headlines (Advent's doors, The Naughty List, the approved premieres) as data, and readFilmChecks comes over unchanged from main. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,8 @@ android {
|
||||
applicationId = "tv.orbit.classic"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 173
|
||||
versionName = "0.108.13-arcade"
|
||||
versionCode = 174
|
||||
versionName = "0.108.14-event-ideas"
|
||||
}
|
||||
buildFeatures { compose = true }
|
||||
compileOptions { sourceCompatibility = JavaVersion.VERSION_17; targetCompatibility = JavaVersion.VERSION_17 }
|
||||
|
||||
@@ -134,7 +134,7 @@ internal class ChannelScheduler(private val store: ScheduleStore, private val cl
|
||||
o.optString("name") to o.optJSONArray("members")?.let { a -> (0 until a.length()).map { a.getString(it) } }.orEmpty() } else emptyList()
|
||||
val studios = EventStudios.associateWith { catalogue.filmsByStudio(it.first) }
|
||||
val read = catalogue.ideaFilms(lines.keys).map(::enrich)
|
||||
val held = seasonalFilms().map { it.first.itemId } + ApprovedDebuts.map { it.film.itemId }
|
||||
val held = SeasonHeldFilms
|
||||
val eventFilms = DriveInEvents.flatMap { e -> e.films.map { it.itemId } }.toSet() + held
|
||||
val seeds = ideaSeeds(collections, studios, read, eventFilms)
|
||||
var films = catalogue.ideaFilms(seeds.flatMap { it.members }).associateBy { it.id }.mapValues { enrich(it.value) }
|
||||
|
||||
@@ -11,3 +11,57 @@ internal const val PREMIERE_BOOKING_ACTIVE = false
|
||||
internal const val PREMIERE_WINDOW_MS = 28 * 24 * 60 * 60_000L
|
||||
/** A theatrical premiere's debut: its own label in What's On, apart from a series' FIRST ON ORBIT. */
|
||||
internal const val PREMIERE_WHY = "orbit premiere: debut"
|
||||
|
||||
/**
|
||||
* The headline events Orbit has scheduled for the season (Advent's doors, The Naughty List's nights and the approved premieres,
|
||||
* which arrive with the Advent release), as (what, when, hours): what a Drive-In event idea mustn't compete with on this line.
|
||||
*/
|
||||
private val SeasonHeadlines: List<Triple<String, java.time.LocalDateTime, Int>> = listOf(
|
||||
Triple("Advent's Door 1", java.time.LocalDateTime.of(2026, 12, 1, 18, 0), 2),
|
||||
Triple("Advent's Door 2", java.time.LocalDateTime.of(2026, 12, 2, 18, 0), 2),
|
||||
Triple("Advent's Door 3", java.time.LocalDateTime.of(2026, 12, 3, 18, 0), 2),
|
||||
Triple("Advent's Door 4", java.time.LocalDateTime.of(2026, 12, 4, 18, 0), 2),
|
||||
Triple("Advent's Door 5", java.time.LocalDateTime.of(2026, 12, 5, 18, 0), 2),
|
||||
Triple("Advent's Door 6", java.time.LocalDateTime.of(2026, 12, 6, 18, 0), 2),
|
||||
Triple("Advent's Door 7", java.time.LocalDateTime.of(2026, 12, 7, 18, 0), 2),
|
||||
Triple("Advent's Door 8", java.time.LocalDateTime.of(2026, 12, 8, 18, 0), 2),
|
||||
Triple("Advent's Door 9", java.time.LocalDateTime.of(2026, 12, 9, 18, 0), 2),
|
||||
Triple("Advent's Door 10", java.time.LocalDateTime.of(2026, 12, 10, 18, 0), 2),
|
||||
Triple("Advent's Door 11", java.time.LocalDateTime.of(2026, 12, 11, 18, 0), 2),
|
||||
Triple("Advent's Door 12", java.time.LocalDateTime.of(2026, 12, 12, 18, 0), 2),
|
||||
Triple("Advent's Door 13", java.time.LocalDateTime.of(2026, 12, 13, 18, 0), 2),
|
||||
Triple("Advent's Door 14", java.time.LocalDateTime.of(2026, 12, 14, 18, 0), 2),
|
||||
Triple("Advent's Door 15", java.time.LocalDateTime.of(2026, 12, 15, 18, 0), 2),
|
||||
Triple("Advent's Door 16", java.time.LocalDateTime.of(2026, 12, 16, 18, 0), 2),
|
||||
Triple("Advent's Door 17", java.time.LocalDateTime.of(2026, 12, 17, 18, 0), 2),
|
||||
Triple("Advent's Door 18", java.time.LocalDateTime.of(2026, 12, 18, 18, 0), 2),
|
||||
Triple("Advent's Door 19", java.time.LocalDateTime.of(2026, 12, 19, 17, 0), 2),
|
||||
Triple("Advent's Door 20", java.time.LocalDateTime.of(2026, 12, 20, 18, 0), 2),
|
||||
Triple("Advent's Door 21", java.time.LocalDateTime.of(2026, 12, 21, 18, 0), 2),
|
||||
Triple("Advent's Door 22", java.time.LocalDateTime.of(2026, 12, 22, 18, 0), 2),
|
||||
Triple("Advent's Door 23", java.time.LocalDateTime.of(2026, 12, 23, 18, 0), 2),
|
||||
Triple("Advent's Door 24", java.time.LocalDateTime.of(2026, 12, 24, 18, 0), 2),
|
||||
Triple("Advent's Door 25", java.time.LocalDateTime.of(2026, 12, 25, 17, 0), 2),
|
||||
Triple("The Naughty List's Gremlins", java.time.LocalDateTime.of(2026, 12, 3, 23, 0), 2),
|
||||
Triple("The Naughty List's Krampus", java.time.LocalDateTime.of(2026, 12, 10, 23, 0), 2),
|
||||
Triple("The Naughty List's Better Watch Out", java.time.LocalDateTime.of(2026, 12, 17, 23, 0), 2),
|
||||
Triple("The Naughty List's Saint", java.time.LocalDateTime.of(2026, 12, 21, 23, 0), 2),
|
||||
Triple("The Naughty List's Anna and the Apocalypse", java.time.LocalDateTime.of(2026, 12, 22, 23, 0), 2),
|
||||
Triple("The Naughty List's Black Christmas", java.time.LocalDateTime.of(2026, 12, 23, 23, 0), 2),
|
||||
Triple("the premiere of Avatar: Fire and Ash", java.time.LocalDateTime.of(2026, 11, 27, 20, 0), 3),
|
||||
Triple("the premiere of Spider-Man: Brand New Day", java.time.LocalDateTime.of(2026, 12, 11, 20, 0), 3))
|
||||
|
||||
/** Films held for the season's fixed events on this line: an event idea never uses them. */
|
||||
internal val SeasonHeldFilms: Set<String> = setOf("5688746", "5706930", "5642871", "6048904", "7416998", "5847706", "5848529", "5684856", "6399934", "5848040", "6062933", "5848691", "5689513", "6259980", "6056877", "6035893", "6933347", "6948638", "5848022", "5689633", "6067366", "6048026", "7485376", "5688583", "5847993", "5669960", "6387682", "6398602", "7317412", "6045853", "6053560", "7789551", "8006401")
|
||||
|
||||
/**
|
||||
* Editorial competition (Paul, 5 November 2026): whether a slot from [startMs] to [endMs] shares its airtime with another headline
|
||||
* event anywhere on Orbit (a Drive-In event, an Advent door, a Naughty List night, a premiere). Returns what it competes with, or null.
|
||||
*/
|
||||
internal fun competingEvent(startMs: Long, endMs: Long, zone: java.time.ZoneId = java.time.ZoneId.systemDefault(), except: String? = null): String? {
|
||||
fun ms(t: java.time.LocalDateTime) = t.atZone(zone).toInstant().toEpochMilli()
|
||||
val hour = 60 * 60_000L
|
||||
val events = DriveInEvents.map { e -> Triple("the Drive-In's ${e.name}", ms(e.evening.atTime(e.hour, e.minute)), ms(e.evening.atTime(e.hour, e.minute)) + e.films.size * 2 * hour) } +
|
||||
SeasonHeadlines.filter { except == null || !it.first.contains(except) }.map { (what, at, hours) -> Triple(what, ms(at), ms(at) + hours * hour) }
|
||||
return events.firstOrNull { (_, s, e) -> s < endMs && startMs < e }?.first
|
||||
}
|
||||
|
||||
@@ -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.108.13\"")
|
||||
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.108.14\"")
|
||||
request.token?.let { connection.setRequestProperty("X-Emby-Token", it) }
|
||||
if (request.method == "POST") {
|
||||
val bytes = request.body.orEmpty().toByteArray(Charsets.UTF_8)
|
||||
|
||||
@@ -229,8 +229,11 @@ internal class EmbySampleSession(private val transport: EmbyTransport, private v
|
||||
* The curated Drive-In events' films, read by their exact library items in one request, with their media, IMDb IDs and
|
||||
* cast, each checked by [checkEventFilm]. Read only: nothing is changed in the library.
|
||||
*/
|
||||
fun readEventFilms(events: List<DriveInEvent>): Map<String, EventFilmCheck> {
|
||||
val films = events.flatMap { event -> event.films.map { it to event.person } }.distinctBy { it.first.itemId }
|
||||
fun readEventFilms(events: List<DriveInEvent>): Map<String, EventFilmCheck> = readFilmChecks(events.flatMap { event -> event.films.map { it to event.person } })
|
||||
|
||||
/** Exact films, each with the person its cast must include (or null), read and checked as [readEventFilms] does. */
|
||||
fun readFilmChecks(listed: List<Pair<EventFilm, String?>>): Map<String, EventFilmCheck> {
|
||||
val films = listed.distinctBy { it.first.itemId }
|
||||
val ids = films.map { it.first.itemId }.filter(EmbyClient::validId)
|
||||
if (ids.isEmpty()) return emptyMap()
|
||||
val reply = transport.execute(EmbyRequest("GET", "/Items?UserId=$userId&Ids=${ids.joinToString(",")}&Fields=MediaSources,ProviderIds,ProductionYear,People" +
|
||||
|
||||
@@ -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.108.13")
|
||||
next.setRequestProperty("User-Agent", "OrbitTV/0.108.14")
|
||||
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()) {
|
||||
|
||||
Reference in New Issue
Block a user