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 0000000..82d25f6 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ 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 c209e78..5a54799 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..a94da2d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d6..f38b0eb 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..353f3a2 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a307..621a149 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..9443703 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index 28d4b77..9cd67b6 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..59f5546 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index aa7d642..f05cbca 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9545fc5 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..38f1b72 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #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