diff --git a/android/app/src/main/java/tv/orbit/classic/ChannelScheduler.kt b/android/app/src/main/java/tv/orbit/classic/ChannelScheduler.kt index e3ba891..04ab18c 100644 --- a/android/app/src/main/java/tv/orbit/classic/ChannelScheduler.kt +++ b/android/app/src/main/java/tv/orbit/classic/ChannelScheduler.kt @@ -3,6 +3,7 @@ package tv.orbit.classic import android.util.Log import org.json.JSONArray import org.json.JSONObject +import java.time.LocalDateTime internal const val POOL_REFRESH_MS = 7 * 24 * 60 * 60_000L @@ -161,8 +162,14 @@ internal class ChannelScheduler(private val store: ScheduleStore, private val cl val checks = try { session.readFilmChecks(bills.flatMap { b -> b.films.map { EventFilm(it.title, it.year ?: 0, it.imdb.orEmpty(), it.id) to null } }) } catch (failure: EmbyCheckException) { emptyMap() } val verified = bills.filter { b -> b.films.all { checks[it.id]?.film != null } } val verifiedSeeds = seeds.filter { s -> verified.any { it.key.startsWith(s.key + "|") } } + // This week's ideas already shown, so a new one never repeats one, nor takes its films or its evening. + val current = EventIdeaHistory.load(historyFile).filter(::live).map { h -> + val ids = h.getJSONArray("films").let { a -> (0 until a.length()).map { a.getString(it) } } + EventIdea(h.optString("signature").substringBeforeLast(':'), h.optString("theme"), h.optString("source"), runCatching { IdeaFormat.valueOf(h.optString("format")) }.getOrDefault(IdeaFormat.FridayDouble), + h.optString("evening").takeIf { it.isNotEmpty() && it != "null" }?.let { LocalDateTime.parse(it) }, ids.mapNotNull { films[it] }, emptyList(), 0.0) + } val ideas = weeklyEventIdeas(verifiedSeeds, films, unavailable, { format, taken -> ideaEvening(format, today.plusDays(1), drivePlanEnd, taken, format.films * 2 * 60 * 60_000L, zone) }, - EventIdeaHistory.dismissed(historyFile), limit = (EVENT_IDEAS_PER_WEEK - shownThisWeek).coerceAtLeast(0)) + EventIdeaHistory.dismissed(historyFile), limit = (EVENT_IDEAS_PER_WEEK - shownThisWeek).coerceAtLeast(0), current = current) EventIdeaHistory.record(historyFile, ideas, now) val thisWeek = EventIdeaHistory.load(historyFile).filter(::live) runCatching { file.writeText(JSONObject().put("day", today.toString()).put("rules", EVENT_IDEA_RULES).put("ideas", JSONArray(thisWeek.map { h -> diff --git a/android/app/src/main/java/tv/orbit/classic/EventIdeas.kt b/android/app/src/main/java/tv/orbit/classic/EventIdeas.kt index 42fc291..4537f8c 100644 --- a/android/app/src/main/java/tv/orbit/classic/EventIdeas.kt +++ b/android/app/src/main/java/tv/orbit/classic/EventIdeas.kt @@ -202,9 +202,11 @@ internal fun billFor(seed: IdeaSeed, films: Map, unavailable: * worthwhile proposes nothing. */ internal fun weeklyEventIdeas(seeds: List, films: Map, unavailable: (IdeaFilm) -> String?, freeEvening: (IdeaFormat, Set) -> LocalDateTime?, - dismissed: Set, limit: Int = EVENT_IDEAS_PER_WEEK): List { - val bills = seeds.mapNotNull { billFor(it, films, unavailable).first }.filter { it.signature !in dismissed }.sortedByDescending { it.score } - val out = mutableListOf(); val taken = mutableSetOf(); val used = mutableSetOf() + dismissed: Set, limit: Int = EVENT_IDEAS_PER_WEEK, + /** Ideas already shown this week: never proposed twice, and their films and evenings are taken. */ + current: List = emptyList()): List { + val bills = seeds.mapNotNull { billFor(it, films, unavailable).first }.filter { it.signature !in dismissed && current.none { c -> c.key.substringBefore('|') == it.key.substringBefore('|') } }.sortedByDescending { it.score } + val out = mutableListOf(); val taken = current.mapNotNull { it.evening }.toMutableSet(); val used = current.flatMap { c -> c.films.map { it.id } }.toMutableSet() for (b in bills) { if (out.size >= limit) break if (b.films.any { it.id in used }) continue diff --git a/android/app/src/test/java/tv/orbit/classic/EventIdeasTest.kt b/android/app/src/test/java/tv/orbit/classic/EventIdeasTest.kt index 3a713f8..ed25014 100644 --- a/android/app/src/test/java/tv/orbit/classic/EventIdeasTest.kt +++ b/android/app/src/test/java/tv/orbit/classic/EventIdeasTest.kt @@ -62,6 +62,9 @@ class EventIdeasTest { assertTrue(weeklyEventIdeas(seeds, films, none, evening, emptySet(), limit = 0).isEmpty()) val weak = films.mapValues { (_, f) -> IdeaFilm(f.id, f.title, f.year, f.imdb, f.runtimeMs, f.rating, f.genres, f.studios, community = 6.5, notable = true) } assertTrue(weeklyEventIdeas(seeds, weak, none, evening, emptySet()).isEmpty()) // no filler + // Already shown this week: never again, and its evening and films are taken. + val more = weeklyEventIdeas(seeds, films, none, evening, emptySet(), limit = 1, current = listOf(week[0])) + assertTrue(more.none { it.key == week[0].key }); assertTrue(more.none { it.evening == week[0].evening }) val dismissed = setOf(week[0].signature) assertTrue(weeklyEventIdeas(seeds, films, none, evening, dismissed).none { it.signature == week[0].signature }) }