diff --git a/src/video/actors.rs b/src/video/actors.rs index 66e0341..5aae520 100644 --- a/src/video/actors.rs +++ b/src/video/actors.rs @@ -140,7 +140,9 @@ async fn is_h264_encoded(video_path: &str) -> bool { /// Check if a video has rotation metadata /// Returns the rotation angle in degrees (0, 90, 180, 270) or 0 if none detected +/// Checks both legacy stream tags and modern display matrix side data async fn get_video_rotation(video_path: &str) -> i32 { + // Check legacy rotate stream tag (older videos) let output = tokio::process::Command::new("ffprobe") .arg("-v") .arg("error") @@ -154,18 +156,52 @@ async fn get_video_rotation(video_path: &str) -> i32 { .output() .await; - match output { - Ok(output) if output.status.success() => { + if let Ok(output) = output { + if output.status.success() { let rotation_str = String::from_utf8_lossy(&output.stdout); let rotation_str = rotation_str.trim(); - if rotation_str.is_empty() { - 0 - } else { - rotation_str.parse::().unwrap_or(0) + if !rotation_str.is_empty() { + if let Ok(rotation) = rotation_str.parse::() { + if rotation != 0 { + debug!("Detected rotation {}° from stream tag for {}", rotation, video_path); + return rotation; + } + } } } - _ => 0, } + + // Check display matrix side data (modern videos, e.g. iPhone) + let output = tokio::process::Command::new("ffprobe") + .arg("-v") + .arg("error") + .arg("-select_streams") + .arg("v:0") + .arg("-show_entries") + .arg("side_data=rotation") + .arg("-of") + .arg("default=noprint_wrappers=1:nokey=1") + .arg(video_path) + .output() + .await; + + if let Ok(output) = output { + if output.status.success() { + let rotation_str = String::from_utf8_lossy(&output.stdout); + let rotation_str = rotation_str.trim(); + if !rotation_str.is_empty() { + if let Ok(rotation) = rotation_str.parse::() { + let rotation = rotation.abs() as i32; + if rotation != 0 { + debug!("Detected rotation {}° from display matrix for {}", rotation, video_path); + return rotation; + } + } + } + } + } + + 0 } pub struct VideoPlaylistManager {