diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt index c153d6c6a..daa1d3ac0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt @@ -112,12 +112,20 @@ class LocalSource(private val context: Context) : CatalogueSource { } } - // Copy the cover from the first chapter found. - if (thumbnail_url == null) { - val chapters = fetchChapterList(this).toBlocking().first() - if (chapters.isNotEmpty()) { + val chapters = fetchChapterList(this).toBlocking().first() + if (chapters.isNotEmpty()) { + val chapter = chapters.last() + val format = getFormat(chapter) + if (format is Format.Epub) { + EpubFile(format.file).use { epub -> + epub.fillMangaMetadata(this) + } + } + + // Copy the cover from the first chapter found. + if (thumbnail_url == null) { try { - val dest = updateCover(chapters.last(), this) + val dest = updateCover(chapter, this) thumbnail_url = dest?.absolutePath } catch (e: Exception) { Timber.e(e) @@ -158,14 +166,22 @@ class LocalSource(private val context: Context) : CatalogueSource { .map { chapterFile -> SChapter.create().apply { url = "${manga.url}/${chapterFile.name}" - val chapName = if (chapterFile.isDirectory) { + name = if (chapterFile.isDirectory) { chapterFile.name } else { chapterFile.nameWithoutExtension } - val chapNameCut = chapName.replace(manga.title, "", true).trim(' ', '-', '_') - name = if (chapNameCut.isEmpty()) chapName else chapNameCut date_upload = chapterFile.lastModified() + + val format = getFormat(this) + if (format is Format.Epub) { + EpubFile(format.file).use { epub -> + epub.fillChapterMetadata(this) + } + } + + val chapNameCut = stripMangaTitle(name, manga.title) + if (chapNameCut.isNotEmpty()) name = chapNameCut ChapterRecognition.parseChapterNumber(this, manga) } } @@ -180,6 +196,40 @@ class LocalSource(private val context: Context) : CatalogueSource { return Observable.just(chapters) } + /** + * Strips the manga title from a chapter name, matching only based on alphanumeric and whitespace + * characters. + */ + private fun stripMangaTitle(chapterName: String, mangaTitle: String): String { + var chapterNameIndex = 0 + var mangaTitleIndex = 0 + while (chapterNameIndex < chapterName.length && mangaTitleIndex < mangaTitle.length) { + val chapterChar = chapterName.get(chapterNameIndex) + val mangaChar = mangaTitle.get(mangaTitleIndex) + if (!chapterChar.equals(mangaChar, true)) { + val invalidChapterChar = !chapterChar.isLetterOrDigit() && !chapterChar.isWhitespace() + val invalidMangaChar = !mangaChar.isLetterOrDigit() && !mangaChar.isWhitespace() + + if (!invalidChapterChar && !invalidMangaChar) { + return chapterName + } + + if (invalidChapterChar) { + chapterNameIndex++ + } + + if (invalidMangaChar) { + mangaTitleIndex++ + } + } else { + chapterNameIndex++ + mangaTitleIndex++ + } + } + + return chapterName.substring(chapterNameIndex).trimStart(' ', '-', '_', ',', ':') + } + override fun fetchPageList(chapter: SChapter): Observable> { return Observable.error(Exception("Unused")) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/app/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt index e9e8dd874..7f1e966f6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt @@ -1,8 +1,13 @@ package eu.kanade.tachiyomi.util.storage +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga import java.io.Closeable import java.io.File import java.io.InputStream +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.Locale import java.util.zip.ZipEntry import java.util.zip.ZipFile import org.jsoup.Jsoup @@ -44,6 +49,58 @@ class EpubFile(file: File) : Closeable { return zip.getEntry(name) } + /** + * Fills manga metadata using this epub file's metadata. + */ + fun fillMangaMetadata(manga: SManga) { + 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 fillChapterMetadata(chapter: SChapter) { + val ref = getPackageHref() + val doc = getPackageDocument(ref) + + val title = doc.getElementsByTag("dc:title").first() + val publisher = doc.getElementsByTag("dc:publisher").first() + val creator = doc.getElementsByTag("dc:creator").first() + var date = doc.getElementsByTag("dc:date").first() + if (date == null) { + date = doc.select("meta[property=dcterms:modified]").first() + } + + if (title != null) { + chapter.name = title.text() + } + + if (publisher != null) { + chapter.scanlator = publisher.text() + } else if (creator != null) { + chapter.scanlator = creator.text() + } + + if (date != null) { + val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()) + try { + val parsedDate = dateFormat.parse(date.text()) + if (parsedDate != null) { + chapter.date_upload = parsedDate.time + } + } catch (e: ParseException) { + // Empty + } + } + } + /** * Returns the path of all the images found in the epub file. */