//! Metadata enrichment from external sources. pub mod books; pub mod googlebooks; pub mod lastfm; pub mod musicbrainz; pub mod openlibrary; pub mod tmdb; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::{ error::Result, model::{MediaId, MediaItem}, }; /// Externally-sourced metadata for a media item. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExternalMetadata { pub id: Uuid, pub media_id: MediaId, pub source: EnrichmentSourceType, pub external_id: Option, pub metadata_json: String, pub confidence: f64, pub last_updated: DateTime, } /// Supported enrichment data sources. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum EnrichmentSourceType { #[serde(rename = "musicbrainz")] MusicBrainz, #[serde(rename = "tmdb")] Tmdb, #[serde(rename = "lastfm")] LastFm, #[serde(rename = "openlibrary")] OpenLibrary, #[serde(rename = "googlebooks")] GoogleBooks, } impl std::fmt::Display for EnrichmentSourceType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = match self { Self::MusicBrainz => "musicbrainz", Self::Tmdb => "tmdb", Self::LastFm => "lastfm", Self::OpenLibrary => "openlibrary", Self::GoogleBooks => "googlebooks", }; write!(f, "{s}") } } impl std::str::FromStr for EnrichmentSourceType { type Err = String; fn from_str(s: &str) -> std::result::Result { match s { "musicbrainz" => Ok(Self::MusicBrainz), "tmdb" => Ok(Self::Tmdb), "lastfm" => Ok(Self::LastFm), "openlibrary" => Ok(Self::OpenLibrary), "googlebooks" => Ok(Self::GoogleBooks), _ => Err(format!("unknown enrichment source: {s}")), } } } /// Trait for metadata enrichment providers. #[async_trait::async_trait] pub trait MetadataEnricher: Send + Sync { fn source(&self) -> EnrichmentSourceType; async fn enrich(&self, item: &MediaItem) -> Result>; }