From 2a391a61392ce98b7bc788222d17679580673fe9 Mon Sep 17 00:00:00 2001 From: FourTOne5 <59261191+FourTOne5@users.noreply.github.com> Date: Sun, 27 Jun 2021 01:46:41 +0600 Subject: [PATCH] Bilibili Comics Update (#7889) * Update BilibiliComics.kt * Update BilibiliDto.kt * Create BilibiliComicsUrlActivity.kt * Update BilibiliComics.kt * Update build.gradle * Update BilibiliComicsUrlActivity.kt --- src/en/bilibilicomics/build.gradle | 2 +- .../en/bilibilicomics/BilibiliComics.kt | 98 ++++++++++++++++++- .../BilibiliComicsUrlActivity.kt | 44 +++++++++ .../en/bilibilicomics/BilibiliDto.kt | 5 + 4 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComicsUrlActivity.kt diff --git a/src/en/bilibilicomics/build.gradle b/src/en/bilibilicomics/build.gradle index 46bd6f846..851a37258 100644 --- a/src/en/bilibilicomics/build.gradle +++ b/src/en/bilibilicomics/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Bilibili Comics' pkgNameSuffix = 'en.bilibilicomics' extClass = '.BilibiliComics' - extVersionCode = 3 + extVersionCode = 4 libVersion = '1.2' containsNsfw = true } diff --git a/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComics.kt b/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComics.kt index 2afb7634d..54baf7426 100644 --- a/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComics.kt +++ b/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComics.kt @@ -28,6 +28,7 @@ import rx.Observable import uy.kohesive.injekt.injectLazy import java.text.ParseException import java.text.SimpleDateFormat +import java.util.Calendar import java.util.Locale import java.util.concurrent.TimeUnit @@ -40,7 +41,7 @@ class BilibiliComics : HttpSource() { override val lang = "en" - override val supportsLatest = false + override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient.newBuilder() .addInterceptor(RateLimitInterceptor(1, 1, TimeUnit.SECONDS)) @@ -93,6 +94,62 @@ class BilibiliComics : HttpSource() { url = "/detail/mc${comic.comicId}" } + override fun latestUpdatesRequest(page: Int): Request { + val requestPayload = buildJsonObject { + put("day", day) + } + val requestBody = requestPayload.toString().toRequestBody(JSON_MEDIA_TYPE) + + val newHeaders = headersBuilder() + .add("Content-Length", requestBody.contentLength().toString()) + .add("Content-Type", requestBody.contentType().toString()) + .build() + + return POST( + "$baseUrl/$BASE_API_ENDPOINT/GetSchedule?device=pc&platform=web", + headers = newHeaders, + body = requestBody + ) + } + + override fun latestUpdatesParse(response: Response): MangasPage { + val result = json.decodeFromString>(response.body!!.string()) + + if (result.code != 0) { + return MangasPage(emptyList(), hasNextPage = false) + } + + val comicList = result.data!!.list + .map(::latestMangaFromObject) + + return MangasPage(comicList, hasNextPage = false) + } + + private fun latestMangaFromObject(comic: BilibiliComicDto): SManga = SManga.create().apply { + title = comic.title + thumbnail_url = comic.verticalCover + url = "/detail/mc${comic.comicId}" + } + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + return when { + query.startsWith(prefixIdSearch) -> { + val id = query.removePrefix(prefixIdSearch) + client.newCall(mangaDetailsApiRequestById(id)).asObservableSuccess() + .map { response -> + mangaDetailsParse(response).let { MangasPage(listOf(it), false) } + } + } + + else -> { + client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess() + .map { response -> + searchMangaParse(response) + } + } + } + } + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { val jsonPayload = buildJsonObject { put("area_id", -1) @@ -170,6 +227,25 @@ class BilibiliComics : HttpSource() { ) } + private fun mangaDetailsApiRequestById(id: String): Request { + val comicId = id.toInt() + + val jsonPayload = buildJsonObject { put("comic_id", comicId) } + val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE) + + val newHeaders = headersBuilder() + .add("Content-Length", requestBody.contentLength().toString()) + .add("Content-Type", requestBody.contentType().toString()) + .set("Referer", "$baseUrl/detail/mc$id") + .build() + + return POST( + "$baseUrl/$BASE_API_ENDPOINT/ComicDetail?device=pc&platform=web", + headers = newHeaders, + body = requestBody + ) + } + override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { val result = json.decodeFromString>(response.body!!.string()) val comic = result.data!! @@ -260,10 +336,6 @@ class BilibiliComics : HttpSource() { return "${page.url}?token=${page.token}" } - override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException("Not used") - - override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used") - private fun String.toDate(): Long { return try { DATE_FORMATTER.parse(this)?.time ?: 0L @@ -272,6 +344,20 @@ class BilibiliComics : HttpSource() { } } + private val day: Int + get() { + return when (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) { + Calendar.SUNDAY -> 0 + Calendar.MONDAY -> 1 + Calendar.TUESDAY -> 2 + Calendar.WEDNESDAY -> 3 + Calendar.THURSDAY -> 4 + Calendar.FRIDAY -> 5 + Calendar.SATURDAY -> 6 + else -> 0 + } + } + companion object { private const val BASE_API_ENDPOINT = "twirp/comic.v1.Comic" @@ -280,6 +366,8 @@ class BilibiliComics : HttpSource() { private val JSON_MEDIA_TYPE = "application/json;charset=UTF-8".toMediaType() private const val FEATURED_ID = 3 + + const val prefixIdSearch = "id:" private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } } diff --git a/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComicsUrlActivity.kt b/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComicsUrlActivity.kt new file mode 100644 index 000000000..d57d5db71 --- /dev/null +++ b/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliComicsUrlActivity.kt @@ -0,0 +1,44 @@ +package eu.kanade.tachiyomi.extension.en.bilibilicomics + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +/** + * Springboard that accepts https://www.bilibilicomics.com/detail/xxx intents and redirects them to + * the main tachiyomi process. The idea is to not install the intent filter unless + * you have this extension installed, but still let the main tachiyomi app control + * things. + * + * Main goal was to make it easier to open manga in Tachiyomi in spite of the DDoS blocking + * the usual search screen from working. + */ +class BilibiliComicsUrlActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size > 1) { + val titleid = pathSegments[1] + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "${BilibiliComics.prefixIdSearch}${titleid.removePrefix("mc")}") + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("BilibiliUrlActivity", e.toString()) + } + } else { + Log.e("BilibiliUrlActivity", "could not parse uri from intent $intent") + } + + finish() + exitProcess(0) + } +} diff --git a/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliDto.kt b/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliDto.kt index e7dc0175a..f8e6d934d 100644 --- a/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliDto.kt +++ b/src/en/bilibilicomics/src/eu/kanade/tachiyomi/extension/en/bilibilicomics/BilibiliDto.kt @@ -15,6 +15,11 @@ data class BilibiliFeaturedDto( @SerialName("roll_six_comics") val rollSixComics: List = emptyList() ) +@Serializable +data class BilibiliScheduleDto( + val list: List = emptyList() +) + @Serializable data class BilibiliSearchDto( val list: List = emptyList()