Remove MimiHentai (#10860)

remove dead source
This commit is contained in:
are-are-are 2025-10-04 01:43:54 +07:00 committed by Draff
parent 9d05af7e2d
commit 00046adbdc
Signed by: Draff
GPG Key ID: E8A89F3211677653
8 changed files with 0 additions and 323 deletions

View File

@ -1,7 +0,0 @@
ext {
extName = "MiMiHentai"
extClass = ".MiMiHentai"
extVersionCode = 3
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,76 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.mimihentai
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import keiyoushi.utils.tryParse
import kotlinx.serialization.Serializable
import java.text.SimpleDateFormat
import java.util.Locale
@Serializable
class ListingDto(
val data: List<MangaDto>,
val totalPage: Long,
val currentPage: Long,
)
@Serializable
class MangaDto(
private val id: Long,
private val title: String,
private val coverUrl: String,
private val description: String,
private val authors: List<Author>,
private val genres: List<Genre>,
) {
fun toSManga(): SManga = SManga.create().apply {
title = this@MangaDto.title
thumbnail_url = coverUrl
url = "$id"
description = this@MangaDto.description
author = authors.joinToString { i -> i.name }
genre = genres.joinToString { i -> i.name }
initialized = true
}
}
@Serializable
class Author(
val name: String,
)
@Serializable
class Genre(
val name: String,
)
@Serializable
class ChapterDto(
private val id: Long,
private val title: String,
private val createdAt: String,
) {
fun toSChapter(mangaId: String): SChapter = SChapter.create().apply {
name = title
date_upload = dateFormat.tryParse(createdAt)
url = "$mangaId/$id"
}
}
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", Locale.US)
@Serializable
class PageListDto(
val pages: List<ListPages>,
)
@Serializable
class ListPages(
val imageUrl: String,
val drm: String? = null,
)
@Serializable
class Genres(
val id: Long,
val name: String,
)

View File

@ -1,240 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.mimihentai
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
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 eu.kanade.tachiyomi.source.online.HttpSource
import keiyoushi.utils.parseAs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import rx.Observable
class MiMiHentai : HttpSource() {
override val name: String = "MiMiHentai"
override val lang: String = "vi"
override val baseUrl: String = "https://mimihentai.com"
private val apiUrl: String = "$baseUrl/api/v1/manga"
override val supportsLatest: Boolean = true
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.rateLimit(3)
.build()
override fun latestUpdatesRequest(page: Int): Request = GET(
apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("tatcatruyen")
addQueryParameter("page", (page - 1).toString())
addQueryParameter("sort", "updated_at")
addQueryParameter("ex", "196")
}.build(),
headers,
)
override fun latestUpdatesParse(response: Response): MangasPage {
val res = response.parseAs<ListingDto>()
val hasNextPage = res.currentPage != res.totalPage
return MangasPage(res.data.map { it.toSManga() }, hasNextPage)
}
override fun getMangaUrl(manga: SManga): String = "$baseUrl/g/${manga.url}"
override fun chapterListRequest(manga: SManga): Request {
val id = manga.url.substringAfterLast("/")
val url = apiUrl.toHttpUrl().newBuilder()
.addPathSegment("gallery")
.addPathSegment(id)
.build()
return GET(url, headers)
}
override fun chapterListParse(response: Response): List<SChapter> {
val segments = response.request.url.pathSegments
val mangaId = segments.last()
val res = response.parseAs<List<ChapterDto>>()
return res.map { it.toSChapter(mangaId) }
}
override fun getChapterUrl(chapter: SChapter): String {
val mangaId = chapter.url.substringBefore('/')
val chapterId = chapter.url.substringAfter('/')
return "$baseUrl/g/$mangaId/chapter/$chapterId"
}
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
override fun mangaDetailsRequest(manga: SManga): Request {
val id = manga.url.substringAfterLast("/")
val url = apiUrl.toHttpUrl().newBuilder()
.addPathSegment("info")
.addPathSegment(id)
.build()
return GET(url, headers)
}
override fun mangaDetailsParse(response: Response): SManga {
val res = response.parseAs<MangaDto>()
return res.toSManga()
}
override fun pageListRequest(chapter: SChapter): Request {
return GET("$apiUrl/chapter?id=${chapter.url.substringAfterLast("/")}", headers)
}
override fun pageListParse(response: Response): List<Page> {
val res = response.parseAs<PageListDto>()
return res.pages.mapIndexed { index, url ->
Page(index, imageUrl = url.imageUrl)
}
}
override fun popularMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
override fun popularMangaRequest(page: Int): Request = GET(
apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("tatcatruyen")
addQueryParameter("page", (page - 1).toString())
addQueryParameter("sort", "views")
addQueryParameter("ex", "196")
}.build(),
headers,
)
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return when {
query.startsWith(PREFIX_ID_SEARCH) -> {
val id = query.removePrefix(PREFIX_ID_SEARCH)
client.newCall(searchMangaByIdRequest(id))
.asObservableSuccess()
.map { response -> searchMangaByIdParse(response) }
}
query.toIntOrNull() != null -> {
client.newCall(searchMangaByIdRequest(query))
.asObservableSuccess()
.map { response -> searchMangaByIdParse(response) }
}
else -> super.fetchSearchManga(page, query, filters)
}
}
private fun searchMangaByIdRequest(id: String) = GET("$apiUrl/info/$id", headers)
override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response)
private fun searchMangaByIdParse(response: Response): MangasPage {
val details = mangaDetailsParse(response)
return MangasPage(listOf(details), false)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("advance-search")
addQueryParameter("author", "")
addQueryParameter("character", "")
addQueryParameter("parody", "")
addQueryParameter("page", (page - 1).toString())
addQueryParameter("name", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filters ->
when (filters) {
is SortByList ->
{
val sort = getSortByList()[filters.state]
addQueryParameter("sort", sort.id)
}
is GenreList -> filters.state.forEach {
when (it.state) {
Filter.TriState.STATE_INCLUDE -> addQueryParameter("genre", it.id)
Filter.TriState.STATE_EXCLUDE -> addQueryParameter("ex", it.id)
}
}
is TextField -> setQueryParameter(filters.key, filters.state)
else -> {}
}
}
}.build()
return GET(url, headers)
}
private fun genresRequest(): Request = GET("$apiUrl/genres", headers)
private fun parseGenres(response: Response): List<Pair<Long, String>> {
return response.parseAs<List<Genres>>().map { Pair(it.id, it.name) }
}
private var fetchGenresAttempts: Int = 0
private fun fetchGenres() {
if (fetchGenresAttempts >= 3 || genreList.isEmpty()) {
launchIO {
try {
client.newCall(genresRequest()).await()
.use { parseGenres(it) }
.takeIf { it.isNotEmpty() }
?.also { genreList = it }
} catch (_: Exception) {
} finally {
fetchGenresAttempts++
}
}
}
}
private fun launchIO(block: suspend () -> Unit) = GlobalScope.launch(Dispatchers.IO) { block() }
private var genreList: List<Pair<Long, String>> = emptyList()
private class GenreList(name: String, pairs: List<Pair<Long, String>>) : GenresFilter(name, pairs)
private open class GenresFilter(title: String, pairs: List<Pair<Long, String>>) :
Filter.Group<GenreCheckBox>(title, pairs.map { GenreCheckBox(it.second, it.first.toString()) })
class GenreCheckBox(name: String, val id: String = name) : Filter.TriState(name)
private class SortByList(sort: Array<SortBy>) : Filter.Select<SortBy>("Sắp xếp", sort)
private class SortBy(name: String, val id: String) : Filter.CheckBox(name) {
override fun toString(): String {
return name
}
}
private class TextField(name: String, val key: String) : Filter.Text(name)
override fun getFilterList(): FilterList {
fetchGenres()
return FilterList(
SortByList(getSortByList()),
TextField("Tác giả", "author"),
TextField("Parody", "parody"),
TextField("Nhân vật", "character"),
if (genreList.isEmpty()) {
Filter.Header("Nhấn 'Làm mới' để thử tải thể loại")
} else {
GenreList("Thể loại", genreList)
},
)
}
private fun getSortByList() = arrayOf(
SortBy("Mới", "updated_at"),
SortBy("Likes", "likes"),
SortBy("Views", "views"),
SortBy("Lưu", "follows"),
SortBy("Tên", "title"),
)
companion object {
private const val PREFIX_ID_SEARCH = "id:"
}
}