diff --git a/app/build.gradle b/app/build.gradle index 0574ceaac..00a9d4e72 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,15 +6,15 @@ plugins { } android { - compileSdkVersion 30 + compileSdkVersion 31 buildToolsVersion '30.0.3' defaultConfig { applicationId 'org.koitharu.kotatsu' minSdkVersion 21 - targetSdkVersion 30 - versionCode 369 - versionName '2.0-b1' + targetSdkVersion 31 + versionCode 370 + versionName '2.0-b2' generatedDensities = [] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -82,7 +82,7 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' 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' //noinspection LifecycleAnnotationProcessorWithJava8 kapt 'androidx.lifecycle:lifecycle-compiler:2.3.1' @@ -93,13 +93,13 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.1' 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-viewbinding:4.3.0' 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.github.solkin:disk-lru-cache:1.3' diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt index 86c6b884d..7dde9de2e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt @@ -77,6 +77,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe val doc = loaderContext.httpGet(manga.url.withDomain()).parseHtml() val root = doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root") + val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US) return manga.copy( description = root.getElementById("description")?.html()?.substringBeforeLast(" "favdesc" } - private fun parseChapterDate(string: String): Long { - return SimpleDateFormat("yyyy-MM-dd", Locale.US).tryParse(string) - } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt index 6bcce48f6..2f422cdad 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt @@ -109,6 +109,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) : val doc = loaderContext.httpGet(manga.url.withDomain(), HEADER).parseHtml() val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent") ?: throw ParseException("Cannot find root") + val dateFormat = SimpleDateFormat("dd.MM.yy", Locale.US) return manga.copy( description = root.selectFirst("div.manga-description")?.html(), 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(), number = i + 1, url = href, - uploadDate = parseChapterDate(tr.select("td.d-none").text()), + uploadDate = dateFormat.tryParse(tr.selectFirst("td.d-none")?.text()), scanlator = translators, source = source ) @@ -234,10 +235,6 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) : return loaderContext.httpPost(url, payload) } - private fun parseChapterDate(string: String): Long { - return SimpleDateFormat("dd.MM.yy", Locale.US).tryParse(string) - } - private companion object { private const val PAGE_SIZE = 70 diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt index b74aaabb5..efa574f8c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt @@ -80,6 +80,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : val info = root.selectFirst("div.media-content") val chaptersDoc = loaderContext.httpGet("$fullUrl?section=chapters").parseHtml() val scripts = chaptersDoc.select("script") + val dateFormat = SimpleDateFormat("yyy-MM-dd", Locale.US) var chapters: ArrayList? = null scripts@ for (script in scripts) { val raw = script.html().lines() @@ -113,7 +114,9 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : url = url, source = source, number = total - i, - uploadDate = parseChapterDate(item.getString("chapter_created_at").substringBefore(" ")), + uploadDate = dateFormat.tryParse( + item.getString("chapter_created_at").substringBefore(" ") + ), scanlator = scanlator, 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) - } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaOwlRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaOwlRepository.kt index b47712ad8..ade883600 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaOwlRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaOwlRepository.kt @@ -74,6 +74,7 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit val doc = loaderContext.httpGet(manga.publicUrl).parseHtml() 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 dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.US) return manga.copy( description = info.selectFirst(".description")?.html(), largeCoverUrl = info.select("img").first()?.let { img -> @@ -100,7 +101,7 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit name = a.select("label").text(), number = i + 1, url = href, - uploadDate = parseChapterDate(li.select("small:last-of-type").text()), + uploadDate = dateFormat.tryParse(li.selectFirst("small:last-of-type")?.text()), source = MangaSource.MANGAOWL ) } @@ -157,9 +158,4 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit SortOrder.UPDATED -> "3" else -> "3" } - - private fun parseChapterDate(string: String): Long { - return SimpleDateFormat("MM/dd/yyyy", Locale.US).tryParse(string) - } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt index d40bdf96f..b23bce7a5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt @@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.utils.ext.* +import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* @@ -97,6 +98,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : val info = root.selectFirst("div.detail_info")?.selectFirst("ul") val chaptersList = root.selectFirst("div.chapter_content") ?.selectFirst("ul.chapter_list")?.select("li")?.asReversed() + val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US) return manga.copy( tags = manga.tags + info?.select("li")?.find { x -> x.selectFirst("b")?.ownText() == "Genre(s):" @@ -118,7 +120,10 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : url = href, source = MangaSource.MANGATOWN, 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}" } ) } @@ -169,11 +174,12 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : } } - private fun parseChapterDate(date: String): Long { + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { return when { + date.isNullOrEmpty() -> 0L date.contains("Today") -> Calendar.getInstance().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) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt index 068016887..5cb3a1ef2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt @@ -6,6 +6,7 @@ import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.utils.WordSet import org.koitharu.kotatsu.utils.ext.* +import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* @@ -116,6 +117,7 @@ class MangareadRepository( "manga" to mangaId.toString() ) ).parseHtml() + val dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US) return manga.copy( tags = root.selectFirst("div.genres-content")?.select("a") ?.mapNotNullToSet { a -> @@ -140,7 +142,10 @@ class MangareadRepository( name = a!!.ownText(), number = i + 1, 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 ) } @@ -165,7 +170,8 @@ class MangareadRepository( } } - private fun parseChapterDate(date: String?): Long { + private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long { + date ?: return 0 return when { date.endsWith(" ago", ignoreCase = true) -> { @@ -240,9 +246,5 @@ class MangareadRepository( val pos = it.indexOf('=') it.substring(0, pos) to it.substring(pos + 1) }.toMutableMap() - - private val dateFormat by lazy { - SimpleDateFormat("MMMM dd, yyyy", Locale.US) - } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt index 5c0ad5185..0c00df31a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt @@ -161,7 +161,7 @@ abstract class NineMangaRepository( else -> null } - fun parseChapterDateByLang(date: String): Long { + private fun parseChapterDateByLang(date: String): Long { val dateWords = date.split(" ") if (dateWords.size == 3) { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt index 3a817b76b..bc37ead73 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt @@ -94,6 +94,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito val chapters = loaderContext.httpGet( url = "https://api.$domain/api/titles/chapters/?branch_id=$branchId" ).parseJson().getJSONArray("content") + val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US) return manga.copy( description = content.getString("description"), state = when (content.optJSONObject("status")?.getInt("id")) { @@ -110,7 +111,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito }, chapters = chapters.mapIndexed { i, jo -> 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") MangaChapter( id = generateUid(id), @@ -127,7 +128,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito append(name) } }, - uploadDate = parseChapterDate(jo.getString("upload_date")), + uploadDate = dateFormat.tryParse(jo.getString("upload_date")), scanlator = publishers.optJSONObject(0)?.getStringOrNull("name"), source = MangaSource.REMANGA ) @@ -178,10 +179,6 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito source = source ) - private fun parseChapterDate(string: String): Long { - return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US).tryParse(string) - } - private companion object { const val PAGE_SIZE = 30 diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ParseExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ParseExt.kt index 89e02566f..4da5a2cff 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ParseExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ParseExt.kt @@ -10,7 +10,7 @@ import org.jsoup.nodes.Document import org.jsoup.nodes.Element import org.jsoup.nodes.Node import org.jsoup.select.Elements -import java.text.SimpleDateFormat +import java.text.DateFormat fun Response.parseHtml(): Document { try { @@ -100,6 +100,10 @@ fun Element.css(property: String): String? { return css.substringAfter(':').removeSuffix(';').trim() } -fun SimpleDateFormat.tryParse(str: String): Long = runCatching { - parse(str)?.time ?: 0L -}.getOrDefault(0L) \ No newline at end of file +fun DateFormat.tryParse(str: String?): Long = if (str.isNullOrEmpty()) { + 0L +} else { + runCatching { + parse(str)?.time ?: 0L + }.getOrDefault(0L) +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt index 05d310506..7c66bacaf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt @@ -52,6 +52,10 @@ fun String.toTitleCase(): String { return replaceFirstChar { x -> x.uppercase() } } +fun String.toTitleCase(locale: Locale): String { + return replaceFirstChar { x -> x.uppercase(locale) } +} + fun String.transliterate(skipMissing: Boolean): String { val cyr = charArrayOf( 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п',