Skip to content

Commit

Permalink
feat: 为视频播放添加请求头&重构路由
Browse files Browse the repository at this point in the history
  • Loading branch information
muedsa committed Oct 18, 2024
1 parent d48791c commit 8c0ee89
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 182 deletions.
2 changes: 1 addition & 1 deletion TvBoxPlugin
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ fun RightSideDrawerWithNavDrawerContent(
}
}

class RightSideDrawerWithNavController(
val navController: NavController,
val drawerRoute: String,
class RightSideDrawerWithNavController<T : Any>(
private val navController: NavController,
private val drawerRoute: T,
) : RightSideDrawerController() {

override fun pop(content: @Composable () -> Unit) {
Expand Down
58 changes: 15 additions & 43 deletions app/src/main/java/com/muedsa/tvbox/screens/AppNavigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.dialog
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.muedsa.compose.tv.LocalNavHostControllerProvider
import com.muedsa.compose.tv.LocalRightSideDrawerControllerProvider
import com.muedsa.compose.tv.widget.FullWidthDialogProperties
Expand All @@ -21,81 +22,52 @@ import com.muedsa.tvbox.screens.setting.AppSettingScreen
fun AppNavigation(navController: NavHostController = rememberNavController()) {

val drawerController =
RightSideDrawerWithNavController(navController, NavigationItems.RightSideDrawer.route)
RightSideDrawerWithNavController(navController, NavigationItems.RightSideDrawer)

LocalNavHostControllerProvider(navController) {
LocalRightSideDrawerControllerProvider(drawerController) {
NavHost(
navController = navController,
startDestination = NavigationItems.Main.route
startDestination = NavigationItems.Main
) {
// 入口页
composable(route = NavigationItems.Main.route) {
composable<NavigationItems.Main> {
PluginManageScreen()
}

// 当前选择的插件主页
composable(route = NavigationItems.PluginHome.route) {
composable<NavigationItems.PluginHome> {
PluginScreen()
}

// 视频详情页
composable(
route = NavigationItems.Detail.route,
arguments = NavigationItems.Detail.args
) {
MediaDetailScreen()
composable<NavigationItems.Detail> {
val navItem = it.toRoute<NavigationItems.Detail>()
MediaDetailScreen(navItem = navItem)
}

// 播放页
composable(
route = NavigationItems.Player.route,
arguments = NavigationItems.Player.args
) {
PlaybackScreen()
composable<NavigationItems.Player> {
val navItem = it.toRoute<NavigationItems.Player>()
PlaybackScreen(navItem = navItem)
}

// 设置 Dialog
dialog(
route = NavigationItems.Setting.route,
dialogProperties = FullWidthDialogProperties()
) {
dialog<NavigationItems.Setting>(dialogProperties = FullWidthDialogProperties()) {
AppSettingScreen()
}

// rightSideDrawer
dialog(
route = NavigationItems.RightSideDrawer.route,
dialogProperties = FullWidthDialogProperties()
) {
dialog<NavigationItems.RightSideDrawer>(dialogProperties = FullWidthDialogProperties()) {
RightSideDrawerWithNavDrawerContent(controller = drawerController)
}
}
}
}
}


fun buildJumpRoute(
navItem: NavigationItems,
pathParams: List<String>?
): String {
var route = navItem.route
if (navItem.args.isNotEmpty()) {
checkNotNull(pathParams) { "route nav failure, $route#$pathParams" }
check(pathParams.size == navItem.args.size) { "route nav failure, $route#$pathParams" }
for (i in 0 until navItem.args.size) {
route = route.replace("{${navItem.args[i].name}}", pathParams[i])
}
}
return route
}

fun NavHostController.nav(
navItem: NavigationItems,
pathParams: List<String>? = null
) {
navigate(route = buildJumpRoute(navItem, pathParams)) {
fun NavHostController.nav(navItem: NavigationItems) {
navigate(navItem) {
launchSingleTop = true
}
}
87 changes: 32 additions & 55 deletions app/src/main/java/com/muedsa/tvbox/screens/NavigationItems.kt
Original file line number Diff line number Diff line change
@@ -1,58 +1,35 @@
package com.muedsa.tvbox.screens

import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType
import androidx.navigation.navArgument

sealed class NavigationItems(
val route: String,
val args: List<NamedNavArgument> = emptyList(),
) {
data object Main : NavigationItems(route ="home")

data object PluginHome : NavigationItems(route = "plugin_home")

data object Detail : NavigationItems(
route = "detail?id={id}&url={url}",
args = listOf(
navArgument("id") {
type = NavType.StringType
defaultValue = ""
},
navArgument("url") {
type = NavType.StringType
defaultValue = ""
},
)
)

data object Player : NavigationItems(
route = "player?url={url}&pluginPackage={pluginPackage}&mediaId={mediaId}&episodeId={episodeId}&danEpisodeId={danEpisodeId}",
args = listOf(
navArgument("url") {
type = NavType.StringType
defaultValue = ""
},
navArgument("pluginPackage") {
type = NavType.StringType
defaultValue = ""
},
navArgument("mediaId") {
type = NavType.StringType
defaultValue = ""
},
navArgument("episodeId") {
type = NavType.StringType
defaultValue = ""
},
navArgument("danEpisodeId") {
type = NavType.LongType
defaultValue = -1
},
)
)

data object Setting : NavigationItems(route = "setting")

data object RightSideDrawer : NavigationItems(route = "right_side_drawer")
import kotlinx.serialization.Serializable

@Serializable
sealed interface NavigationItems {

@Serializable
data object Main : NavigationItems

@Serializable
data object PluginHome : NavigationItems

@Serializable
data class Detail(
val id: String,
val url: String
) : NavigationItems

@Serializable
data class Player(
val url: String,
val httpHeadersJson: String? = null,
val pluginPackage: String,
val mediaId: String,
val episodeId: String,
val danEpisodeId: Long = -1,
) : NavigationItems

@Serializable
data object Setting : NavigationItems

@Serializable
data object RightSideDrawer : NavigationItems
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
package com.muedsa.tvbox.screens.detail

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.hilt.navigation.compose.hiltViewModel
import com.muedsa.compose.tv.useLocalToastMsgBoxController
import com.muedsa.compose.tv.widget.ErrorScreen
import com.muedsa.compose.tv.widget.LoadingScreen
import com.muedsa.tvbox.screens.NavigationItems

@Composable
fun MediaDetailScreen(
navItem: NavigationItems.Detail,
mediaDetailScreenViewModel: MediaDetailScreenViewModel = hiltViewModel()
) {
val toastController = useLocalToastMsgBoxController()
val uiState by mediaDetailScreenViewModel.uiState.collectAsState()

LaunchedEffect(navItem) {
mediaDetailScreenViewModel.refreshMediaDetail(navItem)
}

when (val s = uiState) {
is MediaDetailScreenUiState.Loading -> LoadingScreen()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.muedsa.tvbox.screens.detail

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.muedsa.tvbox.api.data.MediaDetail
Expand All @@ -26,6 +25,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
Expand All @@ -38,23 +38,25 @@ import javax.inject.Inject
class MediaDetailScreenViewModel @Inject constructor(
private val danDanPlayApiService: DanDanPlayApiService,
private val favoriteMediaDao: FavoriteMediaDao,
private val episodeProgressDao: EpisodeProgressDao,
savedStateHandle: SavedStateHandle,
private val episodeProgressDao: EpisodeProgressDao
) : ViewModel() {

private val _refreshMediaDetailFlow = MutableStateFlow<NavigationItems.Detail?>(null)
private val _refreshFavoriteFlow = MutableStateFlow(0)
private val _refreshProgressListFlow = MutableStateFlow(0)

private val _mediaDetailFlow = combine(
savedStateHandle.getStateFlow<String?>(MEDIA_ID_SAVED_STATE_KEY, null).filterNotNull(),
savedStateHandle.getStateFlow<String?>(MEDIA_URL_SAVED_STATE_KEY, null).filterNotNull()
) { id, url ->
val plugin = PluginManager.getCurrentPlugin()
Triple(plugin.pluginInfo, plugin.options, plugin.mediaDetailService.getDetailData(id, url))
}.shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(60_000)
)
private val _mediaDetailFlow = _refreshMediaDetailFlow
.filterNotNull()
.map { navItem ->
val plugin = PluginManager.getCurrentPlugin()
val detail = withContext(Dispatchers.IO) {
plugin.mediaDetailService.getDetailData(navItem.id, navItem.url)
}
Triple(plugin.pluginInfo, plugin.options, detail)
}.shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(60_000)
)

private val _favoriteFlow = combine(_refreshFavoriteFlow, _mediaDetailFlow) { _, pd ->
favoriteMediaDao.getOneByPluginPackageAndMediaId(
Expand Down Expand Up @@ -148,6 +150,13 @@ class MediaDetailScreenViewModel @Inject constructor(
initialValue = MediaDetailScreenUiState.Loading
)

fun refreshMediaDetail(navItem: NavigationItems.Detail) {
viewModelScope.launch {
_refreshMediaDetailFlow.emit(navItem)
}
}


fun refreshProgressList() {
viewModelScope.launch {
_refreshProgressListFlow.update {
Expand Down Expand Up @@ -216,11 +225,6 @@ class MediaDetailScreenViewModel @Inject constructor(
}
}
}

companion object {
val MEDIA_ID_SAVED_STATE_KEY: String = NavigationItems.Detail.args[0].name
val MEDIA_URL_SAVED_STATE_KEY: String = NavigationItems.Detail.args[1].name
}
}

sealed interface MediaDetailScreenUiState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ import com.muedsa.tvbox.screens.NavigationItems
import com.muedsa.tvbox.screens.nav
import com.muedsa.tvbox.screens.plugin.home.MediaCardRow
import com.muedsa.tvbox.theme.FavoriteIconColor
import com.muedsa.tvbox.tool.LenientJson
import kotlinx.serialization.encodeToString
import timber.log.Timber

@Composable
Expand Down Expand Up @@ -267,22 +269,23 @@ fun MediaDetailWidget(
enabled = !episodeClickLoading,
onEpisodeClick = { episode, danEpisode ->
episodeClickLoading = true
Timber.d("click episode ${mediaDetail.id}-${episode.id}")
Timber.d("click episode ${mediaDetail.id}-${episode.name}")

mediaDetailScreenViewModel.getEpisodePlayInfo(
playSource = episodePlaySource,
episode = episode,
onSuccess = {
episodeClickLoading = false
Timber.d("episode:${mediaDetail.id}-${episode.name}, url:${it.url}")
navController.nav(
navItem = NavigationItems.Player,
pathParams = listOf(
it.url,
pluginInfo.packageName,
mediaDetail.id,
episode.id,
if (enabledDanmakuState.value && danEpisode != null)
danEpisode.episodeId.toString() else "-1"
NavigationItems.Player(
url = it.url,
httpHeadersJson = it.httpHeaders?.let { h -> LenientJson.encodeToString(h) },
pluginPackage = pluginInfo.packageName,
mediaId = mediaDetail.id,
episodeId = episode.id,
danEpisodeId = if (enabledDanmakuState.value && danEpisode != null)
danEpisode.episodeId else -1
)
)
},
Expand All @@ -304,10 +307,7 @@ fun MediaDetailWidget(
MediaCardRow(
row = it,
onItemClick = { _, mediaCard ->
navController.nav(
NavigationItems.Detail,
listOf(mediaCard.id, mediaCard.detailUrl)
)
navController.nav(NavigationItems.Detail(mediaCard.id, mediaCard.detailUrl))
}
)
}
Expand Down
Loading

0 comments on commit 8c0ee89

Please sign in to comment.