Initial commit

This commit is contained in:
FourTOne5
2024-01-09 04:12:39 +06:00
commit 600c345dfe
8593 changed files with 150590 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View 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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View 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"),
)
}

View File

@@ -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)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -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>

View File

@@ -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),
)
}

View File

@@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@@ -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"
}
}

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View 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")

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View 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"),
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Some files were not shown because too many files have changed in this diff Show More