diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/DocumentParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/DocumentParser.kt index 32f42095..60c070c7 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/DocumentParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/DocumentParser.kt @@ -16,6 +16,8 @@ import ua.acclorite.book_story.core.helpers.clearMarkdown import ua.acclorite.book_story.core.helpers.containsVisibleText import ua.acclorite.book_story.domain.model.reader.ReaderText import java.io.File +import java.net.URLDecoder +import java.nio.charset.StandardCharsets import java.util.zip.ZipEntry import java.util.zip.ZipFile import javax.inject.Inject @@ -71,7 +73,7 @@ class DocumentParser @Inject constructor( if (!link.startsWith("http") || element.wholeText().isBlank()) return@forEach if (link.startsWith("http://")) { - link = link.replace("http://", "https://") + link = link.replaceFirst("http://", "https://") } element.prepend("[") @@ -84,6 +86,7 @@ class DocumentParser @Inject constructor( .trim() .substringAfterLast(File.separator) .lowercase() + .let { src -> URLDecoder.decode(src, StandardCharsets.UTF_8.name()) } .takeIf { it.containsVisibleText() && imageEntries?.any { image -> it == image.name.substringAfterLast(File.separator).lowercase() @@ -103,6 +106,7 @@ class DocumentParser @Inject constructor( .trim() .substringAfterLast(File.separator) .lowercase() + .let { src -> URLDecoder.decode(src, StandardCharsets.UTF_8.name()) } .takeIf { it.containsVisibleText() && imageEntries?.any { image -> it == image.name.substringAfterLast(File.separator).lowercase() diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/FileParser.kt index f082a027..90822926 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/FileParser.kt @@ -11,6 +11,5 @@ import ua.acclorite.book_story.data.model.library.BookWithCover interface FileParser { - suspend fun parse(cachedFile: CachedFile): BookWithCover? } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/TextParser.kt index 969a51f7..7d70f502 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/TextParser.kt @@ -10,6 +10,5 @@ import ua.acclorite.book_story.data.model.file.CachedFile import ua.acclorite.book_story.domain.model.reader.ReaderText interface TextParser { - suspend fun parse(cachedFile: CachedFile): List } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt index 0093bae8..6408d061 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt @@ -11,6 +11,7 @@ import android.graphics.BitmapFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jsoup.Jsoup +import org.jsoup.parser.Parser import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText import ua.acclorite.book_story.data.model.file.CachedFile @@ -18,6 +19,8 @@ import ua.acclorite.book_story.data.model.library.BookWithCover import ua.acclorite.book_story.data.parser.FileParser import ua.acclorite.book_story.domain.model.library.Book import java.io.File +import java.net.URLDecoder +import java.nio.charset.StandardCharsets import java.util.zip.ZipFile import javax.inject.Inject @@ -40,7 +43,7 @@ class EpubFileParser @Inject constructor() : FileParser { .getInputStream(opfEntry) .bufferedReader() .use { it.readText() } - val document = Jsoup.parse(opfContent) + val document = Jsoup.parse(opfContent, Parser.xmlParser()) val title = document.select("metadata > dc|title").text().trim().run { ifBlank { @@ -67,18 +70,24 @@ class EpubFileParser @Inject constructor() : FileParser { val coverImage = document .select("metadata > meta[name=cover]") .attr("content") - .run { - if (isNotBlank()) { + .let { coverId -> + if (coverId.isNotBlank()) { document - .select("manifest > item[id=$this]") + .select("manifest > item[id=$coverId]") .attr("href") - .apply { if (isNotBlank()) return@run this } + .also { src -> + if (src.isBlank()) return@also + return@let src + } } document .select("manifest > item[media-type*=image]") - .firstOrNull()?.attr("href") + .firstOrNull() + ?.attr("href") + ?.also { src -> if (src.isBlank()) return@let null } } + ?.let { src -> URLDecoder.decode(src, StandardCharsets.UTF_8.name()) } book = BookWithCover( book = Book( diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt index 8f41dd18..93952d5a 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.withContext import kotlinx.coroutines.yield import org.jsoup.Jsoup +import org.jsoup.parser.Parser import ua.acclorite.book_story.core.data.ExtensionsData import ua.acclorite.book_story.core.helpers.addAll import ua.acclorite.book_story.core.helpers.containsVisibleText @@ -25,6 +26,8 @@ import ua.acclorite.book_story.data.parser.DocumentParser import ua.acclorite.book_story.data.parser.TextParser import ua.acclorite.book_story.domain.model.reader.ReaderText import java.io.File +import java.net.URLDecoder +import java.nio.charset.StandardCharsets import java.util.concurrent.ConcurrentLinkedQueue import java.util.zip.ZipEntry import java.util.zip.ZipFile @@ -117,7 +120,7 @@ class EpubTextParser @Inject constructor( val unformattedText = ConcurrentLinkedQueue>>() // Asynchronously getting all chapters with text - val jobs = chapterEntries.mapIndexed { index, entry -> + chapterEntries.mapIndexed { index, entry -> async(dispatcher) { yield() @@ -131,8 +134,7 @@ class EpubTextParser @Inject constructor( yield() } - } - jobs.awaitAll() + }.awaitAll() // Sorting chapters in correct order readerText.addAll { @@ -167,7 +169,7 @@ class EpubTextParser @Inject constructor( zip.getInputStream(entry) }.bufferedReader().use { it.readText() } var readerText = documentParser.parseDocument( - document = Jsoup.parse(content), + document = Jsoup.parse(content, Parser.htmlParser()), zipFile = zip, imageEntries = imageEntries, includeChapter = false @@ -236,21 +238,21 @@ class EpubTextParser @Inject constructor( } val source = navPoint.selectFirst("content")?.attr("src")?.trim() - .let { source -> - if (source.isNullOrBlank()) return@forEach - source.toUri().path ?: source - }.substringAfterLast(File.separator) + .also { src -> if (src.isNullOrBlank()) return@forEach } + .let { src -> URLDecoder.decode(src, StandardCharsets.UTF_8.name()) } + .let { src -> src.toUri().path ?: src } + .substringAfterLast(File.separator) val parent = navPoint.parent() - .let { parent -> - if (parent == null) return@let null + ?.let { parent -> if (!parent.tagName().equals("navPoint", ignoreCase = true)) return@let null val parentSource = parent.selectFirst("content")?.attr("src")?.trim() - .let { parentSource -> - if (parentSource.isNullOrBlank()) return@forEach - parentSource.toUri().path ?: parentSource - }.substringAfterLast(File.separator) + .also { src -> if (src.isNullOrBlank()) return@forEach } + .let { src -> URLDecoder.decode(src, StandardCharsets.UTF_8.name()) } + .let { src -> src.toUri().path ?: src } + .substringAfterLast(File.separator) + if (parentSource == source) return@let null return@let parentSource } @@ -278,9 +280,7 @@ class EpubTextParser @Inject constructor( chapterTitleMap: Map? ): ReaderText.Chapter? { if (chapterTitleMap.isNullOrEmpty()) return null - return chapterTitleMap.getOrElse(chapterSource.substringAfterLast(File.separator)) { - null - } + return chapterTitleMap.getOrElse(chapterSource.substringAfterLast(File.separator)) { null } } /** @@ -297,19 +297,27 @@ class EpubTextParser @Inject constructor( val opfContent = getInputStream(opfEntry).bufferedReader().use { it.readText() } - val document = Jsoup.parse(opfContent) + val document = Jsoup.parse(opfContent, Parser.xmlParser()) val zipEntries = entries().toList() val manifestItems = document.select("manifest > item").associate { - it.attr("id") to it.attr("href") + it.attr("id").to( + it.attr("href").let { src -> + URLDecoder.decode(src, StandardCharsets.UTF_8.name()) + } + ) } document.select("spine > itemref").mapNotNull { itemRef -> val spineId = itemRef.attr("idref") val chapterSource = manifestItems[spineId] - ?.substringAfterLast(File.separator) - ?.lowercase() - ?: return@mapNotNull null + .let { src -> + if (src.isNullOrBlank()) return@mapNotNull null + URLDecoder.decode( + src.substringAfterLast(File.separator).lowercase(), + StandardCharsets.UTF_8.name() + ) + } zipEntries.find { entry -> entry.name.substringAfterLast(File.separator).lowercase() == chapterSource