mirror of
https://github.com/keiyoushi/extensions-source.git
synced 2024-11-22 02:12:42 +01:00
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:
parent
706200386d
commit
fb7b7ac3cb
@ -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" />
|
||||
|
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'Comick'
|
||||
extClass = '.ComickFunFactory'
|
||||
extVersionCode = 41
|
||||
extClass = '.ComickFactory'
|
||||
extVersionCode = 42
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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]!!
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
@ -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()) {
|
Loading…
Reference in New Issue
Block a user