diff --git a/src/ja/comicmeteor/build.gradle b/src/ja/comicmeteor/build.gradle new file mode 100644 index 000000000..6264d98db --- /dev/null +++ b/src/ja/comicmeteor/build.gradle @@ -0,0 +1,11 @@ +ext { + extName = "Comic Meteor" + extClass = ".ComicMeteor" + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" + +dependencies { + implementation(project(":lib:speedbinb")) +} diff --git a/src/ja/comicmeteor/res/mipmap-hdpi/ic_launcher.png b/src/ja/comicmeteor/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..185966c0a Binary files /dev/null and b/src/ja/comicmeteor/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/ja/comicmeteor/res/mipmap-mdpi/ic_launcher.png b/src/ja/comicmeteor/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..994b793c4 Binary files /dev/null and b/src/ja/comicmeteor/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/ja/comicmeteor/res/mipmap-xhdpi/ic_launcher.png b/src/ja/comicmeteor/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..f87997f8b Binary files /dev/null and b/src/ja/comicmeteor/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/ja/comicmeteor/res/mipmap-xxhdpi/ic_launcher.png b/src/ja/comicmeteor/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..2e47a78df Binary files /dev/null and b/src/ja/comicmeteor/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/ja/comicmeteor/res/mipmap-xxxhdpi/ic_launcher.png b/src/ja/comicmeteor/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..349bf7bd2 Binary files /dev/null and b/src/ja/comicmeteor/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/ja/comicmeteor/src/eu/kanade/tachiyomi/extension/ja/comicmeteor/ComicMeteor.kt b/src/ja/comicmeteor/src/eu/kanade/tachiyomi/extension/ja/comicmeteor/ComicMeteor.kt new file mode 100644 index 000000000..77474db8b --- /dev/null +++ b/src/ja/comicmeteor/src/eu/kanade/tachiyomi/extension/ja/comicmeteor/ComicMeteor.kt @@ -0,0 +1,153 @@ +package eu.kanade.tachiyomi.extension.ja.comicmeteor + +import eu.kanade.tachiyomi.lib.speedbinb.SpeedBinbInterceptor +import eu.kanade.tachiyomi.lib.speedbinb.SpeedBinbReader +import eu.kanade.tachiyomi.network.GET +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.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.json.Json +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class ComicMeteor : ParsedHttpSource() { + + override val name = "COMICメテオ" + + override val baseUrl = "https://comic-meteor.jp" + + override val lang = "ja" + + override val supportsLatest = false + + private val json = Injekt.get() + + override val client = network.client.newBuilder() + .addInterceptor(SpeedBinbInterceptor(json)) + .apply { + val interceptors = interceptors() + val index = interceptors.indexOfFirst { "Brotli" in it.javaClass.simpleName } + if (index >= 0) { + interceptors.add(interceptors.removeAt(index)) + } + } + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") + + override fun popularMangaRequest(page: Int) = GET( + "$baseUrl/wp-admin/admin-ajax.php?action=get_flex_titles_for_toppage&page=$page&get_num=16", + headers, + ) + + override fun popularMangaParse(response: Response): MangasPage { + val document = Jsoup.parseBodyFragment(response.body.string(), baseUrl) + val manga = document.select(popularMangaSelector()).map { popularMangaFromElement(it) } + val hasNextPage = manga.size == 16 + + return MangasPage(manga, hasNextPage) + } + + override fun popularMangaSelector() = ".update_work_size .update_work_info_img a" + + override fun popularMangaFromElement(element: Element) = SManga.create().apply { + setUrlWithoutDomain(element.attr("href")) + element.selectFirst("img")!!.let { + title = it.attr("alt") + thumbnail_url = it.absUrl("src") + } + } + + override fun popularMangaNextPageSelector() = throw UnsupportedOperationException() + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + + override fun latestUpdatesSelector() = throw UnsupportedOperationException() + + override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException() + + override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException() + + private lateinit var directory: List + + override fun fetchSearchManga( + page: Int, + query: String, + filters: FilterList, + ): Observable { + return if (page == 1) { + client.newCall(searchMangaRequest(page, query, filters)) + .asObservableSuccess() + .map { searchMangaParse(it) } + } else { + Observable.just(parseDirectory(page)) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = baseUrl.toHttpUrl().newBuilder() + .addPathSegment("comicsearch") + .addPathSegment("") + .addQueryParameter("search", query) + .build() + + return GET(url, headers) + } + + override fun searchMangaParse(response: Response): MangasPage { + val document = response.asJsoup() + + directory = document.select(searchMangaSelector()) + return parseDirectory(1) + } + + private fun parseDirectory(page: Int): MangasPage { + val endRange = minOf(page * 24, directory.size) + val manga = directory.subList((page - 1) * 24, endRange).map { searchMangaFromElement(it) } + val hasNextPage = endRange < directory.lastIndex + + return MangasPage(manga, hasNextPage) + } + + override fun searchMangaSelector() = ".read_comic_size .read_comic_info_img a" + + override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) + + override fun searchMangaNextPageSelector() = throw UnsupportedOperationException() + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + title = document.selectFirst("h2.h2ttl")!!.text() + author = document.selectFirst(".work_author_intro_name") + ?.text() + ?.substringAfter("著者 :") + description = document.selectFirst(".work_story_txt")?.text() + genre = document.select(".category_link_box a").joinToString { it.text() } + thumbnail_url = document.selectFirst(".latest_info_img img")?.absUrl("src") + } + + override fun chapterListSelector() = ".work_episode_box .work_episode_table:has(.work_episode_link_orange)" + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + name = element.selectFirst(".work_episode_txt")!!.ownText() + } + + private val reader by lazy { SpeedBinbReader(client, headers, json) } + + override fun pageListParse(document: Document) = + reader.pageListParse(document) + + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() +}