Differ extra attempts to load local series' covers until chapter loading

(cherry picked from commit 82bdf634194734851c429d60b68f9ce7c7e51d91)

# Conflicts:
#	source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt
#	source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt
This commit is contained in:
arkon 2023-11-26 22:46:55 -05:00 committed by Jobobby04
parent 22cc0de7cd
commit 2bb0ee9543
3 changed files with 59 additions and 82 deletions

View File

@ -12,6 +12,8 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
import eu.kanade.tachiyomi.util.storage.CbzCrypto import eu.kanade.tachiyomi.util.storage.CbzCrypto
import eu.kanade.tachiyomi.util.storage.EpubFile import eu.kanade.tachiyomi.util.storage.EpubFile
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.encodeToStream
@ -40,8 +42,7 @@ import tachiyomi.source.local.image.LocalCoverManager
import tachiyomi.source.local.io.Archive import tachiyomi.source.local.io.Archive
import tachiyomi.source.local.io.Format import tachiyomi.source.local.io.Format
import tachiyomi.source.local.io.LocalSourceFileSystem import tachiyomi.source.local.io.LocalSourceFileSystem
import tachiyomi.source.local.metadata.fillChapterMetadata import tachiyomi.source.local.metadata.fillMetadata
import tachiyomi.source.local.metadata.fillMangaMetadata
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -80,20 +81,17 @@ actual class LocalSource(
override suspend fun getLatestUpdates(page: Int) = getSearchManga(page, "", LATEST_FILTERS) override suspend fun getLatestUpdates(page: Int) = getSearchManga(page, "", LATEST_FILTERS)
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage { override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage = withIOContext {
val baseDirFiles = fileSystem.getFilesInBaseDirectory() val lastModifiedLimit = if (filters === LATEST_FILTERS) {
val lastModifiedLimit by lazy { System.currentTimeMillis() - LATEST_THRESHOLD
if (filters === LATEST_FILTERS) { } else {
System.currentTimeMillis() - LATEST_THRESHOLD 0L
} else {
0L
}
} }
// SY --> // SY -->
val allowLocalSourceHiddenFolders = allowHiddenFiles() val allowLocalSourceHiddenFolders = allowHiddenFiles()
// SY <-- // SY <--
var mangaDirs = baseDirFiles var mangaDirs = fileSystem.getFilesInBaseDirectory()
// Filter out files that are hidden and is not a folder // Filter out files that are hidden and is not a folder
.filter { .filter {
it.isDirectory && it.isDirectory &&
@ -103,8 +101,10 @@ actual class LocalSource(
) /* SY <-- */ ) /* SY <-- */
} }
.distinctBy { it.name } .distinctBy { it.name }
.filter { // Filter by query or last modified .filter {
if (lastModifiedLimit == 0L) { if (lastModifiedLimit == 0L && query.isBlank()) {
true
} else if (lastModifiedLimit == 0L) {
it.name.orEmpty().contains(query, ignoreCase = true) it.name.orEmpty().contains(query, ignoreCase = true)
} else { } else {
it.lastModified() >= lastModifiedLimit it.lastModified() >= lastModifiedLimit
@ -127,47 +127,29 @@ actual class LocalSource(
mangaDirs.sortedByDescending(UniFile::lastModified) mangaDirs.sortedByDescending(UniFile::lastModified)
} }
} }
else -> { else -> {
/* Do nothing */ /* Do nothing */
} }
} }
} }
// Transform mangaDirs to list of SManga val mangas = mangaDirs
val mangas = mangaDirs.map { mangaDir -> .map { mangaDir ->
SManga.create().apply { async {
title = mangaDir.name.orEmpty() SManga.create().apply {
url = mangaDir.name.orEmpty() title = mangaDir.name.orEmpty()
url = mangaDir.name.orEmpty()
// Try to find the cover // Try to find the cover
coverManager.find(mangaDir.name.orEmpty()) coverManager.find(mangaDir.name.orEmpty())?.let {
?.takeIf(UniFile::exists) thumbnail_url = it.filePath
?.let { thumbnail_url = it.uri.toString() } }
}
}
// Fetch chapters of all the manga
mangas.forEach { manga ->
val chapters = getChapterList(manga)
if (chapters.isNotEmpty()) {
val chapter = chapters.last()
val format = getFormat(chapter)
if (format is Format.Epub) {
EpubFile(format.file).use { epub ->
epub.fillMangaMetadata(manga)
} }
} }
// Copy the cover from the first chapter found if not available
if (manga.thumbnail_url == null) {
updateCover(chapter, manga)
}
} }
} .awaitAll()
return MangasPage(mangas.toList(), false) MangasPage(mangas, false)
} }
// SY --> // SY -->
@ -190,13 +172,13 @@ actual class LocalSource(
// Manga details related // Manga details related
override suspend fun getMangaDetails(manga: SManga): SManga = withIOContext { override suspend fun getMangaDetails(manga: SManga): SManga = withIOContext {
coverManager.find(manga.url)?.let { coverManager.find(manga.url)?.let {
manga.thumbnail_url = it.uri.toString() manga.thumbnail_url = it.filePath
} }
// Augment manga details based on metadata files // Augment manga details based on metadata files
try { try {
val mangaDir = fileSystem.getMangaDirectory(manga.url) val mangaDir by lazy { fileSystem.getMangaDirectory(manga.url) }
val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url).toList() val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url)
val comicInfoFile = mangaDirFiles val comicInfoFile = mangaDirFiles
.firstOrNull { it.name == COMIC_INFO_FILE } .firstOrNull { it.name == COMIC_INFO_FILE }
@ -272,7 +254,7 @@ actual class LocalSource(
} // SY <-- } // SY <--
else { else {
// Avoid re-scanning // Avoid re-scanning
File("$folderPath/.noxml").createNewFile() mangaDir?.createFile(".noxml")
} }
} }
} }
@ -353,18 +335,18 @@ actual class LocalSource(
} }
// Chapters // Chapters
override suspend fun getChapterList(manga: SManga): List<SChapter> { override suspend fun getChapterList(manga: SManga): List<SChapter> = withIOContext {
return fileSystem.getFilesInMangaDirectory(manga.url) val chapters = fileSystem.getFilesInMangaDirectory(manga.url)
// Only keep supported formats // Only keep supported formats
.filter { it.isDirectory || Archive.isSupported(it) } .filter { it.isDirectory || Archive.isSupported(it) }
.map { chapterFile -> .map { chapterFile ->
SChapter.create().apply { SChapter.create().apply {
url = "${manga.url}/${chapterFile.name}" url = "${manga.url}/${chapterFile.name}"
name = if (chapterFile.isDirectory) { name = if (chapterFile.isDirectory) {
chapterFile.name.orEmpty() chapterFile.name
} else { } else {
chapterFile.nameWithoutExtension.orEmpty() chapterFile.nameWithoutExtension
} }.orEmpty()
date_upload = chapterFile.lastModified() date_upload = chapterFile.lastModified()
chapter_number = ChapterRecognition chapter_number = ChapterRecognition
.parseChapterNumber(manga.title, this.name, this.chapter_number.toDouble()) .parseChapterNumber(manga.title, this.name, this.chapter_number.toDouble())
@ -373,7 +355,7 @@ actual class LocalSource(
val format = Format.valueOf(chapterFile) val format = Format.valueOf(chapterFile)
if (format is Format.Epub) { if (format is Format.Epub) {
EpubFile(format.file).use { epub -> EpubFile(format.file).use { epub ->
epub.fillChapterMetadata(this) epub.fillMetadata(manga, this)
} }
} }
} }
@ -382,7 +364,15 @@ actual class LocalSource(
val c = c2.chapter_number.compareTo(c1.chapter_number) val c = c2.chapter_number.compareTo(c1.chapter_number)
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
} }
.toList()
// Copy the cover from the first chapter found if not available
if (manga.thumbnail_url.isNullOrBlank()) {
chapters.lastOrNull()?.let { chapter ->
updateCover(chapter, manga)
}
}
chapters
} }
// Filters // Filters
@ -393,7 +383,7 @@ actual class LocalSource(
fun getFormat(chapter: SChapter): Format { fun getFormat(chapter: SChapter): Format {
try { try {
val (mangaDirName, chapterName) = chapter.url.split(File.separator, limit = 2) val (mangaDirName, chapterName) = chapter.url.split('/', limit = 2)
return fileSystem.getBaseDirectory() return fileSystem.getBaseDirectory()
?.findFile(mangaDirName) ?.findFile(mangaDirName)
?.findFile(chapterName) ?.findFile(chapterName)

View File

@ -16,16 +16,15 @@ actual class LocalSourceFileSystem(
} }
actual fun getMangaDirectory(name: String): UniFile? { actual fun getMangaDirectory(name: String): UniFile? {
return getFilesInBaseDirectory() return getBaseDirectory()
// Get the first mangaDir or null ?.findFile(name, true)
.firstOrNull { it.isDirectory && it.name == name } ?.takeIf { it.isDirectory }
} }
actual fun getFilesInMangaDirectory(name: String): List<UniFile> { actual fun getFilesInMangaDirectory(name: String): List<UniFile> {
return getFilesInBaseDirectory() return getBaseDirectory()
// Filter out ones that are not related to the manga and is not a directory ?.findFile(name, true)
.filter { it.isDirectory && it.name == name } ?.takeIf { it.isDirectory }
// Get all the files inside the filtered folders ?.listFiles().orEmpty().toList()
.flatMap { it.listFiles().orEmpty().toList() }
} }
} }

View File

@ -8,37 +8,25 @@ import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
/** /**
* Fills manga metadata using this epub file's metadata. * Fills manga and chapter metadata using this epub file's metadata.
*/ */
fun EpubFile.fillMangaMetadata(manga: SManga) { fun EpubFile.fillMetadata(manga: SManga, chapter: SChapter) {
val ref = getPackageHref()
val doc = getPackageDocument(ref)
val creator = doc.getElementsByTag("dc:creator").first()
val description = doc.getElementsByTag("dc:description").first()
manga.author = creator?.text()
manga.description = description?.text()
}
/**
* Fills chapter metadata using this epub file's metadata.
*/
fun EpubFile.fillChapterMetadata(chapter: SChapter) {
val ref = getPackageHref() val ref = getPackageHref()
val doc = getPackageDocument(ref) val doc = getPackageDocument(ref)
val title = doc.getElementsByTag("dc:title").first() val title = doc.getElementsByTag("dc:title").first()
val publisher = doc.getElementsByTag("dc:publisher").first() val publisher = doc.getElementsByTag("dc:publisher").first()
val creator = doc.getElementsByTag("dc:creator").first() val creator = doc.getElementsByTag("dc:creator").first()
val description = doc.getElementsByTag("dc:description").first()
var date = doc.getElementsByTag("dc:date").first() var date = doc.getElementsByTag("dc:date").first()
if (date == null) { if (date == null) {
date = doc.select("meta[property=dcterms:modified]").first() date = doc.select("meta[property=dcterms:modified]").first()
} }
if (title != null) { creator?.text()?.let { manga.author = it }
chapter.name = title.text() description?.text()?.let { manga.description = it }
}
title?.text()?.let { chapter.name = it }
if (publisher != null) { if (publisher != null) {
chapter.scanlator = publisher.text() chapter.scanlator = publisher.text()