forked from Kohi-den/extensions-source
Compare commits
3 commits
f84834770d
...
fc7e6bee65
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fc7e6bee65 | ||
![]() |
ae982dcfd5 | ||
![]() |
9e4b2204bb |
4 changed files with 50 additions and 29 deletions
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Hanime1'
|
extName = 'Hanime1'
|
||||||
extClass = '.Hanime1'
|
extClass = '.Hanime1'
|
||||||
extVersionCode = 4
|
extVersionCode = 5
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.animeextension.zh.hanime1
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||||
|
@ -16,12 +17,15 @@ import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
import okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
|
@ -65,14 +69,32 @@ class Hanime1 : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun animeDetailsParse(response: Response): SAnime {
|
override fun animeDetailsParse(response: Response): SAnime {
|
||||||
val jsoup = response.asJsoup()
|
val doc = response.asJsoup()
|
||||||
return SAnime.create().apply {
|
return SAnime.create().apply {
|
||||||
genre = jsoup.select(".single-video-tag").not("[data-toggle]").eachText().joinToString()
|
genre = doc.select(".single-video-tag").not("[data-toggle]").eachText().joinToString()
|
||||||
author = jsoup.select("#video-artist-name").text()
|
author = doc.select("#video-artist-name").text()
|
||||||
jsoup.select("script[type=application/ld+json]").first()?.data()?.let {
|
doc.select("script[type=application/ld+json]").first()?.data()?.let {
|
||||||
val info = json.decodeFromString<JsonElement>(it).jsonObject
|
val info = json.decodeFromString<JsonElement>(it).jsonObject
|
||||||
title = info["name"]!!.jsonPrimitive.content
|
title = info["name"]!!.jsonPrimitive.content
|
||||||
description = info["description"]!!.jsonPrimitive.content
|
description = info["description"]!!.jsonPrimitive.content
|
||||||
|
thumbnail_url = info["thumbnailUrl"]?.jsonArray?.get(0)?.jsonPrimitive?.content
|
||||||
|
}
|
||||||
|
val type = doc.select("a#video-artist-name + a").text().trim()
|
||||||
|
if (type == "裏番" || type == "泡麵番") {
|
||||||
|
// Use the series cover image for bangumi entries instead of the episode image.
|
||||||
|
runBlocking {
|
||||||
|
try {
|
||||||
|
val animesPage =
|
||||||
|
getSearchAnime(
|
||||||
|
1,
|
||||||
|
title,
|
||||||
|
AnimeFilterList(GenreFilter(arrayOf("", type)).apply { state = 1 }),
|
||||||
|
)
|
||||||
|
thumbnail_url = animesPage.animes.first().thumbnail_url
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(name, "Failed to get bangumi cover image")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +159,7 @@ class Hanime1 : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
|
|
||||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||||
val jsoup = response.asJsoup()
|
val jsoup = response.asJsoup()
|
||||||
val nodes = jsoup.select("div.search-doujin-videos.hidden-xs")
|
val nodes = jsoup.select("div.search-doujin-videos.hidden-xs:not(:has(a[target=_blank]))")
|
||||||
val list = if (nodes.isNotEmpty()) {
|
val list = if (nodes.isNotEmpty()) {
|
||||||
nodes.map {
|
nodes.map {
|
||||||
SAnime.create().apply {
|
SAnime.create().apply {
|
||||||
|
@ -216,11 +238,12 @@ class Hanime1 : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
return chain.proceed(chain.request())
|
return chain.proceed(chain.request())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
private fun updateFilters() {
|
private fun updateFilters() {
|
||||||
filterUpdateState = FilterUpdateState.UPDATING
|
filterUpdateState = FilterUpdateState.UPDATING
|
||||||
val exceptionHandler =
|
val exceptionHandler =
|
||||||
CoroutineExceptionHandler { _, _ -> filterUpdateState = FilterUpdateState.FAILED }
|
CoroutineExceptionHandler { _, _ -> filterUpdateState = FilterUpdateState.FAILED }
|
||||||
CoroutineScope(Dispatchers.IO + exceptionHandler).launch {
|
GlobalScope.launch(Dispatchers.IO + exceptionHandler) {
|
||||||
val jsoup = client.newCall(GET("$baseUrl/search")).awaitSuccess().asJsoup()
|
val jsoup = client.newCall(GET("$baseUrl/search")).awaitSuccess().asJsoup()
|
||||||
val genreList = jsoup.select("div.genre-option div.hentai-sort-options").eachText()
|
val genreList = jsoup.select("div.genre-option div.hentai-sort-options").eachText()
|
||||||
val sortList =
|
val sortList =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Xfani'
|
extName = 'Xfani'
|
||||||
extClass = '.Xfani'
|
extClass = '.Xfani'
|
||||||
extVersionCode = 5
|
extVersionCode = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -20,8 +20,9 @@ import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
@ -53,7 +54,7 @@ enum class FilterUpdateState {
|
||||||
|
|
||||||
class Xfani : AnimeHttpSource(), ConfigurableAnimeSource {
|
class Xfani : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
override val baseUrl: String
|
override val baseUrl: String
|
||||||
get() = "https://dick.xfani.com"
|
get() = "https://dm.xifanacg.com"
|
||||||
override val lang: String
|
override val lang: String
|
||||||
get() = "zh"
|
get() = "zh"
|
||||||
override val name: String
|
override val name: String
|
||||||
|
@ -117,10 +118,16 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun animeDetailsParse(response: Response): SAnime {
|
override fun animeDetailsParse(response: Response): SAnime {
|
||||||
val jsoup = response.asJsoup()
|
val doc = response.asJsoup()
|
||||||
return SAnime.create().apply {
|
return SAnime.create().apply {
|
||||||
description = jsoup.select("#height_limit.text").text()
|
description = doc.select("#height_limit.text").text()
|
||||||
title = jsoup.select(".slide-info-title").text()
|
title = doc.select(".slide-info-title").text()
|
||||||
|
author = doc.select(".slide-info:contains(导演 :)").text().removePrefix("导演 :")
|
||||||
|
.removeSuffix(",")
|
||||||
|
artist = doc.select(".slide-info:contains(演员 :)").text().removePrefix("演员 :")
|
||||||
|
.removeSuffix(",")
|
||||||
|
genre = doc.select(".slide-info:contains(类型 :)").text().removePrefix("类型 :")
|
||||||
|
.removeSuffix(",").replace(",", ", ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,19 +241,13 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
return vodListToAnimePageList(response)
|
return vodListToAnimePageList(response)
|
||||||
}
|
}
|
||||||
val jsoup = response.asJsoup()
|
val jsoup = response.asJsoup()
|
||||||
val items = jsoup.select("div.public-list-box.search-box.flex.rel")
|
val items = jsoup.select("div.search-list")
|
||||||
val animeList = items.map { item ->
|
val animeList = items.map { item ->
|
||||||
SAnime.create().apply {
|
SAnime.create().apply {
|
||||||
title = item.select(".thumb-txt").text()
|
title = item.select("div.detail-info > a").text()
|
||||||
url = item.select("div.left.public-list-bj a.public-list-exp").attr("href")
|
url = item.select("div.detail-info > a").attr("href")
|
||||||
thumbnail_url =
|
thumbnail_url =
|
||||||
item.select("div.left.public-list-bj img[data-src]").attr("data-src")
|
item.select("div.detail-pic img[data-src]").attr("data-src")
|
||||||
author = item.select("div.thumb-actor").text().removeSuffix("/")
|
|
||||||
artist = item.select("div.thumb-director").text().removeSuffix("/")
|
|
||||||
description = item.select(".thumb-blurb").text()
|
|
||||||
genre = item.select("div.thumb-else").text()
|
|
||||||
val statusString = item.select("div.left.public-list-bj .public-list-prb").text()
|
|
||||||
status = STATUS_STR_MAPPING.getOrElse(statusString) { SAnime.ONGOING }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tip = jsoup.select("div.pages div.page-tip").text()
|
val tip = jsoup.select("div.pages div.page-tip").text()
|
||||||
|
@ -259,12 +260,13 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
return numbers.size == 2 && numbers[0] != numbers[1]
|
return numbers.size == 2 && numbers[0] != numbers[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
private fun updateFilter() {
|
private fun updateFilter() {
|
||||||
filterState = FilterUpdateState.UPDATING
|
filterState = FilterUpdateState.UPDATING
|
||||||
val handler = CoroutineExceptionHandler { _, _ ->
|
val handler = CoroutineExceptionHandler { _, _ ->
|
||||||
filterState = FilterUpdateState.FAILED
|
filterState = FilterUpdateState.FAILED
|
||||||
}
|
}
|
||||||
CoroutineScope(Dispatchers.IO + handler).launch {
|
GlobalScope.launch(Dispatchers.IO + handler) {
|
||||||
val jsoup = client.newCall(GET("$baseUrl/show/1/html")).awaitSuccess().asJsoup()
|
val jsoup = client.newCall(GET("$baseUrl/show/1/html")).awaitSuccess().asJsoup()
|
||||||
// update class and year filter type
|
// update class and year filter type
|
||||||
val classList = jsoup.select("li[data-type=class]").eachAttr("data-val")
|
val classList = jsoup.select("li[data-type=class]").eachAttr("data-val")
|
||||||
|
@ -393,9 +395,5 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource {
|
||||||
const val PREF_KEY_FILTER_YEAR = "PREF_KEY_FILTER_YEAR"
|
const val PREF_KEY_FILTER_YEAR = "PREF_KEY_FILTER_YEAR"
|
||||||
|
|
||||||
const val DEFAULT_VIDEO_SOURCE = "0"
|
const val DEFAULT_VIDEO_SOURCE = "0"
|
||||||
|
|
||||||
val STATUS_STR_MAPPING = mapOf(
|
|
||||||
"已完结" to SAnime.COMPLETED,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue