/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ use crate::plugins::{ImportContext, ImportPlugin, PluginInfo}; use anyhow::{Result, anyhow}; use jellycommon::{Chapter, NodeID, SourceTrack, SourceTrackKind, TrackSource}; use jellyremuxer::matroska::Segment; use std::path::Path; pub struct MediaInfo; impl ImportPlugin for MediaInfo { fn info(&self) -> PluginInfo { PluginInfo { name: "media-info", handle_media: true, ..Default::default() } } fn media(&self, ct: &ImportContext, node: NodeID, path: &Path, seg: &Segment) -> Result<()> { let tracks = seg .tracks .as_ref() .ok_or(anyhow!("no tracks"))? .entries .iter() .map(|track| SourceTrack { codec: track.codec_id.clone(), language: track.language.clone(), name: track.name.clone().unwrap_or_default(), federated: Vec::new(), kind: if let Some(video) = &track.video { SourceTrackKind::Video { width: video.pixel_width, height: video.pixel_height, fps: video.frame_rate, } } else if let Some(audio) = &track.audio { SourceTrackKind::Audio { channels: audio.channels as usize, sample_rate: audio.sampling_frequency, bit_depth: audio.bit_depth.map(|r| r as usize), } } else { SourceTrackKind::Subtitle }, source: TrackSource::Local(path.to_owned(), track.track_number), }) .collect::>(); let size = path.metadata()?.len(); ct.db.update_node_init(node, |node| { node.storage_size = size; node.media = Some(jellycommon::MediaInfo { chapters: seg .chapters .clone() .map(|c| { let mut chaps = Vec::new(); if let Some(ee) = c.edition_entries.first() { for ca in &ee.chapter_atoms { let mut labels = Vec::new(); for cd in &ca.displays { for lang in &cd.languages { labels.push((lang.to_owned(), cd.string.clone())) } } chaps.push(Chapter { labels, time_start: Some(ca.time_start as f64 * 1e-9), time_end: ca.time_end.map(|ts| ts as f64 * 1e-9), }) } } chaps }) .unwrap_or_default(), duration: fix_invalid_runtime( seg.info.duration.unwrap_or_default() * seg.info.timestamp_scale as f64 * 1e-9, ), tracks, }); })?; Ok(()) } } fn fix_invalid_runtime(d: f64) -> f64 { match d { // Broken durations found experimentally 359999.999 | 359999.000 | 86399.999 | 86399.99900000001 => 0., x => x, } }