Hitomi: small refactor (#3389)
* Hitomi: small refactor * bump * coauthor Co-authored-by: ZIDOUZI <53157536+ZIDOUZI@users.noreply.github.com> * fix potential oom and optimize language query when some sort is applied, it will already fetch correct language so no need for separate query --------- Co-authored-by: ZIDOUZI <53157536+ZIDOUZI@users.noreply.github.com>
This commit is contained in:
parent
61b0ab972d
commit
76fe2af9ca
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Hitomi'
|
||||
extClass = '.HitomiFactory'
|
||||
extVersionCode = 29
|
||||
extVersionCode = 30
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
package eu.kanade.tachiyomi.extension.all.hitomi
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
|
@ -22,16 +17,16 @@ import kotlinx.coroutines.sync.Mutex
|
|||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Call
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.security.MessageDigest
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.LinkedList
|
||||
import java.util.Locale
|
||||
|
@ -41,7 +36,7 @@ import kotlin.math.min
|
|||
class Hitomi(
|
||||
override val lang: String,
|
||||
private val nozomiLang: String,
|
||||
) : ConfigurableSource, HttpSource() {
|
||||
) : HttpSource() {
|
||||
|
||||
override val name = "Hitomi"
|
||||
|
||||
|
@ -57,12 +52,6 @@ class Hitomi(
|
|||
|
||||
override val client = network.cloudflareClient
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
private var iconified = preferences.getBoolean(PREF_TAG_GENDER_ICON, false)
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.set("referer", "$baseUrl/")
|
||||
.set("origin", baseUrl)
|
||||
|
@ -119,7 +108,8 @@ class Hitomi(
|
|||
.build()
|
||||
}
|
||||
|
||||
return client.newCall(GET(url, rangeHeaders)).awaitSuccess().use { it.body.bytes() }
|
||||
return client.newCall(GET(url, rangeHeaders, CacheControl.FORCE_NETWORK))
|
||||
.awaitSuccess().use { it.body.bytes() }
|
||||
}
|
||||
|
||||
private suspend fun hitomiSearch(
|
||||
|
@ -168,6 +158,10 @@ class Hitomi(
|
|||
}
|
||||
}
|
||||
|
||||
if (language != "all" && sortBy == Pair(null, "index")) {
|
||||
terms += "language:$language"
|
||||
}
|
||||
|
||||
val positiveTerms = LinkedList<String>()
|
||||
val negativeTerms = LinkedList<String>()
|
||||
|
||||
|
@ -181,22 +175,35 @@ class Hitomi(
|
|||
|
||||
val positiveResults = positiveTerms.map {
|
||||
async {
|
||||
runCatching {
|
||||
try {
|
||||
getGalleryIDsForQuery(it, language)
|
||||
}.getOrDefault(ArrayList())
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (e.message?.equals("HTTP error 404") == true) {
|
||||
throw Exception("Unknown query: \"$it\"")
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val negativeResults = negativeTerms.map {
|
||||
async {
|
||||
runCatching {
|
||||
try {
|
||||
getGalleryIDsForQuery(it, language)
|
||||
}.getOrDefault(ArrayList())
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (e.message?.equals("HTTP error 404") == true) {
|
||||
throw Exception("Unknown query: \"$it\"")
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val results = when {
|
||||
positiveTerms.isEmpty() -> getGalleryIDsFromNozomi(sortBy.first, sortBy.second, language)
|
||||
positiveTerms.isEmpty() || sortBy != Pair(null, "index")
|
||||
-> getGalleryIDsFromNozomi(sortBy.first, sortBy.second, language)
|
||||
else -> ArrayList()
|
||||
}
|
||||
|
||||
|
@ -451,12 +458,18 @@ class Hitomi(
|
|||
private suspend fun Collection<Int>.toMangaList() = coroutineScope {
|
||||
map { id ->
|
||||
async {
|
||||
runCatching {
|
||||
try {
|
||||
client.newCall(GET("$ltnUrl/galleries/$id.js", headers))
|
||||
.awaitSuccess()
|
||||
.parseScriptAs<Gallery>()
|
||||
.toSManga()
|
||||
}.getOrNull()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (e.message?.equals("HTTP error 404") == true) {
|
||||
return@async null
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll().filterNotNull()
|
||||
}
|
||||
|
@ -466,7 +479,7 @@ class Hitomi(
|
|||
url = galleryurl
|
||||
author = groups?.joinToString { it.formatted }
|
||||
artist = artists?.joinToString { it.formatted }
|
||||
genre = tags?.joinToString { it.getFormatted(iconified) }
|
||||
genre = tags?.joinToString { it.formatted }
|
||||
thumbnail_url = files.first().let {
|
||||
val hash = it.hash
|
||||
val imageId = imageIdFromHash(hash)
|
||||
|
@ -479,7 +492,7 @@ class Hitomi(
|
|||
append("Series: ", it, "\n")
|
||||
}
|
||||
characters?.joinToString { it.formatted }?.let {
|
||||
append("Characters: ", it, "\n\n")
|
||||
append("Characters: ", it, "\n")
|
||||
}
|
||||
append("Type: ", type, "\n")
|
||||
append("Pages: ", files.size, "\n")
|
||||
|
@ -504,26 +517,21 @@ class Hitomi(
|
|||
|
||||
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request {
|
||||
val id = manga.url
|
||||
.substringAfterLast("-")
|
||||
.substringBefore(".")
|
||||
|
||||
return GET("$ltnUrl/galleries/$id.js#${manga.url}", headers)
|
||||
}
|
||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val gallery = response.parseScriptAs<Gallery>()
|
||||
val mangaUrl = response.request.url.fragment!!
|
||||
|
||||
return listOf(
|
||||
SChapter.create().apply {
|
||||
name = "Chapter"
|
||||
url = mangaUrl
|
||||
url = gallery.galleryurl
|
||||
scanlator = gallery.type
|
||||
date_upload = runCatching {
|
||||
date_upload = try {
|
||||
dateFormat.parse(gallery.date.substringBeforeLast("-"))!!.time
|
||||
}.getOrDefault(0L)
|
||||
} catch (_: ParseException) {
|
||||
0L
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -542,6 +550,9 @@ class Hitomi(
|
|||
|
||||
override fun pageListParse(response: Response) = runBlocking {
|
||||
val gallery = response.parseScriptAs<Gallery>()
|
||||
val id = gallery.galleryurl
|
||||
.substringAfterLast("-")
|
||||
.substringBefore(".")
|
||||
|
||||
gallery.files.mapIndexed { idx, img ->
|
||||
val hash = img.hash
|
||||
|
@ -637,25 +648,9 @@ class Hitomi(
|
|||
|
||||
override fun popularMangaParse(response: Response) = throw UnsupportedOperationException()
|
||||
override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException()
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
SwitchPreferenceCompat(screen.context).apply {
|
||||
key = PREF_TAG_GENDER_ICON
|
||||
title = "Show gender as text or icon in tags (requires refresh)"
|
||||
summaryOff = "Show gender as text"
|
||||
summaryOn = "Show gender as icon"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
iconified = newValue == true
|
||||
true
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
||||
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException()
|
||||
override fun searchMangaParse(response: Response) = throw UnsupportedOperationException()
|
||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
|
||||
companion object {
|
||||
private const val PREF_TAG_GENDER_ICON = "pref_tag_gender_icon"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
|
|||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
@Serializable
|
||||
data class Gallery(
|
||||
class Gallery(
|
||||
val galleryurl: String,
|
||||
val title: String,
|
||||
val date: String,
|
||||
|
@ -19,49 +19,49 @@ data class Gallery(
|
|||
)
|
||||
|
||||
@Serializable
|
||||
data class ImageFile(
|
||||
class ImageFile(
|
||||
val hash: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Tag(
|
||||
val female: JsonPrimitive?,
|
||||
val male: JsonPrimitive?,
|
||||
val tag: String,
|
||||
class Tag(
|
||||
private val female: JsonPrimitive?,
|
||||
private val male: JsonPrimitive?,
|
||||
private val tag: String,
|
||||
) {
|
||||
fun getFormatted(iconified: Boolean) = if (female?.content == "1") {
|
||||
tag.toCamelCase() + if (iconified) " ♀" else " (Female)"
|
||||
val formatted get() = if (female?.content == "1") {
|
||||
tag.toCamelCase() + " ♀"
|
||||
} else if (male?.content == "1") {
|
||||
tag.toCamelCase() + if (iconified) " ♂" else " (Male)"
|
||||
tag.toCamelCase() + " ♂"
|
||||
} else {
|
||||
tag.toCamelCase()
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Artist(
|
||||
val artist: String,
|
||||
class Artist(
|
||||
private val artist: String,
|
||||
) {
|
||||
val formatted get() = artist.toCamelCase()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Group(
|
||||
val group: String,
|
||||
class Group(
|
||||
private val group: String,
|
||||
) {
|
||||
val formatted get() = group.toCamelCase()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Character(
|
||||
val character: String,
|
||||
class Character(
|
||||
private val character: String,
|
||||
) {
|
||||
val formatted get() = character.toCamelCase()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Parody(
|
||||
val parody: String,
|
||||
class Parody(
|
||||
private val parody: String,
|
||||
) {
|
||||
val formatted get() = parody.toCamelCase()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue