Fix some manga sources
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.core.exceptions
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||
|
||||
class AuthRequiredException(
|
||||
val url: String
|
||||
) : RuntimeException("Authorization required"), ResolvableException {
|
||||
|
||||
@StringRes
|
||||
override val resolveTextId: Int = R.string.sign_in
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.exceptions
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import okio.IOException
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||
@@ -8,5 +9,6 @@ class CloudFlareProtectedException(
|
||||
val url: String
|
||||
) : IOException("Protected by CloudFlare"), ResolvableException {
|
||||
|
||||
@StringRes
|
||||
override val resolveTextId: Int = R.string.captcha_solve
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
|
||||
import org.koitharu.kotatsu.core.exceptions.AuthRequiredException
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
@@ -18,6 +19,7 @@ class ExceptionResolver(
|
||||
|
||||
suspend fun resolve(e: ResolvableException): Boolean = when (e) {
|
||||
is CloudFlareProtectedException -> resolveCF(e.url)
|
||||
is AuthRequiredException -> false //TODO
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ enum class MangaSource(
|
||||
YAOICHAN("Яой-тян", "ru", YaoiChanRepository::class.java),
|
||||
MANGATOWN("MangaTown", "en", MangaTownRepository::class.java),
|
||||
MANGALIB("MangaLib", "ru", MangaLibRepository::class.java),
|
||||
NUDEMOON("Nude-Moon", "ru", NudeMoonRepository::class.java),
|
||||
|
||||
// NUDEMOON("Nude-Moon", "ru", NudeMoonRepository::class.java),
|
||||
MANGAREAD("MangaRead", "en", MangareadRepository::class.java),
|
||||
REMANGA("Remanga", "ru", RemangaRepository::class.java),
|
||||
HENTAILIB("HentaiLib", "ru", HentaiLibRepository::class.java);
|
||||
|
||||
@@ -20,7 +20,7 @@ val parserModule
|
||||
factory<MangaRepository>(named(MangaSource.YAOICHAN)) { YaoiChanRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.MANGATOWN)) { MangaTownRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.MANGALIB)) { MangaLibRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.NUDEMOON)) { NudeMoonRepository(get()) }
|
||||
// factory<MangaRepository>(named(MangaSource.NUDEMOON)) { NudeMoonRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.MANGAREAD)) { MangareadRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.REMANGA)) { RemangaRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.HENTAILIB)) { HentaiLibRepository(get()) }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.parser
|
||||
|
||||
import android.net.Uri
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
@@ -26,10 +26,9 @@ abstract class RemoteMangaRepository(
|
||||
abstract fun onCreatePreferences(): Set<String>
|
||||
|
||||
protected fun generateUid(url: String): Long {
|
||||
val uri = Uri.parse(url)
|
||||
val path = uri.path ?: error("Cannot generate uid: bad uri \"$url\"")
|
||||
val uri = url.toHttpUrl()
|
||||
val x = source.name.hashCode()
|
||||
val y = path.hashCode()
|
||||
val y = "${uri.encodedPath}?${uri.query}".hashCode()
|
||||
return (x.toLong() shl 32) or (y.toLong() and 0xffffffffL)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
protected abstract val defaultDomain: String
|
||||
|
||||
override val sortOrders: Set<SortOrder> = EnumSet.of(
|
||||
SortOrder.UPDATED, SortOrder.POPULARITY,
|
||||
SortOrder.NEWEST, SortOrder.RATING
|
||||
//FIXME SortOrder.ALPHABETICAL
|
||||
SortOrder.UPDATED,
|
||||
SortOrder.POPULARITY,
|
||||
SortOrder.NEWEST,
|
||||
SortOrder.RATING
|
||||
)
|
||||
|
||||
override suspend fun getList(
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.collection.arraySetOf
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.exceptions.AuthRequiredException
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.*
|
||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||
@@ -144,6 +145,9 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = loaderContext.httpGet(chapter.url).parseHtml()
|
||||
if (doc.location()?.endsWith("/register") == true) {
|
||||
throw AuthRequiredException("/login".inContextOf(doc))
|
||||
}
|
||||
val scripts = doc.head().select("script")
|
||||
val pg = doc.body().getElementById("pg").html()
|
||||
.substringAfter('=')
|
||||
|
||||
@@ -49,7 +49,8 @@ class MangareadRepository(
|
||||
Manga(
|
||||
id = href.longHashCode(),
|
||||
url = href,
|
||||
coverUrl = div.selectFirst("img").absUrl("src"),
|
||||
coverUrl = div.selectFirst("img").attr("data-srcset")
|
||||
.split(',').firstOrNull()?.substringBeforeLast(' ').orEmpty(),
|
||||
title = summary.selectFirst("h3").text(),
|
||||
rating = div.selectFirst("span.total_votes")?.ownText()
|
||||
?.toFloatOrNull()?.div(5f) ?: -1f,
|
||||
@@ -75,13 +76,17 @@ class MangareadRepository(
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val domain = conf.getDomain(DOMAIN)
|
||||
val doc = loaderContext.httpGet("https://$domain/manga/").parseHtml()
|
||||
val root = doc.body().getElementById("main-sidebar")
|
||||
.selectFirst(".genres_wrap")
|
||||
.selectFirst("ul")
|
||||
return root.select("li").mapToSet { li ->
|
||||
val root = doc.body().selectFirst("header")
|
||||
.selectFirst("ul.second-menu")
|
||||
return root.select("li").mapNotNullToSet { li ->
|
||||
val a = li.selectFirst("a")
|
||||
val href = a.attr("href").removeSuffix("/")
|
||||
.substringAfterLast("genres/", "")
|
||||
if (href.isEmpty()) {
|
||||
return@mapNotNullToSet null
|
||||
}
|
||||
MangaTag(
|
||||
key = a.attr("href").removeSuffix("/").substringAfterLast('/'),
|
||||
key = href,
|
||||
title = a.text(),
|
||||
source = MangaSource.MANGAREAD
|
||||
)
|
||||
@@ -119,7 +124,8 @@ class MangareadRepository(
|
||||
} ?: manga.tags,
|
||||
description = root2.selectFirst("div.description-summary")
|
||||
?.selectFirst("div.summary__content")
|
||||
?.select("p")?.drop(1)
|
||||
?.select("p")
|
||||
?.filterNot { it.ownText().startsWith("A brief description") }
|
||||
?.joinToString { it.html() },
|
||||
chapters = doc2.select("li").asReversed().mapIndexed { i, li ->
|
||||
val a = li.selectFirst("a")
|
||||
@@ -142,7 +148,7 @@ class MangareadRepository(
|
||||
?: throw ParseException("Root not found")
|
||||
return root.select("div.page-break").map { div ->
|
||||
val img = div.selectFirst("img")
|
||||
val url = img.absUrl("src")
|
||||
val url = img.absUrl("data-src")
|
||||
MangaPage(
|
||||
id = url.longHashCode(),
|
||||
url = url,
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.parser.site
|
||||
|
||||
import androidx.collection.arraySetOf
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
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.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/*
|
||||
class NudeMoonRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
|
||||
|
||||
override val source = MangaSource.NUDEMOON
|
||||
@@ -153,4 +144,4 @@ class NudeMoonRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
|
||||
private const val DEFAULT_DOMAIN = "nude-moon.me"
|
||||
private val pageUrlPatter = Pattern.compile(".*\\?page=[0-9]+$")
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
@@ -5,6 +5,6 @@ import org.koitharu.kotatsu.core.model.MangaSource
|
||||
|
||||
class SelfMangaRepository(loaderContext: MangaLoaderContext) : GroupleRepository(loaderContext) {
|
||||
|
||||
override val defaultDomain = "selfmanga.ru"
|
||||
override val defaultDomain = "selfmanga.live"
|
||||
override val source = MangaSource.SELFMANGA
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.AuthRequiredException
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
@@ -58,6 +59,7 @@ fun Throwable.toErrorFooter() = ErrorFooter(
|
||||
)
|
||||
|
||||
private fun getErrorIcon(error: Throwable) = when (error) {
|
||||
is AuthRequiredException,
|
||||
is CloudFlareProtectedException -> R.drawable.ic_denied_large
|
||||
else -> R.drawable.ic_error_large
|
||||
}
|
||||
@@ -45,10 +45,7 @@ import org.koitharu.kotatsu.utils.GridTouchHelper
|
||||
import org.koitharu.kotatsu.utils.ScreenOrientationHelper
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
import org.koitharu.kotatsu.utils.anim.Motion
|
||||
import org.koitharu.kotatsu.utils.ext.hasGlobalPoint
|
||||
import org.koitharu.kotatsu.utils.ext.hideAnimated
|
||||
import org.koitharu.kotatsu.utils.ext.hitTest
|
||||
import org.koitharu.kotatsu.utils.ext.showAnimated
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
ChaptersDialog.OnChapterChangeListener,
|
||||
@@ -213,7 +210,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
private fun onError(e: Throwable) {
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
.setTitle(R.string.error_occurred)
|
||||
.setMessage(e.message)
|
||||
.setMessage(e.getDisplayMessage(resources))
|
||||
.setPositiveButton(R.string.close, null)
|
||||
if (viewModel.content.value?.pages.isNullOrEmpty()) {
|
||||
dialog.setOnDismissListener {
|
||||
|
||||
@@ -85,7 +85,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
||||
findPreference<PreferenceScreen>(AppSettings.KEY_REMOTE_SOURCES)?.run {
|
||||
val total = MangaSource.values().size - 1
|
||||
summary = getString(
|
||||
R.string.enabled_d_from_d, total - settings.hiddenSources.size, total
|
||||
R.string.enabled_d_of_d, total - settings.hiddenSources.size, total
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@ import android.content.res.Resources
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.delay
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
||||
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
|
||||
import org.koitharu.kotatsu.core.exceptions.WrongPasswordException
|
||||
import org.koitharu.kotatsu.core.exceptions.*
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.SocketTimeoutException
|
||||
|
||||
@@ -28,6 +25,7 @@ suspend inline fun <T, R> T.retryUntilSuccess(maxAttempts: Int, action: T.() ->
|
||||
}
|
||||
|
||||
fun Throwable.getDisplayMessage(resources: Resources) = when (this) {
|
||||
is AuthRequiredException -> resources.getString(R.string.auth_required)
|
||||
is CloudFlareProtectedException -> resources.getString(R.string.captcha_required)
|
||||
is UnsupportedOperationException -> resources.getString(R.string.operation_not_supported)
|
||||
is UnsupportedFileException -> resources.getString(R.string.text_file_not_supported)
|
||||
|
||||
Reference in New Issue
Block a user