embystream 0.0.32

Another Emby streaming application (frontend/backend separation) written in Rust.
Documentation
use crate::config::backend::GoogleDriveConfig;

pub const BACKEND_TYPE: &str = "googleDrive";
pub const ACCEL_REDIRECT_PREFIX: &str = "/_origin/google-drive";

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DriveLookup {
    DriveId(String),
    DriveName(String),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResolvedGoogleDrivePath {
    pub lookup: DriveLookup,
    pub drive_name: String,
    pub logical_path: String,
    pub relative_path: String,
}

fn normalize_path_segments(path: &str) -> Vec<&str> {
    path.split(['/', '\\'])
        .filter(|segment| !segment.is_empty())
        .collect()
}

fn join_segments(segments: &[&str]) -> String {
    if segments.is_empty() {
        "/".to_string()
    } else {
        format!("/{}", segments.join("/"))
    }
}

pub fn resolve_google_drive_path(
    raw_path: &str,
    cfg: &GoogleDriveConfig,
) -> Result<ResolvedGoogleDrivePath, &'static str> {
    let segments = normalize_path_segments(raw_path);
    if segments.is_empty() {
        return Err("googleDrive path is empty");
    }

    let configured_drive_name = cfg.drive_name.trim();
    let drive_name_in_path_index = if configured_drive_name.is_empty() {
        None
    } else {
        segments
            .iter()
            .position(|segment| *segment == configured_drive_name)
    };

    let logical_segments: Vec<&str> = match drive_name_in_path_index {
        Some(idx) => segments[idx..].to_vec(),
        None if !configured_drive_name.is_empty() => {
            let mut merged = Vec::with_capacity(segments.len() + 1);
            merged.push(configured_drive_name);
            merged.extend(segments.iter().copied());
            merged
        }
        None => segments.clone(),
    };

    let drive_name = if configured_drive_name.is_empty() {
        logical_segments[0].to_string()
    } else {
        configured_drive_name.to_string()
    };

    let relative_segments = if logical_segments
        .first()
        .copied()
        .is_some_and(|segment| segment == drive_name)
    {
        &logical_segments[1..]
    } else {
        &logical_segments
    };

    let lookup = if cfg.drive_id.trim().is_empty() {
        DriveLookup::DriveName(drive_name.clone())
    } else {
        DriveLookup::DriveId(cfg.drive_id.trim().to_string())
    };

    Ok(ResolvedGoogleDrivePath {
        lookup,
        drive_name,
        logical_path: join_segments(&logical_segments),
        relative_path: join_segments(relative_segments),
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn infer_drive_name_from_first_segment_when_config_is_empty() {
        let cfg = GoogleDriveConfig::default();
        let resolved = resolve_google_drive_path(
            "/pilipili/pilipili/电视剧/2026/test/Season 01/test.mkv",
            &cfg,
        )
        .expect("resolve");

        assert_eq!(resolved.lookup, DriveLookup::DriveName("pilipili".into()));
        assert_eq!(resolved.drive_name, "pilipili");
        assert_eq!(
            resolved.logical_path,
            "/pilipili/pilipili/电视剧/2026/test/Season 01/test.mkv"
        );
        assert_eq!(
            resolved.relative_path,
            "/pilipili/电视剧/2026/test/Season 01/test.mkv"
        );
    }

    #[test]
    fn configured_drive_name_crops_prefix_and_keeps_duplicate_folder() {
        let cfg = GoogleDriveConfig {
            drive_name: "pilipili".into(),
            ..Default::default()
        };
        let resolved = resolve_google_drive_path(
            "/mnt/media/pilipili/pilipili/电视剧/2026/test/Season 01/test.mkv",
            &cfg,
        )
        .expect("resolve");

        assert_eq!(resolved.lookup, DriveLookup::DriveName("pilipili".into()));
        assert_eq!(
            resolved.logical_path,
            "/pilipili/pilipili/电视剧/2026/test/Season 01/test.mkv"
        );
        assert_eq!(
            resolved.relative_path,
            "/pilipili/电视剧/2026/test/Season 01/test.mkv"
        );
    }

    #[test]
    fn configured_drive_name_is_prepended_when_path_lacks_drive_segment() {
        let cfg = GoogleDriveConfig {
            drive_name: "pilipili".into(),
            ..Default::default()
        };
        let resolved =
            resolve_google_drive_path("/电影/2026/test/test.mkv", &cfg)
                .expect("resolve");

        assert_eq!(resolved.logical_path, "/pilipili/电影/2026/test/test.mkv");
        assert_eq!(resolved.relative_path, "/电影/2026/test/test.mkv");
    }

    #[test]
    fn drive_id_takes_precedence_over_drive_name_for_lookup() {
        let cfg = GoogleDriveConfig {
            drive_id: "drive-123".into(),
            drive_name: "pilipili".into(),
            ..Default::default()
        };
        let resolved = resolve_google_drive_path(
            "/mnt/media/pilipili/电影/2026/test/test.mkv",
            &cfg,
        )
        .expect("resolve");

        assert_eq!(resolved.lookup, DriveLookup::DriveId("drive-123".into()));
        assert_eq!(resolved.drive_name, "pilipili");
        assert_eq!(resolved.logical_path, "/pilipili/电影/2026/test/test.mkv");
        assert_eq!(resolved.relative_path, "/电影/2026/test/test.mkv");
    }
}