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
							
								
									9fa6b8cb51
								
							
						
					
					
						commit
						9602aa5dd5
					
				| @ -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) | ||||
|     } | ||||
| 
 | ||||
|     private fun paginatedChapterListRequest(mangaUrl: String, page: Int): Request { | ||||
|         return GET( | ||||
|             "$apiUrl$mangaUrl".toHttpUrl().newBuilder().apply { | ||||
|         val mangaUrl = manga.url.removeSuffix("#") | ||||
|         val url = "$apiUrl$mangaUrl".toHttpUrl().newBuilder().apply { | ||||
|             addPathSegment("chapters") | ||||
|                 if (comickFunLang != "all") addQueryParameter("lang", comickFunLang) | ||||
|             if (comickLang != "all") addQueryParameter("lang", comickLang) | ||||
|             addQueryParameter("tachiyomi", "true") | ||||
|                 addQueryParameter("page", "$page") | ||||
|             }.build(), | ||||
|             headers, | ||||
|         ) | ||||
|             addQueryParameter("limit", "$CHAPTERS_LIMIT") | ||||
|         }.build() | ||||
| 
 | ||||
|         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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 AwkwardPeak7
						AwkwardPeak7