From 236849b6ebbcaf4d0f22422caca6056bcac1ff05 Mon Sep 17 00:00:00 2001 From: Secozzi <49240133+Secozzi@users.noreply.github.com> Date: Sat, 11 Nov 2023 11:18:45 +0000 Subject: [PATCH] feat(localanime): Allow for custom episode data (#1203) --- .../core/metadata/tachiyomi/ChapterDetails.kt | 11 ++++++ .../core/metadata/tachiyomi/EpisodeDetails.kt | 11 ++++++ .../local/entries/anime/LocalAnimeSource.kt | 35 ++++++++++++++++-- .../local/entries/manga/LocalMangaSource.kt | 36 +++++++++++++++++-- 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/ChapterDetails.kt create mode 100644 core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/EpisodeDetails.kt diff --git a/core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/ChapterDetails.kt b/core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/ChapterDetails.kt new file mode 100644 index 000000000..b32c56194 --- /dev/null +++ b/core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/ChapterDetails.kt @@ -0,0 +1,11 @@ +package tachiyomi.core.metadata.tachiyomi + +import kotlinx.serialization.Serializable + +@Serializable +class ChapterDetails( + val chapter_number: Float, + val name: String? = null, + val date_upload: String? = null, + val scanlator: String? = null, +) diff --git a/core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/EpisodeDetails.kt b/core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/EpisodeDetails.kt new file mode 100644 index 000000000..b2ebf2798 --- /dev/null +++ b/core-metadata/src/main/java/tachiyomi/core/metadata/tachiyomi/EpisodeDetails.kt @@ -0,0 +1,11 @@ +package tachiyomi.core.metadata.tachiyomi + +import kotlinx.serialization.Serializable + +@Serializable +class EpisodeDetails( + val episode_number: Float, + val name: String? = null, + val date_upload: String? = null, + val scanlator: String? = null, +) diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/anime/LocalAnimeSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/anime/LocalAnimeSource.kt index 15da3f17b..ad2080821 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/anime/LocalAnimeSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/anime/LocalAnimeSource.kt @@ -17,6 +17,7 @@ import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority import rx.Observable import tachiyomi.core.metadata.tachiyomi.AnimeDetails +import tachiyomi.core.metadata.tachiyomi.EpisodeDetails import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.system.logcat import tachiyomi.domain.entries.anime.model.Anime @@ -28,7 +29,10 @@ import tachiyomi.source.local.io.ArchiveAnime import tachiyomi.source.local.io.anime.LocalAnimeSourceFileSystem import uy.kohesive.injekt.injectLazy import java.io.File +import java.text.SimpleDateFormat +import java.util.Locale import java.util.concurrent.TimeUnit +import kotlin.math.abs actual class LocalAnimeSource( private val context: Context, @@ -138,7 +142,7 @@ actual class LocalAnimeSource( val animeDirFiles = fileSystem.getFilesInAnimeDirectory(anime.url).toList() animeDirFiles - .firstOrNull { it.extension == "json" } + .firstOrNull { it.extension == "json" && it.nameWithoutExtension == "details" } ?.let { file -> json.decodeFromStream(file.inputStream()).run { title?.let { anime.title = it } @@ -155,6 +159,15 @@ actual class LocalAnimeSource( // Episodes override suspend fun getEpisodeList(anime: SAnime): List { + val episodesData = fileSystem.getFilesInAnimeDirectory(anime.url) + .firstOrNull { + it.extension == "json" && it.nameWithoutExtension == "episodes" + }?.let { file -> + runCatching { + json.decodeFromStream>(file.inputStream()) + }.getOrNull() + } + return fileSystem.getFilesInAnimeDirectory(anime.url) // Only keep supported formats .filter { it.isDirectory || ArchiveAnime.isSupported(it) } @@ -168,11 +181,21 @@ actual class LocalAnimeSource( } date_upload = episodeFile.lastModified() - episode_number = EpisodeRecognition.parseEpisodeNumber( + val episodeNumber = EpisodeRecognition.parseEpisodeNumber( anime.title, this.name, this.episode_number, ) + episode_number = episodeNumber + + // Overwrite data from episodes.json file + episodesData?.also { dataList -> + dataList.firstOrNull { it.episode_number.equalsTo(episodeNumber) }?.also { data -> + data.name?.also { name = it } + data.date_upload?.also { date_upload = parseDate(it) } + scanlator = data.scanlator + } + } } } .sortedWith { e1, e2 -> @@ -182,6 +205,14 @@ actual class LocalAnimeSource( .toList() } + private fun parseDate(isoDate: String): Long { + return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()).parse(isoDate)?.time ?: 0L + } + + private fun Float.equalsTo(other: Float): Boolean { + return abs(this - other) < 0.0001 + } + // Filters override fun getFilterList() = AnimeFilterList(AnimeOrderBy.Popular(context)) diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/manga/LocalMangaSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/manga/LocalMangaSource.kt index f762690d3..bf3f40223 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/manga/LocalMangaSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/entries/manga/LocalMangaSource.kt @@ -20,6 +20,7 @@ import rx.Observable import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.copyFromComicInfo +import tachiyomi.core.metadata.tachiyomi.ChapterDetails import tachiyomi.core.metadata.tachiyomi.MangaDetails import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.system.ImageUtil @@ -39,8 +40,11 @@ import java.io.File import java.io.FileInputStream import java.io.InputStream import java.nio.charset.StandardCharsets +import java.text.SimpleDateFormat +import java.util.Locale import java.util.concurrent.TimeUnit import java.util.zip.ZipFile +import kotlin.math.abs import com.github.junrar.Archive as JunrarArchive actual class LocalMangaSource( @@ -162,7 +166,7 @@ actual class LocalMangaSource( val noXmlFile = mangaDirFiles .firstOrNull { it.name == ".noxml" } val legacyJsonDetailsFile = mangaDirFiles - .firstOrNull { it.extension == "json" } + .firstOrNull { it.extension == "json" && it.nameWithoutExtension == "details" } when { // Top level ComicInfo.xml @@ -256,6 +260,15 @@ actual class LocalMangaSource( // Chapters override suspend fun getChapterList(manga: SManga): List { + val chaptersData = fileSystem.getFilesInMangaDirectory(manga.url) + .firstOrNull { + it.extension == "json" && it.nameWithoutExtension == "chapters" + }?.let { file -> + runCatching { + json.decodeFromStream>(file.inputStream()) + }.getOrNull() + } + return fileSystem.getFilesInMangaDirectory(manga.url) // Only keep supported formats .filter { it.isDirectory || ArchiveManga.isSupported(it) } @@ -268,11 +281,13 @@ actual class LocalMangaSource( chapterFile.nameWithoutExtension } date_upload = chapterFile.lastModified() - chapter_number = ChapterRecognition.parseChapterNumber( + + val chapterNumber = ChapterRecognition.parseChapterNumber( manga.title, this.name, this.chapter_number, ) + chapter_number = chapterNumber val format = Format.valueOf(chapterFile) if (format is Format.Epub) { @@ -280,6 +295,15 @@ actual class LocalMangaSource( epub.fillChapterMetadata(this) } } + + // Overwrite data from chapters.json file + chaptersData?.also { dataList -> + dataList.firstOrNull { it.chapter_number.equalsTo(chapterNumber) }?.also { data -> + data.name?.also { name = it } + data.date_upload?.also { date_upload = parseDate(it) } + scanlator = data.scanlator + } + } } } .sortedWith { c1, c2 -> @@ -289,6 +313,14 @@ actual class LocalMangaSource( .toList() } + private fun parseDate(isoDate: String): Long { + return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()).parse(isoDate)?.time ?: 0L + } + + private fun Float.equalsTo(other: Float): Boolean { + return abs(this - other) < 0.0001 + } + // Filters override fun getFilterList() = FilterList(MangaOrderBy.Popular(context))