From 258a69d4dc28bf1b071f40ced0f1e69581ef59e1 Mon Sep 17 00:00:00 2001 From: ModerRAS Date: Fri, 10 Jul 2026 18:33:59 +0800 Subject: [PATCH] Import MLIP release dates --- .../tv/scanner/MlipLibraryIndexImporter.kt | 28 ++++++++++++++++-- .../scanner/MlipLibraryIndexImporterTest.kt | 29 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/scanner/src/main/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporter.kt b/scanner/src/main/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporter.kt index df550222..be6178fe 100644 --- a/scanner/src/main/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporter.kt +++ b/scanner/src/main/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporter.kt @@ -169,8 +169,15 @@ class MlipLibraryIndexImporter @Inject constructor( private fun readSnapshot(database: SQLiteDatabase, sourceId: Long): MlipSnapshot { val genresBySeriesId = database.readGenresBySeriesId() val externalIdsBySeriesId = database.readExternalIdsBySeriesId() + val releaseDatesBySeriesId = database.readReleaseDatesBySeriesId() val posterBySeriesId = database.readPosterBySeriesId() - val seriesById = database.readSeries(sourceId, genresBySeriesId, externalIdsBySeriesId, posterBySeriesId) + val seriesById = database.readSeries( + sourceId, + genresBySeriesId, + externalIdsBySeriesId, + releaseDatesBySeriesId, + posterBySeriesId, + ) val mediaFiles = mutableListOf() var skippedFiles = 0 var nonIntegerEpisodes = 0 @@ -264,6 +271,7 @@ class MlipLibraryIndexImporter @Inject constructor( sourceId: Long, genresBySeriesId: Map>, externalIdsBySeriesId: Map, + releaseDatesBySeriesId: Map, posterBySeriesId: Map, ): Map { val result = linkedMapOf() @@ -288,7 +296,8 @@ class MlipLibraryIndexImporter @Inject constructor( titleCn = title.takeIf { originalTitle != null && it != originalTitle }, summary = cursor.stringOrNull("summary").orEmpty(), genres = genresBySeriesId[id].orEmpty(), - airDate = cursor.intOrNull("year")?.takeIf { it > 0 }?.toString(), + airDate = releaseDatesBySeriesId[id] + ?: cursor.intOrNull("year")?.takeIf { it > 0 }?.toString(), bangumiId = externalIds.bangumiId, tmdbId = externalIds.tmdbId, ) @@ -303,6 +312,21 @@ class MlipLibraryIndexImporter @Inject constructor( return result } + private fun SQLiteDatabase.readReleaseDatesBySeriesId(): Map { + if ("series_release_date" !in tableNames()) return emptyMap() + val result = mutableMapOf() + rawQuery( + "SELECT series_id, air_date FROM series_release_date", + emptyArray(), + ).use { cursor -> + while (cursor.moveToNext()) { + val airDate = cursor.string("air_date").trim() + if (airDate.isNotEmpty()) result[cursor.long("series_id")] = airDate + } + } + return result + } + private fun SQLiteDatabase.readGenresBySeriesId(): Map> { val result = mutableMapOf>() rawQuery( diff --git a/scanner/src/test/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporterTest.kt b/scanner/src/test/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporterTest.kt index 6b7df406..1a659b28 100644 --- a/scanner/src/test/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporterTest.kt +++ b/scanner/src/test/kotlin/com/miruplay/tv/scanner/MlipLibraryIndexImporterTest.kt @@ -236,7 +236,7 @@ class MlipLibraryIndexImporterTest { assertEquals("Original Title", anime.title) assertEquals("中文标题", anime.titleCn) assertEquals("简介", anime.summary) - assertEquals("2024", anime.airDate) + assertEquals("2024-04-03", anime.airDate) assertEquals(listOf("科幻"), anime.genres) assertEquals(431767, anime.bangumiId) assertEquals(98765, anime.tmdbId) @@ -256,6 +256,28 @@ class MlipLibraryIndexImporterTest { } } + @Test + fun `legacy mlip database falls back to release year`() = runBlocking { + val databaseFile = File.createTempFile("mlip-test-", ".db") + val source = mlipSource() + val metadataRepository = RecordingMetadataRepository() + val mediaSource = FakeMediaSource( + info = source, + streams = mapOf("library.db" to { databaseFile.inputStream() }), + ) + try { + createMlipDatabase(databaseFile, includeReleaseDate = false) + + val result = MlipLibraryIndexImporter(RecordingIndexRepository(), metadataRepository) + .importLibrary(source, mediaSource) + + assertTrue(result.isSuccess()) + assertEquals("2024", metadataRepository.anime.single().airDate) + } finally { + databaseFile.delete() + } + } + private suspend fun importDatabase(databaseFile: File): Result { val source = mlipSource() val mediaSource = FakeMediaSource( @@ -284,6 +306,7 @@ class MlipLibraryIndexImporterTest { file: File, userVersion: Int = 1, includeRequiredTables: Boolean = true, + includeReleaseDate: Boolean = true, mediaPath: String = "Series/01.mkv", ) { file.delete() @@ -303,6 +326,10 @@ class MlipLibraryIndexImporterTest { database.execSQL("CREATE TABLE series_external_id (series_id INTEGER NOT NULL, provider INTEGER NOT NULL, value TEXT NOT NULL)") database.execSQL("CREATE TABLE episode_external_id (episode_id INTEGER NOT NULL, provider INTEGER NOT NULL, value TEXT NOT NULL)") database.execSQL("CREATE TABLE capability (key TEXT PRIMARY KEY, value TEXT NOT NULL)") + if (includeReleaseDate) { + database.execSQL("CREATE TABLE series_release_date (series_id INTEGER PRIMARY KEY, air_date TEXT NOT NULL)") + database.execSQL("INSERT INTO series_release_date (series_id, air_date) VALUES (1, '2024-04-03')") + } database.execSQL("INSERT INTO series (id, uuid, title, original_title, summary, year, series_type) VALUES (1, 'series-uuid', '中文标题', 'Original Title', '简介', 2024, 1)") database.execSQL("INSERT INTO episode (id, uuid, series_id, season, episode, sort_order, title, summary, runtime) VALUES (10, 'episode-uuid-1', 1, 1, 1.0, 1.0, '第 1 集', '', 1440)") database.execSQL("INSERT INTO episode (id, uuid, series_id, season, episode, sort_order, title, summary, runtime) VALUES (11, 'episode-uuid-1-5', 1, 1, 1.5, 1.5, '第 1.5 集', '', 1440)")