mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Choose backend to play media in context menu (#1325)
## Description Adds a context menu option to choose the player backend or transcoding to start playback. This allows for temporarily swapping to another backend without having to updating the settings. ### Related issues A couple comments have suggested this, but I don't think there's a specific feature request ### Testing Emulator ## Screenshots <img width="1280" height="720" alt="play_with Large" src="https://github.com/user-attachments/assets/2a75756f-5d7f-45f6-8ea6-a406f3d7f3e1" /> ## AI or LLM usage None
This commit is contained in:
parent
c718d903a4
commit
b94faceb2f
4 changed files with 91 additions and 16 deletions
|
|
@ -17,12 +17,14 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.ChosenStreams
|
import com.github.damontecres.wholphin.data.ChosenStreams
|
||||||
import com.github.damontecres.wholphin.data.model.AudioItem
|
import com.github.damontecres.wholphin.data.model.AudioItem
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
||||||
import com.github.damontecres.wholphin.data.model.Person
|
import com.github.damontecres.wholphin.data.model.Person
|
||||||
|
import com.github.damontecres.wholphin.preferences.PlayerBackend
|
||||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.util.supportedPlayableTypes
|
import com.github.damontecres.wholphin.util.supportedPlayableTypes
|
||||||
|
|
@ -185,6 +187,7 @@ fun ContextMenu(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var chooseVersion by remember { mutableStateOf<DialogParams?>(null) }
|
var chooseVersion by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
var showDeleteDialog by remember { mutableStateOf(false) }
|
var showDeleteDialog by remember { mutableStateOf(false) }
|
||||||
|
var showPlayWithDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val dialogItems =
|
val dialogItems =
|
||||||
remember(context, item, chosenStreams) {
|
remember(context, item, chosenStreams) {
|
||||||
|
|
@ -211,6 +214,7 @@ fun ContextMenu(
|
||||||
) { idx ->
|
) { idx ->
|
||||||
val source = item.data.mediaSources!![idx]
|
val source = item.data.mediaSources!![idx]
|
||||||
actions.onChooseVersion.invoke(item, source)
|
actions.onChooseVersion.invoke(item, source)
|
||||||
|
onDismissRequest.invoke()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onChooseTracks = { type ->
|
onChooseTracks = { type ->
|
||||||
|
|
@ -239,6 +243,7 @@ fun ContextMenu(
|
||||||
itemPlayback = chosenStreams?.itemPlayback,
|
itemPlayback = chosenStreams?.itemPlayback,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
onDismissRequest.invoke()
|
||||||
},
|
},
|
||||||
preferredSubtitleLanguage = preferredSubtitleLanguage,
|
preferredSubtitleLanguage = preferredSubtitleLanguage,
|
||||||
)
|
)
|
||||||
|
|
@ -249,6 +254,7 @@ fun ContextMenu(
|
||||||
actions.onClearChosenStreams.invoke(chosenStreams)
|
actions.onClearChosenStreams.invoke(chosenStreams)
|
||||||
},
|
},
|
||||||
onClickDelete = { showDeleteDialog = true },
|
onClickDelete = { showDeleteDialog = true },
|
||||||
|
onClickPlayWith = { showPlayWithDialog = true },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
DialogPopup(
|
DialogPopup(
|
||||||
|
|
@ -281,6 +287,31 @@ fun ContextMenu(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (showPlayWithDialog) {
|
||||||
|
val dialogItems =
|
||||||
|
remember {
|
||||||
|
buildPlayWith(context) { transcode, backend ->
|
||||||
|
onDismissRequest.invoke()
|
||||||
|
actions.navigateTo(
|
||||||
|
Destination.Playback(
|
||||||
|
itemId = item.id,
|
||||||
|
positionMs = item.resumeMs,
|
||||||
|
forceTranscoding = transcode,
|
||||||
|
backend = backend,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DialogPopup(
|
||||||
|
showDialog = true,
|
||||||
|
title = stringResource(R.string.play_with),
|
||||||
|
dialogItems = dialogItems,
|
||||||
|
onDismissRequest = { showPlayWithDialog = false },
|
||||||
|
dismissOnClick = true,
|
||||||
|
waitToLoad = false,
|
||||||
|
elevation = 3.dp,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildContextMenuItems(
|
private fun buildContextMenuItems(
|
||||||
|
|
@ -302,6 +333,7 @@ private fun buildContextMenuItems(
|
||||||
onShowOverview: () -> Unit,
|
onShowOverview: () -> Unit,
|
||||||
onClearChosenStreams: () -> Unit,
|
onClearChosenStreams: () -> Unit,
|
||||||
onClickDelete: () -> Unit,
|
onClickDelete: () -> Unit,
|
||||||
|
onClickPlayWith: () -> Unit,
|
||||||
): List<DialogItem> =
|
): List<DialogItem> =
|
||||||
buildList {
|
buildList {
|
||||||
if (showGoTo) {
|
if (showGoTo) {
|
||||||
|
|
@ -316,7 +348,7 @@ private fun buildContextMenuItems(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (item.type in supportedPlayableTypes) {
|
if (item.type in supportedPlayableTypes) {
|
||||||
if (item.playbackPosition >= Duration.ZERO) {
|
if (item.playbackPosition > Duration.ZERO) {
|
||||||
add(
|
add(
|
||||||
DialogItem(
|
DialogItem(
|
||||||
context.getString(R.string.resume),
|
context.getString(R.string.resume),
|
||||||
|
|
@ -566,18 +598,11 @@ private fun buildContextMenuItems(
|
||||||
if (item.type in supportedPlayableTypes) {
|
if (item.type in supportedPlayableTypes) {
|
||||||
add(
|
add(
|
||||||
DialogItem(
|
DialogItem(
|
||||||
context.getString(R.string.play_with_transcoding),
|
context.getString(R.string.play_with),
|
||||||
Icons.Default.PlayArrow,
|
Icons.Default.PlayArrow,
|
||||||
dismissOnClick = true,
|
dismissOnClick = false,
|
||||||
) {
|
onClick = onClickPlayWith,
|
||||||
actions.navigateTo(
|
),
|
||||||
Destination.Playback(
|
|
||||||
item.id,
|
|
||||||
item.resumeMs ?: 0L,
|
|
||||||
forceTranscoding = true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (item.data.mediaSources?.isNotEmpty() == true) {
|
if (item.data.mediaSources?.isNotEmpty() == true) {
|
||||||
|
|
@ -593,6 +618,35 @@ private fun buildContextMenuItems(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildPlayWith(
|
||||||
|
context: Context,
|
||||||
|
onClick: (Boolean, PlayerBackend?) -> Unit,
|
||||||
|
) = buildList {
|
||||||
|
val entries =
|
||||||
|
PlayerBackend.entries
|
||||||
|
.filterNot { it == PlayerBackend.UNRECOGNIZED }
|
||||||
|
.zip(context.resources.getStringArray(R.array.player_backend_options))
|
||||||
|
.filterNot { it.first == PlayerBackend.PREFER_MPV }
|
||||||
|
entries.forEach { (backend, title) ->
|
||||||
|
add(
|
||||||
|
DialogItem(
|
||||||
|
title,
|
||||||
|
dismissOnClick = true,
|
||||||
|
) {
|
||||||
|
onClick.invoke(false, backend)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(
|
||||||
|
DialogItem(
|
||||||
|
context.getString(R.string.transcoding),
|
||||||
|
dismissOnClick = true,
|
||||||
|
) {
|
||||||
|
onClick.invoke(true, null)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContextMenu(
|
fun ContextMenu(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,25 @@ fun DestinationContent(
|
||||||
HomeSettingsPage(preferences, modifier)
|
HomeSettingsPage(preferences, modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Destination.PlaybackList,
|
is Destination.Playback -> {
|
||||||
is Destination.Playback,
|
val backend =
|
||||||
-> {
|
destination.backend ?: preferences.appPreferences.playbackPreferences.playerBackend
|
||||||
|
if (backend == PlayerBackend.EXTERNAL_PLAYER) {
|
||||||
|
PlayExternalPage(
|
||||||
|
preferences = preferences,
|
||||||
|
destination = destination,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
PlaybackPage(
|
||||||
|
preferences = preferences,
|
||||||
|
destination = destination,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is Destination.PlaybackList -> {
|
||||||
if (preferences.appPreferences.playbackPreferences.playerBackend == PlayerBackend.EXTERNAL_PLAYER) {
|
if (preferences.appPreferences.playbackPreferences.playerBackend == PlayerBackend.EXTERNAL_PLAYER) {
|
||||||
PlayExternalPage(
|
PlayExternalPage(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
|
|
|
||||||
|
|
@ -231,8 +231,11 @@ class PlaybackViewModel
|
||||||
) {
|
) {
|
||||||
val softwareDecoding =
|
val softwareDecoding =
|
||||||
!preferences.appPreferences.playbackPreferences.mpvOptions.enableHardwareDecoding
|
!preferences.appPreferences.playbackPreferences.mpvOptions.enableHardwareDecoding
|
||||||
|
val requestBackend =
|
||||||
|
(destination as? Destination.Playback)?.backend
|
||||||
|
?: preferences.appPreferences.playbackPreferences.playerBackend
|
||||||
val playerBackend =
|
val playerBackend =
|
||||||
when (preferences.appPreferences.playbackPreferences.playerBackend) {
|
when (requestBackend) {
|
||||||
PlayerBackend.UNRECOGNIZED,
|
PlayerBackend.UNRECOGNIZED,
|
||||||
PlayerBackend.EXO_PLAYER,
|
PlayerBackend.EXO_PLAYER,
|
||||||
-> PlayerBackend.EXO_PLAYER
|
-> PlayerBackend.EXO_PLAYER
|
||||||
|
|
|
||||||
|
|
@ -771,6 +771,8 @@
|
||||||
<string name="show_more">Show %1$s more</string>
|
<string name="show_more">Show %1$s more</string>
|
||||||
<string name="skip_behavior">Skip behavior</string>
|
<string name="skip_behavior">Skip behavior</string>
|
||||||
<string name="skip_behavior_summary">For intros, credits, etc</string>
|
<string name="skip_behavior_summary">For intros, credits, etc</string>
|
||||||
|
<string name="play_with">Play with</string>
|
||||||
|
<string name="transcoding">Transcoding</string>
|
||||||
<array name="ass_subtitle_modes">
|
<array name="ass_subtitle_modes">
|
||||||
<item>@string/ass_subtitle_mode_libass</item>
|
<item>@string/ass_subtitle_mode_libass</item>
|
||||||
<item>@string/ass_subtitle_mode_exoplayer</item>
|
<item>@string/ass_subtitle_mode_exoplayer</item>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue