diff --git a/src/ja/mangacross/build.gradle b/src/ja/mangacross/build.gradle index 050c78dca..046131555 100644 --- a/src/ja/mangacross/build.gradle +++ b/src/ja/mangacross/build.gradle @@ -1,7 +1,10 @@ ext { - extName = 'Manga Cross' - extClass = '.MangaCross' - extVersionCode = 5 + extName = "Champion Cross" + extClass = ".MangaCross" + themePkg = 'comiciviewer' + baseUrl = "https://championcross.jp" + overrideVersionCode = 5 + isNsfw = false } apply from: "$rootDir/common.gradle" diff --git a/src/ja/mangacross/res/mipmap-hdpi/ic_launcher.png b/src/ja/mangacross/res/mipmap-hdpi/ic_launcher.png index 04e5120c9..7786c72bf 100644 Binary files a/src/ja/mangacross/res/mipmap-hdpi/ic_launcher.png and b/src/ja/mangacross/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/ja/mangacross/res/mipmap-mdpi/ic_launcher.png b/src/ja/mangacross/res/mipmap-mdpi/ic_launcher.png index 38675b075..8337a0619 100644 Binary files a/src/ja/mangacross/res/mipmap-mdpi/ic_launcher.png and b/src/ja/mangacross/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/ja/mangacross/res/mipmap-xhdpi/ic_launcher.png b/src/ja/mangacross/res/mipmap-xhdpi/ic_launcher.png index 87338395c..9bd9c9293 100644 Binary files a/src/ja/mangacross/res/mipmap-xhdpi/ic_launcher.png and b/src/ja/mangacross/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/ja/mangacross/res/mipmap-xxhdpi/ic_launcher.png b/src/ja/mangacross/res/mipmap-xxhdpi/ic_launcher.png index b1e573e3a..5e4dc7bd0 100644 Binary files a/src/ja/mangacross/res/mipmap-xxhdpi/ic_launcher.png and b/src/ja/mangacross/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/ja/mangacross/res/mipmap-xxxhdpi/ic_launcher.png b/src/ja/mangacross/res/mipmap-xxxhdpi/ic_launcher.png index 24326c941..c9ab96f14 100644 Binary files a/src/ja/mangacross/res/mipmap-xxxhdpi/ic_launcher.png and b/src/ja/mangacross/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/ja/mangacross/src/eu/kanade/tachiyomi/extension/ja/mangacross/MangaCross.kt b/src/ja/mangacross/src/eu/kanade/tachiyomi/extension/ja/mangacross/MangaCross.kt index 74364193d..470ca480a 100644 --- a/src/ja/mangacross/src/eu/kanade/tachiyomi/extension/ja/mangacross/MangaCross.kt +++ b/src/ja/mangacross/src/eu/kanade/tachiyomi/extension/ja/mangacross/MangaCross.kt @@ -1,115 +1,12 @@ package eu.kanade.tachiyomi.extension.ja.mangacross -import android.util.Log -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservableSuccess -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.SManga -import eu.kanade.tachiyomi.source.online.HttpSource -import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromStream -import okhttp3.Response -import rx.Observable -import uy.kohesive.injekt.injectLazy -import kotlin.concurrent.thread +import eu.kanade.tachiyomi.multisrc.comiciviewer.ComiciViewer -class MangaCross : HttpSource() { - override val name = "Manga Cross" - override val lang = "ja" - override val baseUrl = "https://mangacross.jp" - override val supportsLatest = true - - private val json: Json by injectLazy() - - // Pagination does not work. 9999 is a dummy large number. - override fun popularMangaRequest(page: Int) = GET("$baseUrl/api/comics.json?count=9999", headers) - override fun popularMangaParse(response: Response) = - MangasPage(response.parseAs().comics.map { it.toSManga() }, false) - - override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/api/episodes.json?page=$page", headers) - override fun latestUpdatesParse(response: Response): MangasPage { - val result: MCEpisodeList = response.parseAs() - return MangasPage(result.episodes.map { it.comic!!.toSManga() }, result.current_page < result.total_pages) - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = - if (query.isNotEmpty()) { - GET("$baseUrl/api/comics/keywords/$query.json", headers) - } else { - when (val tag = filters.filterIsInstance().firstOrNull()?.getTag()) { - null -> popularMangaRequest(page) - is MCComicCategory -> GET("$baseUrl/api/comics/categories/${tag.name}.json", headers) - is MCComicGenre -> GET("$baseUrl/api/comics/tags/${tag.name}.json", headers) - } - } - - override fun searchMangaParse(response: Response) = popularMangaParse(response) - - override fun fetchMangaDetails(manga: SManga): Observable = - client.newCall(chapterListRequest(manga)).asObservableSuccess() - .map { mangaDetailsParse(it).apply { initialized = true } } - - // mangaDetailsRequest untouched in order to let WebView open web page instead of json - - override fun mangaDetailsParse(response: Response) = response.parseAs().comic.toSManga() - - override fun chapterListRequest(manga: SManga) = GET("$baseUrl/api${manga.url}.json", headers) - - override fun chapterListParse(response: Response) = response.parseAs().comic.toSChapterList() - - override fun pageListParse(response: Response): List { - return try { - response.parseAs().episode_pages.mapIndexed { i, it -> - Page(i, imageUrl = it.image.original_url) - } - } catch (e: SerializationException) { - throw Exception("Chapter is no longer available!") - } - } - - override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() - - private lateinit var tags: List> - private var isFetchingTags = false - - private fun fetchTags() { - if (isFetchingTags) return - isFetchingTags = true - thread { - try { - val response = client.newCall(GET("$baseUrl/api/menus.json", headers)).execute() - val filterList = response.parseAs().toFilterList() - tags = listOf(Pair("All", null)) + filterList - } catch (e: Exception) { - Log.e("MangaCross", "Failed to fetch filters ($e)") - } finally { - isFetchingTags = false - } - } - } - - override fun getFilterList() = - if (::tags.isInitialized) { - FilterList( - Filter.Header("NOTE: Ignored if using text search!"), - TagFilter("Tag", tags), - ) - } else { - fetchTags() - FilterList( - Filter.Header("Fetching tags..."), - Filter.Header("Go back to previous screen and retry."), - ) - } - - private class TagFilter(name: String, private val tags: List>) : - Filter.Select(name, tags.map { it.first }.toTypedArray()) { - fun getTag() = tags[state].second - } - - private inline fun Response.parseAs(): T = json.decodeFromStream(this.body.byteStream()) +// MangaCross became ChampionCross +class MangaCross : ComiciViewer( + "Champion Cross", + "https://championcross.jp", + "ja", +) { + override val versionId = 2 } diff --git a/src/ja/mangacross/src/eu/kanade/tachiyomi/extension/ja/mangacross/MangaCrossDto.kt b/src/ja/mangacross/src/eu/kanade/tachiyomi/extension/ja/mangacross/MangaCrossDto.kt deleted file mode 100644 index c95412ba2..000000000 --- a/src/ja/mangacross/src/eu/kanade/tachiyomi/extension/ja/mangacross/MangaCrossDto.kt +++ /dev/null @@ -1,185 +0,0 @@ -package eu.kanade.tachiyomi.extension.ja.mangacross - -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.Serializable -import org.jsoup.Jsoup -import java.text.DateFormat.getDateTimeInstance -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.TimeZone - -@Serializable -data class MCComicList( - val comics: List, -// Note: Pagination does not work. Pages after 1 return nothing. -// val current_count: Int, -// val current_page: Int?, -// val total_count: Int, -// val total_pages: Int, -) - -// Useless fields are omitted while interesting ones are commented -@Serializable -data class MCComic( - val dir_name: String, - val title: String, - val author: String, - val comic_category: MCComicCategory? = null, - val comic_tags: List, - val image_double_url: String, // horizontal - val list_image_double_url: String, // square -// val restricted: Boolean, // is NSFW -// Details below - val outline: String? = null, - val episodes: List? = null, -// val books: List? = null, -) { - fun toSManga() = SManga.create().apply { - url = "/comics/$dir_name" - title = this@MCComic.title - author = this@MCComic.author - description = getDescription() - genre = getGenre() - status = getStatus() - thumbnail_url = list_image_double_url - } - - fun toSChapterList() = episodes!! - // .filter { it.status == "public" } // preserve private chapters in case user downloaded before - .map { it.toSChapter("/comics/$dir_name") } - - private fun getDescription() = listOfNotNull( - episodes?.firstOrNull()?.getNextDatePrefix(), - outline?.stripHtml(), - ).joinToString("\n") - - private fun getGenre() = listOfNotNull( - comic_category?.display_name, - comic_tags.joinToString(", ") { it.name }, - ).joinToString(", ") - - private fun getStatus() = when { - episodes?.firstOrNull()?.episode_next_date.isNullOrEmpty() -> SManga.UNKNOWN - else -> SManga.ONGOING - } - - private fun String.stripHtml() = Jsoup.parseBodyFragment(this).text() -} - -sealed class MCComicTag - -@Serializable -data class MCComicCategory(val name: String, val display_name: String) : MCComicTag() - -@Serializable -data class MCComicGenre(val name: String) : MCComicTag() - -@Serializable -data class MCComicDetails(val comic: MCComic) - -@Serializable -data class MCEpisodeList( - val episodes: List, -// Note: Pagination works. - val current_count: Int, - val current_page: Int, - val total_count: Int, - val total_pages: Int, -) - -@Serializable -data class MCEpisode( -// val id: Long, - val volume: String, - val sort_volume: Int, - val title: String, - val publish_start: String, // all dates are in ISO time format - val publish_end: String?, -// Note: AFAIK these dates are always identical to those above. -// val member_publish_start: String, -// val member_publish_end: String?, - val status: String, // public or private -// val page_url: String, -// val list_image_double_url: String, - val episode_next_date: String?, - val next_date_customize_text: String?, - val comic: MCComic? = null, // in latest -) { - fun toSChapter(urlPrefix: String) = SChapter.create().apply { - url = "$urlPrefix/$sort_volume/viewer.json" - val prefix = if (status == "public") "" else "🔒 " - name = "$prefix$volume $title" - // milliseconds are always 000 - date_upload = JST_FORMAT_LIST.parseJST(publish_start)!!.time - // show end date in scanlator field - scanlator = publish_end?.let { "~" + LOCAL_FORMAT_LIST.format(JST_FORMAT_LIST.parseJST(it)!!) } - } - - fun getNextDatePrefix(): String? = when { - !episode_next_date.isNullOrEmpty() -> { - val date = JST_FORMAT_DESC.parseJST(episode_next_date)!!.apply { - time += 10 * 3600 * 1000 // 10 am JST - } - "【Next: ${LOCAL_FORMAT_DESC.format(date)}】" - } - !next_date_customize_text.isNullOrEmpty() -> "【$next_date_customize_text】" - else -> null - } - - companion object { - // for thread-safety - private val JST_FORMAT_DESC = getJSTFormat() - private val JST_FORMAT_LIST = getJSTFormat() - private val LOCAL_FORMAT_DESC = getDateTimeInstance() - private val LOCAL_FORMAT_LIST = getDateTimeInstance() - - private fun SimpleDateFormat.parseJST(date: String) = parse(date.removeSuffix("+09:00")) - private fun getJSTFormat() = - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH).apply { - timeZone = TimeZone.getTimeZone("GMT+09:00") - } - } -} - -@Serializable -data class MCBook( - val cover_url: String, // resolution is too low -) - -@Serializable -data class MCViewer( -// val sort_volume: Int, -// val created_at: String, -// val updated_at: String, -// val volume: String, -// val title: String, -// val page_count: Int, -// episode_viewer_setting: { page_direction: "horizontal" } - val episode_pages: List, -) - -@Serializable -data class MCEpisodePage( -// val order_index: Int, - val image: MCImage, -// val is_spread_start_page: Boolean, -) - -@Serializable -data class MCImage( -// val pc_url: String, // has highest resolution but is upscaled from original, thus unnecessary -// val sp_url: String, -// val thumbnail_url: String, - val original_url: String, -// {pc,sp,thumbnail,original}_geometry: { width: Int, height: Int } -) - -@Serializable -data class MCMenu( - val comic_categories: List, - val comic_tags: List, -) { - fun toFilterList(): List> = - comic_categories.map { Pair(it.display_name, it) } + comic_tags.map { Pair(it.name, it) } -}