diff --git a/src/vi/mimihentai/build.gradle b/src/vi/mimihentai/build.gradle deleted file mode 100644 index 46092426b..000000000 --- a/src/vi/mimihentai/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -ext { - extName = "MiMiHentai" - extClass = ".MiMiHentai" - extVersionCode = 3 - isNsfw = true -} -apply from: "$rootDir/common.gradle" diff --git a/src/vi/mimihentai/res/mipmap-hdpi/ic_launcher.png b/src/vi/mimihentai/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index f9dd79537..000000000 Binary files a/src/vi/mimihentai/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/vi/mimihentai/res/mipmap-mdpi/ic_launcher.png b/src/vi/mimihentai/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index ef0a3a6c8..000000000 Binary files a/src/vi/mimihentai/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/vi/mimihentai/res/mipmap-xhdpi/ic_launcher.png b/src/vi/mimihentai/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index b453b2e1b..000000000 Binary files a/src/vi/mimihentai/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/vi/mimihentai/res/mipmap-xxhdpi/ic_launcher.png b/src/vi/mimihentai/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 0f3f09ec2..000000000 Binary files a/src/vi/mimihentai/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/vi/mimihentai/res/mipmap-xxxhdpi/ic_launcher.png b/src/vi/mimihentai/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 47ef71842..000000000 Binary files a/src/vi/mimihentai/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/vi/mimihentai/src/eu/kanade/tachiyomi/extension/vi/mimihentai/Dto.kt b/src/vi/mimihentai/src/eu/kanade/tachiyomi/extension/vi/mimihentai/Dto.kt deleted file mode 100644 index e7a47e918..000000000 --- a/src/vi/mimihentai/src/eu/kanade/tachiyomi/extension/vi/mimihentai/Dto.kt +++ /dev/null @@ -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, - 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, - private val genres: List, -) { - 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, -) - -@Serializable -class ListPages( - val imageUrl: String, - val drm: String? = null, -) - -@Serializable -class Genres( - val id: Long, - val name: String, -) diff --git a/src/vi/mimihentai/src/eu/kanade/tachiyomi/extension/vi/mimihentai/MiMiHentai.kt b/src/vi/mimihentai/src/eu/kanade/tachiyomi/extension/vi/mimihentai/MiMiHentai.kt deleted file mode 100644 index 5f86e5659..000000000 --- a/src/vi/mimihentai/src/eu/kanade/tachiyomi/extension/vi/mimihentai/MiMiHentai.kt +++ /dev/null @@ -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() - 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 { - val segments = response.request.url.pathSegments - val mangaId = segments.last() - val res = response.parseAs>() - 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() - return res.toSManga() - } - - override fun pageListRequest(chapter: SChapter): Request { - return GET("$apiUrl/chapter?id=${chapter.url.substringAfterLast("/")}", headers) - } - - override fun pageListParse(response: Response): List { - val res = response.parseAs() - 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 { - 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> { - return response.parseAs>().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> = emptyList() - - private class GenreList(name: String, pairs: List>) : GenresFilter(name, pairs) - private open class GenresFilter(title: String, pairs: List>) : - Filter.Group(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) : Filter.Select("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:" - } -}