Anchira - Add tag grouping, allow to get the source URL without the keys (#306)

This commit is contained in:
Fermín Cirella 2024-01-17 01:45:50 -03:00 committed by Draff
parent d61cbfc0c1
commit c311614a2e
4 changed files with 46 additions and 45 deletions

View File

@ -2,7 +2,7 @@ ext {
extName = 'Anchira' extName = 'Anchira'
pkgNameSuffix = 'en.anchira' pkgNameSuffix = 'en.anchira'
extClass = '.Anchira' extClass = '.Anchira'
extVersionCode = 4 extVersionCode = 5
isNsfw = true isNsfw = true
} }

View File

@ -65,6 +65,9 @@ class Anchira : HttpSource(), ConfigurableSource {
override fun latestUpdatesRequest(page: Int) = GET("$libraryUrl?page=$page", headers) override fun latestUpdatesRequest(page: Int) = GET("$libraryUrl?page=$page", headers)
override fun latestUpdatesParse(response: Response): MangasPage { override fun latestUpdatesParse(response: Response): MangasPage {
// Ugly but it works
anchiraData.isNotEmpty()
val data = json.decodeFromString<LibraryResponse>(response.body.string()) val data = json.decodeFromString<LibraryResponse>(response.body.string())
return MangasPage( return MangasPage(
@ -75,7 +78,7 @@ class Anchira : HttpSource(), ConfigurableSource {
thumbnail_url = "$cdnUrl/${it.id}/${it.key}/m/${it.thumbnailIndex + 1}" thumbnail_url = "$cdnUrl/${it.id}/${it.key}/m/${it.thumbnailIndex + 1}"
artist = it.tags.filter { it.namespace == 1 }.joinToString(", ") { it.name } artist = it.tags.filter { it.namespace == 1 }.joinToString(", ") { it.name }
author = it.tags.filter { it.namespace == 2 }.joinToString(", ") { it.name } author = it.tags.filter { it.namespace == 2 }.joinToString(", ") { it.name }
genre = prepareTags(it.tags) genre = prepareTags(it.tags, preferences.useTagGrouping)
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
status = SManga.COMPLETED status = SManga.COMPLETED
} }
@ -165,7 +168,7 @@ class Anchira : HttpSource(), ConfigurableSource {
"$cdnUrl/${data.id}/${data.key}/b/${data.thumbnailIndex + 1}" "$cdnUrl/${data.id}/${data.key}/b/${data.thumbnailIndex + 1}"
artist = data.tags.filter { it.namespace == 1 }.joinToString(", ") { it.name } artist = data.tags.filter { it.namespace == 1 }.joinToString(", ") { it.name }
author = data.tags.filter { it.namespace == 2 }.joinToString(", ") { it.name } author = data.tags.filter { it.namespace == 2 }.joinToString(", ") { it.name }
genre = prepareTags(data.tags) genre = prepareTags(data.tags, preferences.useTagGrouping)
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
status = SManga.COMPLETED status = SManga.COMPLETED
} }
@ -173,7 +176,7 @@ class Anchira : HttpSource(), ConfigurableSource {
override fun getMangaUrl(manga: SManga) = if (preferences.openSource) { override fun getMangaUrl(manga: SManga) = if (preferences.openSource) {
val id = manga.url.split("/").reversed()[1].toInt() val id = manga.url.split("/").reversed()[1].toInt()
anchiraData.galleries.find { it.id == id }?.url ?: "$baseUrl${manga.url}" anchiraData.find { it.id == id }?.url ?: "$baseUrl${manga.url}"
} else { } else {
"$baseUrl${manga.url}" "$baseUrl${manga.url}"
} }
@ -216,16 +219,15 @@ class Anchira : HttpSource(), ConfigurableSource {
} }
private fun getImageData(entry: Entry): ImageData { private fun getImageData(entry: Entry): ImageData {
val keys = anchiraData.galleries.find { it.id == entry.id } val keys = anchiraData.find { it.id == entry.id }
if (keys != null) { if (keys?.key != null && keys.hash != null) {
return ImageData(keys.id, keys.key, keys.hash, keys.names) return ImageData(keys.id, keys.key, keys.hash, keys.names)
} }
try { try {
val response = val response =
client.newCall(GET("$libraryUrl/${entry.id}/${entry.key}/data", headers)).execute() client.newCall(GET("$libraryUrl/${entry.id}/${entry.key}/data", headers)).execute()
val body = response.body
return json.decodeFromString(response.body.string()) return json.decodeFromString(response.body.string())
} catch (_: IOException) { } catch (_: IOException) {
@ -255,14 +257,21 @@ class Anchira : HttpSource(), ConfigurableSource {
val openSourcePref = SwitchPreferenceCompat(screen.context).apply { val openSourcePref = SwitchPreferenceCompat(screen.context).apply {
key = OPEN_SOURCE_PREF key = OPEN_SOURCE_PREF
title = "Open in FAKKU in WebView" title = "Open source website in WebView"
summary = summary = "Enable to open the original source website of the gallery (if available) instead of Anchira."
"Enable to open the search the book in FAKKU when opening the manga or chapter in WebView. If only one result exists, it will open that one." setDefaultValue(false)
}
val useTagGrouping = SwitchPreferenceCompat(screen.context).apply {
key = USE_TAG_GROUPING
title = "Group tags"
summary = "Enable to group tags together by artist, circle, parody, magazine and general tags"
setDefaultValue(false) setDefaultValue(false)
} }
screen.addPreference(imageQualityPref) screen.addPreference(imageQualityPref)
screen.addPreference(openSourcePref) screen.addPreference(openSourcePref)
screen.addPreference(useTagGrouping)
} }
override fun getFilterList() = FilterList( override fun getFilterList() = FilterList(
@ -294,6 +303,9 @@ class Anchira : HttpSource(), ConfigurableSource {
private val SharedPreferences.openSource private val SharedPreferences.openSource
get() = getBoolean(OPEN_SOURCE_PREF, false) get() = getBoolean(OPEN_SOURCE_PREF, false)
private val SharedPreferences.useTagGrouping
get() = getBoolean(USE_TAG_GROUPING, false)
private fun apiInterceptor(chain: Interceptor.Chain): Response { private fun apiInterceptor(chain: Interceptor.Chain): Response {
val request = chain.request() val request = chain.request()
val requestUrl = request.url.toString() val requestUrl = request.url.toString()
@ -327,13 +339,14 @@ class Anchira : HttpSource(), ConfigurableSource {
private val anchiraData by lazy { private val anchiraData by lazy {
client.newCall(GET(DATA_JSON, headers)).execute() client.newCall(GET(DATA_JSON, headers)).execute()
.use { json.decodeFromStream<AnchiraData>(it.body.byteStream()) } .use { json.decodeFromStream<List<EntryKey>>(it.body.byteStream()) }
} }
companion object { companion object {
private const val IMAGE_QUALITY_PREF = "image_quality" private const val IMAGE_QUALITY_PREF = "image_quality"
private const val OPEN_SOURCE_PREF = "use_manga_source" private const val OPEN_SOURCE_PREF = "use_manga_source"
private const val USE_TAG_GROUPING = "use_tag_grouping"
private const val DATA_JSON = private const val DATA_JSON =
"https://gist.githubusercontent.com/LetrixZ/2b559cc5829d1c221c701e02ecd81411/raw/site_data.json" "https://gist.githubusercontent.com/LetrixZ/2b559cc5829d1c221c701e02ecd81411/raw/data-v5.json"
} }
} }

View File

@ -48,14 +48,8 @@ data class ImageData(
@Serializable @Serializable
data class EntryKey( data class EntryKey(
val id: Int, val id: Int,
val key: String, val key: String? = null,
val hash: String, val hash: String? = null,
val url: String?, val url: String? = null,
val names: List<String> = emptyList(), val names: List<String> = emptyList(),
) )
@Serializable
data class AnchiraData(
val key: String,
val galleries: List<EntryKey>,
)

View File

@ -1,34 +1,28 @@
package eu.kanade.tachiyomi.extension.en.anchira package eu.kanade.tachiyomi.extension.en.anchira
import android.util.Base64
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.ResponseBody
import okio.ByteString.Companion.decodeBase64
object AnchiraHelper { object AnchiraHelper {
const val KEY = "ZnVja19uaWdnZXJzX2FuZF9mYWdnb3RzLF9hbmRfZGVhdGhfdG9fYWxsX2pld3M="
val json = Json { ignoreUnknownKeys = true }
fun getPathFromUrl(url: String) = "${url.split("/").reversed()[1]}/${url.split("/").last()}" fun getPathFromUrl(url: String) = "${url.split("/").reversed()[1]}/${url.split("/").last()}"
inline fun <reified T> decodeBytes(body: ResponseBody, key: String = KEY): T { fun prepareTags(tags: List<Tag>, group: Boolean) = tags.map {
val encryptedText = body.string().decodeBase64()!!
return json.decodeFromString(
XXTEA.decryptToString(
encryptedText.toByteArray(),
key = Base64.decode(key, Base64.DEFAULT).decodeToString(),
)!!,
)
}
fun prepareTags(tags: List<Tag>) = tags.map {
if (it.namespace == null) { if (it.namespace == null) {
it.namespace = 6 it.namespace = 6
} }
it it
}.sortedBy { it.namespace }.map { }
return@map it.name.lowercase() .sortedBy { it.namespace }
}.joinToString(", ") { it } .map {
val tag = it.name.lowercase()
return@map if (group) {
when (it.namespace) {
1 -> "artist:$tag"
2 -> "circle:$tag"
3 -> "parody:$tag"
4 -> "magazine:$tag"
else -> "tag:$tag"
}
} else {
tag
}
}
.joinToString(", ") { it }
} }