fix(player): mpv draws all Linux video, and no longer runs text from a URL
Security (DR-298, DR-299): - The pinned libmpv crate's Mpv::command joins its arguments and calls mpv_command_string, which parses `;` as a command separator. Stream URLs carry server-controlled ids and TranscodingUrl, and a download's file:// path carries its track title, so a crafted title could run any mpv command, `run` included. Every call now goes through mpv_command::command, an argv built for mpv_command. The same parse broke loadfile for every downloaded title containing a space. - mpv's tls-verify defaults to no, and its URLs carry the ApiKey. Every handle is now hardened with tls-verify=yes and ytdl=no before its first loadfile, and fails construction if it cannot be. Linux video (DR-235 phase 1): - native_video::enabled() is unconditional on Linux; the JELLYTAU_NATIVE_VIDEO opt-in is retired. No platform reports a webview video fallback, so the Settings switch no longer appears. Windows keeps the webview element until mpv reaches it (DR-237). - The Linux device profile is unchanged (still h264, DR-234), so this ships the configuration that was tested under the env var.
This commit is contained in:
@@ -2187,32 +2187,16 @@ pub struct PlaybackCapabilities {
|
||||
/// native backend. Native audio exists on Linux (mpv) and Android
|
||||
/// (ExoPlayer); everything else (Windows, future desktops) uses the webview.
|
||||
pub uses_webview_audio: bool,
|
||||
/// True when video can be rendered by a native surface composited *behind*
|
||||
/// a transparent webview. Android only: ExoPlayer draws into a SurfaceView
|
||||
/// beneath the WebView. Linux cannot do this (WebKitGTK/Wayland
|
||||
/// compositing), so it stays on the HTML5 element.
|
||||
/// True when video is rendered by a native surface composited *behind* a
|
||||
/// transparent webview: ExoPlayer's SurfaceView on Android, mpv's GL area on
|
||||
/// Linux.
|
||||
pub supports_native_video: bool,
|
||||
/// True when the user may send video to the webview element instead of the
|
||||
/// native renderer — the frontend offers the switch only then, and honours
|
||||
/// the stored preference only then. See [`webview_video_fallback`].
|
||||
/// the stored preference only then. False on every platform since DR-235.
|
||||
pub webview_video_fallback: bool,
|
||||
}
|
||||
|
||||
/// Whether the user may send video to the webview `<video>` element instead of
|
||||
/// the native renderer.
|
||||
///
|
||||
/// Never on Android: ExoPlayer is its only video renderer. Downloads there are
|
||||
/// the untouched source file (DR-293), and the webview decodes none of the
|
||||
/// AC-3/E-AC-3/DTS/TrueHD that ExoPlayer plays through the FFmpeg extension, so
|
||||
/// the fallback would be a silent film. Beside mpv's native video on Linux the
|
||||
/// webview is still the tested fallback; everywhere else it is the only
|
||||
/// renderer and there is nothing to switch.
|
||||
///
|
||||
/// TRACES: UR-003, UR-071 | DR-293 | UT-259
|
||||
pub fn webview_video_fallback(is_android: bool, native_video_enabled: bool) -> bool {
|
||||
!is_android && native_video_enabled
|
||||
}
|
||||
|
||||
/// Report this platform's playback capabilities to the frontend.
|
||||
///
|
||||
/// TRACES: UR-003, UR-005 | DR-004, DR-023, DR-024
|
||||
@@ -2227,11 +2211,12 @@ pub async fn player_get_capabilities() -> Result<PlaybackCapabilities, String> {
|
||||
// TRACES: UR-080 | DR-235
|
||||
supports_native_video: cfg!(target_os = "android")
|
||||
|| crate::player::native_video::enabled(),
|
||||
// TRACES: UR-003, UR-071 | DR-293
|
||||
webview_video_fallback: webview_video_fallback(
|
||||
cfg!(target_os = "android"),
|
||||
crate::player::native_video::enabled(),
|
||||
),
|
||||
// No platform offers one: Android since DR-293, Linux since DR-235,
|
||||
// and on Windows the webview is the only video renderer, so there is
|
||||
// nothing to fall back *from*. Kept on the wire until phase 3 deletes
|
||||
// the frontend switch with the rest of the webview video path.
|
||||
// TRACES: UR-080, UR-003 | DR-235, DR-293
|
||||
webview_video_fallback: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2246,7 +2231,8 @@ pub(super) fn get_player_status(controller: &PlayerController) -> PlayerStatus {
|
||||
// two fight over the audio. TRACES: UR-080 | DR-235
|
||||
(VideoBackend::Native, false)
|
||||
} else {
|
||||
// Linux and other platforms use HTML5 video element in frontend
|
||||
// Windows: the webview <video> element is its only video renderer
|
||||
// until mpv reaches it (DR-237).
|
||||
(VideoBackend::Html5, true)
|
||||
};
|
||||
|
||||
@@ -3087,35 +3073,33 @@ pub async fn player_disable_jellyfin(player: State<'_, PlayerStateWrapper>) -> R
|
||||
mod tests {
|
||||
use crate::utils::lock::MutexSafe;
|
||||
|
||||
/// Android has one video renderer, ExoPlayer. The webview element could only
|
||||
/// be reached by the user switching native video off, and a file downloaded
|
||||
/// as the untouched original — AC-3 audio included — plays silent there,
|
||||
/// so the switch is gone on Android (DR-293). Where mpv draws video on Linux
|
||||
/// the webview is still the tested fallback, so the switch stays there;
|
||||
/// everywhere else the webview is the only renderer and there is nothing to
|
||||
/// switch.
|
||||
/// The webview is not a video renderer anywhere the app ships a native one:
|
||||
/// Android since DR-293, Linux since DR-235 made mpv its only video path.
|
||||
/// So the frontend is never offered the switch, and a stored "native video
|
||||
/// off" from before cannot send Linux video back to the `<video>` element.
|
||||
///
|
||||
/// TRACES: UR-003, UR-071 | DR-293 | UT-259
|
||||
#[test]
|
||||
fn test_webview_video_fallback_is_offered_only_beside_mpv_native_video() {
|
||||
use super::webview_video_fallback;
|
||||
/// TRACES: UR-080, UR-003 | DR-235, DR-293 | UT-272
|
||||
#[tokio::test]
|
||||
async fn test_no_platform_offers_a_webview_video_fallback() {
|
||||
let caps = super::player_get_capabilities().await.unwrap();
|
||||
assert!(!caps.webview_video_fallback);
|
||||
if cfg!(target_os = "linux") {
|
||||
assert!(caps.supports_native_video, "mpv draws video on Linux");
|
||||
assert!(!caps.uses_webview_audio);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
!webview_video_fallback(true, false),
|
||||
"Android: ExoPlayer is the only video renderer"
|
||||
);
|
||||
assert!(
|
||||
!webview_video_fallback(true, true),
|
||||
"Android never falls back, whatever else is switched on"
|
||||
);
|
||||
assert!(
|
||||
webview_video_fallback(false, true),
|
||||
"Linux with mpv native video: the webview is the fallback"
|
||||
);
|
||||
assert!(
|
||||
!webview_video_fallback(false, false),
|
||||
"the webview is the only renderer; nothing to fall back from"
|
||||
);
|
||||
/// And the status the video page reads agrees: on Linux the frontend is told
|
||||
/// the native backend renders, never to load a `<video>` element.
|
||||
///
|
||||
/// TRACES: UR-080 | DR-235 | UT-272
|
||||
#[test]
|
||||
fn test_linux_video_is_not_sent_to_the_webview() {
|
||||
let controller = crate::player::PlayerController::default();
|
||||
let status = super::get_player_status(&controller);
|
||||
if cfg!(target_os = "linux") {
|
||||
assert!(!status.use_html5_element);
|
||||
}
|
||||
}
|
||||
|
||||
/// UT-206 — the volume the command hands on is always a real number in
|
||||
|
||||
Reference in New Issue
Block a user