feat(localanime): Allow for custom episode data (#1203)

This commit is contained in:
Secozzi 2023-11-11 11:18:45 +00:00 committed by GitHub
parent 8b1934fc36
commit 236849b6eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 4 deletions

View file

@ -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,
)

View file

@ -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,
)

View file

@ -17,6 +17,7 @@ import kotlinx.serialization.json.decodeFromStream
import logcat.LogPriority import logcat.LogPriority
import rx.Observable import rx.Observable
import tachiyomi.core.metadata.tachiyomi.AnimeDetails import tachiyomi.core.metadata.tachiyomi.AnimeDetails
import tachiyomi.core.metadata.tachiyomi.EpisodeDetails
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.entries.anime.model.Anime 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 tachiyomi.source.local.io.anime.LocalAnimeSourceFileSystem
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.abs
actual class LocalAnimeSource( actual class LocalAnimeSource(
private val context: Context, private val context: Context,
@ -138,7 +142,7 @@ actual class LocalAnimeSource(
val animeDirFiles = fileSystem.getFilesInAnimeDirectory(anime.url).toList() val animeDirFiles = fileSystem.getFilesInAnimeDirectory(anime.url).toList()
animeDirFiles animeDirFiles
.firstOrNull { it.extension == "json" } .firstOrNull { it.extension == "json" && it.nameWithoutExtension == "details" }
?.let { file -> ?.let { file ->
json.decodeFromStream<AnimeDetails>(file.inputStream()).run { json.decodeFromStream<AnimeDetails>(file.inputStream()).run {
title?.let { anime.title = it } title?.let { anime.title = it }
@ -155,6 +159,15 @@ actual class LocalAnimeSource(
// Episodes // Episodes
override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> { override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
val episodesData = fileSystem.getFilesInAnimeDirectory(anime.url)
.firstOrNull {
it.extension == "json" && it.nameWithoutExtension == "episodes"
}?.let { file ->
runCatching {
json.decodeFromStream<List<EpisodeDetails>>(file.inputStream())
}.getOrNull()
}
return fileSystem.getFilesInAnimeDirectory(anime.url) return fileSystem.getFilesInAnimeDirectory(anime.url)
// Only keep supported formats // Only keep supported formats
.filter { it.isDirectory || ArchiveAnime.isSupported(it) } .filter { it.isDirectory || ArchiveAnime.isSupported(it) }
@ -168,11 +181,21 @@ actual class LocalAnimeSource(
} }
date_upload = episodeFile.lastModified() date_upload = episodeFile.lastModified()
episode_number = EpisodeRecognition.parseEpisodeNumber( val episodeNumber = EpisodeRecognition.parseEpisodeNumber(
anime.title, anime.title,
this.name, this.name,
this.episode_number, 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 -> .sortedWith { e1, e2 ->
@ -182,6 +205,14 @@ actual class LocalAnimeSource(
.toList() .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 // Filters
override fun getFilterList() = AnimeFilterList(AnimeOrderBy.Popular(context)) override fun getFilterList() = AnimeFilterList(AnimeOrderBy.Popular(context))

View file

@ -20,6 +20,7 @@ import rx.Observable
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfo
import tachiyomi.core.metadata.comicinfo.copyFromComicInfo import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
import tachiyomi.core.metadata.tachiyomi.ChapterDetails
import tachiyomi.core.metadata.tachiyomi.MangaDetails import tachiyomi.core.metadata.tachiyomi.MangaDetails
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.ImageUtil import tachiyomi.core.util.system.ImageUtil
@ -39,8 +40,11 @@ import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.InputStream import java.io.InputStream
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.zip.ZipFile import java.util.zip.ZipFile
import kotlin.math.abs
import com.github.junrar.Archive as JunrarArchive import com.github.junrar.Archive as JunrarArchive
actual class LocalMangaSource( actual class LocalMangaSource(
@ -162,7 +166,7 @@ actual class LocalMangaSource(
val noXmlFile = mangaDirFiles val noXmlFile = mangaDirFiles
.firstOrNull { it.name == ".noxml" } .firstOrNull { it.name == ".noxml" }
val legacyJsonDetailsFile = mangaDirFiles val legacyJsonDetailsFile = mangaDirFiles
.firstOrNull { it.extension == "json" } .firstOrNull { it.extension == "json" && it.nameWithoutExtension == "details" }
when { when {
// Top level ComicInfo.xml // Top level ComicInfo.xml
@ -256,6 +260,15 @@ actual class LocalMangaSource(
// Chapters // Chapters
override suspend fun getChapterList(manga: SManga): List<SChapter> { override suspend fun getChapterList(manga: SManga): List<SChapter> {
val chaptersData = fileSystem.getFilesInMangaDirectory(manga.url)
.firstOrNull {
it.extension == "json" && it.nameWithoutExtension == "chapters"
}?.let { file ->
runCatching {
json.decodeFromStream<List<ChapterDetails>>(file.inputStream())
}.getOrNull()
}
return fileSystem.getFilesInMangaDirectory(manga.url) return fileSystem.getFilesInMangaDirectory(manga.url)
// Only keep supported formats // Only keep supported formats
.filter { it.isDirectory || ArchiveManga.isSupported(it) } .filter { it.isDirectory || ArchiveManga.isSupported(it) }
@ -268,11 +281,13 @@ actual class LocalMangaSource(
chapterFile.nameWithoutExtension chapterFile.nameWithoutExtension
} }
date_upload = chapterFile.lastModified() date_upload = chapterFile.lastModified()
chapter_number = ChapterRecognition.parseChapterNumber(
val chapterNumber = ChapterRecognition.parseChapterNumber(
manga.title, manga.title,
this.name, this.name,
this.chapter_number, this.chapter_number,
) )
chapter_number = chapterNumber
val format = Format.valueOf(chapterFile) val format = Format.valueOf(chapterFile)
if (format is Format.Epub) { if (format is Format.Epub) {
@ -280,6 +295,15 @@ actual class LocalMangaSource(
epub.fillChapterMetadata(this) 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 -> .sortedWith { c1, c2 ->
@ -289,6 +313,14 @@ actual class LocalMangaSource(
.toList() .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 // Filters
override fun getFilterList() = FilterList(MangaOrderBy.Popular(context)) override fun getFilterList() = FilterList(MangaOrderBy.Popular(context))