Bilibili (Multisrc): Refactor some codes (#14164)

* Bilibili (Multisrc): Refactor some codes

* Lint

* Fix import
This commit is contained in:
AntsyLich 2022-11-07 21:14:17 +06:00 committed by GitHub
parent a55da72c76
commit a3b612a940
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 81 deletions

View File

@ -14,25 +14,13 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="www.bilibilicomics.com"
android:pathPattern="/detail/mc..*"
android:scheme="https" />
<data android:scheme="https" />
<data
android:host="bilibilicomics.com"
android:pathPattern="/detail/mc..*"
android:scheme="https" />
<data android:host="bilibilicomics.com" />
<data android:host="m.bilibilicomics.com" />
<data android:host="www.bilibilicomics.com" />
<data
android:host="www.m.bilibilicomics.com"
android:pathPattern="/detail/mc..*"
android:scheme="https" />
<data
android:host="m.bilibilicomics.com"
android:pathPattern="/detail/mc..*"
android:scheme="https" />
<data android:pathPattern="/detail/mc..*" />
</intent-filter>
</activity>
</application>

View File

@ -31,6 +31,7 @@ import okhttp3.Response
import okio.Buffer
import java.io.IOException
import java.net.URLDecoder
import java.util.Calendar
class BilibiliComicsFactory : SourceFactory {
override fun createSources() = listOf(
@ -67,6 +68,9 @@ abstract class BilibiliComics(lang: String) : Bilibili(
private var accessTokenCookie: BilibiliAccessTokenCookie? = null
private val dayOfWeek: Int
get() = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1
override fun latestUpdatesRequest(page: Int): Request {
val jsonPayload = buildJsonObject { put("day", dayOfWeek) }
val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)

View File

@ -14,15 +14,12 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="manga.bilibili.com"
android:pathPattern="/detail/mc..*"
android:scheme="https" />
<data android:scheme="https" />
<data
android:host="manga.bilibili.com"
android:pathPattern="/m/detail/mc..*"
android:scheme="https" />
<data android:host="manga.bilibili.com" />
<data android:pathPattern="/detail/mc..*" />
<data android:pathPattern="/m/detail/mc..*" />
</intent-filter>
</activity>
</application>

View File

@ -34,7 +34,6 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
abstract class Bilibili(
@ -46,7 +45,7 @@ abstract class Bilibili(
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(::expiredTokenIntercept)
.addInterceptor(::expiredImageTokenIntercept)
.rateLimitHost(baseUrl.toHttpUrl(), 1)
.rateLimitHost(CDN_URL.toHttpUrl(), 2)
.rateLimitHost(COVER_CDN_URL.toHttpUrl(), 2)
@ -76,9 +75,6 @@ abstract class Bilibili(
protected open val signedIn: Boolean = false
protected val dayOfWeek: Int
get() = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1
override fun popularMangaRequest(page: Int): Request = searchMangaRequest(
page = page,
query = "",
@ -100,11 +96,9 @@ abstract class Bilibili(
override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.startsWith(PREFIX_ID_SEARCH) && query.matches(ID_SEARCH_PATTERN)) {
val comicId = query
.removePrefix(PREFIX_ID_SEARCH)
.removePrefix("mc")
return mangaDetailsApiRequest("/detail/mc$comicId")
ID_SEARCH_PATTERN.matchEntire(query)?.let {
val (id) = it.destructured
return mangaDetailsApiRequest("/detail/mc$id")
}
val price = filters.firstInstanceOrNull<PriceFilter>()?.state ?: 0
@ -144,12 +138,13 @@ abstract class Bilibili(
}
override fun searchMangaParse(response: Response): MangasPage {
if (response.request.url.toString().contains("ComicDetail")) {
val requestUrl = response.request.url.toString()
if (requestUrl.contains("ComicDetail")) {
val comic = mangaDetailsParse(response)
return MangasPage(listOf(comic), hasNextPage = false)
}
if (response.request.url.toString().contains("ClassPage")) {
if (requestUrl.contains("ClassPage")) {
val result = response.parseAs<List<BilibiliComicDto>>()
if (result.code != 0) {
@ -210,8 +205,7 @@ abstract class Bilibili(
}
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val result = response.parseAs<BilibiliComicDto>()
val comic = result.data!!
val comic = response.parseAs<BilibiliComicDto>().data!!
title = comic.title
author = comic.authorName.joinToString()
@ -231,7 +225,7 @@ abstract class Bilibili(
append("\n${intl.totalChapterCount}: ${intl.localize(comic.episodeList.size)}")
if (comic.updateWeekdays.isNotEmpty() && status == SManga.ONGOING) {
append("\n${intl.updatedEvery}: ${intl.getWeekdays(comic.updateWeekdays)}")
append("\n${intl.getUpdateDays(comic.updateWeekdays)}")
}
}
thumbnail_url = comic.verticalCover
@ -254,9 +248,8 @@ abstract class Bilibili(
}
protected open fun chapterFromObject(episode: BilibiliEpisodeDto, comicId: Int): SChapter = SChapter.create().apply {
name = intl.episodePrefix + episode.shortTitle +
(if (episode.title.isNotBlank()) " - " + episode.title else "")
date_upload = episode.publicationTime.substringBefore("T").toDate()
name = episode.shortTitle.plus(if (episode.title.isNotBlank()) " - ${episode.title}" else "")
date_upload = episode.publicationTime.toDate()
url = "/mc$comicId/${episode.id}"
}
@ -373,46 +366,45 @@ abstract class Bilibili(
return FilterList(filters)
}
private fun expiredTokenIntercept(chain: Interceptor.Chain): Response {
private fun expiredImageTokenIntercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
// Get a new image token if the current one expired.
if (response.code == 403 && chain.request().url.toString().contains(CDN_URL)) {
response.close()
val imagePath = chain.request().url.toString()
.substringAfter(CDN_URL)
.substringBefore("?token=")
val imageTokenRequest = imageTokenRequest(listOf(imagePath))
val imageTokenResponse = chain.proceed(imageTokenRequest)
val imageTokenResult = imageTokenResponse.parseAs<List<BilibiliPageDto>>()
imageTokenResponse.close()
val newPage = imageTokenResult.data!![0]
val newPage = imageTokenResult.data!!.first()
val newPageUrl = "${newPage.url}?token=${newPage.token}"
val newRequest = imageRequest(Page(0, "", newPageUrl))
imageTokenResponse.close()
response.close()
return chain.proceed(newRequest)
}
return response
}
protected val SharedPreferences.chapterImageQuality
private val SharedPreferences.chapterImageQuality
get() = when (getString("${IMAGE_QUALITY_PREF_KEY}_$lang", IMAGE_QUALITY_PREF_DEFAULT_VALUE)!!) {
"raw" -> "1600w"
"hd" -> "1000w"
"sd" -> "800w_50q"
else -> "raw+"
"hd" -> "1600w"
"sd" -> "1000w"
"low" -> "800w_50q"
else -> "raw"
}
protected val SharedPreferences.chapterImageFormat
private val SharedPreferences.chapterImageFormat
get() = getString("${IMAGE_FORMAT_PREF_KEY}_$lang", IMAGE_FORMAT_PREF_DEFAULT_VALUE)!!
private inline fun <reified R> List<*>.firstInstanceOrNull(): R? =
filterIsInstance<R>().firstOrNull()
private inline fun <reified R> List<*>.firstInstanceOrNull(): R? = firstOrNull { it is R } as? R
protected open fun HttpUrl.Builder.addCommonParameters(): HttpUrl.Builder = let {
protected open fun HttpUrl.Builder.addCommonParameters(): HttpUrl.Builder = apply {
if (name == "BILIBILI COMICS") {
addQueryParameter("lang", apiLang)
addQueryParameter("sys_lang", apiLang)
@ -420,8 +412,6 @@ abstract class Bilibili(
addQueryParameter("device", "pc")
addQueryParameter("platform", "web")
return@let it
}
protected inline fun <reified T> Response.parseAs(): BilibiliResultDto<T> = use {
@ -447,10 +437,10 @@ abstract class Bilibili(
private const val SEARCH_PER_PAGE = 9
const val PREFIX_ID_SEARCH = "id:"
private val ID_SEARCH_PATTERN = "^id:(mc)?(\\d+)$".toRegex()
private val ID_SEARCH_PATTERN = "^${PREFIX_ID_SEARCH}mc(\\d+)$".toRegex()
private const val IMAGE_QUALITY_PREF_KEY = "chapterImageQuality"
private val IMAGE_QUALITY_PREF_ENTRY_VALUES = arrayOf("raw+", "raw", "hd", "sd")
private val IMAGE_QUALITY_PREF_ENTRY_VALUES = arrayOf("raw", "hd", "sd", "low")
private val IMAGE_QUALITY_PREF_DEFAULT_VALUE = IMAGE_QUALITY_PREF_ENTRY_VALUES[1]
private const val IMAGE_FORMAT_PREF_KEY = "chapterImageFormat"
@ -461,7 +451,7 @@ abstract class Bilibili(
const val THUMBNAIL_RESOLUTION = "@512w.jpg"
private val DATE_FORMATTER by lazy {
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH)
}
private const val EMOJI_LOCKED = "\uD83D\uDD12"

View File

@ -65,7 +65,7 @@ data class BilibiliImageDto(
) {
fun url(quality: String, format: String): String {
val imageWidth = if (quality == "raw+") "${width}w" else quality
val imageWidth = if (quality == "raw") "${width}w" else quality
return "$path@$imageWidth.$format"
}

View File

@ -10,7 +10,7 @@ class BilibiliGenerator : ThemeSourceGenerator {
override val themeClass = "Bilibili"
override val baseVersionCode: Int = 7
override val baseVersionCode: Int = 8
override val sources = listOf(
MultiLang(

View File

@ -43,12 +43,6 @@ class BilibiliIntl(private val lang: String) {
else -> "Price"
}
val episodePrefix: String = when (lang) {
CHINESE, SIMPLIFIED_CHINESE -> ""
SPANISH -> "Cap. "
else -> "Ep. "
}
fun hasPaidChaptersWarning(chapterCount: Int): String = when (lang) {
CHINESE, SIMPLIFIED_CHINESE ->
"${Bilibili.EMOJI_WARNING} 此漫画有 ${chapterCount.localized} 个付费章节,已在目录中隐藏。" +
@ -73,8 +67,8 @@ class BilibiliIntl(private val lang: String) {
}
val imageQualityPrefEntries: Array<String> = when (lang) {
CHINESE, SIMPLIFIED_CHINESE -> arrayOf("原图+", "原图 (1600w)", " (1000w)", " (800w)")
else -> arrayOf("Raw+", "Raw (1600w)", "HD (1000w)", "SD (800w)")
CHINESE, SIMPLIFIED_CHINESE -> arrayOf("原图", "高清 (1600w)", "标清 (1000w)", " (800w)")
else -> arrayOf("Raw", "HD (1600w)", "SD (1000w)", "Low (800w)")
}
val imageFormatPrefTitle: String = when (lang) {
@ -191,18 +185,27 @@ class BilibiliIntl(private val lang: String) {
else -> "Total chapter count"
}
val updatedEvery: String = when (lang) {
CHINESE, SIMPLIFIED_CHINESE -> "周更新时间"
SPANISH -> "Actualizado en"
else -> "Updated every"
private val updatesDaily: String = when (lang) {
CHINESE, SIMPLIFIED_CHINESE -> "日更新"
SPANISH -> "Actualizaciones diarias"
else -> "Updates daily"
}
fun getWeekdays(dayIndexes: List<Int>): String {
val weekdays = dateFormatSymbols.weekdays
.filter(String::isNotBlank)
.map { dayName -> dayName.replaceFirstChar { it.uppercase(locale) } }
private fun updatesEvery(days: String): String = when (lang) {
CHINESE, SIMPLIFIED_CHINESE -> "${days}更新"
SPANISH -> "Actualizaciones todos los $days"
else -> "Updates every $days"
}
return dayIndexes.joinToString { weekdays[it] }
fun getUpdateDays(dayIndexes: List<Int>): String {
val shortWeekDays = dateFormatSymbols.shortWeekdays.filterNot(String::isBlank)
if (dayIndexes.size == shortWeekDays.size) return updatesDaily
val shortWeekDaysUpperCased = shortWeekDays.map {
it.replaceFirstChar { char -> char.uppercase(locale) }
}
val days = dayIndexes.joinToString { shortWeekDaysUpperCased[it] }
return updatesEvery(days)
}
fun localize(value: Int) = value.localized

View File

@ -23,7 +23,8 @@ class BilibiliUrlActivity : Activity() {
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val titleId = if (pathSegments.size == 3) pathSegments[2] else pathSegments[1]
// Mobile site of https://manga.bilibili.com starts with path "m"
val titleId = if (pathSegments[0] == "m") pathSegments[2] else pathSegments[1]
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"