From bed6e4787a1008d0afadfc7d2f0b94309c3fc440 Mon Sep 17 00:00:00 2001 From: MUEDSA <7676275+muedsa@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:41:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=80=E5=88=9D=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 +- app/build.gradle.kts | 4 +- app/src/main/AndroidManifest.xml | 2 +- app/src/main/ic_launcher-playstore.png | Bin 0 -> 17387 bytes .../muedsa/tvbox/demoplugin/PluginPrefs.kt | 5 - .../tvbox/demoplugin/helper/ListHelper.kt | 11 - .../demoplugin/model/BangumiDetailsResp.kt | 12 - .../tvbox/demoplugin/model/BangumiEpisode.kt | 13 - .../tvbox/demoplugin/model/BangumiInfo.kt | 33 -- .../tvbox/demoplugin/model/BangumiSearch.kt | 19 -- .../demoplugin/model/BangumiSearchResp.kt | 13 - .../tvbox/demoplugin/model/BangumiSeason.kt | 11 - .../demoplugin/model/BangumiSeasonsResp.kt | 12 - .../tvbox/demoplugin/model/BangumiShin.kt | 17 -- .../tvbox/demoplugin/model/BangumiShinResp.kt | 12 - .../tvbox/demoplugin/model/BangumiTitle.kt | 10 - .../service/DanDanPlayApiService.kt | 36 --- .../demoplugin/service/MainScreenService.kt | 38 --- .../demoplugin/service/MediaCatalogService.kt | 113 ------- .../demoplugin/service/MediaDetailService.kt | 61 ---- .../demoplugin/service/MediaSearchService.kt | 30 -- .../main/java/com/muedsa/tvbox/ha/HaConsts.kt | 13 + .../DemoPlugin.kt => ha/HaPlugin.kt} | 58 ++-- .../muedsa/tvbox/ha/helper/HaModelParser.kt | 286 ++++++++++++++++++ .../tvbox/ha/service/MainScreenService.kt | 27 ++ .../tvbox/ha/service/MediaCatalogService.kt | 68 +++++ .../tvbox/ha/service/MediaDetailService.kt | 49 +++ .../tvbox/ha/service/MediaSearchService.kt | 37 +++ .../res/drawable/ic_launcher_foreground.xml | 15 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1404 -> 1088 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2406 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 982 -> 864 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1528 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 1900 -> 1424 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3320 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 2884 -> 2118 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5360 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 3844 -> 2866 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7398 bytes .../res/values/ic_launcher_background.xml | 4 + app/src/main/res/values/strings.xml | 2 +- .../muedsa/tvbox/demoplugin/PluginProvider.kt | 16 - .../service/MediaSearchServiceTest.kt | 17 -- .../tvbox/{demoplugin => ha}/Checker.kt | 4 +- .../{demoplugin => ha}/FakePluginPrefStore.kt | 2 +- .../com/muedsa/tvbox/ha/PluginProvider.kt | 35 +++ .../tvbox/{demoplugin => ha}/PluginTest.kt | 2 +- .../service/MainScreenServiceTest.kt | 8 +- .../service/MediaCatalogServiceTest.kt | 12 +- .../service/MediaDetailServiceTest.kt | 18 +- .../ha/service/MediaSearchServiceTest.kt | 17 ++ settings.gradle.kts | 2 +- 54 files changed, 613 insertions(+), 557 deletions(-) create mode 100644 app/src/main/ic_launcher-playstore.png delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/PluginPrefs.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/helper/ListHelper.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiDetailsResp.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiEpisode.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiInfo.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearch.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearchResp.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeason.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeasonsResp.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShin.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShinResp.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiTitle.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/service/DanDanPlayApiService.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/service/MainScreenService.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaCatalogService.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaDetailService.kt delete mode 100644 app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaSearchService.kt create mode 100644 app/src/main/java/com/muedsa/tvbox/ha/HaConsts.kt rename app/src/main/java/com/muedsa/tvbox/{demoplugin/DemoPlugin.kt => ha/HaPlugin.kt} (50%) create mode 100644 app/src/main/java/com/muedsa/tvbox/ha/helper/HaModelParser.kt create mode 100644 app/src/main/java/com/muedsa/tvbox/ha/service/MainScreenService.kt create mode 100644 app/src/main/java/com/muedsa/tvbox/ha/service/MediaCatalogService.kt create mode 100644 app/src/main/java/com/muedsa/tvbox/ha/service/MediaDetailService.kt create mode 100644 app/src/main/java/com/muedsa/tvbox/ha/service/MediaSearchService.kt create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/values/ic_launcher_background.xml delete mode 100644 app/src/test/java/com/muedsa/tvbox/demoplugin/PluginProvider.kt delete mode 100644 app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaSearchServiceTest.kt rename app/src/test/java/com/muedsa/tvbox/{demoplugin => ha}/Checker.kt (91%) rename app/src/test/java/com/muedsa/tvbox/{demoplugin => ha}/FakePluginPrefStore.kt (95%) create mode 100644 app/src/test/java/com/muedsa/tvbox/ha/PluginProvider.kt rename app/src/test/java/com/muedsa/tvbox/{demoplugin => ha}/PluginTest.kt (95%) rename app/src/test/java/com/muedsa/tvbox/{demoplugin => ha}/service/MainScreenServiceTest.kt (52%) rename app/src/test/java/com/muedsa/tvbox/{demoplugin => ha}/service/MediaCatalogServiceTest.kt (72%) rename app/src/test/java/com/muedsa/tvbox/{demoplugin => ha}/service/MediaDetailServiceTest.kt (73%) create mode 100644 app/src/test/java/com/muedsa/tvbox/ha/service/MediaSearchServiceTest.kt diff --git a/README.md b/README.md index cc092ec..4430e9d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,2 @@ -# TvBoxDemoPlugin -[TvBox](https://github.com/muedsa/TvBox)的demo插件 - -## Use this template(使用此仓库作为模板) -本仓库使用**git submodule**,请在项目Clone后使用`git submodule update --init --recursive`拉取子模块。 -你需要修改以下位置 -- [ ] [settings.gradle.kts](settings.gradle.kts) 中的 `rootProject.name = "你的项目名称"` -- [ ] [app/src/main/res/values/strings.xml](app/src/main/res/values/strings.xml) 中的 `你的插件名称` -- [ ] [app/build.gradle.kts](app/build.gradle.kts) 中的 `namespace = "你的namespace"` -- [ ] [app/build.gradle.kts](app/build.gradle.kts) 中的 `applicationId = "applicationId"` -- [ ] [app/build.gradle.kts](app/build.gradle.kts) 中的 `signingConfigs { // 你的签名 }` -- [ ] [app/src/main/res/mipmap-xxxx](app/src/main/res) 中的 [ic_launcher](app/src/main/res/mipmap-hdpi/ic_launcher.webp) 为你的Icon -- [ ] 编写代码实现插件IPlugin的所有功能,并修改 [app/src/main/AndroidManifest.xml](app/src/main/AndroidManifest.xml) 中的 `` -- [ ] [README.md](README.md) +# ha-plugin +一个[TvBox](https://github.com/muedsa/TvBox)的插件, 提供某个网站的数据源 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3698436..a66198a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,11 +14,11 @@ if (keystorePropertiesFile.exists() && keystorePropertiesFile.canRead()) { } android { - namespace = "com.muedsa.tvbox.demoplugin" + namespace = "com.muedsa.tvbox.ha" compileSdk = 35 defaultConfig { - applicationId = "com.muedsa.tvbox.demoplugin" + applicationId = "com.muedsa.tvbox.ha" minSdk = 24 targetSdk = 35 versionCode = 1 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 43e0af9..7c19245 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ - + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..82d25f62cccc16a81826956370902eb8ad97e175 GIT binary patch literal 17387 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¬m7A+4G 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 literal 0 HcmV?d00001 diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/PluginPrefs.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/PluginPrefs.kt deleted file mode 100644 index 8546c4d..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/PluginPrefs.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.muedsa.tvbox.demoplugin - -import com.muedsa.tvbox.api.store.intPluginPerfKey - -val LAUNCH_COUNT_PREF_KEY = intPluginPerfKey("LAUNCH_COUNT") \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/helper/ListHelper.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/helper/ListHelper.kt deleted file mode 100644 index 15bfa9a..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/helper/ListHelper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.muedsa.tvbox.demoplugin.helper - -fun splitListBySize(inputList: List, size: Int): List> { - val result = mutableListOf>() - var index = 0 - while (index < inputList.size) { - result.add(inputList.subList(index, (index + size).coerceAtMost(inputList.size))) - index += size - } - return result -} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiDetailsResp.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiDetailsResp.kt deleted file mode 100644 index 1caee99..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiDetailsResp.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiDetailsResp( - @SerialName("success") val success: Boolean = false, - @SerialName("errorCode") val errorCode: Int = -1, - @SerialName("errorMessage") val errorMessage: String = "", - @SerialName("bangumi") val bangumi: BangumiInfo? = null -) diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiEpisode.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiEpisode.kt deleted file mode 100644 index e792ce9..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiEpisode.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiEpisode( - @SerialName("episodeId") val episodeId: Long, - @SerialName("episodeTitle") val episodeTitle: String, - @SerialName("episodeNumber") val episodeNumber: String, - @SerialName("lastWatched") val lastWatched: String? = null, - @SerialName("airDate") val airDate: String? = null, -) \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiInfo.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiInfo.kt deleted file mode 100644 index 9afa211..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiInfo.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - - -@Serializable -data class BangumiInfo( - @SerialName("type") val type: String, - @SerialName("typeDescription") val typeDescription: String, - @SerialName("titles") val titles: List, - @SerialName("episodes") val episodes: List, - @SerialName("summary") val summary: String, - @SerialName("metadata") val metadata: List, - @SerialName("bangumiUrl") val bangumiUrl: String, -// @SerialName("userRating") val userRating: Int = 0, -// @SerialName("favoriteStatus") val favoriteStatus: Boolean? = false, -// @SerialName("comment") val comment: List? = null, - @SerialName("ratingDetails") val ratingDetails: Map, - // relateds - // similars - // tags - // onlineDatabases - @SerialName("animeId") val animeId: Int, - @SerialName("animeTitle") val animeTitle: String, - @SerialName("imageUrl") val imageUrl: String, - @SerialName("searchKeyword") val searchKeyword: String, - @SerialName("isOnAir") val isOnAir: Boolean, - @SerialName("airDay") val airDay: Int, - @SerialName("isFavorited") val isFavorited: Boolean = false, - @SerialName("isRestricted") val isRestricted: Boolean = false, - @SerialName("rating") val rating: Float, -) diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearch.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearch.kt deleted file mode 100644 index c1da32d..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearch.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiSearch( - @SerialName("animeId") val animeId: Int, - @SerialName("animeTitle") val animeTitle: String, - @SerialName("type") val type: String, - @SerialName("typeDescription") val typeDescription: String, - @SerialName("imageUrl") val imageUrl: String, - @SerialName("startDate") val startDate: String, - @SerialName("episodeCount") val episodeCount: Int, - @SerialName("rating") val rating: Float, - @SerialName("isFavorited") val isFavorited: Boolean -) { - val startOnlyDate: String by lazy { startDate.substringBefore("T") } -} diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearchResp.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearchResp.kt deleted file mode 100644 index cd02587..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSearchResp.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiSearchResp( - @SerialName("hasMore") val hasMore: Boolean = false, - @SerialName("success") val success: Boolean = false, - @SerialName("errorCode") val errorCode: Int = -1, - @SerialName("errorMessage") val errorMessage: String = "", - @SerialName("animes") val animes: List? = null -) diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeason.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeason.kt deleted file mode 100644 index c41abd6..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeason.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiSeason ( - @SerialName("year") val year: Int, - @SerialName("month") val month: Int, - @SerialName("seasonName") val seasonName: String -) \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeasonsResp.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeasonsResp.kt deleted file mode 100644 index b43ea28..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiSeasonsResp.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiSeasonsResp( - @SerialName("seasons") val seasons: List, - @SerialName("success") val success: Boolean = false, - @SerialName("errorCode") val errorCode: Int = -1, - @SerialName("errorMessage") val errorMessage: String = "", -) \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShin.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShin.kt deleted file mode 100644 index ad0b70a..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShin.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiShin( - @SerialName("animeId") val animeId: Int, - @SerialName("animeTitle") val animeTitle: String, - @SerialName("imageUrl") val imageUrl: String, - @SerialName("searchKeyword") val searchKeyword: String, - @SerialName("isOnAir") val isOnAir: Boolean, - @SerialName("airDay") val airDay: Int, - @SerialName("isFavorited") val isFavorited: Boolean = false, - @SerialName("isRestricted") val isRestricted: Boolean = false, - @SerialName("rating") val rating: Float, -) diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShinResp.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShinResp.kt deleted file mode 100644 index 9174ef5..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiShinResp.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiShinResp( - @SerialName("bangumiList") val bangumiList: List = emptyList(), - @SerialName("success") val success: Boolean = false, - @SerialName("errorCode") val errorCode: Int = -1, - @SerialName("errorMessage") val errorMessage: String = "", -) \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiTitle.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiTitle.kt deleted file mode 100644 index 55c2532..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/model/BangumiTitle.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.muedsa.tvbox.demoplugin.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class BangumiTitle( - @SerialName("language") val language: String, - @SerialName("title") val title: String -) diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/DanDanPlayApiService.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/service/DanDanPlayApiService.kt deleted file mode 100644 index 7b1c67b..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/DanDanPlayApiService.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.muedsa.tvbox.demoplugin.service - -import com.muedsa.tvbox.demoplugin.model.BangumiSearchResp -import com.muedsa.tvbox.demoplugin.model.BangumiDetailsResp -import com.muedsa.tvbox.demoplugin.model.BangumiSearch -import com.muedsa.tvbox.demoplugin.model.BangumiSeasonsResp -import com.muedsa.tvbox.demoplugin.model.BangumiShinResp -import retrofit2.http.GET -import retrofit2.http.Path -import retrofit2.http.Query - -interface DanDanPlayApiService { - - @GET("v2/bangumi/shin") - suspend fun bangumiShin(): BangumiShinResp - - @GET("v2/search/anime") - suspend fun searchAnime( - @Query("keyword") keyword: String, - @Query("type") type: String = "" - ): BangumiSearchResp - - @GET("v2/bangumi/{animeId}") - suspend fun getAnime( - @Path("animeId") animeId: Int - ): BangumiDetailsResp - - @GET("v2/bangumi/season/anime") - suspend fun getSeasonYearMonth(): BangumiSeasonsResp - - @GET("v2/bangumi/season/anime/{year}/{month}") - suspend fun getSeasonAnime( - @Path("year") year: String, - @Path("month") month: String - ): BangumiShinResp -} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MainScreenService.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MainScreenService.kt deleted file mode 100644 index b086615..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MainScreenService.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.muedsa.tvbox.demoplugin.service - -import com.muedsa.tvbox.api.data.MediaCard -import com.muedsa.tvbox.api.data.MediaCardRow -import com.muedsa.tvbox.api.service.IMainScreenService -import com.muedsa.tvbox.demoplugin.helper.splitListBySize - -class MainScreenService( - private val danDanPlayApiService: DanDanPlayApiService -) : IMainScreenService { - - private var rowSize: Int = 30 - - override suspend fun getRowsData(): List { - val resp = danDanPlayApiService.bangumiShin() - if (resp.errorCode != 0) { - throw RuntimeException(resp.errorMessage) - } - if (resp.bangumiList.isEmpty()) - return emptyList() - val rows = splitListBySize(resp.bangumiList, rowSize) - return rows.mapIndexed { index, row -> - MediaCardRow( - title = "新番列表 ${index + 1}", - cardWidth = 210 / 2, - cardHeight = 302 / 2, - list = row.map { - MediaCard( - id = it.animeId.toString(), - title = it.animeTitle, - detailUrl = it.animeId.toString(), - coverImageUrl = it.imageUrl - ) - } - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaCatalogService.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaCatalogService.kt deleted file mode 100644 index a17b4fb..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaCatalogService.kt +++ /dev/null @@ -1,113 +0,0 @@ -package com.muedsa.tvbox.demoplugin.service - -import com.muedsa.tvbox.api.data.MediaCard -import com.muedsa.tvbox.api.data.MediaCatalogConfig -import com.muedsa.tvbox.api.data.MediaCatalogOption -import com.muedsa.tvbox.api.data.MediaCatalogOptionItem -import com.muedsa.tvbox.api.data.PagingResult -import com.muedsa.tvbox.api.service.IMediaCatalogService -import com.muedsa.tvbox.demoplugin.helper.splitListBySize - -class MediaCatalogService( - private val danDanPlayApiService: DanDanPlayApiService -) : IMediaCatalogService { - - override suspend fun getConfig(): MediaCatalogConfig { - val resp = danDanPlayApiService.getSeasonYearMonth() - if (resp.errorCode != 0) { - throw RuntimeException(resp.errorMessage) - } - return MediaCatalogConfig( - initKey = "1", - pageSize = 20, - cardWidth = 210 / 2, - cardHeight = 302 / 2, - catalogOptions = buildList { - if (resp.seasons.isNotEmpty()) { - add( - MediaCatalogOption( - name = "年份", - value = "year", - items = resp.seasons.map{ it.year }.distinct().mapIndexed { index, year -> - MediaCatalogOptionItem( - name = year.toString(), - value = year.toString(), - defaultChecked = index == 0 - ) - }, - required = true - ) - ) - val firstMonth = resp.seasons.first().month - add( - MediaCatalogOption( - name = "月份", - value = "month", - items = buildList { - for (month in 1 .. 12) { - add( - MediaCatalogOptionItem( - name = month.toString(), - value = month.toString(), - defaultChecked = month == firstMonth - ) - ) - } - }, - required = true - ) - ) - - add( - MediaCatalogOption( - name = "Other", - value = "other", - items = buildList { - for (i in 0 .. 8) { - add( - MediaCatalogOptionItem( - name = "other$i", - value = i.toString(), - ) - ) - } - }, - multiple = true - ) - ) - } - } - ) - } - - override suspend fun catalog( - options: List, - loadKey: String, - loadSize: Int - ): PagingResult { - val pageIndex = loadKey.toInt() - 1 - val year = options.find { option -> option.value == "year" }?.items[0]?.value ?: throw RuntimeException("年份为必选项") - val month = options.find { option -> option.value == "month" }?.items[0]?.value ?: throw RuntimeException("月份为必选项") - val resp = danDanPlayApiService.getSeasonAnime(year, month) - if (resp.errorCode != 0) { - throw RuntimeException(resp.errorMessage) - } - val pages = splitListBySize(resp.bangumiList, loadSize) - println("${pages.size}") - pages.forEach { t -> println("${t.size}") } - return PagingResult( - list = if (pageIndex >= 0 && pageIndex < pages.size) { - pages[pageIndex].map { - MediaCard( - id = it.animeId.toString(), - title = it.animeTitle, - detailUrl = it.animeId.toString(), - coverImageUrl = it.imageUrl - ) - } - } else emptyList(), - nextKey = if (pageIndex + 1 < pages.size) "${pageIndex + 2}" else null, - prevKey = if (pageIndex - 1 >= 0) "$pageIndex" else null - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaDetailService.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaDetailService.kt deleted file mode 100644 index 8878137..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaDetailService.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.muedsa.tvbox.demoplugin.service - -import com.muedsa.tvbox.api.data.DanmakuData -import com.muedsa.tvbox.api.data.DanmakuDataFlow -import com.muedsa.tvbox.api.data.MediaDetail -import com.muedsa.tvbox.api.data.MediaEpisode -import com.muedsa.tvbox.api.data.MediaHttpSource -import com.muedsa.tvbox.api.data.MediaPlaySource -import com.muedsa.tvbox.api.data.SavedMediaCard -import com.muedsa.tvbox.api.service.IMediaDetailService - -class MediaDetailService( - private val danDanPlayApiService: DanDanPlayApiService -) : IMediaDetailService { - - override suspend fun getDetailData(mediaId: String, detailUrl: String): MediaDetail { - val resp = danDanPlayApiService.getAnime(mediaId.toInt()) - if (resp.errorCode != 0) { - throw RuntimeException(resp.errorMessage) - } - val bangumi = resp.bangumi ?: throw RuntimeException("bangumi not found") - return MediaDetail( - id = bangumi.animeId.toString(), - title = bangumi.animeTitle, - subTitle = bangumi.typeDescription, - description = bangumi.summary, - detailUrl = bangumi.animeId.toString(), - backgroundImageUrl = bangumi.imageUrl, - playSourceList = listOf( - MediaPlaySource( - id = "bangumi", - name = "bangumi", - episodeList = bangumi.episodes.map { - MediaEpisode( - id = it.episodeId.toString(), - name = it.episodeTitle - ) - } - ) - ), - favoritedMediaCard = SavedMediaCard( - id = bangumi.animeId.toString(), - title = bangumi.animeTitle, - detailUrl = bangumi.animeId.toString(), - coverImageUrl = bangumi.imageUrl, - cardWidth = 210 / 2, - cardHeight = 302 / 2, - ) - ) - } - - override suspend fun getEpisodePlayInfo( - playSource: MediaPlaySource, - episode: MediaEpisode - ): MediaHttpSource = MediaHttpSource(url = "https://media.w3.org/2010/05/sintel/trailer.mp4") - - override suspend fun getEpisodeDanmakuDataList(episode: MediaEpisode): List - = emptyList() - - override suspend fun getEpisodeDanmakuDataFlow(episode: MediaEpisode): DanmakuDataFlow? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaSearchService.kt b/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaSearchService.kt deleted file mode 100644 index 68ff917..0000000 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/service/MediaSearchService.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.muedsa.tvbox.demoplugin.service - -import com.muedsa.tvbox.api.data.MediaCard -import com.muedsa.tvbox.api.data.MediaCardRow -import com.muedsa.tvbox.api.service.IMediaSearchService - -class MediaSearchService( - private val danDanPlayApiService: DanDanPlayApiService -) : IMediaSearchService { - override suspend fun searchMedias(query: String): MediaCardRow { - val resp = danDanPlayApiService.searchAnime(keyword = query) - if (resp.errorCode != 0) { - throw RuntimeException(resp.errorMessage) - } - return MediaCardRow( - title = "search list", - cardWidth = 210 / 2, - cardHeight = 302 / 2, - list = resp.animes?.map { - MediaCard( - id = it.animeId.toString(), - title = it.animeTitle, - detailUrl = it.animeId.toString(), - coverImageUrl = it.imageUrl, - subTitle = it.startOnlyDate - ) - } ?: emptyList() - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/ha/HaConsts.kt b/app/src/main/java/com/muedsa/tvbox/ha/HaConsts.kt new file mode 100644 index 0000000..ddae8cc --- /dev/null +++ b/app/src/main/java/com/muedsa/tvbox/ha/HaConsts.kt @@ -0,0 +1,13 @@ +package com.muedsa.tvbox.ha + +object HaConsts { + const val BASE_URL = "https://hanime1.me" + const val HOME_URL = "$BASE_URL/" + const val WATCH_URL = "$BASE_URL/watch" + const val SEARCH_URL = "$BASE_URL/search" + + const val HORIZONTAL_CARD_WIDTH = 192 + const val HORIZONTAL_CARD_HEIGHT = 108 + const val VERTICAL_CARD_WIDTH = 108 + const val VERTICAL_CARD_HEIGHT = 192 +} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/demoplugin/DemoPlugin.kt b/app/src/main/java/com/muedsa/tvbox/ha/HaPlugin.kt similarity index 50% rename from app/src/main/java/com/muedsa/tvbox/demoplugin/DemoPlugin.kt rename to app/src/main/java/com/muedsa/tvbox/ha/HaPlugin.kt index 7cb5dca..225fe58 100644 --- a/app/src/main/java/com/muedsa/tvbox/demoplugin/DemoPlugin.kt +++ b/app/src/main/java/com/muedsa/tvbox/ha/HaPlugin.kt @@ -1,4 +1,4 @@ -package com.muedsa.tvbox.demoplugin +package com.muedsa.tvbox.ha import com.muedsa.tvbox.api.plugin.IPlugin import com.muedsa.tvbox.api.plugin.PluginOptions @@ -8,55 +8,37 @@ import com.muedsa.tvbox.api.service.IMediaCatalogService import com.muedsa.tvbox.api.service.IMediaDetailService import com.muedsa.tvbox.api.service.IMediaSearchService import com.muedsa.tvbox.api.store.IPluginPerfStore -import com.muedsa.tvbox.demoplugin.service.DanDanPlayApiService -import com.muedsa.tvbox.demoplugin.service.MainScreenService -import com.muedsa.tvbox.demoplugin.service.MediaCatalogService -import com.muedsa.tvbox.demoplugin.service.MediaDetailService -import com.muedsa.tvbox.demoplugin.service.MediaSearchService +import com.muedsa.tvbox.ha.service.MainScreenService +import com.muedsa.tvbox.ha.service.MediaCatalogService +import com.muedsa.tvbox.ha.service.MediaDetailService +import com.muedsa.tvbox.ha.service.MediaSearchService import com.muedsa.tvbox.tool.IPv6Checker import com.muedsa.tvbox.tool.PluginCookieJar import com.muedsa.tvbox.tool.SharedCookieSaver -import com.muedsa.tvbox.tool.createJsonRetrofit import com.muedsa.tvbox.tool.createOkHttpClient -import timber.log.Timber - -class DemoPlugin(tvBoxContext: TvBoxContext) : IPlugin(tvBoxContext = tvBoxContext) { +class HaPlugin(tvBoxContext: TvBoxContext) : IPlugin(tvBoxContext = tvBoxContext) { private val store: IPluginPerfStore = tvBoxContext.store - private val cookieSaver by lazy { SharedCookieSaver(store = store) } - - override var options: PluginOptions = PluginOptions(enableDanDanPlaySearch = true) - - override suspend fun onInit() {} - - override suspend fun onLaunched() { - val count = store.getOrDefault(key = LAUNCH_COUNT_PREF_KEY, default = 0) + 1 - Timber.i("DemoPlugin launched, count:$count") - store.update(key = LAUNCH_COUNT_PREF_KEY, value = count) - } - - private val danDanPlayApiService by lazy { - createJsonRetrofit( - baseUrl = "https://api.dandanplay.net/api/", - service = DanDanPlayApiService::class.java, - okHttpClient = createOkHttpClient( - debug = tvBoxContext.debug, - cookieJar = PluginCookieJar(saver = cookieSaver), - onlyIpv4 = tvBoxContext.iPv6Status != IPv6Checker.IPv6Status.SUPPORTED - ) + private val okHttpClient by lazy { + createOkHttpClient( + debug = tvBoxContext.debug, + cookieJar = PluginCookieJar(saver = cookieSaver), + onlyIpv4 = tvBoxContext.iPv6Status != IPv6Checker.IPv6Status.SUPPORTED, ) } - private val mainScreenService by lazy { MainScreenService(danDanPlayApiService) } - private val mediaDetailService by lazy { MediaDetailService(danDanPlayApiService) } - private val mediaSearchService by lazy { MediaSearchService(danDanPlayApiService) } - private val mediaCatalogService by lazy { MediaCatalogService(danDanPlayApiService) } - override fun provideMainScreenService(): IMainScreenService = mainScreenService + private val mainScreenService by lazy { MainScreenService(okHttpClient = okHttpClient) } + private val mediaDetailService by lazy { MediaDetailService(okHttpClient = okHttpClient) } + private val mediaSearchService by lazy { MediaSearchService(okHttpClient = okHttpClient) } + private val mediaCatalogService by lazy { MediaCatalogService(okHttpClient = okHttpClient) } + override fun provideMainScreenService(): IMainScreenService = mainScreenService override fun provideMediaDetailService(): IMediaDetailService = mediaDetailService - override fun provideMediaSearchService(): IMediaSearchService = mediaSearchService - override fun provideMediaCatalogService(): IMediaCatalogService = mediaCatalogService + + override suspend fun onInit() {} + override suspend fun onLaunched() {} + override var options: PluginOptions = PluginOptions(enableDanDanPlaySearch = true) } \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/ha/helper/HaModelParser.kt b/app/src/main/java/com/muedsa/tvbox/ha/helper/HaModelParser.kt new file mode 100644 index 0000000..b15598c --- /dev/null +++ b/app/src/main/java/com/muedsa/tvbox/ha/helper/HaModelParser.kt @@ -0,0 +1,286 @@ +package com.muedsa.tvbox.ha.helper + +import com.muedsa.tvbox.api.data.MediaCard +import com.muedsa.tvbox.api.data.MediaCardRow +import com.muedsa.tvbox.api.data.MediaCatalogOption +import com.muedsa.tvbox.api.data.MediaCatalogOptionItem +import com.muedsa.tvbox.api.data.MediaDetail +import com.muedsa.tvbox.api.data.MediaEpisode +import com.muedsa.tvbox.api.data.MediaPlaySource +import com.muedsa.tvbox.api.data.PagingResult +import com.muedsa.tvbox.api.data.SavedMediaCard +import com.muedsa.tvbox.ha.HaConsts +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import org.jsoup.nodes.Element +import org.jsoup.select.Evaluator + +val VideoSourceUrlPattern = "const source = '(https://.*?)';".toRegex() +val DigitsPattern = "\\d+".toRegex() +const val TagOptionPrefix = "tags-" + +fun parseHomePageBody(body: Element): List { + val list = mutableListOf() + val rowsWrapper = body.selectFirst(Evaluator.Id("home-rows-wrapper"))!! + var i = 0 + while (i < rowsWrapper.childrenSize()) { + val child = rowsWrapper.child(i) + i++ + if (child.tagName() == "a" && child.childrenSize() > 0) { + val rowTitle = child.selectFirst("h3") + if (rowTitle != null) { + val videosWrapper = + child.nextElementSibling()?.selectFirst(".home-rows-videos-wrapper") + if (videosWrapper != null) { + list.add(parseRow(rowTitle, videosWrapper)) + i++ + } + } + } + } + return list +} + +private fun parseRow(rowTitle: Element, wrapper: Element): MediaCardRow { + val title = rowTitle.text().trim() + val horizontal = wrapper.selectFirst(".home-rows-videos-div") == null + val cards = if (horizontal) + parseRowHorizontalItems(wrapper, ".search-doujin-videos") + else + parseRowVerticalItems(wrapper) + return MediaCardRow( + title = title, + list = cards, + cardWidth = if (horizontal) HaConsts.HORIZONTAL_CARD_WIDTH else HaConsts.VERTICAL_CARD_WIDTH, + cardHeight = if (horizontal) HaConsts.HORIZONTAL_CARD_HEIGHT else HaConsts.VERTICAL_CARD_HEIGHT, + ) +} + +private fun parseRowHorizontalItems( + wrapper: Element, + otherCssQuery: String = "" +): List { + val cards = mutableListOf() + val elements = wrapper.select(".multiple-link-wrapper$otherCssQuery") + val mediaIdSet = mutableSetOf() + for (el in elements) { + val id = el.selectFirst("a[href^=\"https://hanime1.me/watch?v=\"]") + ?.attr("href")?.toHttpUrlOrNull()?.queryParameter("v") + val imgUrl = el.select("img").last()?.attr("src") + val title = el.selectFirst(".card-mobile-title")?.text()?.trim() + val author = el.selectFirst(".card-mobile-user")?.text()?.trim() +// val desc = el.select(".card-mobile-duration").joinToString(" ") { +// it.text() +// } + if (id != null && imgUrl != null && title != null && !mediaIdSet.contains(id)) { + cards.add( + MediaCard( + id = id, + title = title, + subTitle = author, + detailUrl = id, + coverImageUrl = imgUrl, + ) + ) + mediaIdSet.add(id) + } + } + return cards +} + +private fun parseRowVerticalItems(wrapper: Element): List { + val cards = mutableListOf() + val elements = wrapper.select(".home-rows-videos-div") + val mediaIdSet = mutableSetOf() + for (el in elements) { + if (el.parent() != null && el.parent()!!.`is`("a[href^=\"${HaConsts.WATCH_URL}?v=\"]")) { + val id = el.parent()?.attr("href")?.toHttpUrlOrNull()?.queryParameter("v") + val imgUrl = el.selectFirst("img")?.attr("src") + val title = el.selectFirst(".home-rows-videos-title")?.text()?.trim() + if (id != null && imgUrl != null && title != null && !mediaIdSet.contains(id)) { + cards.add( + MediaCard( + id = id, + title = title, + detailUrl = id, + coverImageUrl = imgUrl, + ) + ) + mediaIdSet.add(id) + } + } + } + return cards +} + +fun parseWatchPageBody(body: Element): MediaDetail { + val id = body.selectFirst(Evaluator.Id("video-id"))?.`val`()!! + val videoEl = body.selectFirst(Evaluator.Id("player"))!! + val posterImageUrl = videoEl.attr("poster") + val videoSourceELs = videoEl.select("source[src]") + val playUrl = if (videoSourceELs.isNotEmpty()) { + val videoSourceEl = videoEl.select("source[src]").maxByOrNull { + it.attr("size").toIntOrNull() ?: 0 + } + videoSourceEl?.attr("src")!! + } else if (videoEl.attr("src").isNotBlank()) { + videoEl.attr("src") + } else { + VideoSourceUrlPattern.find(videoEl.nextElementSiblings().select("script").html())!! + .groups[1]!! + .value + } + val author = body.selectFirst(Evaluator.Id("video-artist-name"))?.text()?.trim() ?: "" + val detailEl = body.selectFirst(".video-details-wrapper .video-description-panel")!! + val title = detailEl.child(1).text().trim() + val desc = detailEl.child(2).text().trim() + val playlistEl = body.selectFirst(Evaluator.Id("playlist-scroll")) + val tagsWrapperEl = body.selectFirst(".video-details-wrapper.video-tags-wrapper") + val tags = tagsWrapperEl?.let { + it.select(".single-video-tag:not([data-toggle])") + .map { el -> + el.text().trim() + }.filter { text -> + text.isNotEmpty() + } + } ?: emptyList() + val rows = if (playlistEl != null) + listOf( + MediaCardRow( + title = title, + list = parseRowHorizontalItems(playlistEl, ".related-watch-wrap"), + cardWidth = HaConsts.HORIZONTAL_CARD_WIDTH, + cardHeight = HaConsts.HORIZONTAL_CARD_HEIGHT, + ) + ) + else emptyList() + return MediaDetail( + id = id, + title = title, + subTitle = author, + description = buildString { + if (tags.isNotEmpty()) { + append(tags.joinToString(" | ")) + append("\n") + } + append(desc) + }, + detailUrl = id, + backgroundImageUrl = posterImageUrl, + playSourceList = listOf( + MediaPlaySource( + id = "HA", + name = "HA", + episodeList = listOf( + MediaEpisode( + id = id, + name = "播放", + flag5 = playUrl + ) + ) + ) + ), + favoritedMediaCard = SavedMediaCard( + id = id, + title = title, + detailUrl = id, + coverImageUrl = posterImageUrl, + cardWidth = HaConsts.HORIZONTAL_CARD_WIDTH, + cardHeight = HaConsts.HORIZONTAL_CARD_HEIGHT, + ), + rows = rows, + ) +} + +fun parseSearchOptionsFromSearchPage(body: Element): List { + val genreOption = parseGenresFromSearchPage(body) + val tagOptions = parseTagsFromSearchPage(body) + return buildList { + add(genreOption) + addAll(tagOptions) + } +} + +fun parseGenresFromSearchPage(body: Element): MediaCatalogOption { + val genresEl = body.selectFirst("#genre-modal .modal-body")!! + return MediaCatalogOption( + name = "类型", + value = "genre", + items = genresEl.select(".hentai-sort-options").mapIndexed { index, item -> + val name = item.text().trim() + MediaCatalogOptionItem( + name = name, + value = name, + defaultChecked = index == 0, + ) + }, + required = true, + ) +} + +fun parseTagsFromSearchPage(body: Element): List { + val options = mutableListOf() + val tagsEl = body.selectFirst("#tags .modal-body")!! + tagsEl.children().toList().filter { + it.`is`("h5") || it.`is`("label") + }.forEach { + if (it.`is`("h5")) { + val name = it.text().trim() + options.add( + MediaCatalogOption( + name = name, + value = "$TagOptionPrefix$name", + items = mutableListOf(), + multiple = true, + ) + ) + } else { + val tag = it.selectFirst("input[name=\"tags[]\"]")!!.`val`().trim() + (options.last().items as MutableList) + .add( + MediaCatalogOptionItem( + name = tag, + value = tag, + ) + ) + } + } + return options +} + +fun parsePagedVideosFromSearchPage(body: Element): Pair, Boolean> { + val wrapper = body.selectFirst(Evaluator.Id("home-rows-wrapper"))!! + val horizontal = wrapper.selectFirst(".home-rows-videos-div") == null + val cards = if (horizontal) + parseRowHorizontalItems(wrapper, ".search-doujin-videos") + else + parseRowVerticalItems(wrapper) + val paginationEl = body.selectFirst("ul.pagination[role=\"navigation\"]") + var page = 1 + var maxPage = 1 + paginationEl?.let { + it.selectFirst("li.page-item.active")?.let { activeEl -> + val text = activeEl.text().trim() + if (DigitsPattern.matches(text)) { + page = text.toInt() + maxPage = page + } + } + it.select("li.page-item").toList() + .map { it.text().trim() } + .filter { DigitsPattern.matches(it) } + .map { it.toInt() } + .let { pageNoList -> + if (pageNoList.isNotEmpty()) { + maxPage = pageNoList.max() + } + } + } + return Pair( + PagingResult( + list = cards, + prevKey = if (page > 1) "${page - 1}" else null, + nextKey = if (page < maxPage) "${page + 1}" else null + ), + horizontal + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/ha/service/MainScreenService.kt b/app/src/main/java/com/muedsa/tvbox/ha/service/MainScreenService.kt new file mode 100644 index 0000000..3023527 --- /dev/null +++ b/app/src/main/java/com/muedsa/tvbox/ha/service/MainScreenService.kt @@ -0,0 +1,27 @@ +package com.muedsa.tvbox.ha.service + +import com.muedsa.tvbox.api.data.MediaCardRow +import com.muedsa.tvbox.api.service.IMainScreenService +import com.muedsa.tvbox.ha.HaConsts +import com.muedsa.tvbox.ha.helper.parseHomePageBody +import com.muedsa.tvbox.tool.checkSuccess +import com.muedsa.tvbox.tool.feignChrome +import com.muedsa.tvbox.tool.get +import com.muedsa.tvbox.tool.parseHtml +import com.muedsa.tvbox.tool.toRequestBuild +import okhttp3.OkHttpClient + +class MainScreenService( + private val okHttpClient: OkHttpClient, +) : IMainScreenService { + + override suspend fun getRowsData(): List { + val body = HaConsts.HOME_URL.toRequestBuild() + .feignChrome() + .get(okHttpClient = okHttpClient) + .checkSuccess() + .parseHtml() + .body() + return parseHomePageBody(body).filter { row -> row.list.isNotEmpty() } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/ha/service/MediaCatalogService.kt b/app/src/main/java/com/muedsa/tvbox/ha/service/MediaCatalogService.kt new file mode 100644 index 0000000..c509ea5 --- /dev/null +++ b/app/src/main/java/com/muedsa/tvbox/ha/service/MediaCatalogService.kt @@ -0,0 +1,68 @@ +package com.muedsa.tvbox.ha.service + +import com.muedsa.tvbox.api.data.MediaCard +import com.muedsa.tvbox.api.data.MediaCatalogConfig +import com.muedsa.tvbox.api.data.MediaCatalogOption +import com.muedsa.tvbox.api.data.PagingResult +import com.muedsa.tvbox.api.service.IMediaCatalogService +import com.muedsa.tvbox.ha.HaConsts +import com.muedsa.tvbox.ha.helper.TagOptionPrefix +import com.muedsa.tvbox.ha.helper.parsePagedVideosFromSearchPage +import com.muedsa.tvbox.ha.helper.parseSearchOptionsFromSearchPage +import com.muedsa.tvbox.tool.checkSuccess +import com.muedsa.tvbox.tool.feignChrome +import com.muedsa.tvbox.tool.get +import com.muedsa.tvbox.tool.parseHtml +import com.muedsa.tvbox.tool.toRequestBuild +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request + +class MediaCatalogService( + private val okHttpClient: OkHttpClient, +) : IMediaCatalogService { + + override suspend fun getConfig(): MediaCatalogConfig { + val body = HaConsts.SEARCH_URL.toRequestBuild() + .feignChrome() + .get(okHttpClient = okHttpClient) + .checkSuccess() + .parseHtml() + .body() + val options = parseSearchOptionsFromSearchPage(body) + return MediaCatalogConfig( + initKey = "1", + pageSize = 60, + cardWidth = HaConsts.VERTICAL_CARD_WIDTH, + cardHeight = HaConsts.VERTICAL_CARD_HEIGHT, + catalogOptions = options + ) + } + + override suspend fun catalog( + options: List, + loadKey: String, + loadSize: Int + ): PagingResult { + val genre = options.find { option -> option.value == "genre" }?.items[0]?.value + val tags = options.filter { option -> option.value.startsWith(TagOptionPrefix) } + .flatMap { it.items }.map { it.value } + val body = Request.Builder().url( + HaConsts.SEARCH_URL.toHttpUrl().newBuilder() + .setQueryParameter("query", "") + .setQueryParameter("page", loadKey) + .apply { + if (genre != null) { + setQueryParameter("genre", genre) + } + tags.forEach { addQueryParameter("tag", it) } + } + .build() + ).feignChrome() + .get(okHttpClient = okHttpClient) + .checkSuccess() + .parseHtml() + .body() + return parsePagedVideosFromSearchPage(body).first + } +} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/ha/service/MediaDetailService.kt b/app/src/main/java/com/muedsa/tvbox/ha/service/MediaDetailService.kt new file mode 100644 index 0000000..64dec6c --- /dev/null +++ b/app/src/main/java/com/muedsa/tvbox/ha/service/MediaDetailService.kt @@ -0,0 +1,49 @@ +package com.muedsa.tvbox.ha.service + +import com.muedsa.tvbox.api.data.DanmakuData +import com.muedsa.tvbox.api.data.DanmakuDataFlow +import com.muedsa.tvbox.api.data.MediaDetail +import com.muedsa.tvbox.api.data.MediaEpisode +import com.muedsa.tvbox.api.data.MediaHttpSource +import com.muedsa.tvbox.api.data.MediaPlaySource +import com.muedsa.tvbox.api.service.IMediaDetailService +import com.muedsa.tvbox.ha.HaConsts +import com.muedsa.tvbox.ha.helper.parseWatchPageBody +import com.muedsa.tvbox.tool.checkSuccess +import com.muedsa.tvbox.tool.feignChrome +import com.muedsa.tvbox.tool.get +import com.muedsa.tvbox.tool.parseHtml +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request + +class MediaDetailService( + private val okHttpClient: OkHttpClient, +) : IMediaDetailService { + + override suspend fun getDetailData(mediaId: String, detailUrl: String): MediaDetail { + val body = Request.Builder() + .url( + HaConsts.WATCH_URL.toHttpUrl().newBuilder() + .addQueryParameter("v", mediaId) + .build() + ) + .feignChrome() + .get(okHttpClient = okHttpClient) + .checkSuccess() + .parseHtml() + .body() + return parseWatchPageBody(body) + } + + override suspend fun getEpisodePlayInfo( + playSource: MediaPlaySource, + episode: MediaEpisode + ): MediaHttpSource = + MediaHttpSource(url = episode.flag5 ?: throw RuntimeException("flag5 is empty")) + + override suspend fun getEpisodeDanmakuDataList(episode: MediaEpisode): List + = emptyList() + + override suspend fun getEpisodeDanmakuDataFlow(episode: MediaEpisode): DanmakuDataFlow? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/muedsa/tvbox/ha/service/MediaSearchService.kt b/app/src/main/java/com/muedsa/tvbox/ha/service/MediaSearchService.kt new file mode 100644 index 0000000..fc01f52 --- /dev/null +++ b/app/src/main/java/com/muedsa/tvbox/ha/service/MediaSearchService.kt @@ -0,0 +1,37 @@ +package com.muedsa.tvbox.ha.service + +import com.muedsa.tvbox.api.data.MediaCardRow +import com.muedsa.tvbox.api.service.IMediaSearchService +import com.muedsa.tvbox.ha.HaConsts +import com.muedsa.tvbox.ha.helper.parsePagedVideosFromSearchPage +import com.muedsa.tvbox.tool.checkSuccess +import com.muedsa.tvbox.tool.feignChrome +import com.muedsa.tvbox.tool.get +import com.muedsa.tvbox.tool.parseHtml +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request + +class MediaSearchService( + private val okHttpClient: OkHttpClient, +) : IMediaSearchService { + override suspend fun searchMedias(query: String): MediaCardRow { + val body = Request.Builder().url( + HaConsts.SEARCH_URL.toHttpUrl().newBuilder() + .setQueryParameter("query", query) + .setQueryParameter("page", "1") + .build() + ).feignChrome() + .get(okHttpClient = okHttpClient) + .checkSuccess() + .parseHtml() + .body() + val result = parsePagedVideosFromSearchPage(body) + return MediaCardRow( + title = "search list", + cardWidth = if (result.second) HaConsts.HORIZONTAL_CARD_WIDTH else HaConsts.VERTICAL_CARD_WIDTH, + cardHeight = if (result.second) HaConsts.HORIZONTAL_CARD_HEIGHT else HaConsts.VERTICAL_CARD_HEIGHT, + list = result.first.list + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..b3b3bf6 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index c209e78ecd372343283f4157dcfd918ec5165bb3..5a547995eeb4cfc3465c6b6bdde181f545e70cae 100644 GIT binary patch literal 1088 zcmV-G1i$-INk&FE1ONb6MM6+kP&iC11ONapN5Byf=La{EBtc63gNhaSZyYi^Rl&{T zgX2b$6e$iVTg#Ri|(Arr4aY4 z$ZgvuqvJ+@0|KROs6YuyP=Xpz3rbLe5|p71)PxdLC%47~2`0cK|9+JKV<$;4XC6DN zu!GS_k_3|^lQTJH0!&~gsMxWSBr|7yNn!_62`;ml#Lk(=&g4{d5{#Y6Niabrs3d2e zBr#)0rxIY2bq975OcI=VFlU~}aAix9#jZHO{Qm!8rpL}ko}4SF9ZZ0koB$In|A~Af zSAnsE36}j({#f@3eM7|rNtj?~D4+;0tQ-2LuVA3W(i(~v9{~kR7idy+TMSbVO0o1?aeqc=2AhY-1NXT#RnXF1;>^q$ik}k?jQuB*_q%$Psnez|WFICbHVb0x`)X7)G zxhJbLzAVmNV-1(IWpZ;z%QX!rX4EQMW&8;~Zs$o({{R3k9WsSm9ERM^(}n8+!}YU8 zYo$>8lxY1G8k1;S6AbvT6^F$K!xK(EnqNwj4(@_0hir0C&&;yTOSf+Lll4&G#tuqC0B@X zq%43w)Kdiz(RQr}R0lvknxG1ZtvY(<@->Tg^ECuOi34+k)B$jhhzp;{*Ggv~VEw)f zr9fW;0DL&x76M=ql~XD-zPOJV$0`BnO8u3R_R^f@p#~=LMGXN|quI_1zLSZFc8=)Q zvG-Qa1_LO(cXhM^`sUIf8< literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..a94da2d78f947ae6f86a9a2739a429959c5ddb4e GIT binary patch literal 2406 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 Y-y!SpSD`!!PzwO#aG~?0)EG<%0GU;p)&Kwi literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..f38b0eb68d0c477c7c5a70700d04485a158e179a 100644 GIT binary patch literal 864 zcmV-m1E2g-Nk&Fk0{{S5MM6+kP&iCW0{{RoFTe{BPX;TJZCBM09Uuk&g7R11YYn{M z!Eqx=l9DsCFK`O~9pQh&Y>g3w4b zh(?W4Yid0Izf2?=lc_N^G>tUx{V%=J%cPM6Au(yxmgK$vX(WvhRT|B|#J`Uwzr#NX ze@Zl(KP2ENC6Gt~GawnfwE;LuKV$>~X-7XTHsjI^rsX(xmygvvb*_8n&d7zWmW?$% z^JE9DfB-g&acCCToI7t!AheKTnA`TazORqZ>7D-v&^h-Y<$m9HJ8pfrJp$6MZ8IdN zQtKzSZQHi98{2l)w(Z@{yS5c;<79e%S5^0P)j9h;qW=@XhDy_|+_c9z(~xr;oUz`n zZpB@_jByLj`Uk2yl~)b)Gj8>Uw0t{EX0o1YYN%^eT-QL&UzjPl+2>knB;}3N@{PG= zpPDoov{7p#^~v)r=e|3UylVKU?MWIL2La%mMkVWotr{5zgi9Jx%32KHm8KIFM@5&C zE-BZq2NTh=oA+`w37&?XM5ks9AHN|=kMz};O)n<{!1B9V7+WwU{S2g217kIyV;FVg z@^A^TaPO<|@Ipl|fS~xvk};4zR1`V^+h5NTMjYBTR)@5e7)9Fz9HoTQ`!{V3Q$)Lg zge#KyL}Zj#lBz)9+QZ4hj0E!7nWw<Z!zr8i=>S80M^AhMll;bqT<7&aiqvO%DuIF%@XSku|!mU{bT_tATl!I zt&=NAtRRn?>^a>?ErV8HYc=^HGuf{iYN%^eT-QMDKbcEl+~-|AX;}Z>mou5yzYiU) qZLLb%T01`e_p)HzH{kDY<$nTvIkV>8e*Vh+yg9S&>QnAwLnQ#oIhy4F literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!T)o^MyUIpq+qTu& ze1Pryf^GGq`fM{3AV=H&!+Lh}U8!x`wr$(CZQHhO+qUg${%7X5h`7@1Ax`zSN&nVi zsqU1qN*f!ns&6;R7Ls1mor_^3DQ@V$Sc@DUFoB?L+wcdn1Go-vxhDWM3;Y!)p#~aM zG@)_mV?j^^pfrO_KlGN{?AoV4qSVHH5nBMuAJ$6Q%7cD3wHQBFS`Vkcxqs!>u~h(U zT(=PTahcL}Eh)cfk*dG=;cXr%5Z1>TI9)|r6iMZO-+xu6B)6<~u&xdU&~evwNGknl z{Uoe92@>+>?*LPi0hFqA{W8(7U)J$i!oz0(Q-c9?^^-x$x~2hn3oup1fGZl@w_Ky* zVd|QDx;$ZoF~GVHS9lyu6%Nof0P8-on=OFkkmZ>O;JyV_Lr&niN*1~*dH+7Vk={+S z4rFx_xbKouBL1rewnB(yPq)Lg0Ka0y&};++g9t1t{d(nvu#6x8lnxmHaEDqq&9W0g z$RMES;}~&?g>oU26af5-5o~DK-Gb)i4aAf70{yg0}_miEpNyM>1xS zC4w(;T*0(PDj#mvstpL(^KQ9!H453ZxFwB?YQU1p^`Rg^5%62V6XkHlWS{1ZRZ6B} z(s~(MC_}JQ`krPpD?l5%-jp#-OVXIthilKv@N5lG*#6V<@kVr{TK}!i)Vk1M(dMZ2 z#H7R{dQCHqvd=ySXbu?Bgsp^Lp6BfzC@#Q3kQKa6t)CK%TV#=dLSe%(K+gW9aqm<%aZK4UJ-XiO0Uu!5+&>srkez~QcgG#fsjq2C=TomL)_ zW7kWh&8U5vf3g4&L(t3nlmP%-n6_v*ivuFCiHqdD;wfOJ&jmvW!V*{kFW92Ie9_gn z01#OX8KwXNiN0<9;i^+GmW6!H{=_#zl(&MaeeG=Bybl0A*vaKb0A5kW#GH{E{88K5 z`_`hO+b!sKt#b}n*nI$SgS?QSEUU}y^ad9i{P2&t#T=cpII!DQf*^P)n7Od)fHTCM z;c&7in7`bOYE471=-lQb27~n(3>f_=ST>3aK=lm)3#J|2TgWGy!C-BJL6iCZ?C8Gx zjp71OYb>CFxqEV^Pa`&iB&{Y*f+nLc=Nzd6=XaxamjMZCf?eC%mJRKjko5ESXT`fD zqmSCAU9i8L0Xft)6tJK>0>>p=T~BWn{OFssSg{rT(wnXRz3(?d)Z2xESkMt{ru;+O zy0L2(Gjo^?Q}Bi7TOP`VxwPiXH-hcIPr?{|7#9IA1;xMj501cj{Lg>tJK*M~*9Y(a ed*r2!(Z{a1fD9;l`9zQk@*8WPyo|zzL(KqRRr~w^ literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a3070fe34c611c42c0d3ad3013a0dce358be0..621a149d8a486dee45a6d6e6bc94c279e161ec88 100644 GIT binary patch literal 1424 zcmV;B1#kLNNk&G91pok7MM6+kP&iC`1pojqU%(d-wT6PWZ6t?3?A^;CA|}Ak)@SEX z7U$;X8lilIWLtaM<{yR`X3S*z3)60*JMWfiskMru|0p@KgAOw|Zrezblz%E4%RV=Z zosp}rIe~yOG`1?+xW4ZM$hK|Uw4;y8b=kIU+qP}n ze2Q(`wr$_!X`k%e)As&>(o9y_x-SYNNonZ6TI30$gy{bS&_8Pc_4+{z(EkW2Nr+N% zdjUg)V4wez$Ecp7bojuZ5=}TsGGt&1XdT_7H}raT^zghJoeUWyxlG7xCUk^?AZOaJ z(1e4eZdm9T1uI|Z5AMGL^ju|TQtMojJkX*-zpeZ$Azl}jprynCKv4}m?oEZ;uM*@| zX?C+v$Y+tHMTs7jnLkn7l%7+qn@BPN$ac3_d5A>MvogJVHow)aLec9{UOoWD2A~_m zE)=bvr4<0s--Q9#9~bVq*l2o|*qJC>Gqn>nQ$kNyS!wx;l@tqSA|y2&wM!iam87m- z>bPY`KuQ{Z`8cdwOinKz&t{VnE?wAQkkh}tNu zW+`L^^pm+b8<*}Z^g583px=Ov?q8oje|C5Spzvv|T)dw@`~7t~K;*L1TdAFhzO6UL zbdE>q5S!4mp=Fs2M)eXA9dhPEv=Av*?vXICL{PenZ338%nkCK`*9Apoj}k1F?wYgiUL+)PB&Oq&g;-A+&o1=KQ?#88wB5z(;#il#F}^%Pp$ zZK%lLQ)V-s54e+^BeLdBc>meuYiy0~jQJ`Qyo#w_4P(DsIdpveiuInUr=h@1tzBCU zB$M@KULtCxZ3ZnDjxGCVmsKN=(NZ`>M1KL_vK3hx9*>7vwQVE`Jts92RKU@&l{X^+ z==el<)@LceznDe>Hd*WPw9+bt;w}t9K3r+u;oL2l`5G^U#@4QvY)-(O=B| z0Aiafv1Kw&3WvF$HV5~Tt6;uMM1Q_9!DV1`s!8DXF4_&x2+35@o z1`H>zxDs0?^I#_CTmYWMRXPL2$lQ7sYrK>V^CC5jZO+w7QRUOzj&GcN66uxyXM%o@ zgL&xU>2$?zcadQKng2rs);B89ubbC&1lr>aO$IdlIMtwE{xurnOohVv0LDr2s6mu9 z3ssoWQnifZ)3Oa~&K_?hRj6Xl#ScLY?D?6hnIJ&-iKrM=$V^Xi5X8X6%h1a-GN9oZ z8t|)E4Wg)?C}Ipp!!pfuByo>cVD1X+Oo<;coG8wAf>uEf&{0PLf znG~pTuitI~&=2Ly5JlG)JzEUD&;3*)a{U8E0Iab#QXp!ftnsaHmh5u?Xn3dBtPKJWIZs$bQh;T@JgX8P7wAPDeh9`<-p#dci3y};9b5152I*+(FbfPZ0nJ-F1T zLnxEt3i7CR6YeLGXgL?An+-sz2wXEg6ma_$#a4A|97GJD6${}P3(z~GYzgSM<-Zub zg%PNa3P9fx8$gNQo_^+*ut0xq0`wO5c3Z~o06^{jaBKiF3k4A8?{v48E($_)+ijJ( z^4AR1JsSa-XyX8y_!F(pYNuxXx@vj#>p64uaMo+LV`bLjPiz(GTmVBH_W(YGXa(4U zgi~)ke`T_7+Tocua|#laAD+ literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..94437037036fcba031bea13fad1fde57b01cd6e1 GIT binary patch literal 3320 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 Cn^RW+ literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index 28d4b77f9f036a47549d47db79c16788749dca10..9cd67b63b447ffd0064e0316c2a16c18c5f238bb 100644 GIT binary patch literal 2118 zcmV-M2)XxCNk&FK2mk3`g*JHp?81KGhlOq(0^nqgmpFLCkB^X+%gv)jH=ABU<8^uFk+DG0`ar8*`RX z&BhIUH7(sH96(mKkB$XG2&*1nJdevfNA-QN z{MM!}`J}7{)1Ade3FoLG7iN4_zo-TP>ZS5({UdaGCMOL{0rU$M!06OW-w(ZMCeC*d zqf;|;O9e1+0vMU*T%55bu{JnLSF78x+jsMuF+9Rja3}T7GO{J+s7dNh*&^_cKS*Pq zbRrgjOBvjx)rPW!1yHz1`_%6n%JZx<*90n3$_ry;YPWXAs>_)s4I59Yx-!S434f33 zo-=94cu>{-ZbPPYdD~UzUPq59*%PY6RTIX3Z&06e>Cu{2P!fmw;8yyzhQ*XFoUkPT zw_L7vGHj@8hsJeS1aK_AkUpB-w%zI-3I^8#X=a>+?||~CKEZY)+Wz(kuXy=YSFEU( zm)P~L2FLtr(Hc2cM6`^0b&h;YNu`R@zcp;1y~O<@U8pFsaVCCWVC^ke)hY(X)zOVrFjp*TwxNC^z0b~nNL}MnoyA)+yuX{Lc8^E6jg%ORKoat5+fwG4)>F))+=oK>%QJa@Nb}He1 z&J`~J_Bxn{j3qm7d-v%AnJZQwFH>t|=7MdO^+UPTnPY9IAyea_xzxYN{7?+0oXHX1hi{A z09;G5I6bhJks5NQ!7>1#?+AGKQx*WHa`=Nl$PlZO$2T7Iu-qbJ39-m809+LC>J1I{ zFhl;HyPPZmAf5}L$CLvHPT2ww+7^+39?vl=r`+M(e;9Bny+^?4@v6#6p9bsI9RON9 z0m~_>DJO>QW>FvokR=4rYUOO2@;SEwgn%L79pzxuaB3U?FRv6ZdNwJiLU9JpCcr7T zeFCgDzAGnHaISe%09B3v%9YBAX-k(yK@>o6i0C`NiwvsUWwz-7&Kdx|uy_Sn@0>4k z);7I9Au)W!Y4#iou&H+o5dFaKd*xO2a0ZNfb;LlOqZ@iQojVh0C34O$0TjT``k`f1 zzIMnevfaa?um-@R&RzWwwQs+W)WKoHsaOC|y!1qGM6|t9+S~nc+V)-mJMPvK9V=2& zLY>Qb_dx)NW*_K@tf~2PEi#JZ92>f62kBxd>4}UHKO`g+3=5V;+t%9Io7^AO6Vd*p zzE(P}q-S$>$2U7b7;O7WPxNNx&+i@L&n)4DjVOJu^j5y zI~V}ma1@K{4n`AL|NcP((fA+Tov>W?zRxo{^{X87zgq3AAT2bXuDWEY;Nley(b%8F zowH7Gy+F-hEo4P4pIea%(uM}2E-$6K5PbmOO#E7i26aG4#ThrdT8L=KRH`*4%B*?w z0|vZn=ckEQCve9s5!|~@o7tzW7iHmt{|zm)Vk@1$B?8YFJoWvOTJvRc8ohc1F``;>HkyK zn(P5^dd%1o8Qt5HLPEfM8FSsdzkKxv-{{j;)U^q60^nX8a^)utZ|i5?I@g_=giV?A zJ;+O*w?DbkAuR-5AOdtixE*Ua z?zglML_TM?eD$&o=j(YfFu4~ApglXMA`YLkpRxFy~?WdzCy?fI7u-TIN+K6u+6-+$;fI?4!70G=01Kkqv-Dt7)SpaLx0@BqHGG6D#$ zt;zexKj$q#G(%$N0UjU)G=LGX1I}&7cKVwN(JJSwK1Z*waTx wXP;I8{GK5zWM0Hh-v87Cuv`9ndP_^sr~8vugwj&PIg6lkB9z90Al*;kTC(r!tQk6;gxj4h%FdHAt(^M3YvYj(!tOeN)+Hvj6+< zzyJRG?^lZfWuR#t!tUKP&(?%3v&Zd$R2YN>lB(Lq`OInY48%4%yTv2 zYe1{G`3)(PDEio5Y@-I5tUf`c%%OCJMtSW56g3iEg%3`$7XSJJHyA z<|7&N)5Xrlgv~%BO24eFd;Hd;uiK%D`EdK|quUeRZDqbh9l)%j%J#0lfrZumvA<_w zu&=AVvdChf6}eqh(bUz`(`Ue*p01{fBAcTgKyDYLs_I+YyJEk+rM@avU~>fB$n)HS zM7pfJydu`i%gfS<{PF94kZDv$t>06sAkheDzu40NJ$5CMW%n^Lls?8^p^QGWURbKu3ZduZQZ((s2? zzE`}<{;Zt7<$C|9R8A~DJ~@%x>TfP zF>TX8)@v|t)q4GjRt<}5s6hLHwRel7>V@&r-O|Av(yh;Q1A{E>Ir>p+%dHD|=l+lT zpr(Dg&>#Nu=!)6bCLr-ZS%|;h)Ij$+e@r8_{qO19QvDe=&1tmpY*0lcA^Cc-#{9fQ z<~$*<&P$Q<_jy#<$40PMofM7aQ}C=jphI`4kLg}Z7CIN#26D{-4v-_CA-LiE@(%{y!BzsU%gG`Q?sjLUf%qFSl0y)2#ae*+EI>s|i`d^V$Dn)qmzqRq6VJRY|{4ujsIU%#bnqU6MR&-1I_43=|5(6Jr;Jvert) zE?S|Tmn}Tv<-??sxV5@9t}3D=>YZ0JrQe$CO~|EY=Lj9RM&4svQHPQL6%pV5fPFiH zfXDx;l@~et{*{U*#c#Dvzu)|znDO7$#CRx)Z&yp-}SrD{&|(MQtfUz~n35@RLfUy=aqrhCX0M}J_r5QsK~NmRCR|Nm&L z41UdsLjWxSUlL41r^0K&nCCK>fdR-!MYjFg(z9_mF^C|#ZQw?`)f6uVzF^`bRnVY& zo}@M06J&_+>w9@jpaO4snmU;0t-(zYW1qVBHtuD!d?%?AtN7Plp><-1Y8Rqb20ZaP zTCgn*-Sri4Q8Xn>=gNaWQ57%!D35UkA@ksOlPB*Dvw}t02ENAqw|kFhn%ZyyW%+t{ zNdM!uqEM^;2}f+tECHbwLmH*!nZVrb$-az%t50Y2pg(HqhvY-^-lb}>^6l{$jOI6} zo_kBzj%8aX|6H5M0Y<)7pzz_wLkIpRm!;PzY)9+24wk2&TT{w--phDGDCOz{cN_ca zpnm7`$oDy=HX%0i-`769*0M6(e5j-?(?24%)<)&46y0e&6@HCDZAm9W6Ib#Y#BF6- z=30crHGg+RRTe%VBC>T00OV6F+gQDAK38Ne3N9bm|62tPccBJi)5{B z4zc^Db72XiBd}v$CF|yU{Z=M|DZ%-(XarYNclODlb1Kz1_EKLy(NSLCN`eUl(rBCL zT*jx@wNvze0|TSqgE(QArOZU)_?qH(sj#TwzElLs9q)(0u!_P|R%Cy_0JFQxgGV>1 zz4?_uq<8_gM0`c*Hh|;UMz~vrg1gQXp{ufg`hM_qU;U>+zmvc5blCLSq@PrEBSGR# z&8=2Z4uXN`F3p73ueD1l{s{k$WipAvSh5W7ABe?4)t;r@V?y`bNB5FvBuE|0VRTb< zM1Hn^?DSsJY+sX@T5xW=#>T9VEV|?<(=6|ge$X6Sb05!LFdjDcoq*gM(Zq=t;_)Le&jyt(&9jzR73noru`a# zN*<`KwGa^gZU3-)MSLF0aFag#f0<>E(bYTeHmtdbns#|I)-$)mJ`q9ctQ8g0=ET?| zdO}eZ*b_p>ygRTtR^5Ggdam=Zb5wmd{}np+Jn1d_=M`~P=M67jj})fH4ztb5yQqQW z^C|C&^LHAK-u+ooIK)yM)QM?t;|<{P;;{`p=BclzAN#JzL4jCwXkQB1Dy{=^KR`=~ zTrr)y7eiYBzSNs_DvO=4A6#EgGS-zY%Vi)N*Yb`U;6o}KR}dq{r9pT5wqZ@3NOE8- z9-(}D|Nc5732CSYQbL)!gPQ#RbD8BhK3dl{sUuPvei0tkvnJBxDEAYTesU8H$)g(Plra{VH(v3u^CO1~(+ zU0O7#)jaS4{NcwA+LuSm&VBcX2#Im3xg)W}ySNw%->orn1taZ&+d)}8gJTqA!u|5P z{yv?zol_3|(1(%M(EVU=cp?L`{Pi|ixk{U)*guFML3P!OSlz;zGA#T+E@8@cgQ_mv1o7RSU=Zo_82F?&&2r;WE z@wk}JHYEZ9nYUc(Vv~iTCa3u8e4q(yq<29VoNbKk|`mq%I6u)My=gPIDuUb&lzf4`MEA9^g8u z)vp8|$$HE9m_BTV?lOosIGa4jud=jIbw)O2eCMfyw2*S8?hjWw^nqws$O*M$3I1)x zR0PWFb3$ySOcGTe1dz%N0l;RPc`x%05FtT^f^j{YGcz+YGcz+YGcz+YGcz+Y6}erKc+2R{#M*1O-|E2)5C?MYV0asiw1S+qP}nwryj!?cHqK zYw{sTlH9hL1u7g4=Zpg(+YeCR`xas!L=WRIKLkWF4D+2lUhdurdUH+5cZPpo4D+3F z&YLTK7xwgj>hAu{cc`5GYH+BHwa%3bSE~Hib;a>=z3)_0X0C9~5xE&(y&1nCLyxff zIOEfwwaKg&vD(0DC#$`zVywU@&!_%uq4__#Xoep7>GUDtRId3>WHDCC_*}&5ZC0aL ztp;L^%lM3B?X4YFGTu21hL`hOm+_++y~Jw#r+^n@F^`A+bnA>_KE~oA`Fy(W@%Vt( zr@sE}W!6z!lNB4D*wjDm*qXQ@JEUXUQJ3*S=`46X(@%va! z0{~WWkKO@Jt3&tH06_C>m8xpy(%Q@T?9bR|>}MMQczht6d>#P!>iFqt?C^00e;vWv zf4oHpnWgiF|IYY2QdM)Q>X=?tb;Q1`{^WZdpny8~&UXfX@29F>lNz{Qx+=y4)ObKK zd1?U0@2sj`YOE$>cd^egSEj-yl-6qS*$imtfFAiT#TKqs)mv=SbGLz zrO3$Gv!-GpysC{btDm5z04S4Q1B`x-Bcgg0if^;5Hmn;}UW`U3WR4=?z3R@a%>q_? zKtdU^XR`-GMQ3GTY<7xOt6l|aS~as;AyKRLWKL0THr;u;_v`N49bcDx94J)LP#CR9 zsi^GbOR6K9Sgn<+XKbFly#+$O8JFz# zG?a83JCH=#He^#Z8T}wt_hufqN%Cr(r~n0p5xf6tsHR*eV#c17Oa|v{-T+CoyDn6u zh>+c0Fmq*!&e*8{65H=!bL~Ns=K05-3=JDHIK)=x$`U+R-I=xZl4rizAG8qglI!4( zij)Ouz9A{<)Lc277!8q5-GS#E1ibP(ZUQ`2ev>EJYG|54WH5c#lrFq&0 z0F;>QHUSY*P3LTkZXrAO9X34wk|h7U4Nz0kO>*4ll*uq{y^OI9-~$rfVv=Ozg9K{u zf!#O8%v#fMV%}gsACMl44;Vfu!Rv9#0zgmWFgE|v$aP{(n)hZE1JZ-&VZ!IIJ}YB?TvWJaj(~<9($20pi4DrCf-jVZ486a@4$X{gEQKL96p>Nov<9g93`v@)TuF) z9R1t|={!IOyc4Pe>@M~>l&r}$7<-Rv-zlXpo*Z8i1u@LDui_7Z@0}ALr23{nH`&XGG{g{)JwzyjF|4|alKc@v`T%B{h#+I*CAiM;ltZN3z4l`8N5a~R zc8^##e#-Xbt>n7Hk;50M8Zu&wsfYyAl%7wE1EjofK46l-{vD2eg{wZM)P#E7b&u^> zi7w~Zc8nqE{3W-;UywSDp0?4%sueR>j*({8aQgEbP81o(k4~z0++UI$_acR{8B*Q{ z3K%ZZiH|!+PK#+8zT9;=XrRa0Z_7E8kY$=u<1`%#QrfG*B+5IzL>$IF?a(Ck^zB52 zp03@+#Ys{_7b57er=U@S-ODG$+<);1`7ez?ufYYoy$CJNMM?Qa&?PZOgdH{=Ur!OJTz>@tYT>?Zqgb%Ts*C z6qFbSW{e0}TPLeM&>_u5bRjmNPYb5Jh1W2fOW!dJcB8KZP)! zh@f3(5S@^ZlEe$&a$RlUSoIDs&3WB)I7>NPs8`OhCMzy34`Lj@gb1hEJ>aky0S5?= z-87MII%NZ7$h8L<(Ej{;ADQmLj}w-orNf8XFCQnJz8pWvO+isYJY9`paYQ8D-XuGc z-fR-{?BhTI0M66>xhAhRlK*g;n4h^Y1REUsm5i$onplSk0N{D$MK_HhtM@}B1u;}O zYz)x=PFXOVq5!Ao9(40l#w7b>L~2kJ$_OIT7|kMM0Sq@7cy2ur^7@M!z~=K1xj{n+ zA`t9F0js5ij}Wi9#5`oj?Rwm?vyoah#;Q z_9ElL-4NOGw$rOX2dTcH&unQDQyn!M7~4wt1UeNaGw>5zJ#r)C4pTiq-3^BqUCJ{I zQes=bw+ly1wX)ht$Qc{)PMm+P1_0`(BjaJqz61Z{Rd>$r;mgY}#Yy#D1Ny$zBEIj6 zs&-bp2{~H8d`n*O0i3Qk5T?vyClILqMClTbl@+F~f8XC_`#CHovUQM>ArId$XH&uD zlSZbqG=R~Vjju8UZZ$n?m_M#hyz zaq&@0iGiX2pGjp2brG?-ej716?N#`v(nI z^~5g!C!+UvXc- z$#IJB={3(T#?Pp#;X-7Ou=^;|=0xRHm*f-7MIS#=q}=;;xZn&0p@f`_sGiy-c-;OTMOrK76WD<;118-9+?fIwvNZkBT26Qi>;}-JzAF zTQTMvb*o)64DcFrp5~QMq%1gepN@PJPKq_*^eJA?t*TPPiO5BiTjRq}BN-RyaGUA@ zgXm%Nn}yhJ+}P?(wvzU-M-y!BJzY|{9;5JqNv|ECf#=vIv*uqUa-(^Fo}O} zcI4Tc`!9;Tu_taHB=k;>J-z2?L-Y96QbR=Igq4q|viYxs&FjhA_yr9ulX!+rQ4(3^DR{ z#@}*q6KSOOw(Qjw?Mb++3h@UgvEV z!2C{w1P977$3)n39IsOKGu0&s-zs7;g5PG>HH>M4rP zv53aXR`EPhkO`{*QPp#tV7DE9AK8yX&NB~Xf&GSSPf{nJuL?7iHFo~z%)%(74I)(D zoeD&KOn*Af5T^60|9$$AuPBhc%S$h8z$y8GzaYg$^j9|dba|7Zs^kqKp$t)pXiCif z4rv539n{@wQWk)dl#MaFk92@@bYl~$e(y}KxbMxp;S*rHu zYTpC_FFsCM#t5pzB-V=V>!7hiF>8pIWr5#xLab%1cGu3;e*m~aXSjEYIp>7}UV%z^Wqa1Ktn+&E_RmD` z^>Na4Cof}!PN^=s{kGhi#Uh)NruBv{glJ9lL0LLDr{Vwn{!cltLp#Fz-bKXuDYgwq zv@O)Rp1|g8V;~}}+iikqPx1*xtoHhWV);E;aXA&ywaIp^F2hu1DDH-`?oZV!;q03f zbEz(VPf>i%59feYex(fK(InnP~@Sx#@FIW*Ab=NsBNj>o#ZJ^b0BiF zGnY<^IaxZbRnJPudCO#G*Umw57JZHqI^){F5>|1>Vt(qZjAAu8F@30oo`FB?TCYjg|LkBI)$28P8+Dl$ z+GFzj97c3MZEj$5G5NEFj?9tG>s1@3s>)ot)-1Kq7F{peynD1xS#=m|c1QHr0;Owq z39HJYh5=T0at- z19CzhYPJ{u3b8r3I|mnr(K1iV9HJGncbh7vt_-6I(PE)JvYtzhZCR}1b$$wZbv0Q@ zxouE?X*ono7|oSaR|u;TonQkZb10%NY_?ix>oHG`ZNpbsTvvLL-=~LGh^>)5#%Td< z`k`NM5d6N1LdD%)-ZdhO7DTIsj)Bj^iMg~Sq%#&*u;cI0p(HVv59p5-B3jK6UFCvv zsDh=sZE8fxCWV0?MYqqbvBmDd$~w*4 zO&>-pqAgJPwI1(N)T06qjVwfJVU#t<}AFwut&e4Vm(9# zBCEE!vvND{%wreYq_i|Mv$S-Rg~rb8-LA5;xwfozy$ZbLoxy+z1e;t9bF?ASTFa(x zsV(km=G0YO+cI@oYkAuo4X&o38)tdvnc`f7P1UaUhK34(8XDSN)lEUp7eDvT=4zS- OFE=NHmuV{Ps{#PiQhFKy literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index aa7d6427e6fa1074b79ccd52ef67ac15c5637e85..f05cbcab491a7d99c45f320bc027c9caa49f8a3c 100644 GIT binary patch literal 2866 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 Q4Um~dsP&2gQd<`R0Q-iFWdHyG literal 3844 zcmV+f5Bu;^Nk&He4gdgGMM6+kP&il$0000G0002L006%L06|PpNQVLd01cqCZJQ!l zdEc+9kGs3OD-bz^9uc|AA8?1rA#x4f-93WH-QAt;uJ6U6Yp<>o!9>IaV6aUZ*?W>} zs4%E?srLW`CJh0GCIK@hTkrW7A15Iu%N&?Q^$0+!{Tv&|t^Y@u%!L zglTg&?Q5q#ijZ;&HBQ?FNPp;k3J5!&{^+SGq?AX~SiOM9jJMRpyP?RCr@z38AQyy&WRMaC;n4una$~nJKSp?q|s8F00c9?Q! zY_ovvjTFm+DeQM^LXJ#v0}6HRt3R1%5PT*}W!k8BEM;Jrj8dIceFo2fhzTqaB3KKk zGlCLI)gU25(#u6ch6GeB1k@eHq7l{EHXv0n6xE#ws#ri}08kkCf8hUt{|Ejb`2YW* zvg}0nSSX1m=76s?sZhRY$K=3dpJ+y*eDULGnL2}4>4nvW^7_<~wIM_5fjvwt4h1|g z)g0Z6ZFq9j<~9~b8((~TN{Z?ZQfw|is&Xp~AC61sj;xItKyCHdI|tCMC_LbXF>~vR z=w6V3^H=W4CbAgR4#xw}ETTwu2guW~=Crl@SMXv85jQ=%y!s^?m4PI0My7MWICO;- z175jm%&PcPWh8QdOU(#8bp4!N7ET-+)N}N2zk2)8ch|4Q&lPFNQgT-thu053`r*h3 z_8dI@G;`zn;lH$zX3RzIk`E8~`J=BBdR}qD%n@vVG1834)!pS1Y?zVkJGtsa(sB~y zNfMYKsOJb%5J(0ivK8d+l2D2y&5X!cg3BG!AJ}910|_${nF}sC1QF^nLIhzXk-Y#x z0)&1iK!O;Og0Ky!;`b~v%b$`S4E&fB)1NB4v@8wr( z&+NX4e^&o)ecb=)dd~C!{(1e6t?&9j{l8%U*k4)?`(L3;Qjw z#w7FS+U(94MaJKS!J9O8^$)36_J8;thW#2$y9i{bB{?M{QS_inZIJ!jwqAbfXYVd$ zQ5fC$6Nc9hFi8m^;oI-%C#BS|c8vy+@{jx6hFcf^_;2VRgkoN(0h!_VSGmgNPRsxI z8$rTo0LaYq-H5i&gtj81=&xU?H-Y2==G@uQV7E`@+2E9XQW@{&j`?EOktk|Ho{HU>ZqDzvgjwBmdex z&uZNd2C1h{{}2k6Ys9$*nFP3;K%u!MhW`uZy7Sn`1M1zs@Es&;z*Z>Gsh@-3Fe6pE zQD2@cqF((NrRevgvLsvM_8;;iNyJ5nyPyy?e!kvKjGj`6diRFBEe49Oa7wwkJFV7Z z$YT&DWloYu-H?3<0BKn9L&JYDT-SK~*6c5pi18P26$JESKRYj{T7Zk6KiRJcbvOO*{P56Q6s8msbeI3>|j>K9}Q9UBeq*inXKemCm`-<5|-$ZyN4u$(3 z&HcvqehFD%5Yrmykg-^d`=BSa8(i=>ZoC77^mWY{evp(km@aHqhUECBz76YiR+VYK zY_avFC~V3$=`6C4JhfHAQ@DZtUOwH`L;oYX6zK0-uI^?hS$ALfq}A7evR;ohJHij} zHSZdW?EKv9U1s4oD*<(0oQ*;MaQ6@cvGL zuHCPgm_NhVsgp^sfr*ia^Db}swo1?O(_Q2)y+S$CBm+g=9wCOUPbz(x)_GbaKa@A7 zuI&!ynLiZRT#V%_y_-D`0Z5lT*auoe{(U5NylTzFSJW()W-#F6*&A`LNO1bV#Y;QJ zSbLBnp|B^dtK|KIWC|No>JjWBWE@n7O)x{&^E(WMeMvp57#qA8m* zeTow*U@_86B#Fm*rxyYu5PRWaWHx8y> z*qmHEp(AMDl0v)ij(AY8fnH=~ZwwjVAbu*m5;xPfidh@ov6d8g zfJsi&!QyK53Es%sC39ts;54V68koALD4b|%tNHW0bIkZAJKa=W&FomJSEDT>W1xIX z1x%Z>AvNIsSPLcn3RTcHXb@KB?cuM)=x6fcIx>&(GxqZ8w3p#jJ(GVgc*`c0HG}dv zIop&Qim!K1NFwic%07KcjWgHBPUkq7f~lj;TPqVGTiT#cUeim>;nY`>h@a*S{qQex zQ`z62WK|Mj)Y{tfF{;T4P;c8$Q|KU?Joh zIkA^z%X7z|r>4aTh@|StTi!-r1D!g=zb#3d#{{&K3CqE$Iz-UH<%37c zRfkO`&uM%#AD3PHv`g5t0e^O%nVL0d{Xlx^EjEC3#skF@`zl-7PF^0oxW)1!C!JxR zWvuAHH?)61FKA1QeT*_sY7;_Id#!GmV4n`MO{~sv}VLSK` zXRw=Y=Clz*00B(5y^K;gCZMAzjT5+c3IC=)l(9VIDdatpxj3y89WwI|bH&$!ZEvp` zPR!T@#!(|KfI-w?!&+7$N3F6>tD{YO4Qg$d_`nNEdfVCha9vaPn0jI0`)`@*72hq! zpU5ND^P*RoEkbD5o#az(-g=Y)L>HH>Oc%}$ zT3Rs_ih0;4+Lv4Y;@Iv(;fUbQ=i-G(#>vghec~*j(I#r|5mqFiJBpzi&hzEcD{u$< zRsm0BVYn=pT;0>R(itW|*D&;O%bOc7et9ACaH#J>z3A1A~6fdP>pmbM%xzm4>|;c_?B+%sl;Qs2{t!60$^u zH1t@9^6>;?!FuusnISi$f5CL&;z?EqJN$FBuWDA#D5`cy_UvCFIVvf{c?4N0teh;d zET$7aVbj08KTQS!x?Nd1Is8q8qFzs}a=!@nJ;7FSfCY^T@D-gpw`w<6e#X3+;O}1h z$%I!M)0bg|EKUA04Qjn@+x{Rj8vt6Wn!R|3A92z}^$KfF5(#CWr4y#~re1CN4i4w0 z#GsypBR{xA3Er7sgAi(|}1-W?s~n$7?K|9WL8kpVfw-;#b9 z+mn;=ep!162U5R>_t}fOt~tE?s#m( zO-S$7>Ay6*hHdZ)7_oU915WYYCIX;hFI-U2EWYX!pllONr@Q--2o~`!isi6vTPLJ4@(|o=%NHYjo0_S&q*UQIROw@*N-By@PaQ&;YxFZ0aR zX&}LeOEz);#m~Hwm^VAY8DK}b$F4bo{jMN?d!lxKPhNklzr^Cd`0f4oJr^z=I|l`* zm8AHm*fPV`0=lF3Pnnp}&J0N1X@}-D94YvmUabFrLGSnTz7Mu^21F#O5tN#CuY9Vh zUZBH=ez%h*wkf0hBtXJh1SN3d+IF{gzT7lp)j}n?03lt;XSQRAh7qd&v;RwTYDuQ# zbI2*r<>?x-G0@hM{;%{VBD7nLKt~D`T~-HAt5;h%i0_=Ifs=yHma5dhJ+QMG?Ux(a z|E?1CMy1!~oA`FP!k~iG=t&5#>bVdz=peT8HMB6Y)#7PpETtNryT^+Rv3vpJaF^zP z{H}0-LyV9Fu21ID%wO9f1IKlFr1p4c{o-?03vyB-tr5duk^&L$;m_|f$vs`^Sl{j2 z95}oY{LlY+=ZS%J+tZoXCd0*sSU7w^gjovXn+g7uyra5{cU49@yHf#Z^Jl-$9cIfo z+AJuxH$VLb=#+uBbVmUjnx zxb1pZ@-O9=AIk4@S)m6fJ2?{HrNYwwnL3a45muuNjr;6$O`bGEM0T4A2_S$t=86*- zcO+0mywg*jzrZgL6$j(CjU0*nm%ZWgy(3}*`o~VY z5NCB(7o6|3-~^X+>fj7j9CLk7v?K(&?P@ zw#~6^+qP}nwr$(CZ5xYw-}^i7d(M0E*W!$=+jqU&87t%YRo(SwW_iZS%q(tIr`LDP zRGk`6*0wvV#@fo(S=+YRStB*eq&(ZUB}tMbN$>xEMSntnK!-Z-TQefK_cZ|UpnE|H z5&(jcR6APRwr$(CZJRIKwr$(C&NkNGjL{S&TdiA5YvYlF5YByB684-Au=H%(qgc+n zDLUtzyP;@e=l}oX&PPFa^>oh4oT#Y zp!$9Y_7sp1P#%wEN1XZmL_$55!6eHQ>yOyd*_H>&7n_M`vww2PEI?YxET2k}*CMD( z*yxde`Qt9v{_Be0#6syJrcle{Hzi;&sPA;o*OM$BUCs^1iD0rwvMej}@_a{2s%p0>31Yt*DH4Suj0I+fVQf z!K*~%P~!Ffdq!EAZONy|-3+HZek;NlZ@I=@D{BwZ*1Ut@JyMF38BbaqB^mL6VkjLP zhm^{`|9D*E$=}JcwCX1jTp@UaF2xYU#>C4^NMdi3lH#3T3|+dwI%$`4 zsqd}tJB8mij9AI463ileKoEsa5yV9woP??!#MX}1XmF;&fTMx zBMuRL|2o$&{FdJGWdEchZ7AVauK1WF)L2O|uJ8j1hmn?APibst#HYVT@VzTO4uw#X z>+2u+wNE<(Y<*nBHf++m5j+OY1Uh+MmTy3%{xJ%%A6H^BkKl!H-N#m69i z@TrF35jRZIE(FgNMhQCT<6jjt%JvwKBUq<2O58y**FVni*dm6#nBZ%`E4=f-aI5Bw z5-&{VAc8N7UV&3aqs%Wd2a6WvH!RYY0A8IqWh`|_uu$eVLYR)=IARLwNHK_#o=E2z z1Tw!Nv%vE(B@uO{_!N}!B+LqnVz%FqS)Q~%m~!agvJ(Extd$JEEyn#?VtslpRDqxejH#&dkOlT-UY?$y$*XU^{YAm)($Qlx6H?>@?c)kG6u)CbvLlYiC%t z=I8d|$Fp3LPy=R^v|8CnY$hmeC{}y=pK3J%Ur|P6Xmql*KK0vjYsqxvhF*tdiV13g ztK)7*fh~;fZNJ9HU(RAVvcQDf#$q#}piM=uQ$Spk5I96r^V@{w2wt)r`C!cCH5Ii% zIa1)xaJ~xDDjJ&K_!q%%EZGA6O%E~|w0>zLh?`8NaQ>0WiWpj%$M}uRM@$!L%&qd6 zOq%~HY6P}0eH!E$HCjYkhAs05(^BiZqa>42J$EQ>rwm~@;WI2PE@;>?7h+-@1NmuP z!OAR$z$<`6i9&saZP;35kkJ=05gF*JGg(=uh}(_v7)~KN%=T+=glk2DkBlqb2{&fE z-E<A<>Sp^=&3XPz2IiAS(9^C5mMq%mjDC8b|4$mfut7je}%veHn=%Sh)z z2elBVQi~&wb;8J}^0}CZ9IDr>P}rFDDx`E!3TezQzs$?5OG4YWWS-!ykagK@AXr#S zwB&SAO*>HO2#}IatI%&rY>ybnM{XK1F}T(i-FEsY<=Ey893< zG18Dze?w!2F8%4JN$+EQVXNka+gqc(Z z>*J81D6TmtS;lFM#t2Q(4+z#P4hgeup8oJ}b%JO-IASBdOeT#9(#AYVd_EbgF$E(y zrDRqab%)Q#(I5KFN$@wuM0CWYhS>krY9WjK6v~V*oP!XP6z^?)4|5)nra9_W3=}Yp zn7)SDF6TFzfFD)Ssm^s(Ujq})D=84G} z8Epl<1S|X$kQs@rb zsV`aL6sG+~A+wGEnPG#;LGO8-si@_ij=IZqVEYRj#^Ynz8?MW)Y)Q(Yjdmf955+gbNNlS%*lMcqaR zQ_tI{y6oDfu?`85I1ULCl33_#fHxQ&27p@JvX9BC1+SJug6M;H=MAJjw)W{Y%^eaZ z<0Rvx(|#~Pz0RHNH1TG#YZKAOBzlVfIpuz0O~FyV7ZCizNRwve0y6e?nH$Zz)e8sa zx5nFQGNMnNDt#Qk*m05Na~L`x#HGgoWiO8w8C>h^if!ijnR?uYM+omA$F!ZllY$>@+IGA`pg85wLcK{fB(icjkm zUd(U(qo5L(gzrFM7l9l{vSLqibyL6Bwz{{0!t52^7OjrW#tW3@}x1#9ce8EiUoKKx)X(VM;w0&kN@S_F`oL zbs{{ge1T$^qzdLg=RVpDc|g#Q`@48d2)|FEBT_3baSL+~W`~g%khMyS&h0;H>8N&s zpdC(`Z!0HTN>uRwlrnCygHHjAWZtLKwO6Y0r_P|cu1>seZN#D%HqVrF>-@b7dWGqWzigV|vCP4XT|(T2N^zDe9VsBbR)QlaL}@Y zAC!!csN2}vmU#3{N;sv9(~<81aioe8FQKII95i5-f8ZB%3L0uMn&Hqjc(4TN{fD`*E)(@r8=@=@Eb|o~wJDZ2)C#H3a=Ss+VNMF}GLNO%dz#hnC-Cf!&lK_NhM5Wm%GL0mIsdiM5 z@ty;gWl9HuqCvC5!?$0}2ci?AA1j;nV54M;Tun;shldiS z_ir4MJ@PydAPbO%^AayOYBE7RhQ68qn2YsGb53N+94tXPzjIB=_gGNTzL*Wi9xTO4 zPXo-j3;hoB=!x&+R$}vjAV2k)pCsE5p9APHF2;j7DL^m_UP$R8K4vUp}`YKDsypSO!lTqaeG&pGy%lG+C{Kn_5rkM7R887F7M z%c>Vr6je(Q8xBQQxAstJ|ni04%xDPe|wWCKLiUYp<(#&a^@WP-W~DJ}f)Exr1j z1STFc#^;K&^*1W~UkZ>2kO|~hkcB1w!98$qm|>>`itYG1Mq8pqo_5m5f4A6Wij}l% z1DZI2Jcncr=QVsMC(XIl3dl!t1I1T16?`277BG}q{J>9xG1-8qBF zn(TVudz^q1PZn3r?~xXcy1)!RAtlb4bGqZ>oXSlJ8b+!V8H-lgTYXa{+b|FE4qj$? zzaTAa#=%Uo1t@yx_}D-!Uw+G{C7*tW+@RU4-;l+--dogrjsm{Y`RallCQa0%E(V$P zD5Ts9YMN@Jvibg+JvPMS0$|LS^ubtBoklS#O(sp$U>3PRR(j4tN~Xq|<*3hS)ic#g znS&_HHsFC>kaejyU0G4GL{1ocF+shMW{N{=yS(cmph=H<$3~}Hy%Wkc5+3*_Kt7P% z^4}MviI*_l)IO#)=Gx*P`IQ!%4!Pd-bg?KQ5)KRkG@xJDJ6GLxCwU9x><3NTVclYC zuBna`^&_{WalCYMFxghXSMERtXk;;f^seKx9s$4Nn2jj%z`U3Cn9DvtNKkvxs5Y3a z$<}t{WuFpOxO)+RYP?G?*;lgDz$yiPl|$sa>s9M6jQYxs+2i>w*EE7XfRus9}^k;_WU6^l}YNkWJe2 zV1gPX(RZwb|Nl$~oBL|osdw4RuG(OtricC%ui1(?EC<%%F1@tAkUn{)P<(Rx%T`dW z@R&mAo~?y!px$SNe)LauYlmnHkx`g={4T((_Pu0-8!^uXS-E`F{T0Z^i(wxdddja< z8;!@Qf=g?sS21ZDO0%STG)OL;1=JhhU+MGR_--%520{$mSWL6kp;VeQRR$9=!rX>R zE30BsueB9^VLv8&_WP9z#T%d^KVm9pilK#`W{o-(Cz)QgP{<0 zBqEn0VU~XCH|Vauhus9R^+eemq(a5UFjeaU-lsHkrjPBe2#rkT#r>P}W9~rAC+Gp`cp$GU4hC_=QLJAk*U9qUt*Ux$cc zD?c4}*@>$ylhNXbrz_W*Y8{Iv!_)~-fDUkk+VxbvxxYc9@GSqpp%VOS%mEF%cEZfp zJy&_wESGOkR25sjZKx?P@yz*Qs=EFVa3uj6Et+fGkhldcV5!!JJb<%X>Bna5)^(@+ z7o(_Y+_f1x>C{wb-HeBq#k^AfLu$>7;J1oUl4oO1(V3_z2}8eiVv8FNSA4B zxvAaod$yHu7Xw?0N<3B9)|WrT9LM_m%Z4)L2cs zkC;p3{2%Y`_dq{_tmIbt+3UQ7Pvm&)u6VXfRNhj z9>%tid`mu1T5-wzd*9S8pwT^=IdL)Dj0JE?eb&~>k`D6_J;3xcQwR;0KoaUT^IZQl z8FcZ4OYI8YFDV7%XKAhpYqm&d=IS;A+5&c2rQbXtxAzhld-DnhVhxJ%A^b<0a?pxp z-T;pW*cA(a&Pd?-nv@Bkzn`1P)^mz1FW{Rzi;uQ=#RD(vrdv~(EZXh20XN?Xc|?Y@ zTkiWen{@F!a}S^@c%-p<>(vCQmY>F2ekLB6@b4ur7M|XaA#N2)WiVY^JuzO_n!}YO z2vBCgjv*-gzVjt=ZO1W|i^T)6CNtCFRZjSIV8MmQQ;dQ~N?zi%a_%M!Qna)kz`%b6zZuBXDQj?By7JkvA|J)ZOCH+-c+ zu066@x8$>F5f98oU3Wi*dz*9tbn__%Q@KK)WB@|P4zCJBN`M1kWN~(314#(+?}MfF zig+M4Vir1}-Y1~D&mf7b0DA|gGlqW%kOwH6#&)LbQ(kFroiEu)+7*qN$+tC&cwilB zvh8BG<Brf7 z7W9-&;V@Zq*>5LxU~0LZa&cz>jK_vKgTob6L4{4DuA5=HW07l@b&E&@&YJUB`FVbV z8!Jnys07owlRgu0y^6`lw|+G8F%Q7b`U&@|N25AQD5_5GZBSk0?yLHHqXyAE`<^(? z1Ds~XbXzZ~=mN4Hwl z=1;fQxy2lV$IG6nk`duf-mGhPSJfCmZWiGI(BP?^!T#NvAXad=%Vg>FdGD*wzSA7l zB~mxW?<$EvbS0}}27j(+i~dGXysY4Mr$ykB%U{;2rLwCQH`cjBt>V=+eLRnQr*3<{ z5fra(y?fsN;CLfiakNL36T|_658U1=yq(kgzhU&wBwhZDt(aq~i|6~^&kpba5!anT z_2hs$AOZ`0ueMxI-LG!GmRMQR^9I~sJSvD_0++gF*Uqef#UXqF&l`rrU)gM(s{Blx zcRF8X0fW8Q`n-}=f|frS=|;CXWI#7mp2`<54M&(ygViV2Q;t=>q*L-p%v z8VbM=rfcK2b5k|n(gKBGsJY?g z<)^ZM7O((bJ9~XP*Zj`ipf;>$qipV9O!Wqg!mK@WZ#Da>^2x449J2s9UWNl47;J_f zy6vTYb9=#HX~&=6n0=peZsT!#a5~c&_70Bm7Ne*`AVU=aq8^F?$d18b`2ITUpZ5-Y z)CMf#JFzE?&lA1iI%@|IU<=jrs3gOo`E^rqf3_OPA+Y<^{HQ|z4JRmSLm>%@YPjY4 zKb?EJUu`;*$pxxee|(ja0;MQE%$s9?m&V{QgrROHiBk%XxKH?~4dnS6ZiONS ze6-3P0lXTg@6?&>C6ausP~70O`4Et}ArPPKgj?Y;Jf2L}vTq@e1-wVNYBX=Mpq%s} zvQrb}ss@f^>@+R3++xEsa5@jo#z&>VePG+CG}OB0-M5{X-?07O{}0cgvOv@J%#lFV zrHRr)ML(&9<3R4i^(1%1V;5Lj==oG1>+#||fdFU2MiI&+NNTA+Z_1W9aBshNx=)Pt z#47^L3U<#CF6nyssd%_X@N{5Wj;mUVn>W}~$}f#)ZyB4h-LKX~6 z{mPSmRpP*Z2gSGFs5UND6Nf(gn&)181F3a^AkZ}XMH*l{2niv*KUfA*wY^Q~I0$mD zi%D{T^~f0j3E1UczD6S{G7NF&dh@458Fz?ICQ zD#N?7UZdX>EQfU?5rcz3S>nOsQ^x$mH)o@+I?po^BDL95|(ArVk0A+(lCrNZH`g<#=uIF(A( YG_k8`Z>%VSMTq7=79m + + #233333 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fe9cb27..c04c3ba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - TvBoxDemoPlugin + HANIME1.ME \ No newline at end of file diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/PluginProvider.kt b/app/src/test/java/com/muedsa/tvbox/demoplugin/PluginProvider.kt deleted file mode 100644 index 047d473..0000000 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/PluginProvider.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.muedsa.tvbox.demoplugin - -import com.muedsa.tvbox.api.plugin.TvBoxContext -import com.muedsa.tvbox.tool.IPv6Checker - -val TestPlugin by lazy { - DemoPlugin( - tvBoxContext = TvBoxContext( - screenWidth = 1920, - screenHeight = 1080, - debug = true, - store = FakePluginPrefStore(), - iPv6Status = IPv6Checker.checkIPv6Support() - ) - ) -} \ No newline at end of file diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaSearchServiceTest.kt b/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaSearchServiceTest.kt deleted file mode 100644 index 7592851..0000000 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaSearchServiceTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.muedsa.tvbox.demoplugin.service - -import com.muedsa.tvbox.demoplugin.TestPlugin -import com.muedsa.tvbox.demoplugin.checkMediaCardRow -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class MediaSearchServiceTest { - - private val service = TestPlugin.provideMediaSearchService() - - @Test - fun searchMedias_test() = runTest { - val row = service.searchMedias("GIRLS BAND CRY") - checkMediaCardRow(row = row) - } -} \ No newline at end of file diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/Checker.kt b/app/src/test/java/com/muedsa/tvbox/ha/Checker.kt similarity index 91% rename from app/src/test/java/com/muedsa/tvbox/demoplugin/Checker.kt rename to app/src/test/java/com/muedsa/tvbox/ha/Checker.kt index 928a6ab..6a72825 100644 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/Checker.kt +++ b/app/src/test/java/com/muedsa/tvbox/ha/Checker.kt @@ -1,4 +1,4 @@ -package com.muedsa.tvbox.demoplugin +package com.muedsa.tvbox.ha import com.muedsa.tvbox.api.data.MediaCard import com.muedsa.tvbox.api.data.MediaCardRow @@ -25,6 +25,6 @@ fun checkMediaCard(card: MediaCard, cardType: MediaCardType) { if (cardType != MediaCardType.NOT_IMAGE) { check(card.coverImageUrl.isNotEmpty()) } else { - check(card.backgroundColor > 0) + check(card.backgroundColor >= 0) } } \ No newline at end of file diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/FakePluginPrefStore.kt b/app/src/test/java/com/muedsa/tvbox/ha/FakePluginPrefStore.kt similarity index 95% rename from app/src/test/java/com/muedsa/tvbox/demoplugin/FakePluginPrefStore.kt rename to app/src/test/java/com/muedsa/tvbox/ha/FakePluginPrefStore.kt index d06171a..4d69f35 100644 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/FakePluginPrefStore.kt +++ b/app/src/test/java/com/muedsa/tvbox/ha/FakePluginPrefStore.kt @@ -1,4 +1,4 @@ -package com.muedsa.tvbox.demoplugin +package com.muedsa.tvbox.ha import com.muedsa.tvbox.api.store.IPluginPerfStore import com.muedsa.tvbox.api.store.PluginPerfKey diff --git a/app/src/test/java/com/muedsa/tvbox/ha/PluginProvider.kt b/app/src/test/java/com/muedsa/tvbox/ha/PluginProvider.kt new file mode 100644 index 0000000..2c0aba6 --- /dev/null +++ b/app/src/test/java/com/muedsa/tvbox/ha/PluginProvider.kt @@ -0,0 +1,35 @@ +package com.muedsa.tvbox.ha + +import com.muedsa.tvbox.api.plugin.TvBoxContext +import com.muedsa.tvbox.tool.IPv6Checker +import com.muedsa.tvbox.tool.PluginCookieJar +import com.muedsa.tvbox.tool.SharedCookieSaver +import com.muedsa.tvbox.tool.createOkHttpClient +import java.net.InetSocketAddress +import java.net.Proxy + +val TestPluginPrefStore by lazy { + FakePluginPrefStore() +} + +val TestPlugin by lazy { + HaPlugin( + tvBoxContext = TvBoxContext( + screenWidth = 1920, + screenHeight = 1080, + debug = true, + store = TestPluginPrefStore, + iPv6Status = IPv6Checker.checkIPv6Support() + ) + ) +} + +val TestOkHttpClient by lazy { + createOkHttpClient( + debug = true, + cookieJar = PluginCookieJar(saver = SharedCookieSaver(store = TestPluginPrefStore)), + onlyIpv4 = IPv6Checker.checkIPv6Support() != IPv6Checker.IPv6Status.SUPPORTED, + ) { + proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", 23333))) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/PluginTest.kt b/app/src/test/java/com/muedsa/tvbox/ha/PluginTest.kt similarity index 95% rename from app/src/test/java/com/muedsa/tvbox/demoplugin/PluginTest.kt rename to app/src/test/java/com/muedsa/tvbox/ha/PluginTest.kt index 2a686ad..7cd6f84 100644 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/PluginTest.kt +++ b/app/src/test/java/com/muedsa/tvbox/ha/PluginTest.kt @@ -1,4 +1,4 @@ -package com.muedsa.tvbox.demoplugin +package com.muedsa.tvbox.ha import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MainScreenServiceTest.kt b/app/src/test/java/com/muedsa/tvbox/ha/service/MainScreenServiceTest.kt similarity index 52% rename from app/src/test/java/com/muedsa/tvbox/demoplugin/service/MainScreenServiceTest.kt rename to app/src/test/java/com/muedsa/tvbox/ha/service/MainScreenServiceTest.kt index 1cceaa2..fef2dfa 100644 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MainScreenServiceTest.kt +++ b/app/src/test/java/com/muedsa/tvbox/ha/service/MainScreenServiceTest.kt @@ -1,13 +1,13 @@ -package com.muedsa.tvbox.demoplugin.service +package com.muedsa.tvbox.ha.service -import com.muedsa.tvbox.demoplugin.TestPlugin -import com.muedsa.tvbox.demoplugin.checkMediaCardRows +import com.muedsa.tvbox.ha.TestOkHttpClient +import com.muedsa.tvbox.ha.checkMediaCardRows import kotlinx.coroutines.test.runTest import org.junit.Test class MainScreenServiceTest { - private val service = TestPlugin.provideMainScreenService() + private val service = MainScreenService(okHttpClient = TestOkHttpClient) @Test fun getRowsDataTest() = runTest{ diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaCatalogServiceTest.kt b/app/src/test/java/com/muedsa/tvbox/ha/service/MediaCatalogServiceTest.kt similarity index 72% rename from app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaCatalogServiceTest.kt rename to app/src/test/java/com/muedsa/tvbox/ha/service/MediaCatalogServiceTest.kt index 0ec7d24..8ecc77a 100644 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaCatalogServiceTest.kt +++ b/app/src/test/java/com/muedsa/tvbox/ha/service/MediaCatalogServiceTest.kt @@ -1,13 +1,14 @@ -package com.muedsa.tvbox.demoplugin.service +package com.muedsa.tvbox.ha.service -import com.muedsa.tvbox.demoplugin.TestPlugin -import com.muedsa.tvbox.demoplugin.checkMediaCard +import com.muedsa.tvbox.api.data.MediaCatalogOption +import com.muedsa.tvbox.ha.TestOkHttpClient +import com.muedsa.tvbox.ha.checkMediaCard import kotlinx.coroutines.test.runTest import org.junit.Test class MediaCatalogServiceTest { - private val service = TestPlugin.provideMediaCatalogService() + private val service = MediaCatalogService(okHttpClient = TestOkHttpClient) @Test fun getConfig_test() = runTest { @@ -20,13 +21,14 @@ class MediaCatalogServiceTest { check(option.items.size == option.items.distinctBy { it.value }.size) } check(config.cardWidth > 0) + check(config.cardHeight > 0) } @Test fun catalog_test() = runTest { val config = service.getConfig() val pagingResult = service.catalog( - options = config.catalogOptions, + options = MediaCatalogOption.getDefault(config.catalogOptions), loadKey = config.initKey, loadSize = config.pageSize ) diff --git a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaDetailServiceTest.kt b/app/src/test/java/com/muedsa/tvbox/ha/service/MediaDetailServiceTest.kt similarity index 73% rename from app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaDetailServiceTest.kt rename to app/src/test/java/com/muedsa/tvbox/ha/service/MediaDetailServiceTest.kt index 0898d4e..7de9d43 100644 --- a/app/src/test/java/com/muedsa/tvbox/demoplugin/service/MediaDetailServiceTest.kt +++ b/app/src/test/java/com/muedsa/tvbox/ha/service/MediaDetailServiceTest.kt @@ -1,15 +1,15 @@ -package com.muedsa.tvbox.demoplugin.service +package com.muedsa.tvbox.ha.service import com.muedsa.tvbox.api.data.MediaCardType -import com.muedsa.tvbox.demoplugin.TestPlugin -import com.muedsa.tvbox.demoplugin.checkMediaCard -import com.muedsa.tvbox.demoplugin.checkMediaCardRow +import com.muedsa.tvbox.ha.TestOkHttpClient +import com.muedsa.tvbox.ha.checkMediaCard +import com.muedsa.tvbox.ha.checkMediaCardRow import kotlinx.coroutines.test.runTest import org.junit.Test class MediaDetailServiceTest { - private val service = TestPlugin.provideMediaDetailService() + private val service = MediaDetailService(okHttpClient = TestOkHttpClient) @Test fun getDetailData_test() = runTest{ @@ -18,9 +18,11 @@ class MediaDetailServiceTest { check(detail.title.isNotEmpty()) check(detail.detailUrl.isNotEmpty()) check(detail.backgroundImageUrl.isNotEmpty()) - checkMediaCard(detail.favoritedMediaCard, cardType = MediaCardType.STANDARD) - check(detail.favoritedMediaCard.cardWidth > 0) - check(detail.favoritedMediaCard.cardHeight > 0) + detail.favoritedMediaCard?.let { favoritedMediaCard -> + checkMediaCard(favoritedMediaCard, cardType = MediaCardType.STANDARD) + check(favoritedMediaCard.cardWidth > 0) + check(favoritedMediaCard.cardHeight > 0) + } check(detail.playSourceList.isNotEmpty()) detail.playSourceList.forEach { mediaPlaySource -> check(mediaPlaySource.id.isNotEmpty()) diff --git a/app/src/test/java/com/muedsa/tvbox/ha/service/MediaSearchServiceTest.kt b/app/src/test/java/com/muedsa/tvbox/ha/service/MediaSearchServiceTest.kt new file mode 100644 index 0000000..db7ed41 --- /dev/null +++ b/app/src/test/java/com/muedsa/tvbox/ha/service/MediaSearchServiceTest.kt @@ -0,0 +1,17 @@ +package com.muedsa.tvbox.ha.service + +import com.muedsa.tvbox.ha.TestOkHttpClient +import com.muedsa.tvbox.ha.checkMediaCardRow +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class MediaSearchServiceTest { + + private val service = MediaSearchService(okHttpClient = TestOkHttpClient) + + @Test + fun searchMedias_test() = runTest { + val row = service.searchMedias("") + checkMediaCardRow(row = row) + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 34fd4fa..1509d7a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,7 @@ dependencyResolutionManagement { } } -rootProject.name = "TvBoxDemoPlugin" +rootProject.name = "ha-plugin" include(":app") include(":api") project(":api").projectDir = rootDir.resolve("./TvBoxPlugin/api/") \ No newline at end of file