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