mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-20 08:29:17 +01:00
Changes to local source from upstream
Co-Authored-By: arkon <4098258+arkon@users.noreply.github.com>
This commit is contained in:
parent
0885f436e4
commit
e1e8202192
@ -1,42 +1,34 @@
|
|||||||
package eu.kanade.tachiyomi.source
|
package eu.kanade.tachiyomi.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.google.gson.Gson
|
import com.github.junrar.Archive
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonParser
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.model.toMangaInfo
|
|
||||||
import eu.kanade.tachiyomi.source.model.toSChapter
|
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.storage.EpubFile
|
import eu.kanade.tachiyomi.util.storage.EpubFile
|
||||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
import junrar.Archive
|
|
||||||
import junrar.rarfile.FileHeader
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.Scanner
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
class LocalSource(private val context: Context) : CatalogueSource {
|
class LocalSource(private val context: Context) : CatalogueSource {
|
||||||
companion object {
|
companion object {
|
||||||
const val ID = 0L
|
const val ID = 0L
|
||||||
const val HELP_URL = "https://tachiyomi.org/help/guides/reading-local-manga/"
|
const val HELP_URL = "https://tachiyomi.org/help/guides/local-manga/"
|
||||||
|
|
||||||
private const val COVER_NAME = "cover.jpg"
|
private const val COVER_NAME = "cover.jpg"
|
||||||
private val SUPPORTED_ARCHIVE_TYPES = setOf("zip", "rar", "cbr", "cbz", "epub")
|
private val SUPPORTED_ARCHIVE_TYPES = setOf("zip", "rar", "cbr", "cbz", "epub")
|
||||||
@ -47,13 +39,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS)
|
private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS)
|
||||||
|
|
||||||
fun updateCover(context: Context, manga: SManga, input: InputStream): File? {
|
fun updateCover(context: Context, manga: SManga, input: InputStream): File? {
|
||||||
val dir = getBaseDirectories(context).asSequence().firstOrNull()
|
val dir = getBaseDirectories(context).firstOrNull()
|
||||||
if (dir == null) {
|
if (dir == null) {
|
||||||
input.close()
|
input.close()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val cover = File("${dir.absolutePath}/${manga.url}", COVER_NAME)
|
val cover = File("${dir.absolutePath}/${manga.url}", COVER_NAME)
|
||||||
if (cover.exists()) cover.delete()
|
|
||||||
|
|
||||||
cover.parentFile?.mkdirs()
|
cover.parentFile?.mkdirs()
|
||||||
input.use {
|
input.use {
|
||||||
@ -93,22 +84,28 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L
|
if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L
|
||||||
var mangaDirs = baseDirs
|
var mangaDirs = baseDirs
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.mapNotNull { it.listFiles()?.toList() }.flatten()
|
.mapNotNull { it.listFiles()?.toList() }
|
||||||
|
.flatten()
|
||||||
.filter { it.isDirectory }
|
.filter { it.isDirectory }
|
||||||
|
.filterNot { it.name.startsWith('.') }
|
||||||
.filter { if (time == 0L) it.name.contains(query, ignoreCase = true) else it.lastModified() >= time }
|
.filter { if (time == 0L) it.name.contains(query, ignoreCase = true) else it.lastModified() >= time }
|
||||||
.distinctBy { it.name }
|
.distinctBy { it.name }
|
||||||
|
|
||||||
val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state
|
val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state
|
||||||
when (state?.index) {
|
when (state?.index) {
|
||||||
0 -> {
|
0 -> {
|
||||||
if (state.ascending) mangaDirs =
|
mangaDirs = if (state.ascending) {
|
||||||
mangaDirs.sortedBy { it.name.toLowerCase(Locale.ENGLISH) }
|
mangaDirs.sortedBy { it.name.toLowerCase(Locale.ENGLISH) }
|
||||||
else mangaDirs =
|
} else {
|
||||||
mangaDirs.sortedByDescending { it.name.toLowerCase(Locale.ENGLISH) }
|
mangaDirs.sortedByDescending { it.name.toLowerCase(Locale.ENGLISH) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
if (state.ascending) mangaDirs = mangaDirs.sortedBy(File::lastModified)
|
mangaDirs = if (state.ascending) {
|
||||||
else mangaDirs = mangaDirs.sortedByDescending(File::lastModified)
|
mangaDirs.sortedBy(File::lastModified)
|
||||||
|
} else {
|
||||||
|
mangaDirs.sortedByDescending(File::lastModified)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,12 +123,20 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// Copy the cover from the first chapter found.
|
||||||
if (thumbnail_url == null) {
|
if (thumbnail_url == null) {
|
||||||
val chapters = runBlocking { getChapterList(toMangaInfo()).map { it.toSChapter() } }
|
|
||||||
if (chapters.isNotEmpty()) {
|
|
||||||
try {
|
try {
|
||||||
val dest = updateCover(chapters.last(), this)
|
val dest = updateCover(chapter, this)
|
||||||
thumbnail_url = dest?.absolutePath
|
thumbnail_url = dest?.absolutePath
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
@ -140,52 +145,31 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Observable.just(MangasPage(mangas.toList(), false))
|
return Observable.just(MangasPage(mangas.toList(), false))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS)
|
override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS)
|
||||||
|
|
||||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||||
val baseDirs = getBaseDirectories(context)
|
getBaseDirectories(context)
|
||||||
baseDirs
|
.asSequence()
|
||||||
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter { it.extension == "json" }.firstOrNull()?.apply {
|
.firstOrNull { it.extension == "json" }
|
||||||
val json = Gson().fromJson(
|
?.apply {
|
||||||
Scanner(this).useDelimiter("\\Z").next(),
|
val reader = this.inputStream().bufferedReader()
|
||||||
JsonObject::class.java
|
val json = JsonParser.parseReader(reader).asJsonObject
|
||||||
)
|
|
||||||
manga.title = json["title"]?.asString ?: manga.title
|
manga.title = json["title"]?.asString ?: manga.title
|
||||||
manga.author = json["author"]?.asString ?: manga.author
|
manga.author = json["author"]?.asString ?: manga.author
|
||||||
manga.artist = json["artist"]?.asString ?: manga.artist
|
manga.artist = json["artist"]?.asString ?: manga.artist
|
||||||
manga.description = json["description"]?.asString ?: manga.description
|
manga.description = json["description"]?.asString ?: manga.description
|
||||||
manga.genre = json["genre"]?.asJsonArray?.map { it.asString }?.joinToString(", ")
|
manga.genre = json["genre"]?.asJsonArray?.joinToString(", ") { it.asString }
|
||||||
?: manga.genre
|
?: manga.genre
|
||||||
manga.status = json["status"]?.asInt ?: manga.status
|
manga.status = json["status"]?.asInt ?: manga.status
|
||||||
}
|
}
|
||||||
|
|
||||||
val url = manga.url
|
|
||||||
// Try to find the cover
|
|
||||||
for (dir in baseDirs) {
|
|
||||||
val cover = File("${dir.absolutePath}/$url", COVER_NAME)
|
|
||||||
if (cover.exists()) {
|
|
||||||
manga.thumbnail_url = cover.absolutePath
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the cover from the first chapter found.
|
|
||||||
if (manga.thumbnail_url == null) {
|
|
||||||
val chapters = runBlocking { getChapterList(manga.toMangaInfo()).map { it.toSChapter() } }
|
|
||||||
if (chapters.isNotEmpty()) {
|
|
||||||
try {
|
|
||||||
val dest = updateCover(chapters.last(), manga)
|
|
||||||
manga.thumbnail_url = dest?.absolutePath
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Observable.just(manga)
|
return Observable.just(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,33 +213,78 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||||
val chapters =
|
val chapters = getBaseDirectories(context)
|
||||||
getBaseDirectories(context).mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
.asSequence()
|
||||||
.flatten().filter { it.isDirectory || isSupportedFile(it.extension) }
|
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||||
|
.flatten()
|
||||||
|
.filter { it.isDirectory || isSupportedFile(it.extension) }
|
||||||
.map { chapterFile ->
|
.map { chapterFile ->
|
||||||
SChapter.create().apply {
|
SChapter.create().apply {
|
||||||
url = "${manga.url}/${chapterFile.name}"
|
url = "${manga.url}/${chapterFile.name}"
|
||||||
val chapName = if (chapterFile.isDirectory) {
|
name = if (chapterFile.isDirectory) {
|
||||||
chapterFile.name
|
chapterFile.name
|
||||||
} else {
|
} else {
|
||||||
chapterFile.nameWithoutExtension
|
chapterFile.nameWithoutExtension
|
||||||
}
|
}
|
||||||
val chapNameCut =
|
|
||||||
chapName.replace(manga.title, "", true).trim(' ', '-', '_')
|
|
||||||
name = if (chapNameCut.isEmpty()) chapName else chapNameCut
|
|
||||||
date_upload = chapterFile.lastModified()
|
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)
|
ChapterRecognition.parseChapterNumber(this, manga)
|
||||||
}
|
}
|
||||||
}.sortedWith(
|
}
|
||||||
|
.sortedWith(
|
||||||
Comparator { c1, c2 ->
|
Comparator { c1, c2 ->
|
||||||
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()
|
||||||
|
|
||||||
return Observable.just(chapters)
|
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[chapterNameIndex]
|
||||||
|
val mangaChar = mangaTitle[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<List<Page>> {
|
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||||
return Observable.error(Exception("Unused"))
|
return Observable.error(Exception("Unused"))
|
||||||
}
|
}
|
||||||
@ -292,52 +321,37 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCover(chapter: SChapter, manga: SManga): File? {
|
private fun updateCover(chapter: SChapter, manga: SManga): File? {
|
||||||
val format = getFormat(chapter)
|
return when (val format = getFormat(chapter)) {
|
||||||
return when (format) {
|
|
||||||
is Format.Directory -> {
|
is Format.Directory -> {
|
||||||
val entry = format.file.listFiles()
|
val entry = format.file.listFiles()
|
||||||
?.sortedWith(
|
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||||
Comparator<File> { f1, f2 ->
|
|
||||||
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||||
|
|
||||||
entry?.let { updateCover(context, manga, it.inputStream()) }
|
entry?.let { updateCover(context, manga, it.inputStream()) }
|
||||||
}
|
}
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
ZipFile(format.file).use { zip ->
|
ZipFile(format.file).use { zip ->
|
||||||
val entry = zip.entries().toList().sortedWith(
|
val entry = zip.entries().toList()
|
||||||
Comparator<ZipEntry> { f1, f2 ->
|
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||||
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
|
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||||
}
|
|
||||||
).find {
|
|
||||||
!it.isDirectory && ImageUtil.isImage(it.name) {
|
|
||||||
zip.getInputStream(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entry?.let { updateCover(context, manga, zip.getInputStream(it)) }
|
entry?.let { updateCover(context, manga, zip.getInputStream(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
Archive(format.file).use { archive ->
|
Archive(format.file).use { archive ->
|
||||||
val entry = archive.fileHeaders.sortedWith(
|
val entry = archive.fileHeaders
|
||||||
Comparator<FileHeader> { f1, f2 ->
|
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||||
f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString)
|
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
||||||
}
|
|
||||||
).find {
|
|
||||||
!it.isDirectory && ImageUtil.isImage(it.fileNameString) {
|
|
||||||
archive.getInputStream(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entry?.let { updateCover(context, manga, archive.getInputStream(it)) }
|
entry?.let { updateCover(context, manga, archive.getInputStream(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Epub -> {
|
is Format.Epub -> {
|
||||||
EpubFile(format.file).use { epub ->
|
EpubFile(format.file).use { epub ->
|
||||||
val entry = epub.getImagesFromPages().firstOrNull()?.let { epub.getEntry(it) }
|
val entry = epub.getImagesFromPages()
|
||||||
|
.firstOrNull()
|
||||||
|
?.let { epub.getEntry(it) }
|
||||||
|
|
||||||
entry?.let { updateCover(context, manga, epub.getInputStream(it)) }
|
entry?.let { updateCover(context, manga, epub.getInputStream(it)) }
|
||||||
}
|
}
|
||||||
@ -345,8 +359,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OrderBy :
|
private class OrderBy : Filter.Sort("Order by", arrayOf("Title", "Date"), Selection(0, true))
|
||||||
Filter.Sort("Order by", arrayOf("Title", "Date"), Filter.Sort.Selection(0, true))
|
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(OrderBy())
|
override fun getFilterList() = FilterList(OrderBy())
|
||||||
|
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package eu.kanade.tachiyomi.util.storage
|
package eu.kanade.tachiyomi.util.storage
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
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.ZipEntry
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
@ -44,6 +49,58 @@ class EpubFile(file: File) : Closeable {
|
|||||||
return zip.getEntry(name)
|
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.
|
* Returns the path of all the images found in the epub file.
|
||||||
*/
|
*/
|
||||||
@ -93,7 +150,7 @@ class EpubFile(file: File) : Closeable {
|
|||||||
* Returns all the images contained in every page from the epub.
|
* Returns all the images contained in every page from the epub.
|
||||||
*/
|
*/
|
||||||
private fun getImagesFromPages(pages: List<String>, packageHref: String): List<String> {
|
private fun getImagesFromPages(pages: List<String>, packageHref: String): List<String> {
|
||||||
val result = ArrayList<String>()
|
val result = mutableListOf<String>()
|
||||||
val basePath = getParentDirectory(packageHref)
|
val basePath = getParentDirectory(packageHref)
|
||||||
pages.forEach { page ->
|
pages.forEach { page ->
|
||||||
val entryPath = resolveZipPath(basePath, page)
|
val entryPath = resolveZipPath(basePath, page)
|
||||||
@ -118,10 +175,10 @@ class EpubFile(file: File) : Closeable {
|
|||||||
*/
|
*/
|
||||||
private fun getPathSeparator(): String {
|
private fun getPathSeparator(): String {
|
||||||
val meta = zip.getEntry("META-INF\\container.xml")
|
val meta = zip.getEntry("META-INF\\container.xml")
|
||||||
if (meta != null) {
|
return if (meta != null) {
|
||||||
return "\\"
|
"\\"
|
||||||
} else {
|
} else {
|
||||||
return "/"
|
"/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,10 +206,10 @@ class EpubFile(file: File) : Closeable {
|
|||||||
*/
|
*/
|
||||||
private fun getParentDirectory(path: String): String {
|
private fun getParentDirectory(path: String): String {
|
||||||
val separatorIndex = path.lastIndexOf(pathSeparator)
|
val separatorIndex = path.lastIndexOf(pathSeparator)
|
||||||
if (separatorIndex >= 0) {
|
return if (separatorIndex >= 0) {
|
||||||
return path.substring(0, separatorIndex)
|
path.substring(0, separatorIndex)
|
||||||
} else {
|
} else {
|
||||||
return ""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user