Add Zazhimi source (#9364)

* add zazhimi source

* add header

* null

* apply comments

* apply comments
This commit is contained in:
Hualiang 2025-06-25 03:32:14 +08:00 committed by Draff
parent 4e0a48fff7
commit 773613ee71
Signed by: Draff
GPG Key ID: E8A89F3211677653
8 changed files with 189 additions and 0 deletions

View File

@ -0,0 +1,8 @@
ext {
extName = 'Zazhimi'
extClass = '.Zazhimi'
extVersionCode = 1
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -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<NewItem>,
)
@Serializable
data class ShowResponse(
val status: String,
val error: String,
val content: List<ShowItem>,
)
@Serializable
data class SearchResponse(
val status: String,
val error: String,
val magazine: List<SearchItem>,
)
@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
}
}

View File

@ -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<IndexResponse>()
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<SearchResponse>()
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<ShowResponse>()
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<SChapter> {
val result = response.parseAs<ShowResponse>()
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<Page> {
val result = response.parseAs<ShowResponse>()
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()
}