import Foundation /// Deterministic fields pulled from a filename by regex (no network). public struct ParsedFilename: Codable, Sendable, Equatable { public var title: String public var year: Int? public var season: Int? public var episode: Int? public var quality: String? // 2160p / 1080p / 720p / 480p public var codec: String? // x265 / x264 / HEVC / XviD public var releaseSource: String? // BluRay / WEB-DL / HDTV / … public init(title: String, year: Int? = nil, season: Int? = nil, episode: Int? = nil, quality: String? = nil, codec: String? = nil, releaseSource: String? = nil) { self.title = title; self.year = year; self.season = season; self.episode = episode self.quality = quality; self.codec = codec; self.releaseSource = releaseSource } } /// What `media_rec.enrich` returns (snake_case JSON). All optional — the CLI /// degrades to a partial result when TMDB/IMDb aren't configured. public struct EnrichResult: Decodable, Sendable, Equatable { public var tmdb_id: Int? public var media_type: String? public var title: String? public var year: Int? public var overview: String? public var poster_url: String? public var vote_average: Double? public var vote_count: Int? public var imdb_rating: Double? public var imdb_votes: Int? public var genres: [String]? public var tmdb_error: String? public var imdb_error: String? } /// The `.meta` sidecar payload: the parse plus whatever enrichment resolved. /// Cached per-path on plum, mirrored next to the file on black opportunistically. public struct MediaMeta: Codable, Sendable, Equatable { public var path: String public var parsed: ParsedFilename public var resolvedTitle: String? public var mediaType: String? public var overview: String? public var posterURL: String? public var voteAverage: Double? public var voteCount: Int? public var imdbRating: Double? public var imdbVotes: Int? public var genres: [String]? public var enrichedAt: Date? public init(path: String, parsed: ParsedFilename) { self.path = path; self.parsed = parsed } /// Fold a (possibly partial) enrich result into this meta. public mutating func apply(_ e: EnrichResult, at now: Date) { resolvedTitle = e.title ?? resolvedTitle mediaType = e.media_type ?? mediaType overview = e.overview ?? overview posterURL = e.poster_url ?? posterURL voteAverage = e.vote_average ?? voteAverage voteCount = e.vote_count ?? voteCount imdbRating = e.imdb_rating ?? imdbRating imdbVotes = e.imdb_votes ?? imdbVotes genres = e.genres ?? genres enrichedAt = now } }