Chapter date parse optimization

pull/72/head
Koitharu 5 years ago
parent 7ded7fd12a
commit 78f2a13761
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -6,15 +6,15 @@ plugins {
} }
android { android {
compileSdkVersion 30 compileSdkVersion 31
buildToolsVersion '30.0.3' buildToolsVersion '30.0.3'
defaultConfig { defaultConfig {
applicationId 'org.koitharu.kotatsu' applicationId 'org.koitharu.kotatsu'
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 31
versionCode 369 versionCode 370
versionName '2.0-b1' versionName '2.0-b2'
generatedDensities = [] generatedDensities = []
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -82,7 +82,7 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.work:work-runtime-ktx:2.6.0' implementation 'androidx.work:work-runtime-ktx:2.7.0'
implementation 'com.google.android.material:material:1.4.0' implementation 'com.google.android.material:material:1.4.0'
//noinspection LifecycleAnnotationProcessorWithJava8 //noinspection LifecycleAnnotationProcessorWithJava8
kapt 'androidx.lifecycle:lifecycle-compiler:2.3.1' kapt 'androidx.lifecycle:lifecycle-compiler:2.3.1'
@ -93,13 +93,13 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation 'com.squareup.okio:okio:2.10.0' implementation 'com.squareup.okio:okio:2.10.0'
implementation 'org.jsoup:jsoup:1.14.2' implementation 'org.jsoup:jsoup:1.14.3'
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.0' implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.0'
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.0' implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.0'
implementation 'io.insert-koin:koin-android:3.1.2' implementation 'io.insert-koin:koin-android:3.1.2'
implementation 'io.coil-kt:coil-base:1.3.2' implementation 'io.coil-kt:coil-base:1.4.0'
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
implementation 'com.github.solkin:disk-lru-cache:1.3' implementation 'com.github.solkin:disk-lru-cache:1.3'

@ -77,6 +77,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml() val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml()
val root = val root =
doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root") doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root")
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US)
return manga.copy( return manga.copy(
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"), description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
largeCoverUrl = root.getElementById("cover")?.absUrl("src"), largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
@ -87,7 +88,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
name = tr.selectFirst("a")?.text().orEmpty(), name = tr.selectFirst("a")?.text().orEmpty(),
number = i + 1, number = i + 1,
url = href, url = href,
uploadDate = parseChapterDate(tr.selectFirst("div.date")?.text().orEmpty()), uploadDate = dateFormat.tryParse(tr.selectFirst("div.date")?.text()),
source = source source = source
) )
} }
@ -155,8 +156,4 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
else -> "favdesc" else -> "favdesc"
} }
private fun parseChapterDate(string: String): Long {
return SimpleDateFormat("yyyy-MM-dd", Locale.US).tryParse(string)
}
} }

@ -109,6 +109,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
val doc = loaderContext.httpGet(manga.url.withDomain(), HEADER).parseHtml() val doc = loaderContext.httpGet(manga.url.withDomain(), HEADER).parseHtml()
val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent") val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent")
?: throw ParseException("Cannot find root") ?: throw ParseException("Cannot find root")
val dateFormat = SimpleDateFormat("dd.MM.yy", Locale.US)
return manga.copy( return manga.copy(
description = root.selectFirst("div.manga-description")?.html(), description = root.selectFirst("div.manga-description")?.html(),
largeCoverUrl = root.selectFirst("div.subject-cower")?.selectFirst("img")?.attr( largeCoverUrl = root.selectFirst("div.subject-cower")?.selectFirst("img")?.attr(
@ -139,7 +140,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
name = tr.selectFirst("a")?.text().orEmpty().removePrefix(manga.title).trim(), name = tr.selectFirst("a")?.text().orEmpty().removePrefix(manga.title).trim(),
number = i + 1, number = i + 1,
url = href, url = href,
uploadDate = parseChapterDate(tr.select("td.d-none").text()), uploadDate = dateFormat.tryParse(tr.selectFirst("td.d-none")?.text()),
scanlator = translators, scanlator = translators,
source = source source = source
) )
@ -234,10 +235,6 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
return loaderContext.httpPost(url, payload) return loaderContext.httpPost(url, payload)
} }
private fun parseChapterDate(string: String): Long {
return SimpleDateFormat("dd.MM.yy", Locale.US).tryParse(string)
}
private companion object { private companion object {
private const val PAGE_SIZE = 70 private const val PAGE_SIZE = 70

@ -80,6 +80,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
val info = root.selectFirst("div.media-content") val info = root.selectFirst("div.media-content")
val chaptersDoc = loaderContext.httpGet("$fullUrl?section=chapters").parseHtml() val chaptersDoc = loaderContext.httpGet("$fullUrl?section=chapters").parseHtml()
val scripts = chaptersDoc.select("script") val scripts = chaptersDoc.select("script")
val dateFormat = SimpleDateFormat("yyy-MM-dd", Locale.US)
var chapters: ArrayList<MangaChapter>? = null var chapters: ArrayList<MangaChapter>? = null
scripts@ for (script in scripts) { scripts@ for (script in scripts) {
val raw = script.html().lines() val raw = script.html().lines()
@ -113,7 +114,9 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
url = url, url = url,
source = source, source = source,
number = total - i, number = total - i,
uploadDate = parseChapterDate(item.getString("chapter_created_at").substringBefore(" ")), uploadDate = dateFormat.tryParse(
item.getString("chapter_created_at").substringBefore(" ")
),
scanlator = scanlator, scanlator = scanlator,
name = if (nameChapter.isNullOrBlank()) fullNameChapter else "$fullNameChapter - $nameChapter" name = if (nameChapter.isNullOrBlank()) fullNameChapter else "$fullNameChapter - $nameChapter"
) )
@ -241,9 +244,4 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
) )
} }
} }
private fun parseChapterDate(string: String): Long {
return SimpleDateFormat("yyy-MM-dd", Locale.US).tryParse(string)
}
} }

@ -74,6 +74,7 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
val doc = loaderContext.httpGet(manga.publicUrl).parseHtml() val doc = loaderContext.httpGet(manga.publicUrl).parseHtml()
val info = doc.body().selectFirst("div.single_detail") ?: parseFailed("An error occurred while parsing") val info = doc.body().selectFirst("div.single_detail") ?: parseFailed("An error occurred while parsing")
val table = doc.body().selectFirst("div.single-grid-right") ?: parseFailed("An error occurred while parsing") val table = doc.body().selectFirst("div.single-grid-right") ?: parseFailed("An error occurred while parsing")
val dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.US)
return manga.copy( return manga.copy(
description = info.selectFirst(".description")?.html(), description = info.selectFirst(".description")?.html(),
largeCoverUrl = info.select("img").first()?.let { img -> largeCoverUrl = info.select("img").first()?.let { img ->
@ -100,7 +101,7 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
name = a.select("label").text(), name = a.select("label").text(),
number = i + 1, number = i + 1,
url = href, url = href,
uploadDate = parseChapterDate(li.select("small:last-of-type").text()), uploadDate = dateFormat.tryParse(li.selectFirst("small:last-of-type")?.text()),
source = MangaSource.MANGAOWL source = MangaSource.MANGAOWL
) )
} }
@ -157,9 +158,4 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
SortOrder.UPDATED -> "3" SortOrder.UPDATED -> "3"
else -> "3" else -> "3"
} }
private fun parseChapterDate(string: String): Long {
return SimpleDateFormat("MM/dd/yyyy", Locale.US).tryParse(string)
}
} }

@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.core.prefs.SourceSettings
import org.koitharu.kotatsu.utils.ext.* import org.koitharu.kotatsu.utils.ext.*
import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -97,6 +98,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
val info = root.selectFirst("div.detail_info")?.selectFirst("ul") val info = root.selectFirst("div.detail_info")?.selectFirst("ul")
val chaptersList = root.selectFirst("div.chapter_content") val chaptersList = root.selectFirst("div.chapter_content")
?.selectFirst("ul.chapter_list")?.select("li")?.asReversed() ?.selectFirst("ul.chapter_list")?.select("li")?.asReversed()
val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US)
return manga.copy( return manga.copy(
tags = manga.tags + info?.select("li")?.find { x -> tags = manga.tags + info?.select("li")?.find { x ->
x.selectFirst("b")?.ownText() == "Genre(s):" x.selectFirst("b")?.ownText() == "Genre(s):"
@ -118,7 +120,10 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
url = href, url = href,
source = MangaSource.MANGATOWN, source = MangaSource.MANGATOWN,
number = i + 1, number = i + 1,
uploadDate = parseChapterDate(li.selectFirst("span.time")?.text().orEmpty()), uploadDate = parseChapterDate(
dateFormat,
li.selectFirst("span.time")?.text()
),
name = name.ifEmpty { "${manga.title} - ${i + 1}" } name = name.ifEmpty { "${manga.title} - ${i + 1}" }
) )
} }
@ -169,11 +174,12 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
} }
} }
private fun parseChapterDate(date: String): Long { private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long {
return when { return when {
date.isNullOrEmpty() -> 0L
date.contains("Today") -> Calendar.getInstance().timeInMillis date.contains("Today") -> Calendar.getInstance().timeInMillis
date.contains("Yesterday") -> Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, -1) }.timeInMillis date.contains("Yesterday") -> Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, -1) }.timeInMillis
else -> SimpleDateFormat("MMM dd,yyyy", Locale.US).tryParse(date) else -> dateFormat.tryParse(date)
} }
} }

@ -6,6 +6,7 @@ import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.utils.WordSet import org.koitharu.kotatsu.utils.WordSet
import org.koitharu.kotatsu.utils.ext.* import org.koitharu.kotatsu.utils.ext.*
import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -116,6 +117,7 @@ class MangareadRepository(
"manga" to mangaId.toString() "manga" to mangaId.toString()
) )
).parseHtml() ).parseHtml()
val dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US)
return manga.copy( return manga.copy(
tags = root.selectFirst("div.genres-content")?.select("a") tags = root.selectFirst("div.genres-content")?.select("a")
?.mapNotNullToSet { a -> ?.mapNotNullToSet { a ->
@ -140,7 +142,10 @@ class MangareadRepository(
name = a!!.ownText(), name = a!!.ownText(),
number = i + 1, number = i + 1,
url = href, url = href,
uploadDate = parseChapterDate(doc2.selectFirst("span.chapter-release-date i")?.text()), uploadDate = parseChapterDate(
dateFormat,
doc2.selectFirst("span.chapter-release-date i")?.text()
),
source = MangaSource.MANGAREAD source = MangaSource.MANGAREAD
) )
} }
@ -165,7 +170,8 @@ class MangareadRepository(
} }
} }
private fun parseChapterDate(date: String?): Long { private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long {
date ?: return 0 date ?: return 0
return when { return when {
date.endsWith(" ago", ignoreCase = true) -> { date.endsWith(" ago", ignoreCase = true) -> {
@ -240,9 +246,5 @@ class MangareadRepository(
val pos = it.indexOf('=') val pos = it.indexOf('=')
it.substring(0, pos) to it.substring(pos + 1) it.substring(0, pos) to it.substring(pos + 1)
}.toMutableMap() }.toMutableMap()
private val dateFormat by lazy {
SimpleDateFormat("MMMM dd, yyyy", Locale.US)
}
} }
} }

@ -161,7 +161,7 @@ abstract class NineMangaRepository(
else -> null else -> null
} }
fun parseChapterDateByLang(date: String): Long { private fun parseChapterDateByLang(date: String): Long {
val dateWords = date.split(" ") val dateWords = date.split(" ")
if (dateWords.size == 3) { if (dateWords.size == 3) {

@ -94,6 +94,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
val chapters = loaderContext.httpGet( val chapters = loaderContext.httpGet(
url = "https://api.$domain/api/titles/chapters/?branch_id=$branchId" url = "https://api.$domain/api/titles/chapters/?branch_id=$branchId"
).parseJson().getJSONArray("content") ).parseJson().getJSONArray("content")
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)
return manga.copy( return manga.copy(
description = content.getString("description"), description = content.getString("description"),
state = when (content.optJSONObject("status")?.getInt("id")) { state = when (content.optJSONObject("status")?.getInt("id")) {
@ -110,7 +111,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
}, },
chapters = chapters.mapIndexed { i, jo -> chapters = chapters.mapIndexed { i, jo ->
val id = jo.getLong("id") val id = jo.getLong("id")
val name = jo.getString("name").capitalize(Locale.ROOT) val name = jo.getString("name").toTitleCase(Locale.ROOT)
val publishers = jo.getJSONArray("publishers") val publishers = jo.getJSONArray("publishers")
MangaChapter( MangaChapter(
id = generateUid(id), id = generateUid(id),
@ -127,7 +128,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
append(name) append(name)
} }
}, },
uploadDate = parseChapterDate(jo.getString("upload_date")), uploadDate = dateFormat.tryParse(jo.getString("upload_date")),
scanlator = publishers.optJSONObject(0)?.getStringOrNull("name"), scanlator = publishers.optJSONObject(0)?.getStringOrNull("name"),
source = MangaSource.REMANGA source = MangaSource.REMANGA
) )
@ -178,10 +179,6 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
source = source source = source
) )
private fun parseChapterDate(string: String): Long {
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US).tryParse(string)
}
private companion object { private companion object {
const val PAGE_SIZE = 30 const val PAGE_SIZE = 30

@ -10,7 +10,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.nodes.Node import org.jsoup.nodes.Node
import org.jsoup.select.Elements import org.jsoup.select.Elements
import java.text.SimpleDateFormat import java.text.DateFormat
fun Response.parseHtml(): Document { fun Response.parseHtml(): Document {
try { try {
@ -100,6 +100,10 @@ fun Element.css(property: String): String? {
return css.substringAfter(':').removeSuffix(';').trim() return css.substringAfter(':').removeSuffix(';').trim()
} }
fun SimpleDateFormat.tryParse(str: String): Long = runCatching { fun DateFormat.tryParse(str: String?): Long = if (str.isNullOrEmpty()) {
parse(str)?.time ?: 0L 0L
}.getOrDefault(0L) } else {
runCatching {
parse(str)?.time ?: 0L
}.getOrDefault(0L)
}

@ -52,6 +52,10 @@ fun String.toTitleCase(): String {
return replaceFirstChar { x -> x.uppercase() } return replaceFirstChar { x -> x.uppercase() }
} }
fun String.toTitleCase(locale: Locale): String {
return replaceFirstChar { x -> x.uppercase(locale) }
}
fun String.transliterate(skipMissing: Boolean): String { fun String.transliterate(skipMissing: Boolean): String {
val cyr = charArrayOf( val cyr = charArrayOf(
'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п',

Loading…
Cancel
Save