1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
/*
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 <metamuffin.org>
*/
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::<Vec<_>>();
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,
}
}
|