gobby-wiki 0.3.0

Gobby wiki CLI shell
use super::*;

#[derive(Clone, Copy, Default)]
pub(crate) struct VideoDegradationContext<'a> {
    pub(crate) media: &'a [VideoMediaDegradation],
    pub(crate) transcription: Option<&'a TranscriptionDegradation>,
    pub(crate) suppress_frame_sampling: bool,
}

pub(crate) fn video_media_metadata(
    vault_root: &Path,
    asset_path: &Path,
    duration_seconds: Option<u32>,
) -> Result<VideoMediaMetadata, WikiError> {
    let absolute_asset_path = vault_root.join(asset_path);
    let metadata = std::fs::metadata(&absolute_asset_path).map_err(|source| WikiError::Io {
        action: "stat video asset",
        path: Some(absolute_asset_path),
        source,
    })?;
    Ok(VideoMediaMetadata {
        file_size_bytes: metadata.len(),
        duration_seconds,
    })
}

pub(crate) struct VideoSnapshotRef<'a> {
    pub(crate) location: &'a str,
    pub(crate) file_name: &'a str,
    pub(crate) fetched_at: &'a str,
    pub(crate) mime_type: Option<&'a str>,
    pub(crate) duration_seconds: Option<u32>,
    pub(crate) frame_interval_seconds: Option<u32>,
    pub(crate) frame_samples: &'a [VideoFrameSample],
    pub(crate) frame_image_paths: &'a [PathBuf],
    pub(crate) frame_descriptions: &'a [VideoFrameDescription],
    pub(crate) transcript_segments: &'a [TranscriptSegment],
    pub(crate) transcription: Option<&'a TranscriptionOutput>,
}

impl<'a> VideoSnapshotRef<'a> {
    pub(crate) fn from_snapshot(snapshot: &'a VideoSnapshot) -> Self {
        Self {
            location: &snapshot.location,
            file_name: &snapshot.file_name,
            fetched_at: &snapshot.fetched_at,
            mime_type: snapshot.mime_type.as_deref(),
            duration_seconds: snapshot.duration_seconds,
            frame_interval_seconds: snapshot.frame_interval_seconds,
            frame_samples: &snapshot.frame_samples,
            frame_image_paths: &snapshot.frame_image_paths,
            frame_descriptions: &snapshot.frame_descriptions,
            transcript_segments: &snapshot.transcript_segments,
            transcription: snapshot.transcription.as_ref(),
        }
    }

    pub(crate) fn from_file_snapshot(snapshot: &'a VideoFileSnapshot) -> Self {
        Self {
            location: &snapshot.location,
            file_name: &snapshot.file_name,
            fetched_at: &snapshot.fetched_at,
            mime_type: snapshot.mime_type.as_deref(),
            duration_seconds: snapshot.duration_seconds,
            frame_interval_seconds: snapshot.frame_interval_seconds,
            frame_samples: &snapshot.frame_samples,
            frame_image_paths: &snapshot.frame_image_paths,
            frame_descriptions: &snapshot.frame_descriptions,
            transcript_segments: &snapshot.transcript_segments,
            transcription: snapshot.transcription.as_ref(),
        }
    }
}

impl From<VideoIngestResult> for IngestResult {
    fn from(result: VideoIngestResult) -> Self {
        Self {
            record: result.record,
            raw_path: result.raw_path,
            asset_path: Some(result.asset_path),
        }
    }
}

pub(crate) fn render_raw_video_markdown(
    snapshot: &VideoSnapshotRef<'_>,
    source_hash: &str,
    asset_path: &Path,
    frame_interval_seconds: u32,
) -> String {
    let asset_path = path_to_string(asset_path);
    let mut fields = vec![
        ("source_kind", "video".to_string()),
        ("source_location", snapshot.location.to_string()),
        ("fetched_at", snapshot.fetched_at.to_string()),
        ("source_hash", source_hash.to_string()),
        ("source_asset", asset_path.clone()),
    ];
    if let Some(mime_type) = snapshot.mime_type {
        fields.push(("video_mime_type", mime_type.to_string()));
    }
    if let Some(duration_seconds) = snapshot.duration_seconds {
        fields.push(("video_duration_seconds", duration_seconds.to_string()));
    }
    fields.push((
        "video_frame_interval_seconds",
        frame_interval_seconds.to_string(),
    ));
    fields.push((
        "video_frame_description_count",
        snapshot.frame_descriptions.len().to_string(),
    ));
    fields.push((
        "video_transcript_segment_count",
        snapshot.transcript_segments.len().to_string(),
    ));

    let mut markdown = markdown_metadata(&fields);
    markdown.push_str("# ");
    markdown.push_str(&markdown_title(snapshot.file_name));
    markdown.push_str("\n\n");
    markdown.push_str("Original video stored under `");
    markdown.push_str(&asset_path);
    markdown.push_str("`.\n");
    markdown
}

pub(crate) fn format_timestamp(seconds: u32) -> String {
    let hours = seconds / 3600;
    let minutes = (seconds % 3600) / 60;
    let seconds = seconds % 60;
    format!("{hours:02}:{minutes:02}:{seconds:02}")
}