forked from AlmightyHak/extensions-source
Add Anime1.me (#340)
* Add Anime.me * Add Anime.me * Add Anime.me * Add Anime.me * Add Anime.me * Add Anime.me
This commit is contained in:
parent
6d89be05aa
commit
e780630225
7 changed files with 192 additions and 0 deletions
7
src/zh/anime1/build.gradle
Normal file
7
src/zh/anime1/build.gradle
Normal file
|
@ -0,0 +1,7 @@
|
|||
ext {
|
||||
extName = 'Anime1.me'
|
||||
extClass = '.Anime1'
|
||||
extVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
BIN
src/zh/anime1/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/zh/anime1/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
src/zh/anime1/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/zh/anime1/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
src/zh/anime1/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/zh/anime1/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
BIN
src/zh/anime1/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/zh/anime1/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
src/zh/anime1/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/zh/anime1/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9 KiB |
|
@ -0,0 +1,185 @@
|
|||
package eu.kanade.tachiyomi.animeextension.zh.anime1
|
||||
|
||||
import android.webkit.CookieManager
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parseAs
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class Anime1 : AnimeHttpSource() {
|
||||
override val baseUrl: String
|
||||
get() = "https://anime1.me"
|
||||
override val lang: String
|
||||
get() = "zh-hant"
|
||||
override val name: String
|
||||
get() = "Anime1.me"
|
||||
override val supportsLatest: Boolean
|
||||
get() = true
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder().add("referer", "$baseUrl/")
|
||||
|
||||
private val videoApiUrl = "https://v.anime1.me/api"
|
||||
private val dataUrl = "https://d1zquzjgwo9yb.cloudfront.net"
|
||||
private val uploadDateFormat: SimpleDateFormat by lazy {
|
||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
|
||||
}
|
||||
private lateinit var data: JsonArray
|
||||
private val cookieManager
|
||||
get() = CookieManager.getInstance()
|
||||
|
||||
override fun animeDetailsParse(response: Response) = throw UnsupportedOperationException()
|
||||
override suspend fun getAnimeDetails(anime: SAnime): SAnime {
|
||||
return SAnime.create().apply {
|
||||
thumbnail_url = FIX_COVER
|
||||
}
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
var document: Document? = response.asJsoup()
|
||||
val episodes = mutableListOf<SEpisode>()
|
||||
val requestUrl = response.request.url.toString()
|
||||
while (document != null) {
|
||||
val items = document.select("article.post").map {
|
||||
SEpisode.create().apply {
|
||||
name = it.select(".entry-title").text()
|
||||
val url = it.selectFirst(".entry-title a")?.attr("href") ?: requestUrl
|
||||
setUrlWithoutDomain(url)
|
||||
date_upload = it.select("time.updated").attr("datetime").let { date ->
|
||||
runCatching { uploadDateFormat.parse(date)?.time }.getOrNull() ?: 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
episodes.addAll(items)
|
||||
val previousUrl = document.select(".nav-previous a").attr("href")
|
||||
document = if (previousUrl.isBlank()) {
|
||||
null
|
||||
} else {
|
||||
client.newCall(GET(previousUrl)).execute().asJsoup()
|
||||
}
|
||||
}
|
||||
return episodes
|
||||
}
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
val req = document.select("video").attr("data-apireq")
|
||||
val videoResponse: VideoResponse = client.newCall(
|
||||
POST(
|
||||
videoApiUrl,
|
||||
body = "d=$req".toRequestBody("application/x-www-form-urlencoded".toMediaTypeOrNull()),
|
||||
),
|
||||
).execute().parseAs()
|
||||
return videoResponse.s.map {
|
||||
val videoUrl = "https:${it.src}"
|
||||
val newHeaders = cookieManager.getCookie(videoUrl)?.let { cookie ->
|
||||
headers.newBuilder().add("cookie", cookie).build()
|
||||
}
|
||||
Video(videoUrl, it.type, videoUrl, headers = newHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getLatestUpdates(page: Int): AnimesPage {
|
||||
if (!this::data.isInitialized) {
|
||||
data = client.newCall(GET("$dataUrl/?_=${System.currentTimeMillis()}")).awaitSuccess()
|
||||
.parseAs()
|
||||
}
|
||||
val items = data.subList((page - 1) * PAGE_SIZE, (page * PAGE_SIZE).coerceAtMost(data.size))
|
||||
return AnimesPage(
|
||||
items.map {
|
||||
SAnime.create().apply {
|
||||
val array = it.jsonArray
|
||||
val id = array.getContent(0)!!
|
||||
url = "?cat=$id"
|
||||
title = array.getContent(1)!!
|
||||
if (id == "0" || title.contains("</a>")) {
|
||||
val doc = Jsoup.parse(title)
|
||||
doc.selectFirst("a")?.let { link ->
|
||||
url = link.attr("href")
|
||||
}
|
||||
title = doc.text()
|
||||
}
|
||||
status = if (array.getContent(2)?.contains("連載中") == true) {
|
||||
SAnime.ONGOING
|
||||
} else {
|
||||
SAnime.COMPLETED
|
||||
}
|
||||
genre = listOfNotNull(
|
||||
array.getContent(3),
|
||||
array.getContent(4),
|
||||
array.getContent(5),
|
||||
).joinToString()
|
||||
thumbnail_url = FIX_COVER
|
||||
}
|
||||
},
|
||||
items.size == PAGE_SIZE,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getPopularAnime(page: Int): AnimesPage {
|
||||
return getLatestUpdates(page)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
|
||||
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
||||
override fun popularAnimeParse(response: Response) = throw UnsupportedOperationException()
|
||||
override fun popularAnimeRequest(page: Int) = throw UnsupportedOperationException()
|
||||
|
||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||
// The search result is episode
|
||||
val document = response.asJsoup()
|
||||
val items = document.select("article.post .entry-title a").map {
|
||||
SAnime.create().apply {
|
||||
setUrlWithoutDomain(it.attr("href"))
|
||||
title = it.ownText()
|
||||
thumbnail_url = FIX_COVER
|
||||
}
|
||||
}
|
||||
val previous = document.select(".nav-previous")
|
||||
return AnimesPage(items, previous.isNotEmpty())
|
||||
}
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val url = baseUrl.toHttpUrl().newBuilder()
|
||||
if (page > 1) {
|
||||
url.addPathSegments("page/$page")
|
||||
}
|
||||
url.addQueryParameter("s", query)
|
||||
return GET(url.build())
|
||||
}
|
||||
|
||||
private fun JsonArray.getContent(index: Int): String? {
|
||||
return getOrNull(index)?.jsonPrimitive?.contentOrNull
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAGE_SIZE = 20
|
||||
const val FIX_COVER = "https://sta.anicdn.com/playerImg/8.jpg"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class VideoSource(val src: String, val type: String)
|
||||
|
||||
@Serializable
|
||||
data class VideoResponse(val s: List<VideoSource>)
|
Loading…
Add table
Add a link
Reference in a new issue