video-transcriber-mcp 0.7.0

High-performance video transcription MCP server using whisper.cpp for faster transcription
Documentation
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;
use uuid::Uuid;

use crate::transcriber::types::{Segment, VideoMetadata, WhisperModel};

pub type JobStore = Arc<Mutex<HashMap<Uuid, Job>>>;

pub fn new_store() -> JobStore {
    Arc::new(Mutex::new(HashMap::new()))
}

#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum JobStatus {
    Queued,
    Downloading,
    Transcribing,
    Summarizing,
    Complete,
    Failed,
    Cancelled,
}

#[derive(Debug, Clone, Deserialize)]
pub struct JobRequest {
    pub url: String,
    #[serde(default)]
    pub model: Option<String>,
    #[serde(default)]
    pub language: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct JobResult {
    pub transcript: String,
    pub segments: Vec<Segment>,
    pub metadata: VideoMetadata,
    pub summary_md: String,
    pub mermaid_src: String,
    pub key_points: Vec<String>,
    pub model_used: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct Job {
    pub id: Uuid,
    pub status: JobStatus,
    pub url: String,
    /// Device that owns this job — used to refund the credit on failure /
    /// cancellation. Not exposed to the client (different devices polling the
    /// same job_id shouldn't reveal each other's identities). Currently
    /// unread (the device_id is also threaded through the spawn closure), but
    /// kept so future admin / debug endpoints can attribute jobs.
    #[serde(skip)]
    #[allow(dead_code)]
    pub device_id: String,
    pub created_at: i64,
    pub updated_at: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub result: Option<JobResult>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    /// Per-job cancellation signal. Cloned into the spawned pipeline task and
    /// fired by `DELETE /api/jobs/{id}`. Dropping the in-flight futures (Modal
    /// HTTP request, OpenRouter HTTP request) closes their TCP connections so
    /// the remote work stops too.
    #[serde(skip)]
    pub cancel: CancellationToken,
}

pub fn parse_model(s: Option<&str>) -> WhisperModel {
    s.and_then(|m| m.parse::<WhisperModel>().ok())
        .unwrap_or(WhisperModel::Base)
}