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:
parent
0bc4035bfe
commit
0b3d1e6aeb
|
@ -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 |
|
@ -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,
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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",
|
||||||
|
)
|
Loading…
Reference in New Issue