Comick: fix new chapters delay and small refactor (#1354)

* remove chapter pagination

page parameter seems to trigger some cache issue in their api

* update baseUrl

* data class -> class

micro optimization

* small refactor

* remove useless interceptor

* oops

* mutable not needed
This commit is contained in:
AwkwardPeak7 2024-02-18 16:09:05 +05:00 committed by GitHub
parent 706200386d
commit fb7b7ac3cb
8 changed files with 75 additions and 118 deletions

View File

@ -3,7 +3,7 @@
<application>
<activity
android:name=".all.comickfun.ComickFunUrlActivity"
android:name=".all.comickfun.ComickUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
@ -14,6 +14,7 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="comick.io" />
<data android:host="comick.cc" />
<data android:host="comick.ink" />
<data android:host="comick.app" />

View File

@ -1,7 +1,7 @@
ext {
extName = 'Comick'
extClass = '.ComickFunFactory'
extVersionCode = 41
extClass = '.ComickFactory'
extVersionCode = 42
isNsfw = true
}

View File

@ -26,19 +26,16 @@ import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
import kotlin.math.min
abstract class ComickFun(
abstract class Comick(
override val lang: String,
private val comickFunLang: String,
private val comickLang: String,
) : ConfigurableSource, HttpSource() {
override val name = "Comick"
override val baseUrl = "https://comick.cc"
override val baseUrl = "https://comick.io"
private val apiUrl = "https://api.comick.fun"
@ -62,8 +59,9 @@ abstract class ComickFun(
)
}
private val preferences: SharedPreferences by lazy {
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
.newLineIgnoredGroups()
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
@ -151,23 +149,18 @@ abstract class ComickFun(
private val SharedPreferences.scorePosition: String
get() = getString(SCORE_POSITION_PREF, SCORE_POSITION_DEFAULT) ?: SCORE_POSITION_DEFAULT
init {
preferences.newLineIgnoredGroups()
}
override fun headersBuilder() = Headers.Builder().apply {
add("Referer", "$baseUrl/")
add("User-Agent", "Tachiyomi ${System.getProperty("http.agent")}")
}
override val client = network.client.newBuilder()
.addInterceptor(::thumbnailIntercept)
.rateLimit(3, 1)
.build()
/** Popular Manga **/
override fun popularMangaRequest(page: Int): Request {
val url = "$apiUrl/v1.0/search?sort=follow&limit=$limit&page=$page&tachiyomi=true"
val url = "$apiUrl/v1.0/search?sort=follow&limit=$LIMIT&page=$page&tachiyomi=true"
return GET(url, headers)
}
@ -175,13 +168,13 @@ abstract class ComickFun(
val result = response.parseAs<List<SearchManga>>()
return MangasPage(
result.map(SearchManga::toSManga),
hasNextPage = result.size >= limit,
hasNextPage = result.size >= LIMIT,
)
}
/** Latest Manga **/
override fun latestUpdatesRequest(page: Int): Request {
val url = "$apiUrl/v1.0/search?sort=uploaded&limit=$limit&page=$page&tachiyomi=true"
val url = "$apiUrl/v1.0/search?sort=uploaded&limit=$LIMIT&page=$page&tachiyomi=true"
return GET(url, headers)
}
@ -233,8 +226,8 @@ abstract class ComickFun(
}
private fun paginatedSearchPage(page: Int): MangasPage {
val end = min(page * limit, searchResponse.size)
val entries = searchResponse.subList((page - 1) * limit, end)
val end = min(page * LIMIT, searchResponse.size)
val entries = searchResponse.subList((page - 1) * LIMIT, end)
.map(SearchManga::toSManga)
return MangasPage(entries, end < searchResponse.size)
}
@ -317,7 +310,7 @@ abstract class ComickFun(
}
}
addQueryParameter("tachiyomi", "true")
addQueryParameter("limit", "$limit")
addQueryParameter("limit", "$LIMIT")
addQueryParameter("page", "$page")
}.build()
@ -367,7 +360,7 @@ abstract class ComickFun(
val coversUrl =
"$apiUrl/comic/${mangaData.comic.slug ?: mangaData.comic.hid}/covers?tachiyomi=true"
val covers = client.newCall(GET(coversUrl)).execute()
.parseAs<Covers>().md_covers.reversed()
.parseAs<Covers>().mdCovers.reversed()
return mangaData.toSManga(
includeMuTags = preferences.includeMuTags,
covers = if (covers.any { it.vol == "1" }) covers.filter { it.vol == "1" } else covers,
@ -387,19 +380,15 @@ abstract class ComickFun(
throw Exception("Migrate from Comick to Comick")
}
return paginatedChapterListRequest(manga.url.removeSuffix("#"), 1)
}
val mangaUrl = manga.url.removeSuffix("#")
val url = "$apiUrl$mangaUrl".toHttpUrl().newBuilder().apply {
addPathSegment("chapters")
if (comickLang != "all") addQueryParameter("lang", comickLang)
addQueryParameter("tachiyomi", "true")
addQueryParameter("limit", "$CHAPTERS_LIMIT")
}.build()
private fun paginatedChapterListRequest(mangaUrl: String, page: Int): Request {
return GET(
"$apiUrl$mangaUrl".toHttpUrl().newBuilder().apply {
addPathSegment("chapters")
if (comickFunLang != "all") addQueryParameter("lang", comickFunLang)
addQueryParameter("tachiyomi", "true")
addQueryParameter("page", "$page")
}.build(),
headers,
)
return GET(url, headers)
}
override fun chapterListParse(response: Response): List<SChapter> {
@ -409,20 +398,6 @@ abstract class ComickFun(
.substringBefore("/chapters")
.substringAfter(apiUrl)
var resultSize = chapterListResponse.chapters.size
var page = 2
while (chapterListResponse.total > resultSize) {
val newRequest = paginatedChapterListRequest(mangaUrl, page)
val newResponse = client.newCall(newRequest).execute()
val newChapterListResponse = newResponse.parseAs<ChapterList>()
chapterListResponse.chapters += newChapterListResponse.chapters
resultSize += newChapterListResponse.chapters.size
page += 1
}
return chapterListResponse.chapters
.filter {
it.groups.map { g -> g.lowercase() }.intersect(preferences.ignoredGroups).isEmpty()
@ -457,8 +432,8 @@ abstract class ComickFun(
override fun getFilterList() = getFilters()
private fun SharedPreferences.newLineIgnoredGroups() {
if (getBoolean(MIGRATED_IGNORED_GROUPS, false)) return
private fun SharedPreferences.newLineIgnoredGroups(): SharedPreferences {
if (getBoolean(MIGRATED_IGNORED_GROUPS, false)) return this
val ignoredGroups = getString(IGNORED_GROUPS_PREF, "").orEmpty()
edit()
@ -472,6 +447,8 @@ abstract class ComickFun(
)
.putBoolean(MIGRATED_IGNORED_GROUPS, true)
.apply()
return this
}
companion object {
@ -484,14 +461,7 @@ abstract class ComickFun(
private const val FIRST_COVER_DEFAULT = true
private const val SCORE_POSITION_PREF = "ScorePosition"
private const val SCORE_POSITION_DEFAULT = "top"
private const val limit = 20
val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
}
val markdownLinksRegex = "\\[([^]]+)]\\(([^)]+)\\)".toRegex()
val markdownItalicBoldRegex = "\\*+\\s*([^*]*)\\s*\\*+".toRegex()
val markdownItalicRegex = "_+\\s*([^_]*)\\s*_+".toRegex()
private const val LIMIT = 20
private const val CHAPTERS_LIMIT = 99999
}
}

View File

@ -10,7 +10,7 @@ val legacyLanguageMappings = mapOf(
"zh" to "zh-Hans", // Simplified Chinese
).withDefault { it } // country code matches language code
class ComickFunFactory : SourceFactory {
class ComickFactory : SourceFactory {
private val idMap = listOf(
"all" to 982606170401027267,
"en" to 2971557565147974499,
@ -55,7 +55,7 @@ class ComickFunFactory : SourceFactory {
"da" to 7137437402245830147,
).toMap()
override fun createSources(): List<Source> = idMap.keys.map {
object : ComickFun(legacyLanguageMappings.getValue(it), it) {
object : Comick(legacyLanguageMappings.getValue(it), it) {
override val id: Long = idMap[it]!!
}
}

View File

@ -7,7 +7,7 @@ import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
class ComickFunUrlActivity : Activity() {
class ComickUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
@ -15,7 +15,7 @@ class ComickFunUrlActivity : Activity() {
val slug = pathSegments[1]
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "${ComickFun.SLUG_SEARCH_PREFIX}$slug")
putExtra("query", "${Comick.SLUG_SEARCH_PREFIX}$slug")
putExtra("filter", packageName)
}

View File

@ -8,9 +8,9 @@ import java.math.BigDecimal
import java.math.RoundingMode
@Serializable
data class SearchManga(
val hid: String,
val title: String,
class SearchManga(
private val hid: String,
private val title: String,
@SerialName("md_covers") val mdCovers: List<MDcovers> = emptyList(),
@SerialName("cover_url") val cover: String? = null,
) {
@ -23,12 +23,12 @@ data class SearchManga(
}
@Serializable
data class Manga(
class Manga(
val comic: Comic,
val artists: List<Name> = emptyList(),
val authors: List<Name> = emptyList(),
val genres: List<Name> = emptyList(),
val demographic: String? = null,
private val artists: List<Name> = emptyList(),
private val authors: List<Name> = emptyList(),
private val genres: List<Name> = emptyList(),
private val demographic: String? = null,
) {
fun toSManga(
includeMuTags: Boolean = false,
@ -90,10 +90,10 @@ data class Manga(
}
@Serializable
data class Comic(
class Comic(
val hid: String,
val title: String,
val country: String? = null,
private val country: String? = null,
val slug: String? = null,
@SerialName("md_titles") val altTitles: List<Title> = emptyList(),
val desc: String? = null,
@ -125,55 +125,54 @@ data class Comic(
}
@Serializable
data class MdGenres(
class MdGenres(
@SerialName("md_genres") val name: Name? = null,
)
@Serializable
data class MuComicCategories(
class MuComicCategories(
@SerialName("mu_comic_categories") val categories: List<MuCategories?> = emptyList(),
)
@Serializable
data class MuCategories(
class MuCategories(
@SerialName("mu_categories") val category: Title? = null,
)
@Serializable
data class Covers(
val md_covers: List<MDcovers> = emptyList(),
class Covers(
@SerialName("md_covers") val mdCovers: List<MDcovers> = emptyList(),
)
@Serializable
data class MDcovers(
class MDcovers(
val b2key: String?,
val vol: String? = null,
)
@Serializable
data class Title(
class Title(
val title: String?,
)
@Serializable
data class Name(
class Name(
val name: String,
)
@Serializable
data class ChapterList(
val chapters: MutableList<Chapter>,
val total: Int,
class ChapterList(
val chapters: List<Chapter>,
)
@Serializable
data class Chapter(
val hid: String,
val lang: String = "",
val title: String = "",
class Chapter(
private val hid: String,
private val lang: String = "",
private val title: String = "",
@SerialName("created_at") val createdAt: String = "",
val chap: String = "",
val vol: String = "",
private val chap: String = "",
private val vol: String = "",
@SerialName("group_name") val groups: List<String> = emptyList(),
) {
fun toSChapter(mangaUrl: String) = SChapter.create().apply {
@ -185,16 +184,16 @@ data class Chapter(
}
@Serializable
data class PageList(
class PageList(
val chapter: ChapterPageData,
)
@Serializable
data class ChapterPageData(
class ChapterPageData(
val images: List<Page>,
)
@Serializable
data class Page(
class Page(
val url: String? = null,
)

View File

@ -1,13 +1,19 @@
package eu.kanade.tachiyomi.extension.all.comickfun
import eu.kanade.tachiyomi.extension.all.comickfun.ComickFun.Companion.dateFormat
import eu.kanade.tachiyomi.extension.all.comickfun.ComickFun.Companion.markdownItalicBoldRegex
import eu.kanade.tachiyomi.extension.all.comickfun.ComickFun.Companion.markdownItalicRegex
import eu.kanade.tachiyomi.extension.all.comickfun.ComickFun.Companion.markdownLinksRegex
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Interceptor
import okhttp3.Response
import org.jsoup.parser.Parser
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
private val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
}
private val markdownLinksRegex = "\\[([^]]+)]\\(([^)]+)\\)".toRegex()
private val markdownItalicBoldRegex = "\\*+\\s*([^*]*)\\s*\\*+".toRegex()
private val markdownItalicRegex = "_+\\s*([^_]*)\\s*_+".toRegex()
internal fun String.beautifyDescription(): String {
return Parser.unescapeEntities(this, false)
@ -42,25 +48,6 @@ internal fun parseCover(thumbnailUrl: String?, mdCovers: List<MDcovers>): String
return thumbnailUrl?.replaceAfterLast("/", "$b2key#$vol")
}
internal fun thumbnailIntercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val frag = request.url.fragment
if (frag.isNullOrEmpty()) return chain.proceed(request)
val response = chain.proceed(request)
if (!response.isSuccessful && response.code == 404) {
response.close()
val url = request.url.toString()
.replaceAfterLast("/", frag)
return chain.proceed(
request.newBuilder()
.url(url)
.build(),
)
}
return response
}
internal fun beautifyChapterName(vol: String, chap: String, title: String): String {
return buildString {
if (vol.isNotEmpty()) {