├── .github └── workflows │ └── apk.yml ├── .gitignore ├── README.md ├── build.gradle.kts ├── extension-app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── top │ │ └── phj233 │ │ └── easybangumi_extension_gugufan │ │ ├── CartoonFactory.kt │ │ ├── cycanime │ │ ├── CycanimeSource.kt │ │ └── component │ │ │ ├── CycanimeDetailedComponent.kt │ │ │ ├── CycanimePageComponent.kt │ │ │ ├── CycanimePlayComponent.kt │ │ │ ├── CycanimePreferenceComponent.kt │ │ │ ├── CycanimeSearchComponent.kt │ │ │ └── CycanimeUpdateComponent.kt │ │ ├── gugufan │ │ ├── GuguFanSource.kt │ │ └── component │ │ │ ├── GuguFanDetailedComponent.kt │ │ │ ├── GuguFanPageComponent.kt │ │ │ ├── GuguFanPlayComponent.kt │ │ │ ├── GuguFanPreferenceComponent.kt │ │ │ ├── GuguFanSearchComponent.kt │ │ │ └── GuguFanUpdateComponent.kt │ │ ├── nyafun │ │ ├── NyafunSource.kt │ │ └── component │ │ │ ├── NyafunDetailedComponent.kt │ │ │ ├── NyafunPageComponent.kt │ │ │ ├── NyafunPlayComponent.kt │ │ │ ├── NyafunPreferenceComponent.kt │ │ │ ├── NyafunSearchComponent.kt │ │ │ └── NyafunUpdateComponent.kt │ │ └── util │ │ └── CartoonUtil.kt │ └── res │ └── drawable │ ├── cycanime.png │ ├── gugufan.png │ └── nyafun.png ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/workflows/apk.yml: -------------------------------------------------------------------------------- 1 | name: Build and Release APK 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | - 'v*' 7 | jobs: 8 | sign-and-release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Set up JDK 17 14 | uses: actions/setup-java@v3 15 | with: 16 | distribution: 'temurin' 17 | java-version: '17' 18 | - name: Setup Gradle 19 | uses: gradle/gradle-build-action@v3 20 | env: 21 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 22 | KEY_STORE_PWD: ${{ secrets.KEY_STORE_PWD }} 23 | KEY_ALIAS: ${{ secrets.KEY_ALIAS }} 24 | KEY_PWD: ${{ secrets.KEY_PWD }} 25 | with: 26 | gradle-version: 8.7 27 | arguments: assembleRelease 28 | - name: Sign APK 29 | uses: r0adkll/sign-android-release@v1 30 | env: 31 | BUILD_TOOLS_VERSION: "34.0.0" 32 | with: 33 | releaseDirectory: extension-app/build/outputs/apk/release 34 | signingKeyBase64: ${{ secrets.SIGNING_KEY }} 35 | alias: ${{ secrets.KEY_ALIAS }} 36 | keyStorePassword: ${{ secrets.KEY_STORE_PWD }} 37 | keyPassword: ${{ secrets.KEY_PWD }} 38 | - name: Release APK 39 | uses: ncipollo/release-action@v1 40 | with: 41 | artifacts: "extension-app/build/outputs/apk/release/*.apk" 42 | token: ${{ github.token }} 43 | generateReleaseNotes: true 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /captures 7 | .externalNativeBuild 8 | .cxx 9 | local.properties 10 | /gradle 11 | /extension-app/proguard-rules.pro 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 纯纯看番番剧源 2 | 3 | 需要安装 `纯纯看番` 使用,目前支持 `5.4.1` 后的版本 4 | 5 | [EasyBangumi: 纯纯看番(github.com)](https://github.com/easybangumiorg/EasyBangumi) 6 | 7 | 内含 8 | 9 | - [咕咕番 (www.gugufan.com)](https://www.gugufan.com/) 10 | - [次元城动漫 (www.cycanime.com)](https://www.cycanime.com/) 11 | - [Nyafun (www.nyafun.net)](https://www.nyafun.net/) 12 | 13 | # 免责声明 14 | 本项目仅供个人学习和技术研究之用,不得用于任何商业目的。作者不对因使用本项目而导致的任何直接或间接损失承担责任,包括但不限于数据丢失、利润损失或其他附带损害。 15 | 16 | 使用本项目前,请确保您已充分理解其功能和可能存在的风险。对于不正当使用本项目造成的一切后果,作者概不负责。 17 | 18 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id("com.android.application") version "8.5.2" apply false 4 | id("org.jetbrains.kotlin.android") version "1.9.23" apply false 5 | } 6 | -------------------------------------------------------------------------------- /extension-app/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | !build/outputs/apk/release 3 | /src/keystore.properties 4 | -------------------------------------------------------------------------------- /extension-app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName 3 | import java.util.* 4 | 5 | plugins { 6 | id("com.android.application") 7 | id("org.jetbrains.kotlin.android") 8 | } 9 | val keystoreProperties = Properties().apply { 10 | // 尝试从文件加载密钥库信息 11 | try { 12 | load(file("src/keystore.properties").reader()) 13 | } catch (e: Exception) { 14 | // 如果文件不存在,则使用环境变量 15 | setProperty("storeFile", System.getenv("SIGNING_KEY")) 16 | setProperty("storePassword", System.getenv("KEY_STORE_PWD")) 17 | setProperty("keyAlias", System.getenv("KEY_ALIAS")) 18 | setProperty("keyPassword", System.getenv("KEY_PWD")) 19 | } 20 | } 21 | 22 | android { 23 | namespace = "top.phj233.easybangumi_extension_gugufan" 24 | compileSdk = 34 25 | 26 | signingConfigs { 27 | create("release"){ 28 | storeFile = file(keystoreProperties.getProperty("storeFile")) 29 | storePassword = keystoreProperties.getProperty("storePassword") 30 | keyAlias = keystoreProperties.getProperty("keyAlias") 31 | keyPassword = keystoreProperties.getProperty("keyPassword") 32 | } 33 | } 34 | defaultConfig { 35 | applicationId = "top.phj233.easybangumi_extension_gugufan" 36 | minSdk = 24 37 | targetSdk = 34 38 | versionCode = 11 39 | versionName = "1.3.4" 40 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 41 | archivesName.set("纯纯看番咕咕番插件_${versionName}") 42 | } 43 | 44 | buildTypes { 45 | release { 46 | isMinifyEnabled = false 47 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 48 | signingConfig = signingConfigs.getByName("release") 49 | } 50 | } 51 | compileOptions { 52 | sourceCompatibility = JavaVersion.VERSION_17 53 | targetCompatibility = JavaVersion.VERSION_17 54 | } 55 | kotlinOptions { 56 | jvmTarget = "17" 57 | } 58 | dependenciesInfo{ 59 | includeInApk = false 60 | includeInBundle = false 61 | } 62 | } 63 | 64 | dependencies { 65 | compileOnly("io.github.easybangumiorg:extension-api:1.11-SNAPSHOT") 66 | implementation("org.jsoup:jsoup:1.18.1") 67 | } 68 | repositories { 69 | } 70 | -------------------------------------------------------------------------------- /extension-app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /extension-app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 13 | 16 | 19 | 20 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/CartoonFactory.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan 2 | 3 | import com.heyanle.easybangumi4.source_api.Source 4 | import com.heyanle.easybangumi4.source_api.SourceFactory 5 | import top.phj233.easybangumi_extension_gugufan.cycanime.CycanimeSource 6 | import top.phj233.easybangumi_extension_gugufan.gugufan.GuguFanSource 7 | import top.phj233.easybangumi_extension_gugufan.nyafun.NyafunSource 8 | 9 | @Suppress("unused") 10 | class CartoonFactory: SourceFactory { 11 | override fun create(): List { 12 | return listOf( 13 | GuguFanSource(), 14 | CycanimeSource(), 15 | NyafunSource() 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/cycanime/CycanimeSource.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.cycanime 2 | 3 | import com.heyanle.easybangumi4.source_api.utils.api.WebViewHelperV2 4 | import com.heyanle.extension_api.ExtensionIconSource 5 | import com.heyanle.extension_api.ExtensionSource 6 | import top.phj233.easybangumi_extension_gugufan.R 7 | import top.phj233.easybangumi_extension_gugufan.cycanime.component.* 8 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 9 | import kotlin.reflect.KClass 10 | 11 | class CycanimeSource : ExtensionSource(), ExtensionIconSource { 12 | override val describe: String 13 | get() = "纯纯看番-次元城动漫源" 14 | override val label: String 15 | get() = "次元城动漫" 16 | override val version: String 17 | get() = "1.2" 18 | override val versionCode: Int 19 | get() = 11 20 | 21 | override fun getIconResourcesId(): Int { 22 | return R.drawable.cycanime 23 | } 24 | override val sourceKey: String 25 | get() = "cycanime" 26 | 27 | override fun register(): List> { 28 | return listOf( 29 | CycanimePageComponent::class, 30 | CycanimeDetailedComponent::class, 31 | CycanimePlayComponent::class, 32 | CycanimeSearchComponent::class, 33 | CycanimeUpdateComponent::class, 34 | CartoonUtil::class, 35 | CycanimePreferenceComponent::class, 36 | WebViewHelperV2::class 37 | ) 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/cycanime/component/CycanimeDetailedComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.cycanime.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.detailed.DetailedComponent 6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon 7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary 8 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 9 | import com.heyanle.easybangumi4.source_api.withResult 10 | 11 | import kotlinx.coroutines.Dispatchers 12 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 13 | 14 | class CycanimeDetailedComponent(private val cartoonUtil: CartoonUtil) : ComponentWrapper(), DetailedComponent { 15 | override suspend fun getAll(summary: CartoonSummary): SourceResult>> { 16 | return withResult(Dispatchers.IO) { 17 | val cartoon = cartoonUtil.getCartoonDetailById(source,summary.id) 18 | val playLine = cartoonUtil.getPlayLineById(source,summary.id) 19 | Pair(cartoon, playLine) 20 | } 21 | } 22 | 23 | override suspend fun getDetailed(summary: CartoonSummary): SourceResult { 24 | return withResult(Dispatchers.IO) { 25 | cartoonUtil.getCartoonDetailById(source,summary.id) 26 | } 27 | } 28 | 29 | override suspend fun getPlayLine(summary: CartoonSummary): SourceResult> { 30 | return withResult(Dispatchers.IO) { 31 | cartoonUtil.getPlayLineById(source,summary.id) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/cycanime/component/CycanimePageComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.cycanime.component 2 | 3 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 4 | import com.heyanle.easybangumi4.source_api.component.page.PageComponent 5 | import com.heyanle.easybangumi4.source_api.component.page.SourcePage 6 | import com.heyanle.easybangumi4.source_api.withResult 7 | import kotlinx.coroutines.Dispatchers 8 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 9 | 10 | class CycanimePageComponent(private val cartoonUtil: CartoonUtil) : ComponentWrapper(), PageComponent { 11 | override fun getPages(): List { 12 | return listOf( 13 | SourcePage.Group("首页", false) { 14 | withResult(Dispatchers.IO) { 15 | homePage() 16 | } 17 | }, 18 | SourcePage.SingleCartoonPage.WithCover("最近更新", { 1 }){ 19 | withResult(Dispatchers.IO){ 20 | cartoonUtil.getRecentUpdate(source) 21 | } 22 | }, 23 | SourcePage.Group("周番剧表", false) { 24 | withResult(Dispatchers.IO) { 25 | cartoonUtil.weeklyCartoonGroup(source) 26 | } 27 | }, 28 | SourcePage.Group("最新", false){ 29 | withResult(Dispatchers.IO) { 30 | newest() 31 | } 32 | } 33 | ) 34 | } 35 | 36 | private fun homePage(): List { 37 | val pageTab = arrayListOf("推荐", "TV动画", "剧场动画") 38 | return cartoonUtil.createHomePage(pageTab, source) 39 | } 40 | 41 | private fun newest(): List { 42 | val tab = arrayListOf("TV动画", "剧场版", "4K专区") 43 | val tabNum = arrayListOf(20,21,26) 44 | return cartoonUtil.createNewestPage(tab, tabNum, source) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/cycanime/component/CycanimePlayComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.cycanime.component 2 | 3 | import android.util.Log 4 | import com.heyanle.easybangumi4.source_api.SourceResult 5 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 6 | import com.heyanle.easybangumi4.source_api.component.play.PlayComponent 7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary 8 | import com.heyanle.easybangumi4.source_api.entity.Episode 9 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 10 | import com.heyanle.easybangumi4.source_api.entity.PlayerInfo 11 | import com.heyanle.easybangumi4.source_api.withResult 12 | import kotlinx.coroutines.Dispatchers 13 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 14 | 15 | class CycanimePlayComponent( 16 | private val cartoonUtil: CartoonUtil 17 | ) : ComponentWrapper(), PlayComponent { 18 | override suspend fun getPlayInfo( 19 | summary: CartoonSummary, 20 | playLine: PlayLine, 21 | episode: Episode 22 | ): SourceResult { 23 | val cartoonPageUrl = "${cartoonUtil.cycUrl}/watch/${summary.id}/${playLine.id}/${episode.id}.html" 24 | Log.i("CycanimePlayComponent", "cartoonPageUrl: $cartoonPageUrl") 25 | return withResult(Dispatchers.IO) { 26 | cartoonUtil.interceptVideoUrl(cartoonPageUrl).apply { 27 | // this.header = mapOf("Referer" to cartoonUtil.cycUrl.replace("www", "play")) 28 | } 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/cycanime/component/CycanimePreferenceComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.cycanime.component 2 | 3 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 4 | import com.heyanle.easybangumi4.source_api.component.preference.PreferenceComponent 5 | import com.heyanle.easybangumi4.source_api.component.preference.SourcePreference 6 | 7 | class CycanimePreferenceComponent: ComponentWrapper(), PreferenceComponent { 8 | override fun register(): List { 9 | return listOf(SourcePreference.Edit("Cycanime网址 别带/","baseUrl","https://www.cycani.org")) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/cycanime/component/CycanimeSearchComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.cycanime.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.search.SearchComponent 6 | import com.heyanle.easybangumi4.source_api.entity.CartoonCover 7 | import com.heyanle.easybangumi4.source_api.withResult 8 | import kotlinx.coroutines.Dispatchers 9 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 10 | 11 | class CycanimeSearchComponent(private val cartoonUtil: CartoonUtil): ComponentWrapper(), SearchComponent { 12 | override fun getFirstSearchKey(keyword: String): Int { 13 | return 1 14 | } 15 | 16 | override suspend fun search(pageKey: Int, keyword: String): SourceResult>> { 17 | return withResult(Dispatchers.IO) { 18 | cartoonUtil.createSearchPage(source,keyword, pageKey) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/cycanime/component/CycanimeUpdateComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.cycanime.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.update.UpdateComponent 6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon 7 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 8 | import com.heyanle.easybangumi4.source_api.withResult 9 | import kotlinx.coroutines.Dispatchers 10 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 11 | 12 | class CycanimeUpdateComponent(private val cartoonUtil: CartoonUtil): ComponentWrapper(), UpdateComponent { 13 | override suspend fun update(cartoon: Cartoon, oldPlayLine: List): SourceResult { 14 | return withResult(Dispatchers.IO) { 15 | val newPlayLine = cartoonUtil.getPlayLineById(source, cartoon.id) 16 | if (newPlayLine.size > oldPlayLine.size) { 17 | cartoon.isUpdate = true 18 | } 19 | cartoon 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/gugufan/GuguFanSource.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.gugufan 2 | 3 | import com.heyanle.easybangumi4.source_api.utils.api.WebViewHelperV2 4 | import com.heyanle.extension_api.ExtensionIconSource 5 | import com.heyanle.extension_api.ExtensionSource 6 | import top.phj233.easybangumi_extension_gugufan.R 7 | import top.phj233.easybangumi_extension_gugufan.gugufan.component.* 8 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 9 | import kotlin.reflect.KClass 10 | 11 | class GuguFanSource : ExtensionSource(), ExtensionIconSource { 12 | override val describe: String 13 | get() = "纯纯看番-咕咕番剧源" 14 | override val label: String 15 | get() = "咕咕番" 16 | override val version: String 17 | get() = "1.1" 18 | override val versionCode: Int 19 | get() = 11 20 | override val sourceKey: String 21 | get() = "gugufan" 22 | 23 | override fun getIconResourcesId(): Int { 24 | return R.drawable.gugufan 25 | } 26 | 27 | override fun register(): List> { 28 | return listOf( 29 | GuguFanPageComponent::class, 30 | GuguFanDetailedComponent::class, 31 | GuguFanPlayComponent::class, 32 | GuguFanSearchComponent::class, 33 | GuguFanUpdateComponent::class, 34 | CartoonUtil::class, 35 | GuguFanPreferenceComponent::class, 36 | WebViewHelperV2::class 37 | ) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/gugufan/component/GuguFanDetailedComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.gugufan.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.detailed.DetailedComponent 6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon 7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary 8 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 9 | import com.heyanle.easybangumi4.source_api.withResult 10 | import kotlinx.coroutines.Dispatchers 11 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 12 | 13 | class GuguFanDetailedComponent(private val cartoonUtil: CartoonUtil) : ComponentWrapper(), DetailedComponent { 14 | override suspend fun getAll(summary: CartoonSummary): SourceResult>> { 15 | return withResult(Dispatchers.IO) { 16 | val cartoon = cartoonUtil.getCartoonDetailById(source,summary.id) 17 | val playLine = cartoonUtil.getPlayLineById(source,summary.id) 18 | Pair(cartoon, playLine) 19 | } 20 | } 21 | 22 | override suspend fun getDetailed(summary: CartoonSummary): SourceResult { 23 | return withResult(Dispatchers.IO) { 24 | cartoonUtil.getCartoonDetailById(source,summary.id) 25 | } 26 | } 27 | 28 | override suspend fun getPlayLine(summary: CartoonSummary): SourceResult> { 29 | return withResult(Dispatchers.IO) { 30 | cartoonUtil.getPlayLineById(source,summary.id) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/gugufan/component/GuguFanPageComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.gugufan.component 2 | 3 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 4 | import com.heyanle.easybangumi4.source_api.component.page.PageComponent 5 | import com.heyanle.easybangumi4.source_api.component.page.SourcePage 6 | import com.heyanle.easybangumi4.source_api.entity.CartoonCover 7 | import com.heyanle.easybangumi4.source_api.entity.CartoonCoverImpl 8 | import com.heyanle.easybangumi4.source_api.withResult 9 | import kotlinx.coroutines.Dispatchers 10 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 11 | 12 | class GuguFanPageComponent(private val cartoonUtil: CartoonUtil) : ComponentWrapper(), PageComponent { 13 | override fun getPages(): List { 14 | return listOf( 15 | SourcePage.SingleCartoonPage.WithCover("最近更新", { 1 }){ 16 | withResult(Dispatchers.IO){ 17 | recentUpdate() 18 | } 19 | }, 20 | SourcePage.Group("番剧周期表", false){ 21 | withResult(Dispatchers.IO){ 22 | cartoonUtil.weeklyCartoonGroup(source) 23 | } 24 | }, 25 | SourcePage.Group("最新", false){ 26 | withResult(Dispatchers.IO){ 27 | newest() 28 | } 29 | }, 30 | SourcePage.SingleCartoonPage.WithCover("优质精选",{ 1 }){ 31 | withResult(Dispatchers.IO){ 32 | excellent() 33 | } 34 | } 35 | 36 | ) 37 | } 38 | 39 | 40 | private fun recentUpdate(): Pair> { 41 | return cartoonUtil.getRecentUpdate(source) 42 | } 43 | 44 | private fun newest(): List { 45 | val tab = arrayListOf("连载新番", "完结动画", "动漫电影", "特摄动画", "动漫PV") 46 | val tabNum = arrayListOf(6, 7, 21, 23, 28) 47 | return cartoonUtil.createNewestPage(tab, tabNum, source) 48 | } 49 | 50 | private fun excellent(): Pair> { 51 | val cartoons = arrayListOf() 52 | val excellentElements = cartoonUtil.getExcellentElement() 53 | excellentElements.forEach { 54 | cartoons.add( 55 | CartoonCoverImpl( 56 | id = cartoonUtil.getCartoonId(source,it.attr("href")), 57 | title = it.attr("title"), 58 | url = cartoonUtil.guguUrl + it.attr("href"), 59 | coverUrl = it.getElementsByTag("img").attr("data-src") , 60 | source = source.key 61 | ) 62 | ) 63 | } 64 | return Pair(null, cartoons) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/gugufan/component/GuguFanPlayComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.gugufan.component 2 | 3 | import android.util.Log 4 | import com.heyanle.easybangumi4.source_api.SourceResult 5 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 6 | import com.heyanle.easybangumi4.source_api.component.play.PlayComponent 7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary 8 | import com.heyanle.easybangumi4.source_api.entity.Episode 9 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 10 | import com.heyanle.easybangumi4.source_api.entity.PlayerInfo 11 | import com.heyanle.easybangumi4.source_api.withResult 12 | import kotlinx.coroutines.Dispatchers 13 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 14 | 15 | class GuguFanPlayComponent( 16 | private val cartoonUtil: CartoonUtil 17 | ) : ComponentWrapper(), PlayComponent { 18 | override suspend fun getPlayInfo( 19 | summary: CartoonSummary, 20 | playLine: PlayLine, 21 | episode: Episode 22 | ): SourceResult { 23 | return withResult(Dispatchers.IO) { 24 | val guguFanHtml = cartoonUtil.getPlayerPageHtml(summary.id, playLine.id, episode.id) 25 | val cartoonUrl = cartoonUtil.extractVideoUrl(guguFanHtml) 26 | Log.i("GuguFanPlayComponent", "cartoonM3U8Url: $cartoonUrl") 27 | PlayerInfo( 28 | uri = cartoonUrl, 29 | decodeType = if (cartoonUrl.contains("m3u8")) PlayerInfo.DECODE_TYPE_HLS else PlayerInfo.DECODE_TYPE_OTHER 30 | ) 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/gugufan/component/GuguFanPreferenceComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.gugufan.component 2 | 3 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 4 | import com.heyanle.easybangumi4.source_api.component.preference.PreferenceComponent 5 | import com.heyanle.easybangumi4.source_api.component.preference.SourcePreference 6 | 7 | class GuguFanPreferenceComponent: ComponentWrapper(), PreferenceComponent { 8 | override fun register(): List { 9 | return listOf( SourcePreference.Edit("咕咕番动漫网址 别带/","baseUrl","https://www.gugu3.com")) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/gugufan/component/GuguFanSearchComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.gugufan.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.search.SearchComponent 6 | import com.heyanle.easybangumi4.source_api.entity.CartoonCover 7 | import com.heyanle.easybangumi4.source_api.withResult 8 | import kotlinx.coroutines.Dispatchers 9 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 10 | 11 | 12 | class GuguFanSearchComponent(private val cartoonUtil: CartoonUtil): ComponentWrapper(), SearchComponent { 13 | override fun getFirstSearchKey(keyword: String): Int { 14 | return 1 15 | } 16 | 17 | override suspend fun search(pageKey: Int, keyword: String): SourceResult>> { 18 | return withResult(Dispatchers.IO) { 19 | cartoonUtil.createSearchPage(source,keyword, pageKey) 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/gugufan/component/GuguFanUpdateComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.gugufan.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.update.UpdateComponent 6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon 7 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 8 | import com.heyanle.easybangumi4.source_api.withResult 9 | import kotlinx.coroutines.Dispatchers 10 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 11 | 12 | class GuguFanUpdateComponent(private val cartoonUtil: CartoonUtil): ComponentWrapper(), UpdateComponent { 13 | override suspend fun update(cartoon: Cartoon, oldPlayLine: List): SourceResult { 14 | return withResult(Dispatchers.IO) { 15 | val newPlayLine = cartoonUtil.getPlayLineById(source, cartoon.id) 16 | if (newPlayLine.size > oldPlayLine.size) { 17 | cartoon.isUpdate = true 18 | } 19 | cartoon 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/nyafun/NyafunSource.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.nyafun 2 | 3 | import com.heyanle.easybangumi4.source_api.utils.api.WebViewHelperV2 4 | import com.heyanle.extension_api.ExtensionIconSource 5 | import com.heyanle.extension_api.ExtensionSource 6 | import top.phj233.easybangumi_extension_gugufan.R 7 | import top.phj233.easybangumi_extension_gugufan.nyafun.component.* 8 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 9 | import kotlin.reflect.KClass 10 | 11 | class NyafunSource: ExtensionSource(), ExtensionIconSource { 12 | override val describe: String 13 | get() = "纯纯看番-Nyafun动漫源" 14 | override val label: String 15 | get() = "Nyafun" 16 | override val version: String 17 | get() = "1.2" 18 | override val versionCode: Int 19 | get() = 11 20 | 21 | override fun getIconResourcesId(): Int { 22 | return R.drawable.nyafun 23 | } 24 | 25 | override val sourceKey: String 26 | get() = "nyafun" 27 | override fun register(): List> { 28 | return listOf( 29 | NyafunPageComponent::class, 30 | NyafunDetailedComponent::class, 31 | NyafunPlayComponent::class, 32 | NyafunSearchComponent::class, 33 | NyafunUpdateComponent::class, 34 | CartoonUtil::class, 35 | NyafunPreferenceComponent::class, 36 | WebViewHelperV2::class 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/nyafun/component/NyafunDetailedComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.nyafun.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.detailed.DetailedComponent 6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon 7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary 8 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 9 | import com.heyanle.easybangumi4.source_api.withResult 10 | import kotlinx.coroutines.Dispatchers 11 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 12 | 13 | class NyafunDetailedComponent(private val cartoonUtil: CartoonUtil) : ComponentWrapper(), DetailedComponent { 14 | override suspend fun getAll(summary: CartoonSummary): SourceResult>> { 15 | return withResult(Dispatchers.IO){ 16 | val cartoon = cartoonUtil.getCartoonDetailById(source,summary.id) 17 | val playLine = cartoonUtil.getPlayLineById(source,summary.id) 18 | Pair(cartoon, playLine) 19 | } 20 | } 21 | 22 | override suspend fun getDetailed(summary: CartoonSummary): SourceResult { 23 | return withResult(Dispatchers.IO) { 24 | cartoonUtil.getCartoonDetailById(source,summary.id) 25 | } 26 | } 27 | 28 | override suspend fun getPlayLine(summary: CartoonSummary): SourceResult> { 29 | return withResult(Dispatchers.IO) { 30 | cartoonUtil.getPlayLineById(source,summary.id) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/nyafun/component/NyafunPageComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.nyafun.component 2 | 3 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 4 | import com.heyanle.easybangumi4.source_api.component.page.PageComponent 5 | import com.heyanle.easybangumi4.source_api.component.page.SourcePage 6 | import com.heyanle.easybangumi4.source_api.withResult 7 | import kotlinx.coroutines.Dispatchers 8 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 9 | 10 | class NyafunPageComponent(private val cartoonUtil: CartoonUtil) : ComponentWrapper(), PageComponent { 11 | override fun getPages(): List { 12 | return listOf( 13 | SourcePage.Group("首页", false) { 14 | withResult(Dispatchers.IO) { 15 | val tab = arrayListOf("本月热门", "番剧", "剧场") 16 | cartoonUtil.createHomePage(tab, source) 17 | } 18 | }, 19 | SourcePage.SingleCartoonPage.WithCover("最近更新", { 1 }){ 20 | withResult(Dispatchers.IO){ 21 | cartoonUtil.getRecentUpdate(source) 22 | } 23 | }, 24 | SourcePage.Group("周番剧表", false) { 25 | withResult(Dispatchers.IO) { 26 | cartoonUtil.weeklyCartoonGroup(source) 27 | } 28 | }, 29 | SourcePage.Group("番剧", false){ 30 | withResult(Dispatchers.IO) { 31 | cartoonUtil.createNyaAnimeGroup(source, "1") 32 | } 33 | }, 34 | SourcePage.Group("剧场", false){ 35 | withResult(Dispatchers.IO) { 36 | cartoonUtil.createNyaAnimeGroup(source, "2") 37 | } 38 | }, 39 | SourcePage.Group("特摄", false){ 40 | withResult(Dispatchers.IO) { 41 | cartoonUtil.createNyaAnimeGroup(source, "207") 42 | } 43 | }, 44 | ) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/nyafun/component/NyafunPlayComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.nyafun.component 2 | 3 | import android.util.Log 4 | import com.heyanle.easybangumi4.source_api.SourceResult 5 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 6 | import com.heyanle.easybangumi4.source_api.component.play.PlayComponent 7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary 8 | import com.heyanle.easybangumi4.source_api.entity.Episode 9 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 10 | import com.heyanle.easybangumi4.source_api.entity.PlayerInfo 11 | import com.heyanle.easybangumi4.source_api.withResult 12 | import kotlinx.coroutines.Dispatchers 13 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 14 | 15 | class NyafunPlayComponent( 16 | private val cartoonUtil: CartoonUtil 17 | ) : ComponentWrapper(), PlayComponent { 18 | override suspend fun getPlayInfo( 19 | summary: CartoonSummary, 20 | playLine: PlayLine, 21 | episode: Episode 22 | ): SourceResult { 23 | val cartoonPageUrl = "${cartoonUtil.nyaUrl}/play/${summary.id}-${playLine.id}-${episode.id}.html" 24 | Log.i("NyafunPlayComponent", "cartoonPageUrl: $cartoonPageUrl") 25 | return withResult(Dispatchers.IO) { 26 | cartoonUtil.interceptVideoUrl(cartoonPageUrl).apply { 27 | this.header = mapOf("Referer" to cartoonUtil.nyaUrl.replace("www", "play")) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/nyafun/component/NyafunPreferenceComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.nyafun.component 2 | 3 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 4 | import com.heyanle.easybangumi4.source_api.component.preference.PreferenceComponent 5 | import com.heyanle.easybangumi4.source_api.component.preference.SourcePreference 6 | 7 | class NyafunPreferenceComponent:ComponentWrapper(),PreferenceComponent { 8 | override fun register(): List { 9 | return listOf(SourcePreference.Edit("Nyafun网址 别带/","baseUrl","https://www.nyacg.net")) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/nyafun/component/NyafunSearchComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.nyafun.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.search.SearchComponent 6 | import com.heyanle.easybangumi4.source_api.entity.CartoonCover 7 | import com.heyanle.easybangumi4.source_api.withResult 8 | import kotlinx.coroutines.Dispatchers 9 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 10 | 11 | class NyafunSearchComponent(private val cartoonUtil: CartoonUtil): ComponentWrapper(), SearchComponent { 12 | override fun getFirstSearchKey(keyword: String): Int { 13 | return 1 14 | } 15 | 16 | override suspend fun search(pageKey: Int, keyword: String): SourceResult>> { 17 | return withResult(Dispatchers.IO) { 18 | cartoonUtil.createSearchPage(source,keyword, pageKey) 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/nyafun/component/NyafunUpdateComponent.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.nyafun.component 2 | 3 | import com.heyanle.easybangumi4.source_api.SourceResult 4 | import com.heyanle.easybangumi4.source_api.component.ComponentWrapper 5 | import com.heyanle.easybangumi4.source_api.component.update.UpdateComponent 6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon 7 | import com.heyanle.easybangumi4.source_api.entity.PlayLine 8 | import com.heyanle.easybangumi4.source_api.withResult 9 | import kotlinx.coroutines.Dispatchers 10 | import top.phj233.easybangumi_extension_gugufan.util.CartoonUtil 11 | 12 | class NyafunUpdateComponent(private val cartoonUtil: CartoonUtil): ComponentWrapper(), UpdateComponent { 13 | override suspend fun update(cartoon: Cartoon, oldPlayLine: List): SourceResult { 14 | return withResult(Dispatchers.IO) { 15 | val newPlayLine = cartoonUtil.getPlayLineById(source, cartoon.id) 16 | if (newPlayLine.size > oldPlayLine.size) { 17 | cartoon.isUpdate = true 18 | } 19 | cartoon 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /extension-app/src/main/java/top/phj233/easybangumi_extension_gugufan/util/CartoonUtil.kt: -------------------------------------------------------------------------------- 1 | package top.phj233.easybangumi_extension_gugufan.util 2 | 3 | import android.util.Log 4 | import com.google.gson.Gson 5 | import com.heyanle.easybangumi4.source_api.Source 6 | import com.heyanle.easybangumi4.source_api.SourceResult 7 | import com.heyanle.easybangumi4.source_api.component.page.SourcePage 8 | import com.heyanle.easybangumi4.source_api.entity.* 9 | import com.heyanle.easybangumi4.source_api.utils.api.PreferenceHelper 10 | import com.heyanle.easybangumi4.source_api.utils.api.StringHelper 11 | import com.heyanle.easybangumi4.source_api.utils.api.WebViewHelperV2 12 | import com.heyanle.easybangumi4.source_api.withResult 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.runBlocking 15 | import org.jsoup.Jsoup 16 | import org.jsoup.nodes.Document 17 | import org.jsoup.nodes.Element 18 | import org.jsoup.select.Elements 19 | import java.util.stream.Collectors 20 | 21 | class CartoonUtil( 22 | private val webViewHelperV2: WebViewHelperV2, 23 | private val stringHelper: StringHelper, 24 | private val preferenceHelper: PreferenceHelper,) { 25 | 26 | var guguUrl: String = preferenceHelper.get("baseUrl","https://www.gugu3.com") 27 | var cycUrl: String = preferenceHelper.get("baseUrl","https://web.cycback.org") 28 | var nyaUrl: String = preferenceHelper.get("baseUrl","https://www.nyacg.net") 29 | private var userAgent: String = 30 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0" 31 | 32 | /** 33 | * 获取最近更新 34 | * @param source 35 | * @return Elements 最近更新 36 | * @see Elements 37 | */ 38 | fun getRecentUpdate(source: Source): Pair> { 39 | Log.i("CartoonUtil", "获取最近更新 source: ${source.key.split("-")[1]}") 40 | val recentUpdateElement = when (source.key.split("-")[1]) { 41 | "gugufan" -> { 42 | Jsoup.connect("$guguUrl/index.php/map/index.html").userAgent(userAgent).get() 43 | .getElementsByClass("public-list-box public-pic-b [swiper]") 44 | } 45 | "cycanime" -> { 46 | Jsoup.connect("$cycUrl/map.html").userAgent(userAgent) 47 | .get().getElementsByClass("public-list-box public-pic-b") 48 | } 49 | "nyafun" -> { 50 | Jsoup.connect("$nyaUrl/map.html").userAgent(userAgent) 51 | .get().getElementsByClass("public-list-box public-pic-b [swiper]") 52 | } 53 | else -> throw RuntimeException("未知的搜索源") 54 | } 55 | if (recentUpdateElement.size == 0) { 56 | Log.e("CartoonUtil", "获取最近更新失败") 57 | } 58 | val cartoons = arrayListOf() 59 | recentUpdateElement.forEach { 60 | cartoons.add( 61 | CartoonCoverImpl( 62 | id = getCartoonId(source,it.getElementsByClass("public-list-exp").attr("href")), 63 | title = it.getElementsByClass("public-list-exp").attr("title"), 64 | url = when (source.key.split("-")[1]) { 65 | "gugufan" -> { 66 | "$guguUrl${it.getElementsByClass("public-list-exp").attr("href")}" 67 | } 68 | "cycanime" -> { 69 | "$cycUrl${it.getElementsByClass("public-list-exp").attr("href")}" 70 | } 71 | "nyafun" -> { 72 | "$nyaUrl${it.getElementsByClass("public-list-exp").attr("href")}" 73 | } 74 | else -> throw RuntimeException("未知的搜索源") 75 | }, 76 | coverUrl = it.getElementsByTag("img").attr("data-src").ifEmpty { it.getElementsByTag("div").attr("data-original") } , 77 | source = source.key 78 | ) 79 | ) 80 | } 81 | return Pair(null, cartoons) 82 | } 83 | 84 | /** 85 | * 获取番剧详情页 86 | * @param id 番剧id 87 | * @param source 番剧源 88 | * @return Document 番剧详情页Doc 89 | * @see Document 90 | */ 91 | private fun getCartoonPageDocById(source: Source,id: String): Document { 92 | return when (source.key.split("-")[1]) { 93 | "gugufan" -> Jsoup.connect("$guguUrl/index.php/vod/detail/id/$id.html").userAgent(userAgent).get() 94 | "cycanime" -> Jsoup.connect("$cycUrl/bangumi/$id.html").userAgent(userAgent).get() 95 | "nyafun" -> Jsoup.connect("$nyaUrl/bangumi/$id.html").userAgent(userAgent).get() 96 | else -> throw RuntimeException("未知的搜索源") 97 | } 98 | } 99 | 100 | /** 101 | * 获取番剧详情 102 | * @param id 番剧id 103 | * @param source 番剧源 104 | * @return CartoonImpl 番剧详情 105 | * @see CartoonImpl 106 | */ 107 | fun getCartoonDetailById(source: Source,id: String): CartoonImpl { 108 | val videoDocument = getCartoonPageDocById(source,id) 109 | val title = videoDocument.getElementsByClass("slide-info-title hide").text() 110 | val coverUrl = videoDocument.getElementsByClass("detail-pic")[0].getElementsByTag("img").attr("data-src") 111 | val intro = videoDocument.getElementsByClass("text cor3")[0].text() 112 | val tag = when (source.key.split("-")[1]) { 113 | "gugufan" -> { 114 | videoDocument.getElementsByClass("slide-info hide").last()!!.getElementsByTag("a") 115 | .joinToString(",") { it.text().trim() } 116 | } 117 | "cycanime" -> { 118 | videoDocument.getElementsByClass("slide-info hide").last()!!.getElementsByTag("a") 119 | .joinToString(",") { it.text().trim() } 120 | } 121 | "nyafun" -> { 122 | videoDocument.getElementsByClass("slide-info hide").first()!! 123 | .getElementsByTag("a") 124 | .joinToString(",") { it.text().trim() } 125 | } 126 | else -> throw RuntimeException("未知的搜索源") 127 | } 128 | 129 | return CartoonImpl( 130 | id = id, 131 | title = title, 132 | coverUrl = coverUrl, 133 | description = intro, 134 | genre = tag, 135 | url = when (source.key.split("-")[1]) { 136 | "gugufan" -> { 137 | "$guguUrl/index.php/vod/detail/id/$id.html" 138 | } 139 | "cycanime" -> { 140 | "$cycUrl/bangumi/$id.html" 141 | } 142 | "nyafun" -> { 143 | "$nyaUrl/bangumi/$id.html" 144 | } 145 | else -> throw RuntimeException("未知的搜索源") 146 | }, 147 | source = source.key 148 | ) 149 | } 150 | 151 | /** 152 | * 获取番剧播放线路 153 | * @param id 番剧id 154 | * @param source 番剧源 155 | * @return List 番剧播放线路 156 | * @see PlayLine 157 | */ 158 | fun getPlayLineById(source: Source,id: String): List { 159 | val cartoonDoc = getCartoonPageDocById(source,id) 160 | val playLines = arrayListOf() 161 | val playLabel = cartoonDoc.selectXpath("/html/body/div[5]/div[2]/div[1]/div") 162 | playLabel[0].getElementsByTag("a").forEachIndexed { index, it -> 163 | val episodes = arrayListOf() 164 | val episodeElement = cartoonDoc.getElementsByClass("anthology-list-play size")[index].getElementsByTag("a") 165 | episodeElement.forEachIndexed { episodeIndex, element -> 166 | val episodeId = when (source.key.split("-")[1]) { 167 | "gugufan" -> { 168 | val regex = Regex("nid/(\\d+)") 169 | regex.find(episodeElement.attr("href"))!!.groupValues[1] 170 | } 171 | "cycanime" -> { 172 | Regex("/watch/(\\d+)/(\\d+)/(\\d+).html").find(episodeElement.attr("href"))!!.groupValues[3] 173 | } 174 | "nyafun" -> { 175 | Regex("/play/(\\d+)-(\\d+)-(\\d+).html").find(episodeElement.attr("href"))!!.groupValues[3] 176 | } 177 | else -> throw RuntimeException("未知的搜索源") 178 | } 179 | val episodeOrder = episodeIndex + 1 180 | val episodeLabel = element.text().trim() 181 | episodes.add( 182 | Episode( 183 | id = episodeId, 184 | order = episodeOrder, 185 | label = episodeLabel, 186 | ) 187 | ) 188 | } 189 | playLines.add( 190 | PlayLine( 191 | id = (index + 1).toString(), 192 | label = it.text().trim(), 193 | episode = episodes 194 | ) 195 | ) 196 | } 197 | 198 | return playLines 199 | } 200 | 201 | 202 | /** 203 | * 获取搜索结果页数 204 | * @param source 搜索源 205 | * @param keyword 搜索关键词 206 | * @return Int 搜索结果页数 207 | */ 208 | private suspend fun getResultPageSize(source: Source, keyword: String): Int { 209 | val text = when (source.key.split("-")[1]) { 210 | "gugufan" -> { 211 | Jsoup.connect("$guguUrl/index.php/vod/search.html?wd=$keyword").userAgent(userAgent).get().getElementsByClass("page-tip cor5").text() 212 | } 213 | "cycanime" -> { 214 | Jsoup.connect("$cycUrl/search.html?wd=$keyword").userAgent(userAgent).get().getElementsByClass("page-tip cor5").text() 215 | } 216 | "nyafun" -> { 217 | val webView = webViewHelperV2.getGlobalWebView() 218 | webView.loadUrl("$nyaUrl/search/wd/$keyword.html") 219 | webViewHelperV2.openWebPage(webView, onCheck = { 220 | it.url?.contains("search") ?: false 221 | }, onStop = {it.stopLoading()}) 222 | webViewHelperV2.renderedHtml(WebViewHelperV2 223 | .RenderedStrategy("$nyaUrl/search/wd/$keyword.html", "detector-exec.js")).content.let { 224 | Jsoup.parse(it).getElementsByClass("page-tip cor5").text() 225 | } 226 | 227 | } 228 | else -> throw RuntimeException("未知的搜索源") 229 | } 230 | if (text == "") { 231 | return 1 232 | } 233 | val regex = Regex("\\d*/(\\d+)页") 234 | val resultCount = regex.find(text.toString())!!.groupValues[1].toInt() 235 | return resultCount 236 | } 237 | 238 | /** 239 | * 获取搜索结果页 240 | * @param source 搜索源 241 | * @param keyword 搜索关键词 242 | * @param page 页码 243 | * @return Elements 搜索结果 244 | * @see Elements 245 | */ 246 | private fun getSearchResult(source: Source, keyword: String, page: Int): Elements { 247 | return when (source.key.split("-")[1]) { 248 | "gugufan" -> { 249 | Jsoup.connect("$guguUrl/index.php/vod/search/page/$page/wd/$keyword.html").userAgent(userAgent).get() 250 | .getElementsByClass("public-list-exp") 251 | } 252 | "cycanime" -> { 253 | Jsoup.connect("$cycUrl/search/wd/$keyword/page/$page.html").userAgent(userAgent).get() 254 | .getElementsByClass("public-list-exp") 255 | } 256 | "nyafun" -> { 257 | Jsoup.connect("$nyaUrl/search/wd/$keyword/page/$page.html").userAgent(userAgent).get() 258 | .getElementsByClass("public-list-exp") 259 | } 260 | else -> { 261 | throw RuntimeException("未知的搜索源")} 262 | } 263 | } 264 | 265 | /** 266 | * 获取每周番剧 267 | * @param source 搜索源 268 | * @return List 每周番剧组件 269 | * @see SourcePage.SingleCartoonPage 270 | */ 271 | fun weeklyCartoonGroup(source: Source): List { 272 | val pages = arrayListOf() 273 | val weekElement = when (source.key.split("-")[1]) { 274 | "gugufan" -> Jsoup.connect(guguUrl).userAgent(userAgent).get() 275 | "cycanime" -> Jsoup.connect(cycUrl).userAgent(userAgent).get() 276 | "nyafun" -> Jsoup.connect(nyaUrl).userAgent(userAgent).get() 277 | else -> throw RuntimeException("未知的搜索源") 278 | } 279 | val weekTabs = weekElement.getElementsByClass("week-select flex box radius overflow rel").first()!!.getElementsByTag("a") 280 | .map { it.text().trim() } 281 | weekTabs.forEachIndexed { index, element -> 282 | val weekModel = weekElement.getElementById("week-module-${index + 1}") 283 | val weekCartoons = arrayListOf() 284 | val weekModelElement = weekModel!!.getElementsByClass("public-list-div public-list-bj") 285 | weekModelElement.forEach { 286 | weekCartoons.add(CartoonCoverImpl( 287 | id = getCartoonId(source,it.getElementsByTag("a").attr("href")), 288 | title = it.getElementsByTag("a").attr("title"), 289 | url = when(source.key.split("-")[1]){ 290 | "gugufan" -> "$guguUrl${it.getElementsByTag("a").attr("href")}" 291 | "cycanime" -> "$cycUrl${it.getElementsByTag("a").attr("href")}" 292 | "nyafun" -> "$nyaUrl${it.getElementsByTag("a").attr("href")}" 293 | else -> throw RuntimeException("未知的搜索源") 294 | }, 295 | coverUrl = it.getElementsByTag("img").attr("data-src"), 296 | source = source.key 297 | )) 298 | } 299 | val page = SourcePage.SingleCartoonPage.WithCover(element, { 1 }){ 300 | withResult(Dispatchers.IO){ 301 | Pair(null, weekCartoons) 302 | } 303 | } 304 | pages.add(page) 305 | } 306 | return pages 307 | 308 | } 309 | 310 | 311 | /** 312 | * 获取番剧id 313 | * @param source 搜索源 314 | * @param url 番剧url 315 | * @return String 番剧id 316 | */ 317 | fun getCartoonId(source: Source, url: String): String { 318 | val regex: Regex = when (source.key.split("-")[1]) { 319 | "gugufan" -> Regex("id/(\\d+)") 320 | "cycanime" -> Regex("/(\\d+)") 321 | "nyafun" -> Regex("/(\\d+)") 322 | else -> throw RuntimeException("未知的搜索源") 323 | } 324 | return regex.find(url)!!.groupValues[1] 325 | } 326 | 327 | /** 328 | * 拦截视频播放地址 329 | * @param url 视频播放地址 330 | * @return SourceResult 播放信息 331 | * @see SourceResult 332 | * @see PlayerInfo 333 | */ 334 | suspend fun interceptVideoUrl(url: String): PlayerInfo { 335 | val callBackRegex = Regex(".*\\.(mp4|mkv|m3u8).*\\?verify=.*").toString() 336 | val playUrl = webViewHelperV2.renderedHtml(WebViewHelperV2.RenderedStrategy(url, callBackRegex, timeOut = 15000L)).interceptResource 337 | Log.i("CartoonUtil", "playUrl: $playUrl") 338 | return PlayerInfo( 339 | uri = playUrl, 340 | decodeType = if (playUrl.contains("m3u8")){ 341 | PlayerInfo.DECODE_TYPE_HLS 342 | } else { 343 | PlayerInfo.DECODE_TYPE_OTHER 344 | } 345 | ) 346 | } 347 | 348 | private fun createCartoonCover(source: Source, element: Element): CartoonCover { 349 | return CartoonCoverImpl( 350 | id = getCartoonId(source, element.attr("href")), 351 | title = element.let { 352 | it.attr("title").ifEmpty { 353 | it.getElementsByTag("img").attr("alt") 354 | } 355 | }, 356 | url = when (source.key.split("-")[1]) { 357 | "gugufan" -> "$guguUrl${element.attr("href")}" 358 | "cycanime" -> "$cycUrl${element.attr("href")}" 359 | "nyafun" -> "$nyaUrl${element.attr("href")}" 360 | else -> throw RuntimeException("未知的搜索源") 361 | }, 362 | coverUrl = element.getElementsByTag("img").attr("data-src"), 363 | source = source.key 364 | ) 365 | } 366 | 367 | /** 368 | * 创建搜索结果页面数据 369 | * @param source 搜索源 370 | * @param keyword 关键词 371 | * @param pageKey 页码 372 | * @return Pair> 页码和番剧列表 373 | */ 374 | suspend fun createSearchPage(source: Source, keyword: String, pageKey: Int): Pair> { 375 | val pageSize = getResultPageSize(source, keyword) 376 | val cartoonCovers = getSearchResult(source, keyword, pageKey) 377 | val covers = arrayListOf() 378 | cartoonCovers.forEach { 379 | covers.add( 380 | CartoonCoverImpl( 381 | id = getCartoonId(source,it.attr("href")), 382 | title = it.getElementsByTag("img").attr("alt") 383 | .substring(0, it.getElementsByTag("img").attr("alt").length - 3), 384 | url = when(source.key.split("-")[1]){ 385 | "gugufan" -> "$guguUrl${it.attr("href")}" 386 | "cycanime" -> "$cycUrl${it.attr("href")}" 387 | "nyafun" -> "$nyaUrl${it.attr("href")}" 388 | else -> throw RuntimeException("未知的搜索源") 389 | }, 390 | coverUrl = it.getElementsByTag("img").attr("data-src"), 391 | source = source.key 392 | ) 393 | ) 394 | } 395 | val next = if (pageSize > pageKey) pageKey + 1 else null 396 | return Pair(next, covers) 397 | } 398 | 399 | //GuguFan: 400 | /** 401 | * 获取GuguFan的播放页面html 402 | */ 403 | fun getPlayerPageHtml(id: String, sid: String, nid: String): String { 404 | return Jsoup.connect("$guguUrl/index.php/vod/play/id/$id/sid/$sid/nid/$nid.html").userAgent(userAgent).get().html() 405 | } 406 | 407 | /** 408 | * 提取GuguFan的视频播放地址 409 | * @param html 源html 410 | * @return String 视频播放地址 411 | */ 412 | fun extractVideoUrl(html: String): String { 413 | val startIndex = html.indexOf("player_aaaa") 414 | val startBracketIndex = html.indexOf('{', startIndex + 1) 415 | var endIndex = -1 416 | var bracketCount = 0 417 | for (i in (startBracketIndex + 1)..>( 433 | html.substring(startBracketIndex, endIndex + 1), 434 | Map::class.java 435 | )["url"] ?: throw RuntimeException("未获取到播放信息") 436 | } 437 | 438 | /** 439 | * 获取GuguFan的精选番剧 440 | * @return Elements 精选番剧元素 441 | */ 442 | fun getExcellentElement(): Elements { 443 | val pageText = Jsoup.connect("$guguUrl/index.php/label/rb/page/1.html").userAgent(userAgent).get().getElementsByClass("page-tip cor5").text() 444 | val regex = Regex("/(\\d+)") 445 | val pageCount = regex.find(pageText)!!.groupValues[1].toInt() 446 | val excellentElements = arrayListOf() 447 | for (i in 1..pageCount) { 448 | excellentElements.addAll(Jsoup.connect("$guguUrl/index.php/label/rb/page/$i.html").userAgent(userAgent).get().getElementsByClass("public-list-exp")) 449 | } 450 | return Elements(excellentElements) 451 | } 452 | 453 | //CycAnime and GuguFan: 454 | /** 455 | * 获取按最新查询的元素 456 | * @param source 搜索源 457 | * @param id 番剧id 458 | * @return Elements 最新页面 459 | */ 460 | private fun getNewestElement(source: Source, id: Int): Elements { 461 | val url = when (source.key.split("-")[1]) { 462 | "gugufan" -> "$guguUrl/index.php/vod/show/id/$id.html" 463 | "cycanime" -> "$cycUrl/show/$id.html" 464 | else -> throw RuntimeException("未知的搜索源") 465 | } 466 | try { 467 | return runBlocking { 468 | val regex = Regex(""".*etector-exec.js.*""").toString() 469 | val content = webViewHelperV2.renderedHtml(WebViewHelperV2.RenderedStrategy(url, regex, timeOut = 1400L)).content 470 | Log.d("Cartoon", content) 471 | val elements = 472 | Jsoup.parse(content).getElementById("dataList")!!.getElementsByClass("public-list-exp") 473 | return@runBlocking elements 474 | } 475 | 476 | }catch (e: Exception){ 477 | stringHelper.toast("获取最新页面数据失败") 478 | throw RuntimeException("获取最新失败") 479 | } 480 | } 481 | 482 | /** 483 | * 创建最新页面组件 484 | * @param tab 标签 485 | * @param tabNum 标签对应的id 486 | * @return List 标签对应的番剧列表 487 | * @see getNewestElement 488 | * @see SourcePage.SingleCartoonPage.WithCover 489 | */ 490 | fun createNewestPage(tab:ArrayList,tabNum:ArrayList,source: Source): List { 491 | val tabMap = tab.zip(tabNum).associate { (name, num) -> name to num } 492 | val pages = tabMap.map { (name, num) -> 493 | val cartoonCover = getNewestElement(source, num).parallelStream().map { 494 | createCartoonCover(source,it) 495 | }.collect(Collectors.toList()) 496 | SourcePage.SingleCartoonPage.WithCover(name, { 0 }) { 497 | withResult(Dispatchers.IO) { 498 | Pair(null, cartoonCover) 499 | } 500 | } 501 | } 502 | return pages 503 | } 504 | 505 | //CycAnime and NyaFan: 506 | 507 | private fun getPageAnimeElement(kind: String, source: Source): Elements { 508 | return when(kind){ 509 | "tv" -> when(source.key.split("-")[1]){ 510 | "cycanime" -> Jsoup.connect(cycUrl).userAgent(userAgent).get().getElementsByClass("flex wrap border-box public-r hide-b-16 diy-center")[0].getElementsByClass("public-list-exp") 511 | "nyafun" -> Jsoup.connect(nyaUrl).userAgent(userAgent).get().getElementsByClass("flex wrap border-box public-r hide-b-20")[0].getElementsByClass("public-list-exp") 512 | else -> throw RuntimeException("未知的搜索源") 513 | } 514 | "movie" -> when(source.key.split("-")[1]){ 515 | "cycanime" -> Jsoup.connect(cycUrl).userAgent(userAgent).get().getElementsByClass("flex wrap border-box public-r hide-b-16 diy-center")[1].getElementsByClass("public-list-exp") 516 | "nyafun" -> Jsoup.connect(nyaUrl).userAgent(userAgent).get().getElementsByClass("flex wrap border-box public-r hide-b-20")[1].getElementsByClass("public-list-exp") 517 | else -> throw RuntimeException("未知的搜索源") 518 | }else -> throw RuntimeException("未知的kind") 519 | } 520 | } 521 | 522 | 523 | /** 524 | * 获取推荐番剧 525 | * @return Elements 推荐番剧 526 | */ 527 | private fun getRecomElement(source: Source): Elements{ 528 | return when(source.key.split("-")[1]){ 529 | "cycanime" -> Jsoup.connect(cycUrl).userAgent(userAgent).get().getElementsByClass("swiper-wrapper diy-center")[0].getElementsByClass("public-list-exp") 530 | "nyafun" -> Jsoup.connect(nyaUrl).userAgent(userAgent).get().getElementsByClass("swiper-wrapper")[2].getElementsByClass("public-list-exp") 531 | else -> {throw RuntimeException("未知的搜索源")} 532 | } 533 | } 534 | 535 | /** 536 | * 创建首页组件 537 | * @param tab 标签 538 | * @param source 番剧源 539 | * @return List 首页组件 540 | * @see getRecomElement 541 | * @see getPageAnimeElement 542 | * @see SourcePage.SingleCartoonPage 543 | */ 544 | fun createHomePage(tab: ArrayList,source: Source): List { 545 | val recomEl = getRecomElement(source) 546 | val tvAnimeEl = getPageAnimeElement("tv",source) 547 | val movieAnimeEl = getPageAnimeElement("movie",source) 548 | val recomCartoons = recomEl.parallelStream().map { createCartoonCover(source, it) } 549 | .collect(Collectors.toList()) 550 | val tvAnimeCartoons = tvAnimeEl.parallelStream().map { createCartoonCover(source, it) } 551 | .collect(Collectors.toList()) 552 | val movieAnimeCartoons = movieAnimeEl.parallelStream().map { createCartoonCover(source, it) } 553 | .collect(Collectors.toList()) 554 | val pages = tab.mapIndexed { index, title -> 555 | SourcePage.SingleCartoonPage.WithCover(title, { 0 }) { 556 | withResult(Dispatchers.IO) { 557 | when (index) { 558 | 0 -> Pair(null, recomCartoons) 559 | 1 -> Pair(null, tvAnimeCartoons) 560 | 2 -> Pair(null, movieAnimeCartoons) 561 | else -> Pair(null, arrayListOf()) 562 | } 563 | } 564 | } 565 | } 566 | 567 | return pages 568 | } 569 | // NyaFun: 570 | 571 | /** 572 | * 根据番剧种类以及排序方式获取番剧列表 573 | * @param category 番剧种类 574 | * @return List 番剧列表 575 | * @see SourcePage.SingleAsyncPage 576 | */ 577 | fun createNyaAnimeGroup(source: Source,category: String): ArrayList { 578 | val sort = listOf( 579 | "time" to "按最新", 580 | "hits" to "按最热", 581 | "score" to "按评分" 582 | ) 583 | val pages = arrayListOf() 584 | sort.forEach { (key, value) -> 585 | pages.add(SourcePage.SingleCartoonPage.WithCover(value, { 1 }) { 586 | withResult(Dispatchers.IO) { 587 | fetchAnimeOfCategory(source,category,key,it)!! 588 | } 589 | }) 590 | } 591 | return pages 592 | 593 | 594 | 595 | } 596 | private fun fetchAnimeOfCategory(source: Source, category: String, sort: String, page: Int): Pair>? { 597 | val url = "$nyaUrl/show/$category/by/$sort/page/$page.html" 598 | val cartoons: ArrayList = arrayListOf() 599 | Jsoup.connect(url).userAgent(userAgent).get().getElementsByClass("public-list-exp").map { 600 | cartoons.add(createCartoonCover(source, it)) 601 | } 602 | return if (cartoons.isEmpty()) null else page + 1 to cartoons 603 | } 604 | } 605 | 606 | 607 | -------------------------------------------------------------------------------- /extension-app/src/main/res/drawable/cycanime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phj233/EasyBangumi-Extension-GuguFan/85107658552b70dc4db6b76776e7714e5c6989f2/extension-app/src/main/res/drawable/cycanime.png -------------------------------------------------------------------------------- /extension-app/src/main/res/drawable/gugufan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phj233/EasyBangumi-Extension-GuguFan/85107658552b70dc4db6b76776e7714e5c6989f2/extension-app/src/main/res/drawable/gugufan.png -------------------------------------------------------------------------------- /extension-app/src/main/res/drawable/nyafun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phj233/EasyBangumi-Extension-GuguFan/85107658552b70dc4db6b76776e7714e5c6989f2/extension-app/src/main/res/drawable/nyafun.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven( url = uri("https://maven.aliyun.com/repository/public")) 4 | // maven( url = uri("https://mirrors.cloud.tencent.com/nexus/repository/maven-public")) 5 | google() 6 | mavenCentral() 7 | gradlePluginPortal() 8 | } 9 | } 10 | dependencyResolutionManagement { 11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 12 | repositories { 13 | maven( url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) 14 | maven( url = uri("https://jitpack.io")) 15 | maven( url = uri("https://maven.aliyun.com/repository/public")) 16 | // maven( url = uri("https://mirrors.cloud.tencent.com/nexus/repository/maven-public")) 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "easybangumi-extension-gugufan" 23 | include(":extension-app") 24 | --------------------------------------------------------------------------------