diff --git a/src/zh/terrahistoricus/AndroidManifest.xml b/src/zh/terrahistoricus/AndroidManifest.xml new file mode 100644 index 000000000..30deb7f79 --- /dev/null +++ b/src/zh/terrahistoricus/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/src/zh/terrahistoricus/build.gradle b/src/zh/terrahistoricus/build.gradle new file mode 100644 index 000000000..13b0dd776 --- /dev/null +++ b/src/zh/terrahistoricus/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Terra Historicus' + pkgNameSuffix = 'zh.terrahistoricus' + extClass = '.TerraHistoricus' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/zh/terrahistoricus/res/mipmap-hdpi/ic_launcher.png b/src/zh/terrahistoricus/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..2327339be Binary files /dev/null and b/src/zh/terrahistoricus/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/zh/terrahistoricus/res/mipmap-mdpi/ic_launcher.png b/src/zh/terrahistoricus/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..e01962dd3 Binary files /dev/null and b/src/zh/terrahistoricus/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/zh/terrahistoricus/res/mipmap-xhdpi/ic_launcher.png b/src/zh/terrahistoricus/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..7cbda0126 Binary files /dev/null and b/src/zh/terrahistoricus/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/zh/terrahistoricus/res/mipmap-xxhdpi/ic_launcher.png b/src/zh/terrahistoricus/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..0a7408218 Binary files /dev/null and b/src/zh/terrahistoricus/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/zh/terrahistoricus/res/mipmap-xxxhdpi/ic_launcher.png b/src/zh/terrahistoricus/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..3bd9c2bd3 Binary files /dev/null and b/src/zh/terrahistoricus/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/zh/terrahistoricus/res/web_hi_res_512.png b/src/zh/terrahistoricus/res/web_hi_res_512.png new file mode 100644 index 000000000..8540a8891 Binary files /dev/null and b/src/zh/terrahistoricus/res/web_hi_res_512.png differ diff --git a/src/zh/terrahistoricus/src/eu/kanade/tachiyomi/extension/zh/terrahistoricus/TerraHistoricus.kt b/src/zh/terrahistoricus/src/eu/kanade/tachiyomi/extension/zh/terrahistoricus/TerraHistoricus.kt new file mode 100644 index 000000000..bf2f8aebe --- /dev/null +++ b/src/zh/terrahistoricus/src/eu/kanade/tachiyomi/extension/zh/terrahistoricus/TerraHistoricus.kt @@ -0,0 +1,63 @@ +package eu.kanade.tachiyomi.extension.zh.terrahistoricus + +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.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.online.HttpSource +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import okhttp3.Response +import rx.Observable +import uy.kohesive.injekt.injectLazy + +class TerraHistoricus : HttpSource() { + override val name = "泰拉记事社" + override val lang = "zh" + override val baseUrl = "https://terra-historicus.hypergryph.com" + override val supportsLatest = true + + private val json: Json by injectLazy() + + override fun popularMangaRequest(page: Int) = GET("$baseUrl/api/comic") + + override fun popularMangaParse(response: Response) = MangasPage( + json.decodeFromString>>(response.body!!.string()).data.map(THComic::toSManga), + false + ) + + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/api/recentUpdate") + + override fun latestUpdatesParse(response: Response) = MangasPage( + json.decodeFromString>>(response.body!!.string()).data.map(THRecentUpdate::toSManga), + false + ) + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = + throw UnsupportedOperationException("没有搜索功能") + + override fun searchMangaParse(response: Response) = throw UnsupportedOperationException("没有搜索功能") + + override fun mangaDetailsParse(response: Response) = + json.decodeFromString>(response.body!!.string()).data.toSManga() + + override fun chapterListParse(response: Response) = + json.decodeFromString>(response.body!!.string()).data.toSChapterList().orEmpty() + + override fun fetchPageList(chapter: SChapter): Observable> { + return client.newCall(pageListRequest(chapter)) + .asObservableSuccess() + .map { response -> + (0 until json.decodeFromString>(response.body!!.string()).data.pageInfos!!.size).map { + Page(it, "$baseUrl${chapter.url}/page?pageNum=${it + 1}") + } + } + } + + override fun pageListParse(response: Response) = throw UnsupportedOperationException("Not used.") + + override fun imageUrlParse(response: Response) = + json.decodeFromString>(response.body!!.string()).data.url +} diff --git a/src/zh/terrahistoricus/src/eu/kanade/tachiyomi/extension/zh/terrahistoricus/TerraHistoricusDto.kt b/src/zh/terrahistoricus/src/eu/kanade/tachiyomi/extension/zh/terrahistoricus/TerraHistoricusDto.kt new file mode 100644 index 000000000..85be93a25 --- /dev/null +++ b/src/zh/terrahistoricus/src/eu/kanade/tachiyomi/extension/zh/terrahistoricus/TerraHistoricusDto.kt @@ -0,0 +1,93 @@ +package eu.kanade.tachiyomi.extension.zh.terrahistoricus + +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.Serializable + +@Serializable +data class THResult(val code: Int, val msg: String, val data: T) + +@Serializable +data class THComic( + val cid: String, +// val type: Int, + val cover: String, + val title: String, + val subtitle: String, + val authors: List, + val keywords: List? = null, + val introduction: String? = null, +// val direction: String? = null, + val episodes: List? = null, + val updateTime: Long? = null, // timestamp in seconds +) { + private fun getDescription(): String? { + var result = "" + if (subtitle.isNotEmpty()) result += "「$subtitle」" + if (introduction != null) result += introduction + return result.ifEmpty { null } + } + + fun toSManga() = SManga.create().apply { + url = "/api/comic/$cid" + title = this@THComic.title + author = authors.joinToString("、") + thumbnail_url = cover + description = getDescription() + genre = keywords?.joinToString(",")?.replace(",", ", ") + } + + fun toSChapterList() = episodes?.map { episode -> + SChapter.create().apply { + url = "/api/comic/$cid/episode/${episode.cid!!}" + try { + chapter_number = episode.shortTitle?.toFloat() ?: chapter_number + name = episode.title + } catch (e: NumberFormatException) { + name = "${episode.shortTitle} ${episode.title}" + } + date_upload = (updateTime ?: 0L) * 1000 + } + } +} + +@Serializable +data class THRecentUpdate( + val coverUrl: String, + val comicCid: String, + val title: String, +// val subtitle: String, +// val episodeCid: String, +// val episodeType: Int, +// val episodeShortTitle: String, +// val updateTime: Long +) { + fun toSManga() = SManga.create().apply { + url = "/api/comic/$comicCid" + title = this@THRecentUpdate.title + thumbnail_url = coverUrl + } +} + +@Serializable +data class THEpisode( + val cid: String? = null, +// val type: Int, + val shortTitle: String?, + val title: String, // 作品信息中 +// val likes: Int? = null, + val pageInfos: List? = null, // 章节详情中 +) + +@Serializable +data class THPageInfo( +// val width: Int, +// val height: Int, + val doublePage: Boolean, +) + +@Serializable +data class THPage( +// val pageNum: Int, + val url: String, +)