diff --git a/src/zh/zazhimi/build.gradle b/src/zh/zazhimi/build.gradle new file mode 100644 index 000000000..6744cf30b --- /dev/null +++ b/src/zh/zazhimi/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'Zazhimi' + extClass = '.Zazhimi' + extVersionCode = 1 + isNsfw = false +} + +apply from: "$rootDir/common.gradle" diff --git a/src/zh/zazhimi/res/mipmap-hdpi/ic_launcher.png b/src/zh/zazhimi/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..060bb8946 Binary files /dev/null and b/src/zh/zazhimi/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/zh/zazhimi/res/mipmap-mdpi/ic_launcher.png b/src/zh/zazhimi/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..2386f528f Binary files /dev/null and b/src/zh/zazhimi/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/zh/zazhimi/res/mipmap-xhdpi/ic_launcher.png b/src/zh/zazhimi/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..4440015bd Binary files /dev/null and b/src/zh/zazhimi/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/zh/zazhimi/res/mipmap-xxhdpi/ic_launcher.png b/src/zh/zazhimi/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..0633ad414 Binary files /dev/null and b/src/zh/zazhimi/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/zh/zazhimi/res/mipmap-xxxhdpi/ic_launcher.png b/src/zh/zazhimi/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..2f2e891e9 Binary files /dev/null and b/src/zh/zazhimi/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/zh/zazhimi/src/eu/kanade/tachiyomi/extension/zh/zazhimi/Response.kt b/src/zh/zazhimi/src/eu/kanade/tachiyomi/extension/zh/zazhimi/Response.kt new file mode 100644 index 000000000..14434f0d5 --- /dev/null +++ b/src/zh/zazhimi/src/eu/kanade/tachiyomi/extension/zh/zazhimi/Response.kt @@ -0,0 +1,73 @@ +package eu.kanade.tachiyomi.extension.zh.zazhimi + +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.model.UpdateStrategy +import kotlinx.serialization.Serializable + +@Serializable +data class IndexResponse( + val status: String, + val error: String, + val new: List, +) + +@Serializable +data class ShowResponse( + val status: String, + val error: String, + val content: List, +) + +@Serializable +data class SearchResponse( + val status: String, + val error: String, + val magazine: List, +) + +@Serializable +data class NewItem( + val magId: String, + val magName: String, + val magCover: String, + val magDate: String, +) { + fun toSManga(): SManga = SManga.create().apply { + title = this@NewItem.magName + author = this@NewItem.magName.split(" ")[0] + thumbnail_url = this@NewItem.magCover + url = "/show.php?a=${this@NewItem.magId}" + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + initialized = true + } +} + +@Serializable +data class ShowItem( + val magId: String, + val magName: String, + val typeId: String, + val typeName: String, + val cateId: String, + val magPic: String, + val pageUrl: String, + val pageThumbUrl: String, +) { + fun toPage(i: Int): Page = Page(i, imageUrl = this@ShowItem.magPic) +} + +@Serializable +data class SearchItem( + val magId: String, + val magName: String, + val magDate: String, + val pubdate: String, +) { + fun toSManga(): SManga = SManga.create().apply { + title = this@SearchItem.magName + author = this@SearchItem.magName.split(" ")[0] + url = "/show.php?a=${this@SearchItem.magId}" + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + } +} diff --git a/src/zh/zazhimi/src/eu/kanade/tachiyomi/extension/zh/zazhimi/Zazhimi.kt b/src/zh/zazhimi/src/eu/kanade/tachiyomi/extension/zh/zazhimi/Zazhimi.kt new file mode 100644 index 000000000..4d1945dcf --- /dev/null +++ b/src/zh/zazhimi/src/eu/kanade/tachiyomi/extension/zh/zazhimi/Zazhimi.kt @@ -0,0 +1,108 @@ +package eu.kanade.tachiyomi.extension.zh.zazhimi + +import eu.kanade.tachiyomi.network.GET +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 keiyoushi.utils.parseAs +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import java.lang.IllegalStateException + +class Zazhimi : HttpSource() { + + private val apiUrl = "https://android2024.zazhimi.net/api" + + override val baseUrl = "https://www.zazhimi.net" + override val lang = "zh" + override val name = "杂志迷" + override val supportsLatest = false + + override fun headersBuilder() = super.headersBuilder() + .set("User-Agent", "ZaZhiMi_5.3.0") + + // Popular + + override fun popularMangaRequest(page: Int) = GET("$apiUrl/index.php?p=$page&s=20", headers) + + override fun popularMangaParse(response: Response): MangasPage { + val result = response.parseAs() + val mangas = result.new.map(NewItem::toSManga) + return MangasPage(mangas, mangas.isNotEmpty()) + } + + // Latest + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + + override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException() + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = apiUrl.toHttpUrl().newBuilder() + .addPathSegment("search.php") + .addQueryParameter("k", query) + .addQueryParameter("p", page.toString()) + .addQueryParameter("s", "20") + return GET(url.build(), headers) + } + + override fun searchMangaParse(response: Response): MangasPage { + val result = response.parseAs() + return MangasPage(result.magazine.map(SearchItem::toSManga), true) + } + + // Manga Detail Page + + override fun mangaDetailsRequest(manga: SManga) = GET(apiUrl + manga.url, headers) + + override fun mangaDetailsParse(response: Response): SManga { + val result = response.parseAs() + if (result.content.isEmpty()) throw IllegalStateException("漫画内容解析为空!") + val item = result.content[0] + return SManga.create().apply { + title = item.magName + author = item.magName.split(" ")[0] + thumbnail_url = item.magPic + url = "/show.php?a=${item.magId}" + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + } + } + + // Manga Detail Page / Chapters Page (Separate) + + override fun chapterListRequest(manga: SManga) = GET(apiUrl + manga.url, headers) + + override fun chapterListParse(response: Response): List { + val result = response.parseAs() + if (result.content.isEmpty()) return emptyList() + val item = result.content[0] + val chapter = SChapter.create().apply { + url = "/show.php?a=${item.magId}" + name = item.magName + chapter_number = 1F + } + return listOf(chapter) + } + + // Manga View Page + + override fun pageListRequest(chapter: SChapter) = GET(apiUrl + chapter.url, headers) + + override fun pageListParse(response: Response): List { + val result = response.parseAs() + return result.content.mapIndexed { i, it -> it.toPage(i) } + } + + // Image + + // override fun imageRequest(page: Page) = GET(page.url, headers) + + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() +}