Add MIC MIC IDOL (#1013)

* Add MIC MIC IDOL

* Mark as NSFW

* no need to convert to httpurl

* spelling mistake

* Add all tags to filter
This commit is contained in:
beerpsi 2024-02-06 07:29:12 +07:00 committed by GitHub
parent cca756b368
commit 677ccb853b
9 changed files with 298 additions and 0 deletions

View File

@ -0,0 +1,8 @@
ext {
extName = "MIC MIC IDOL"
extClass = ".MicMicIdol"
extVersionCode = 1
isNsfw = true
}
apply from: "$rootDir/common.gradle"

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.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -0,0 +1,49 @@
package eu.kanade.tachiyomi.extension.ja.micmicidol
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class BloggerDto(
val feed: BloggerFeedDto,
)
@Serializable
data class BloggerFeedDto(
@SerialName("openSearch\$totalResults") val totalResults: BloggerTextDto,
@SerialName("openSearch\$startIndex") val startIndex: BloggerTextDto,
@SerialName("openSearch\$itemsPerPage") val itemsPerPage: BloggerTextDto,
val category: List<BloggerCategoryDto> = emptyList(),
val entry: List<BloggerFeedEntryDto> = emptyList(),
)
@Serializable
data class BloggerFeedEntryDto(
val published: BloggerTextDto,
val category: List<BloggerCategoryDto>,
val title: BloggerTextDto,
val content: BloggerTextDto,
val link: List<BloggerLinkDto>,
val author: List<BloggerAuthorDto>,
)
@Serializable
data class BloggerLinkDto(
val rel: String,
val href: String,
)
@Serializable
data class BloggerCategoryDto(
val term: String,
)
@Serializable
data class BloggerAuthorDto(
val name: BloggerTextDto,
)
@Serializable
data class BloggerTextDto(
@SerialName("\$t") val t: String,
)

View File

@ -0,0 +1,182 @@
package eu.kanade.tachiyomi.extension.ja.micmicidol
import android.os.Build
import eu.kanade.tachiyomi.network.GET
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.model.UpdateStrategy
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
class MicMicIdol : HttpSource() {
override val name = "MIC MIC IDOL"
override val baseUrl = "https://www.micmicidol.club"
override val lang = "ja"
override val supportsLatest = false
override val client = network.cloudflareClient
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
private val json: Json by injectLazy()
private val dateFormat by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.getDefault())
} else {
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
}
}
override fun popularMangaRequest(page: Int) = GET(apiUrlBuilder(page).build(), headers)
override fun popularMangaParse(response: Response): MangasPage {
val data = json.decodeFromString<BloggerDto>(response.body.string())
categories = data.feed.category.map { it.term }
val manga = data.feed.entry.map { entry ->
val content = Jsoup.parseBodyFragment(entry.content.t, baseUrl)
SManga.create().apply {
setUrlWithoutDomain(entry.link.first { it.rel == "alternate" }.href + "#${entry.published.t}")
title = entry.title.t
thumbnail_url = content.selectFirst("img")?.absUrl("src")
genre = entry.category.joinToString { it.term }
status = SManga.COMPLETED
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
initialized = true
}
}
val hasNextPage = (data.feed.startIndex.t.toInt() + data.feed.itemsPerPage.t.toInt()) <= data.feed.totalResults.t.toInt()
return MangasPage(manga, hasNextPage)
}
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val filterList = filters.ifEmpty { getFilterList() }
val searchQuery = buildString {
filterList.filterIsInstance<LabelFilter>().forEach {
it.state
.filter { f -> f.state }
.forEach { f ->
append(" label:\"")
append(f.name)
append("\"")
}
}
if (query.isNotEmpty()) {
append(" ")
append(query)
}
}.trim()
val url = apiUrlBuilder(page)
.addQueryParameter("q", searchQuery)
.build()
return GET(url, headers)
}
override fun searchMangaParse(response: Response) = popularMangaParse(response)
override fun fetchMangaDetails(manga: SManga): Observable<SManga> = Observable.just(manga)
override fun getMangaUrl(manga: SManga) = "$baseUrl${manga.url}".substringBefore("#")
override fun mangaDetailsRequest(manga: SManga) = throw UnsupportedOperationException()
override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException()
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
val date = manga.url.substringAfter("#")
return Observable.just(
listOf(
SChapter.create().apply {
url = manga.url.substringBefore("#")
name = "Gallery"
date_upload = runCatching {
dateFormat.parse(date)!!.time
}.getOrDefault(0L)
},
),
)
}
override fun chapterListParse(response: Response) = throw UnsupportedOperationException()
override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup()
return document.select("div.post-body img").mapIndexed { i, it ->
Page(i, imageUrl = it.absUrl("src"))
}
}
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
override fun getFilterList(): FilterList {
val types = getTypes()
val japanMagazines = getJapanMagazines()
val japanFashion = getJapanFashion()
val filters = mutableListOf<Filter<*>>(
LabelFilter("Type", types.map { Label(it) }),
LabelFilter("Japan Magazine", japanMagazines.map { Label(it) }),
LabelFilter("Japan Fashion", japanFashion.map { Label(it) }),
).apply {
if (categories.isEmpty()) {
add(0, Filter.Header("Press 'Reset' to show extra filters"))
add(1, Filter.Separator())
return@apply
}
val others = categories
.filterNot { types.contains(it) || japanMagazines.contains(it) || japanFashion.contains(it) }
add(LabelFilter("Other", others.map { Label(it) }))
}
return FilterList(filters)
}
private var categories = emptyList<String>()
private fun apiUrlBuilder(page: Int) = baseUrl.toHttpUrl().newBuilder().apply {
// Blogger indices start from 1
val startIndex = MAX_RESULTS * (page - 1) + 1
addPathSegments("feeds/posts/default")
addQueryParameter("alt", "json")
addQueryParameter("max-results", MAX_RESULTS.toString())
addQueryParameter("start-index", startIndex.toString())
}
companion object {
private const val MAX_RESULTS = 25
}
}

View File

@ -0,0 +1,59 @@
package eu.kanade.tachiyomi.extension.ja.micmicidol
import eu.kanade.tachiyomi.source.model.Filter
class LabelFilter(name: String, labels: List<Label>) : Filter.Group<Label>(name, labels)
class Label(name: String) : Filter.CheckBox(name)
// copy([...$0.querySelectorAll("li a[href]")].filter(e => e.getAttribute("href") != "#").map((e) => `"${decodeURIComponent(e.getAttribute("href").replace("/search/label/", "").replace("?max-results=50", ""))}",`).join("\n"))
fun getJapanMagazines() = listOf(
"cyzo",
"EnTame",
"EX大衆",
"Friday",
"Flash",
"Shonen Magazine",
"Shonen Sunday",
"Weekly Shonen Champion",
"Weekly Big Comic Spirits",
"Weekly Jitsuwa",
"Weekly Playboy",
"Weekly SPA!",
"Young Animal",
"Young Champion",
"Young Gangan",
"Young Jump",
"Young Magazine",
)
fun getJapanFashion() = listOf(
"andGIRL",
"aR",
"Baila",
"Biteki",
"CanCam",
"Classy",
"ELLE Japan",
"Ginger",
"JJ",
"Maquia",
"Mina",
"MORE",
"Non-no",
"Oggi",
"Ray",
"Scawaii",
"Steady",
"ViVi",
"VoCE",
"With",
)
fun getTypes() = listOf(
"- Cover",
"- Japan Magazine",
"- Japan Fashion Magazine",
"- Japan Idol Photobook",
"- Asia Idol",
)