selene-core 0.3.1

selene-core is the backend for Selene, a local-first music player
Documentation
use std::{path::PathBuf, str::FromStr};

use crate::{
    codec::Codec,
    container::ContainerFormat,
    errors::FfmpegError,
    ffmpeg::{self, ChannelLayout, Stream},
};
use serde::{Deserialize, Serialize};

pub mod accessors;

#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct MediaContainer {
    path: PathBuf,
    format: ContainerFormat,
    stream: Stream,
    duration: f32,
    bit_rate: usize,
}

impl MediaContainer {
    pub(crate) fn set_path(&mut self, path: PathBuf) {
        self.path = path;
    }
}

impl MediaContainer {
    /// Returns the [`MediaContainer`] of the input file
    ///
    /// # Errors
    ///
    /// Errors when the input file either has invalid or unsupported/unrecognized data
    pub fn from_file(file: PathBuf) -> Result<Self, FfmpegError> {
        #[derive(Deserialize)]
        struct StreamRaw {
            codec_name: String,
            sample_rate: String,
            channels: isize,
            channel_layout: Option<String>,
            duration: String,
        }

        #[derive(Deserialize)]
        struct FormatRaw {
            format_name: String,
            duration: String,
            bit_rate: String,
        }

        #[derive(Deserialize)]
        struct Root {
            streams: Vec<StreamRaw>,
            format: FormatRaw,
        }

        let json = ffmpeg::ffprobe_media_container_json(&file)?;

        let root: Root = serde_json::from_str(&json)?;
        let raw_stream = root.streams.into_iter().next().ok_or_else(|| {
            FfmpegError::FfprobeWrapperError("Input json contains no audio streams".to_owned())
        })?;

        Ok(Self {
            path: file,
            format: ContainerFormat::from_str(&root.format.format_name)
                .map_err(FfmpegError::Other)?,
            stream: Stream {
                codec: Codec::from_str(&raw_stream.codec_name)
                    .map_err(|err| FfmpegError::Other(err.to_string()))?,
                sample_rate: raw_stream
                    .sample_rate
                    .parse::<usize>()
                    .map_err(|err| FfmpegError::Other(err.to_string()))?,
                channels: raw_stream.channels,
                channel_layout: match raw_stream.channel_layout {
                    Some(ref s) => Some(ChannelLayout::from_str(s).map_err(FfmpegError::Other)?),
                    None => None,
                },
                duration: raw_stream
                    .duration
                    .parse::<f32>()
                    .map_err(|err| FfmpegError::Other(err.to_string()))?,
            },
            duration: root
                .format
                .duration
                .parse::<f32>()
                .map_err(|err| FfmpegError::Other(err.to_string()))?,
            bit_rate: root
                .format
                .bit_rate
                .parse::<usize>()
                .map_err(|err| FfmpegError::Other(err.to_string()))?,
        })
    }
}