Initial commit
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
BIN
multisrc/overrides/a3manga/a3manga/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
22
multisrc/overrides/a3manga/default/AndroidManifest.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application>
|
||||
<activity
|
||||
android:name="eu.kanade.tachiyomi.multisrc.a3manga.A3MangaUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:host="${SOURCEHOST}" />
|
||||
<data android:host="*.${SOURCEHOST}" />
|
||||
<data android:pathPattern="/truyen-tranh/..*"
|
||||
android:scheme="${SOURCESCHEME}" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
BIN
multisrc/overrides/a3manga/ngonphong/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
7
multisrc/overrides/a3manga/ngonphong/src/NgonPhong.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package eu.kanade.tachiyomi.extension.vi.ngonphong
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.a3manga.A3Manga
|
||||
|
||||
class NgonPhong : A3Manga("Ngôn Phong", "https://www.ngonphong.com", "vi") {
|
||||
override val id: Long = 7268977637085631557
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
BIN
multisrc/overrides/a3manga/ocumeo/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 16 KiB |
BIN
multisrc/overrides/a3manga/teamlanhlung/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 26 KiB |
BIN
multisrc/overrides/bakamanga/manhwaxxl/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
68
multisrc/overrides/bakamanga/manhwaxxl/src/ManhwaXXL.kt
Normal file
@@ -0,0 +1,68 @@
|
||||
package eu.kanade.tachiyomi.extension.en.manhwaxxl
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.bakamanga.BakaManga
|
||||
|
||||
class ManhwaXXL : BakaManga(
|
||||
"Manhwa XXL",
|
||||
"https://manhwaxxl.com",
|
||||
"en",
|
||||
) {
|
||||
override fun getGenreList() = arrayOf(
|
||||
Pair("All", ""),
|
||||
Pair("Action", "action"),
|
||||
Pair("Adaptation", "adaptation"),
|
||||
Pair("Adult", "adult"),
|
||||
Pair("Adventure", "adventure"),
|
||||
Pair("BL", "bl"),
|
||||
Pair("Comedy", "comedy"),
|
||||
Pair("Cooking", "cooking"),
|
||||
Pair("Demons", "demons"),
|
||||
Pair("Drama", "drama"),
|
||||
Pair("Ecchi", "ecchi"),
|
||||
Pair("Fantasy", "fantasy"),
|
||||
Pair("Full color", "full-color"),
|
||||
Pair("Game", "game"),
|
||||
Pair("Gender Bender", "gender-bender"),
|
||||
Pair("GL", "gl"),
|
||||
Pair("Harem", "harem"),
|
||||
Pair("Historical", "historical"),
|
||||
Pair("Horror", "horror"),
|
||||
Pair("Isekai", "isekai"),
|
||||
Pair("Josei", "josei"),
|
||||
Pair("Live action", "live-action"),
|
||||
Pair("Love & Romance", "love-romance"),
|
||||
Pair("Magic", "magic"),
|
||||
Pair("Manga", "manga"),
|
||||
Pair("Manhua", "manhua"),
|
||||
Pair("Manhwa", "manhwa"),
|
||||
Pair("Martial Arts", "martial-arts"),
|
||||
Pair("Mature", "mature"),
|
||||
Pair("Mecha", "mecha"),
|
||||
Pair("Mystery", "mystery"),
|
||||
Pair("Omegaverse", "omegaverse"),
|
||||
Pair("Psychological", "psychological"),
|
||||
Pair("Raw", "raw"),
|
||||
Pair("Reincarnation", "reincarnation"),
|
||||
Pair("Romance", "romance"),
|
||||
Pair("RPG", "rpg"),
|
||||
Pair("School Life", "school-life"),
|
||||
Pair("Sci-fi", "sci-fi"),
|
||||
Pair("Seinen", "seinen"),
|
||||
Pair("Shoujo", "shoujo"),
|
||||
Pair("Shoujo Ai", "shoujo-ai"),
|
||||
Pair("Shounen", "shounen"),
|
||||
Pair("Slice of Life", "slice-of-life"),
|
||||
Pair("Smut", "smut"),
|
||||
Pair("Sports", "sports"),
|
||||
Pair("Supernatural", "supernatural"),
|
||||
Pair("Thriller", "thriller"),
|
||||
Pair("Tragedy", "tragedy"),
|
||||
Pair("Vampire", "vampire"),
|
||||
Pair("Vanilla", "vanilla"),
|
||||
Pair("Webtoon", "webtoon"),
|
||||
Pair("Webtoons", "webtoons"),
|
||||
Pair("Yaoi", "yaoi"),
|
||||
Pair("Yuri", "yuri"),
|
||||
Pair("Zombie", "zombie"),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package eu.kanade.tachiyomi.extension.en.bakkinselfhosted
|
||||
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.multisrc.bakkin.BakkinReaderX
|
||||
|
||||
class BakkinSelfHosted : BakkinReaderX("Bakkin Self-hosted", "", "en") {
|
||||
override val baseUrl by lazy {
|
||||
preferences.getString("baseUrl", "http://127.0.0.1/")!!
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
super.setupPreferenceScreen(screen)
|
||||
EditTextPreference(screen.context).apply {
|
||||
key = "baseUrl"
|
||||
title = "Custom URL"
|
||||
summary = "Connect to a self-hosted Bakkin Reader X server"
|
||||
setDefaultValue("http://127.0.0.1/")
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
// Make sure the URL ends with one slash
|
||||
val url = (newValue as String).trimEnd('/') + '/'
|
||||
preferences.edit().putString("baseUrl", url).commit()
|
||||
}
|
||||
}.let(screen::addPreference)
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
BIN
multisrc/overrides/bakkin/default/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
BIN
multisrc/overrides/bakkin/ultralightscans/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="eu.kanade.tachiyomi.multisrc.bilibili.BilibiliUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:host="bilibilicomics.com" />
|
||||
<data android:host="m.bilibilicomics.com" />
|
||||
<data android:host="www.bilibilicomics.com" />
|
||||
|
||||
<data android:pathPattern="/detail/mc..*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,411 @@
|
||||
package eu.kanade.tachiyomi.extension.all.bilibilicomics
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.Bilibili
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliAccessToken
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliAccessTokenCookie
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliComicDto
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliCredential
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliGetCredential
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliIntl
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliSearchDto
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliTag
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliUnlockedEpisode
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliUserEpisodes
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
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(
|
||||
BilibiliComicsEn(),
|
||||
BilibiliComicsCn(),
|
||||
BilibiliComicsId(),
|
||||
BilibiliComicsEs(),
|
||||
BilibiliComicsFr(),
|
||||
)
|
||||
}
|
||||
|
||||
abstract class BilibiliComics(lang: String) : Bilibili(
|
||||
"BILIBILI COMICS",
|
||||
"https://www.bilibilicomics.com",
|
||||
lang,
|
||||
) {
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.apply { interceptors().add(0, Interceptor { chain -> signedInIntercept(chain) }) }
|
||||
.build()
|
||||
|
||||
init {
|
||||
setAccessTokenCookie(baseUrl.toHttpUrl())
|
||||
}
|
||||
|
||||
override val signedIn: Boolean
|
||||
get() = accessTokenCookie != null
|
||||
|
||||
private val globalApiSubDomain: String
|
||||
get() = GLOBAL_API_SUBDOMAINS[(accessTokenCookie?.area?.toIntOrNull() ?: 1) - 1]
|
||||
|
||||
private val globalApiBaseUrl: String
|
||||
get() = "https://$globalApiSubDomain.bilibilicomics.com"
|
||||
|
||||
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)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Content-Length", requestBody.contentLength().toString())
|
||||
.add("Content-Type", requestBody.contentType().toString())
|
||||
.set("Referer", "$baseUrl/schedule")
|
||||
.build()
|
||||
|
||||
val apiUrl = "$baseUrl/$API_COMIC_V1_COMIC_ENDPOINT/GetSchedule".toHttpUrl().newBuilder()
|
||||
.addCommonParameters()
|
||||
.toString()
|
||||
|
||||
return POST(apiUrl, newHeaders, requestBody)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val result = response.parseAs<BilibiliSearchDto>()
|
||||
|
||||
if (result.code != 0) {
|
||||
return MangasPage(emptyList(), hasNextPage = false)
|
||||
}
|
||||
|
||||
val comicList = result.data!!.list.map(::latestMangaFromObject)
|
||||
|
||||
return MangasPage(comicList, hasNextPage = false)
|
||||
}
|
||||
|
||||
protected open fun latestMangaFromObject(comic: BilibiliComicDto): SManga = SManga.create().apply {
|
||||
title = comic.title
|
||||
thumbnail_url = comic.verticalCover + THUMBNAIL_RESOLUTION
|
||||
url = "/detail/mc${comic.comicId}"
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
if (!signedIn) {
|
||||
return super.chapterListParse(response)
|
||||
}
|
||||
|
||||
val result = response.parseAs<BilibiliComicDto>()
|
||||
|
||||
if (result.code != 0) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val comic = result.data!!
|
||||
|
||||
val userEpisodesRequest = userEpisodesRequest(comic.id)
|
||||
val userEpisodesResponse = client.newCall(userEpisodesRequest).execute()
|
||||
val unlockedEpisodes = userEpisodesParse(userEpisodesResponse)
|
||||
|
||||
return comic.episodeList.map { ep -> chapterFromObject(ep, comic.id, isUnlocked = ep.id in unlockedEpisodes) }
|
||||
}
|
||||
|
||||
private fun userEpisodesRequest(comicId: Int): Request {
|
||||
val jsonPayload = buildJsonObject { put("comic_id", comicId) }
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.set("Referer", baseUrl)
|
||||
.build()
|
||||
|
||||
val apiUrl = "$globalApiBaseUrl/$API_COMIC_V1_USER_ENDPOINT/GetUserEpisodes".toHttpUrl()
|
||||
.newBuilder()
|
||||
.addCommonParameters()
|
||||
.toString()
|
||||
|
||||
return POST(apiUrl, newHeaders, requestBody)
|
||||
}
|
||||
|
||||
private fun userEpisodesParse(response: Response): List<Int> {
|
||||
if (!response.isSuccessful) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val result = response.parseAs<BilibiliUserEpisodes>()
|
||||
|
||||
if (result.code != 0) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return result.data!!.unlockedEpisodes.orEmpty()
|
||||
.map(BilibiliUnlockedEpisode::id)
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
if (!signedIn) {
|
||||
return super.pageListRequest(chapter)
|
||||
}
|
||||
|
||||
val chapterPaths = (baseUrl + chapter.url).toHttpUrl().pathSegments
|
||||
val comicId = chapterPaths[0].removePrefix("mc").toInt()
|
||||
val episodeId = chapterPaths[1].toInt()
|
||||
|
||||
val jsonPayload = BilibiliGetCredential(comicId, episodeId, 1)
|
||||
val requestBody = json.encodeToString(jsonPayload).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.set("Referer", baseUrl + chapter.url)
|
||||
.build()
|
||||
|
||||
val apiUrl = "$globalApiBaseUrl/$API_GLOBAL_V1_USER_ENDPOINT/GetCredential".toHttpUrl()
|
||||
.newBuilder()
|
||||
.addCommonParameters()
|
||||
.toString()
|
||||
|
||||
return POST(apiUrl, newHeaders, requestBody)
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
if (!signedIn) {
|
||||
return super.pageListParse(response)
|
||||
}
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
throw Exception(intl.failedToGetCredential)
|
||||
}
|
||||
|
||||
val result = response.parseAs<BilibiliCredential>()
|
||||
val credential = result.data?.credential ?: ""
|
||||
|
||||
val requestPayload = response.request.bodyString
|
||||
val credentialInfo = json.decodeFromString<BilibiliGetCredential>(requestPayload)
|
||||
val chapterUrl = "/mc${credentialInfo.comicId}/${credentialInfo.episodeId}"
|
||||
|
||||
val imageIndexRequest = imageIndexRequest(chapterUrl, credential)
|
||||
val imageIndexResponse = client.newCall(imageIndexRequest).execute()
|
||||
|
||||
return super.pageListParse(imageIndexResponse)
|
||||
}
|
||||
|
||||
private fun setAccessTokenCookie(url: HttpUrl) {
|
||||
val authCookie = client.cookieJar.loadForRequest(url)
|
||||
.firstOrNull { cookie -> cookie.name == ACCESS_TOKEN_COOKIE_NAME }
|
||||
?.let { cookie -> URLDecoder.decode(cookie.value, "UTF-8") }
|
||||
?.let { jsonString -> json.decodeFromString<BilibiliAccessTokenCookie>(jsonString) }
|
||||
|
||||
if (accessTokenCookie == null) {
|
||||
accessTokenCookie = authCookie
|
||||
} else if (authCookie == null) {
|
||||
accessTokenCookie = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun signedInIntercept(chain: Interceptor.Chain): Response {
|
||||
var request = chain.request()
|
||||
val requestUrl = request.url.toString()
|
||||
|
||||
if (!requestUrl.contains("bilibilicomics.com")) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
setAccessTokenCookie(request.url)
|
||||
|
||||
if (!accessTokenCookie?.accessToken.isNullOrEmpty()) {
|
||||
request = request.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${accessTokenCookie!!.accessToken}")
|
||||
.build()
|
||||
}
|
||||
|
||||
val response = chain.proceed(request)
|
||||
|
||||
// Try to refresh the token if it expired.
|
||||
if (response.code == 401 && !accessTokenCookie?.refreshToken.isNullOrEmpty()) {
|
||||
response.close()
|
||||
|
||||
val refreshTokenRequest = refreshTokenRequest(
|
||||
accessTokenCookie!!.accessToken,
|
||||
accessTokenCookie!!.refreshToken,
|
||||
)
|
||||
val refreshTokenResponse = chain.proceed(refreshTokenRequest)
|
||||
|
||||
accessTokenCookie = refreshTokenParse(refreshTokenResponse)
|
||||
refreshTokenResponse.close()
|
||||
|
||||
request = request.newBuilder()
|
||||
.header("Authorization", "Bearer ${accessTokenCookie!!.accessToken}")
|
||||
.build()
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
private fun refreshTokenRequest(accessToken: String, refreshToken: String): Request {
|
||||
val jsonPayload = buildJsonObject { put("refresh_token", refreshToken) }
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Authorization", "Bearer $accessToken")
|
||||
.set("Referer", baseUrl)
|
||||
.build()
|
||||
|
||||
val apiUrl = "$globalApiBaseUrl/$API_GLOBAL_V1_USER_ENDPOINT/RefreshToken".toHttpUrl()
|
||||
.newBuilder()
|
||||
.addCommonParameters()
|
||||
.toString()
|
||||
|
||||
return POST(apiUrl, newHeaders, requestBody)
|
||||
}
|
||||
|
||||
private fun refreshTokenParse(response: Response): BilibiliAccessTokenCookie {
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException(intl.failedToRefreshToken)
|
||||
}
|
||||
|
||||
val result = response.parseAs<BilibiliAccessToken>()
|
||||
|
||||
if (result.code != 0) {
|
||||
throw IOException(intl.failedToRefreshToken)
|
||||
}
|
||||
|
||||
val accessToken = result.data!!
|
||||
|
||||
return BilibiliAccessTokenCookie(
|
||||
accessToken.accessToken,
|
||||
accessToken.refreshToken,
|
||||
accessTokenCookie!!.area,
|
||||
)
|
||||
}
|
||||
|
||||
private val Request.bodyString: String
|
||||
get() {
|
||||
val requestCopy = newBuilder().build()
|
||||
val buffer = Buffer()
|
||||
|
||||
return runCatching { buffer.apply { requestCopy.body!!.writeTo(this) }.readUtf8() }
|
||||
.getOrNull() ?: ""
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ACCESS_TOKEN_COOKIE_NAME = "access_token"
|
||||
|
||||
private val GLOBAL_API_SUBDOMAINS = arrayOf("us-user", "sg-user")
|
||||
private const val API_GLOBAL_V1_USER_ENDPOINT = "twirp/global.v1.User"
|
||||
private const val API_COMIC_V1_USER_ENDPOINT = "twirp/comic.v1.User"
|
||||
}
|
||||
}
|
||||
|
||||
class BilibiliComicsEn : BilibiliComics(BilibiliIntl.ENGLISH) {
|
||||
|
||||
override fun getAllGenres(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag("All", -1),
|
||||
BilibiliTag("Action", 19),
|
||||
BilibiliTag("Adventure", 22),
|
||||
BilibiliTag("BL", 3),
|
||||
BilibiliTag("Comedy", 14),
|
||||
BilibiliTag("Eastern", 30),
|
||||
BilibiliTag("Fantasy", 11),
|
||||
BilibiliTag("GL", 16),
|
||||
BilibiliTag("Harem", 15),
|
||||
BilibiliTag("Historical", 12),
|
||||
BilibiliTag("Horror", 23),
|
||||
BilibiliTag("Mistery", 17),
|
||||
BilibiliTag("Romance", 13),
|
||||
BilibiliTag("Slice of Life", 21),
|
||||
BilibiliTag("Suspense", 41),
|
||||
BilibiliTag("Teen", 20),
|
||||
)
|
||||
}
|
||||
|
||||
class BilibiliComicsCn : BilibiliComics(BilibiliIntl.SIMPLIFIED_CHINESE) {
|
||||
|
||||
override fun getAllGenres(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag("全部", -1),
|
||||
BilibiliTag("校园", 18),
|
||||
BilibiliTag("都市", 9),
|
||||
BilibiliTag("耽美", 3),
|
||||
BilibiliTag("少女", 20),
|
||||
BilibiliTag("恋爱", 13),
|
||||
BilibiliTag("奇幻", 11),
|
||||
BilibiliTag("热血", 19),
|
||||
BilibiliTag("冒险", 22),
|
||||
BilibiliTag("古风", 12),
|
||||
BilibiliTag("百合", 16),
|
||||
BilibiliTag("玄幻", 30),
|
||||
BilibiliTag("悬疑", 41),
|
||||
BilibiliTag("科幻", 8),
|
||||
)
|
||||
}
|
||||
|
||||
class BilibiliComicsId : BilibiliComics(BilibiliIntl.INDONESIAN) {
|
||||
|
||||
override fun getAllGenres(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag("Semua", -1),
|
||||
BilibiliTag("Aksi", 19),
|
||||
BilibiliTag("Fantasi Timur", 30),
|
||||
BilibiliTag("Fantasi", 11),
|
||||
BilibiliTag("Historis", 12),
|
||||
BilibiliTag("Horror", 23),
|
||||
BilibiliTag("Kampus", 18),
|
||||
BilibiliTag("Komedi", 14),
|
||||
BilibiliTag("Menegangkan", 41),
|
||||
BilibiliTag("Remaja", 20),
|
||||
BilibiliTag("Romantis", 13),
|
||||
)
|
||||
}
|
||||
|
||||
class BilibiliComicsEs : BilibiliComics(BilibiliIntl.SPANISH) {
|
||||
|
||||
override fun getAllGenres(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag("Todos", -1),
|
||||
BilibiliTag("Adolescencia", 105),
|
||||
BilibiliTag("BL", 3),
|
||||
BilibiliTag("Ciberdeportes", 104),
|
||||
BilibiliTag("Ciencia ficción", 8),
|
||||
BilibiliTag("Comedia", 14),
|
||||
BilibiliTag("Fantasía occidental", 106),
|
||||
BilibiliTag("Fantasía", 11),
|
||||
BilibiliTag("Ficción Realista", 116),
|
||||
BilibiliTag("GL", 16),
|
||||
BilibiliTag("Histórico", 12),
|
||||
BilibiliTag("Horror", 23),
|
||||
BilibiliTag("Juvenil", 20),
|
||||
BilibiliTag("Moderno", 111),
|
||||
BilibiliTag("Oriental", 30),
|
||||
BilibiliTag("Romance", 13),
|
||||
BilibiliTag("Suspenso", 41),
|
||||
BilibiliTag("Urbano", 9),
|
||||
BilibiliTag("Wuxia", 103),
|
||||
)
|
||||
}
|
||||
|
||||
class BilibiliComicsFr : BilibiliComics(BilibiliIntl.FRENCH) {
|
||||
|
||||
override fun getAllGenres(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag("Tout", -1),
|
||||
BilibiliTag("BL", 3),
|
||||
BilibiliTag("Science Fiction", 8),
|
||||
BilibiliTag("Historique", 12),
|
||||
BilibiliTag("Romance", 13),
|
||||
BilibiliTag("GL", 16),
|
||||
BilibiliTag("Fantasy Orientale", 30),
|
||||
BilibiliTag("Suspense", 41),
|
||||
BilibiliTag("Moderne", 111),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="eu.kanade.tachiyomi.multisrc.bilibili.BilibiliUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:host="manga.bilibili.com" />
|
||||
|
||||
<data android:pathPattern="/detail/mc..*" />
|
||||
<data android:pathPattern="/m/detail/mc..*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 34 KiB |
BIN
multisrc/overrides/bilibili/bilibilimanga/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
@@ -0,0 +1,83 @@
|
||||
package eu.kanade.tachiyomi.extension.zh.bilibilimanga
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.Bilibili
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliComicDto
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliIntl
|
||||
import eu.kanade.tachiyomi.multisrc.bilibili.BilibiliTag
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Response
|
||||
|
||||
class BilibiliManga : Bilibili(
|
||||
"哔哩哔哩漫画",
|
||||
"https://manga.bilibili.com",
|
||||
BilibiliIntl.SIMPLIFIED_CHINESE,
|
||||
) {
|
||||
|
||||
override val id: Long = 3561131545129718586
|
||||
|
||||
override fun headersBuilder() = Headers.Builder().apply {
|
||||
add("User-Agent", DEFAULT_USER_AGENT)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val result = response.parseAs<BilibiliComicDto>()
|
||||
|
||||
if (result.code != 0) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return result.data!!.episodeList
|
||||
.filter { episode -> episode.isInFree || !episode.isLocked }
|
||||
.map { ep -> chapterFromObject(ep, result.data.id) }
|
||||
}
|
||||
|
||||
override val defaultPopularSort: Int = 0
|
||||
|
||||
override val defaultLatestSort: Int = 1
|
||||
|
||||
override fun getAllSortOptions(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag(intl.sortPopular, 0),
|
||||
BilibiliTag(intl.sortUpdated, 1),
|
||||
BilibiliTag(intl.sortFollowers, 2),
|
||||
BilibiliTag(intl.sortAdded, 3),
|
||||
)
|
||||
|
||||
override fun getAllPrices(): Array<String> =
|
||||
arrayOf(intl.priceAll, intl.priceFree, intl.pricePaid, intl.priceWaitForFree)
|
||||
|
||||
override fun getAllGenres(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag("全部", -1),
|
||||
BilibiliTag("竞技", 1034),
|
||||
BilibiliTag("冒险", 1013),
|
||||
BilibiliTag("热血", 999),
|
||||
BilibiliTag("搞笑", 994),
|
||||
BilibiliTag("恋爱", 995),
|
||||
BilibiliTag("少女", 1026),
|
||||
BilibiliTag("日常", 1020),
|
||||
BilibiliTag("校园", 1001),
|
||||
BilibiliTag("治愈", 1007),
|
||||
BilibiliTag("古风", 997),
|
||||
BilibiliTag("玄幻", 1016),
|
||||
BilibiliTag("奇幻", 998),
|
||||
BilibiliTag("惊奇", 996),
|
||||
BilibiliTag("悬疑", 1023),
|
||||
BilibiliTag("都市", 1002),
|
||||
BilibiliTag("剧情", 1030),
|
||||
BilibiliTag("总裁", 1004),
|
||||
BilibiliTag("科幻", 1015),
|
||||
BilibiliTag("正能量", 1028),
|
||||
)
|
||||
|
||||
override fun getAllAreas(): Array<BilibiliTag> = arrayOf(
|
||||
BilibiliTag("全部", -1),
|
||||
BilibiliTag("大陆", 1),
|
||||
BilibiliTag("日本", 2),
|
||||
BilibiliTag("韩国", 6),
|
||||
BilibiliTag("其他", 5),
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63"
|
||||
}
|
||||
}
|
||||
22
multisrc/overrides/bilibili/default/AndroidManifest.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="eu.kanade.tachiyomi.multisrc.bilibili.BilibiliUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data
|
||||
android:host="${SOURCEHOST}"
|
||||
android:pathPattern="/detail/mc..*"
|
||||
android:scheme="${SOURCESCHEME}" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 35 KiB |
BIN
multisrc/overrides/bilibili/default/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1006 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
BIN
multisrc/overrides/eromuse/default/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
BIN
multisrc/overrides/eromuse/eightmuses/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
7
multisrc/overrides/eromuse/eightmuses/src/EightMuses.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package eu.kanade.tachiyomi.extension.en.eightmuses
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.eromuse.EroMuse
|
||||
import kotlin.ExperimentalStdlibApi
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
class EightMuses : EroMuse("8Muses", "https://comics.8muses.com")
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
BIN
multisrc/overrides/eromuse/erofus/res/web_hi_res_512.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
165
multisrc/overrides/eromuse/erofus/src/Erofus.kt
Normal file
@@ -0,0 +1,165 @@
|
||||
package eu.kanade.tachiyomi.extension.en.erofus
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.eromuse.EroMuse
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
class Erofus : EroMuse("Erofus", "https://www.erofus.com") {
|
||||
|
||||
override val albumSelector = "a.a-click"
|
||||
override val topLevelPathSegment = "comics"
|
||||
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> = fetchManga("$baseUrl/comics/various-authors?sort=viewed&page=1", page, "viewed")
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> = fetchManga("$baseUrl/comics/various-authors?sort=recent&page=1", page, "recent")
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
if (page == 1) {
|
||||
pageStack.clear()
|
||||
|
||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||
currentSortingMode = filterList.filterIsInstance<SortFilter>().first().toQueryValue()
|
||||
|
||||
if (query.isNotBlank()) {
|
||||
// TODO possibly add genre search if a decent list of them can be built
|
||||
pageStack.addLast(StackItem("$baseUrl/?search=$query&sort=$currentSortingMode&page=1", SEARCH_RESULTS_OR_BASE))
|
||||
} else {
|
||||
val albumFilter = filterList.filterIsInstance<AlbumFilter>().first().selection()
|
||||
val url = (baseUrl + albumFilter.pathSegments).toHttpUrl().newBuilder()
|
||||
.addQueryParameter("sort", currentSortingMode)
|
||||
.addQueryParameter("page", "1")
|
||||
|
||||
pageStack.addLast(StackItem(url.toString(), albumFilter.pageType))
|
||||
}
|
||||
}
|
||||
|
||||
return client.newCall(stackRequest())
|
||||
.asObservableSuccess()
|
||||
.map { response -> parseManga(response.asJsoup()) }
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
return SManga.create().apply {
|
||||
with(response.asJsoup()) {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
thumbnail_url = select("$albumSelector img").firstOrNull()?.imgAttr()
|
||||
author = when (getAlbumType(url)) {
|
||||
AUTHOR -> {
|
||||
// eg. https://www.erofus.com/comics/witchking00-comics/adventure-time
|
||||
// eg. https://www.erofus.com/comics/mcc-comics/bearing-gifts/bearing-gifts-issue-1
|
||||
select("div.navigation-breadcrumb li:nth-child(3)").text()
|
||||
}
|
||||
VARIOUS_AUTHORS -> {
|
||||
// eg. https://www.erofus.com/comics/various-authors/artdude41/bat-vore
|
||||
select("div.navigation-breadcrumb li:nth-child(5)").text()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
genre = select("div.album-tag-container a").joinToString { it.text() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val linkedChapterSelector = "a.a-click:has(img)[href^=/comics/]"
|
||||
override val pageThumbnailSelector = "a.a-click:has(img)[href*=/pic/] img"
|
||||
|
||||
override val pageThumbnailPathSegment = "/thumb/"
|
||||
override val pageFullSizePathSegment = "/medium/"
|
||||
|
||||
override fun getAlbumList() = arrayOf(
|
||||
Triple("All Authors", "", SEARCH_RESULTS_OR_BASE),
|
||||
Triple("Various Authors", "/comics/various-authors", VARIOUS_AUTHORS),
|
||||
Triple("Hentai and Manga English", "/comics/hentai-and-manga-english", VARIOUS_AUTHORS),
|
||||
Triple("TabooLicious.xxx Comics", "/comics/taboolicious_xxx-comics", AUTHOR),
|
||||
Triple("IllustratedInterracial.com Comics", "/comics/illustratedinterracial_com-comics", AUTHOR),
|
||||
Triple("ZZZ Comics", "/comics/zzz-comics", AUTHOR),
|
||||
Triple("JohnPersons.com Comics", "/comics/johnpersons_com-comics", AUTHOR),
|
||||
Triple("For members only", "/", AUTHOR),
|
||||
Triple("PalComix Comics", "/comics/palcomix-comics", AUTHOR),
|
||||
Triple("Melkormancin.com Comics", "/comics/melkormancin_com-comics", AUTHOR),
|
||||
Triple("TG Comics", "/comics/tg-comics", AUTHOR),
|
||||
Triple("ShadBase Comics", "/comics/shadbase-comics", AUTHOR),
|
||||
Triple("Filthy Figments Comics", "/comics/filthy-figments-comics", AUTHOR),
|
||||
Triple("Witchking00 Comics", "/comics/witchking00-comics", AUTHOR),
|
||||
Triple("Tease Comix", "/comics/tease-comix", AUTHOR),
|
||||
Triple("PrismGirls Comics", "/comics/prismgirls-comics", AUTHOR),
|
||||
Triple("Croc Comics", "/comics/croc-comics", AUTHOR),
|
||||
Triple("CRAZYXXX3DWORLD Comics", "/comics/crazyxxx3dworld-comics", AUTHOR),
|
||||
Triple("Moiarte Comics", "/comics/moiarte-comics", AUTHOR),
|
||||
Triple("Nicole Heat Comics", "/comics/nicole-heat-comics", AUTHOR),
|
||||
Triple("Expansion Comics", "/comics/expansion-comics", AUTHOR),
|
||||
Triple("DizzyDills Comics", "/comics/dizzydills-comics", AUTHOR),
|
||||
Triple("Hustler Cartoons", "/comics/hustler-cartoons", AUTHOR),
|
||||
Triple("ArtOfJaguar Comics", "/comics/artofjaguar-comics", AUTHOR),
|
||||
Triple("Grow Comics", "/comics/grow-comics", AUTHOR),
|
||||
Triple("Bimbo Story Club Comics", "/comics/bimbo-story-club-comics", AUTHOR),
|
||||
Triple("HentaiTNA.com Comics", "/comics/hentaitna_com-comics", AUTHOR),
|
||||
Triple("ZZomp Comics", "/comics/zzomp-comics", AUTHOR),
|
||||
Triple("Seiren.com.br Comics", "/comics/seiren_com_br-comics", AUTHOR),
|
||||
Triple("DukesHardcoreHoneys.com Comics", "/comics/dukeshardcorehoneys_com-comics", AUTHOR),
|
||||
Triple("Frozen Parody Comics", "/comics/frozen-parody-comics", AUTHOR),
|
||||
Triple("Giantess Club Comics", "/comics/giantess-club-comics", AUTHOR),
|
||||
Triple("Ultimate3DPorn Comics", "/comics/ultimate3dporn-comics", AUTHOR),
|
||||
Triple("Sean Harrington Comics", "/comics/sean-harrington-comics", AUTHOR),
|
||||
Triple("Central Comics", "/comics/central-comics", AUTHOR),
|
||||
Triple("Mana World Comics", "/comics/mana-world-comics", AUTHOR),
|
||||
Triple("The Foxxx Comics", "/comics/the-foxxx-comics", AUTHOR),
|
||||
Triple("Bloody Sugar Comics", "/comics/bloody-sugar-comics", AUTHOR),
|
||||
Triple("Deuce Comics", "/comics/deuce-comics", AUTHOR),
|
||||
Triple("Adult Empire Comics", "/comics/adult-empire-comics", AUTHOR),
|
||||
Triple("SuperHeroineComixxx", "/comics/superheroinecomixxx", AUTHOR),
|
||||
Triple("Sluttish Comics", "/comics/sluttish-comics", AUTHOR),
|
||||
Triple("Damn3D Comics", "/comics/damn3d-comics", AUTHOR),
|
||||
Triple("Fake Celebrities Sex Pictures", "/comics/fake-celebrities-sex-pictures", AUTHOR),
|
||||
Triple("Secret Chest Comics", "/comics/secret-chest-comics", AUTHOR),
|
||||
Triple("Project Bellerophon Comics", "/comics/project-bellerophon-comics", AUTHOR),
|
||||
Triple("Smudge Comics", "/comics/smudge-comics", AUTHOR),
|
||||
Triple("Superheroine Central Comics", "/comics/superheroine-central-comics", AUTHOR),
|
||||
Triple("Jay Marvel Comics", "/comics/jay-marvel-comics", AUTHOR),
|
||||
Triple("Fred Perry Comics", "/comics/fred-perry-comics", AUTHOR),
|
||||
Triple("Seduced Amanda Comics", "/comics/seduced-amanda-comics", AUTHOR),
|
||||
Triple("VGBabes Comics", "/comics/vgbabes-comics", AUTHOR),
|
||||
Triple("SodomSluts.com Comics", "/comics/sodomsluts_com-comics", AUTHOR),
|
||||
Triple("AKABUR Comics", "/comics/akabur-comics", AUTHOR),
|
||||
Triple("eBluberry Comics", "/comics/ebluberry-comics", AUTHOR),
|
||||
Triple("InterracialComicPorn.com Comics", "/comics/interracialcomicporn_com-comics", AUTHOR),
|
||||
Triple("Dubh3d-Dubhgilla Comics", "/comics/dubh3d-dubhgilla-comics", AUTHOR),
|
||||
Triple("Gush Bomb Comix", "/comics/gush-bomb-comix", AUTHOR),
|
||||
Triple("Chiyoji Tomo Comics", "/comics/chiyoji-tomo-comics", AUTHOR),
|
||||
Triple("Mangrowing Comics", "/comics/mangrowing-comics", AUTHOR),
|
||||
Triple("eAdultComics Collection", "/comics/eadultcomics-collection", AUTHOR),
|
||||
Triple("Skulltitti Comics", "/comics/skulltitti-comics", AUTHOR),
|
||||
Triple("James Lemay Comics", "/comics/james-lemay-comics", AUTHOR),
|
||||
Triple("TalesOfPleasure.com Comics", "/comics/talesofpleasure_com-comics", AUTHOR),
|
||||
Triple("Eden Comics", "/comics/eden-comics", AUTHOR),
|
||||
Triple("WorldOfPeach Comics", "/comics/worldofpeach-comics", AUTHOR),
|
||||
Triple("Daniel40 Comics", "/comics/daniel40-comics", AUTHOR),
|
||||
Triple("DontFapGirl Comics", "/comics/dontfapgirl-comics", AUTHOR),
|
||||
Triple("Wingbird Comics", "/comics/wingbird-comics", AUTHOR),
|
||||
Triple("Intrigue3d.com Comics", "/comics/intrigue3d_com-comics", AUTHOR),
|
||||
Triple("Hentaikey Comics", "/comics/hentaikey-comics", AUTHOR),
|
||||
Triple("Kamina1978 Comics", "/comics/kamina1978-comics", AUTHOR),
|
||||
Triple("3DPerils Comics", "/comics/3dperils-comics", AUTHOR),
|
||||
Triple("Tracy Scops Comics", "/comics/tracy-scops-comics", AUTHOR),
|
||||
Triple("Shemale3D Comics", "/comics/shemale3d-comics", AUTHOR),
|
||||
Triple("InterracialSex3D.com Comics", "/comics/Interracialsex3d-Com-Comix", AUTHOR),
|
||||
Triple("MyHentaiGrid Comics", "/comics/myhentaigrid-comics", AUTHOR),
|
||||
Triple("Magnifire Comics", "/comics/magnifire-comics", AUTHOR),
|
||||
Triple("Reptileye Comics", "/comics/reptileye-comics", AUTHOR),
|
||||
Triple("ProjectPinkXXX.com Comics", "/comics/projectpinkxxx_com-comics", AUTHOR),
|
||||
Triple("CallMePlisskin Comics", "/comics/callmeplisskin-comics", AUTHOR),
|
||||
)
|
||||
|
||||
override fun getSortList() = arrayOf(
|
||||
Pair("Viewed", "viewed"),
|
||||
Pair("Liked", "liked"),
|
||||
Pair("Date", "recent"),
|
||||
Pair("A-Z", "az"),
|
||||
)
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 9.5 KiB |