diff --git a/src/zh/xfani/build.gradle b/src/zh/xfani/build.gradle new file mode 100644 index 00000000..03ea0350 --- /dev/null +++ b/src/zh/xfani/build.gradle @@ -0,0 +1,7 @@ +ext { + extName = 'Xfani' + extClass = '.Xfani' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/zh/xfani/res/mipmap-hdpi/ic_launcher.png b/src/zh/xfani/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..73206bc4 Binary files /dev/null and b/src/zh/xfani/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/zh/xfani/res/mipmap-mdpi/ic_launcher.png b/src/zh/xfani/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..6175f3a9 Binary files /dev/null and b/src/zh/xfani/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/zh/xfani/res/mipmap-xhdpi/ic_launcher.png b/src/zh/xfani/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..c73e72b3 Binary files /dev/null and b/src/zh/xfani/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/zh/xfani/res/mipmap-xxhdpi/ic_launcher.png b/src/zh/xfani/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..606c02d5 Binary files /dev/null and b/src/zh/xfani/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/zh/xfani/res/mipmap-xxxhdpi/ic_launcher.png b/src/zh/xfani/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..ae7a4dda Binary files /dev/null and b/src/zh/xfani/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Filters.kt b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Filters.kt new file mode 100644 index 00000000..17c9656b --- /dev/null +++ b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Filters.kt @@ -0,0 +1,64 @@ +package eu.kanade.tachiyomi.animeextension.zh.xfani + +import eu.kanade.tachiyomi.animesource.model.AnimeFilter + +abstract class SelectFilter(name: String, private val options: Array>) : + AnimeFilter.Select(name, options.map { it.first }.toTypedArray()) { + val selected + get() = options[state].second +} + +abstract class TagFilter(name: String, values: Array) : + SelectFilter( + name, + values.mapIndexed { index, s -> + if (index == 0) { + s to "" + } else { + s to s + } + }.toTypedArray(), + ) + +class TypeFilter( + kv: Array> = arrayOf( + "连载新番" to "1", + "完结旧番" to "2", + "剧场版" to "3", + ), +) : SelectFilter("频道", kv) + +class ClassFilter( + tags: Array = arrayOf( + "全部", + "搞笑", + "原创", + "轻小说改", + "恋爱", + "百合", + "漫改", + ), +) : TagFilter("类型", tags) + +class VersionFilter( + tags: Array = arrayOf( + "全部", + "BD", + "OVA", + "SP", + "OAD", + ), +) : TagFilter("版本", tags) + +class LetterFilter( + tags: Array = "ABCDEFGHIJKLMNOPQRSTUYWXYZ".map { it.toString() }.toMutableList() + .also { it.add("0-9") }.toTypedArray(), +) : TagFilter("字母", tags) + +class SortFilter( + kv: Array> = arrayOf( + "按最新" to "time", + "按热门" to "hits", + "按评分" to "score", + ), +) : SelectFilter("排序", kv) diff --git a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Signature.kt b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Signature.kt new file mode 100644 index 00000000..421af488 --- /dev/null +++ b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Signature.kt @@ -0,0 +1,15 @@ +package eu.kanade.tachiyomi.animeextension.zh.xfani + +import java.security.MessageDigest + +private const val UID = "DCC147D11943AF75" + +internal fun generateKey(time: Long): String { + return "DS${time}$UID".md5() +} + +internal fun String.md5(): String { + val md = MessageDigest.getInstance("MD5") + val digest = md.digest(this.toByteArray()) + return digest.joinToString(separator = "") { eachByte -> "%02x".format(eachByte) } +} diff --git a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt new file mode 100644 index 00000000..be71aeb7 --- /dev/null +++ b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt @@ -0,0 +1,212 @@ +package eu.kanade.tachiyomi.animeextension.zh.xfani + +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.animesource.model.AnimeFilter +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.util.asJsoup +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.MultipartBody +import okhttp3.Request +import okhttp3.Response +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy + +class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { + override val baseUrl: String + get() = "https://dick.xfani.com" + override val lang: String + get() = "zh" + override val name: String + get() = "稀饭动漫" + override val supportsLatest: Boolean + get() = true + + private val json by injectLazy() + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + private val numberRegex = Regex("\\d+") + + private val selectedVideoSource + get() = preferences.getString(PREF_KEY_VIDEO_SOURCE, DEFAULT_VIDEO_SOURCE)!!.toInt() + + override fun animeDetailsParse(response: Response): SAnime = SAnime.create() + + override fun episodeListParse(response: Response): List { + val jsoup = response.asJsoup() + val result = jsoup.select("ul.anthology-list-play.size") + val episodeList = if (result.size > selectedVideoSource) { + result[selectedVideoSource] + } else { + result[0] + }.select("li > a") + return episodeList.map { + SEpisode.create().apply { + name = it.text() + url = it.attr("href") + episode_number = numberRegex.find(name)?.value?.toFloat() ?: -1F + } + } + } + + override fun videoListParse(response: Response): List