Add Clip struct to Title — expose clip references for playlist analysis
Title.clips[] contains clip_id, in/out times, duration, source_packets. Apps can detect fake/scrambled playlists by checking unique clip count vs total (253 clips referencing 2 unique = fake). Removed clip_count field (use clips.len()).
This commit is contained in:
+33
-8
@@ -66,14 +66,29 @@ pub struct Title {
|
|||||||
pub duration_secs: f64,
|
pub duration_secs: f64,
|
||||||
/// Total size in bytes
|
/// Total size in bytes
|
||||||
pub size_bytes: u64,
|
pub size_bytes: u64,
|
||||||
/// Number of clips
|
/// Clip references in playback order
|
||||||
pub clip_count: usize,
|
pub clips: Vec<Clip>,
|
||||||
/// All streams (video, audio, subtitle, etc.)
|
/// All streams (video, audio, subtitle, etc.)
|
||||||
pub streams: Vec<Stream>,
|
pub streams: Vec<Stream>,
|
||||||
/// Sector extents for ripping (clip LBA ranges)
|
/// Sector extents for ripping (clip LBA ranges)
|
||||||
pub extents: Vec<Extent>,
|
pub extents: Vec<Extent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A clip reference within a title.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Clip {
|
||||||
|
/// Clip filename without extension (e.g. "00001")
|
||||||
|
pub clip_id: String,
|
||||||
|
/// In-time in 45kHz ticks
|
||||||
|
pub in_time: u32,
|
||||||
|
/// Out-time in 45kHz ticks
|
||||||
|
pub out_time: u32,
|
||||||
|
/// Duration in seconds
|
||||||
|
pub duration_secs: f64,
|
||||||
|
/// Source packet count (from CLPI, 0 if unavailable)
|
||||||
|
pub source_packets: u32,
|
||||||
|
}
|
||||||
|
|
||||||
/// A stream within a title.
|
/// A stream within a title.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
@@ -660,23 +675,33 @@ impl Disc {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse each clip for size and sector extents
|
// Parse each clip for size, duration, and sector extents
|
||||||
let mut extents = Vec::new();
|
let mut extents = Vec::new();
|
||||||
let mut total_size: u64 = 0;
|
let mut total_size: u64 = 0;
|
||||||
let clip_count = parsed.play_items.len();
|
let mut clips = Vec::with_capacity(parsed.play_items.len());
|
||||||
|
|
||||||
for play_item in &parsed.play_items {
|
for play_item in &parsed.play_items {
|
||||||
|
let clip_dur = play_item.out_time.saturating_sub(play_item.in_time) as f64 / 45000.0;
|
||||||
|
let mut pkt_count: u32 = 0;
|
||||||
|
|
||||||
let clpi_path = format!("/BDMV/CLIPINF/{}.clpi", play_item.clip_id);
|
let clpi_path = format!("/BDMV/CLIPINF/{}.clpi", play_item.clip_id);
|
||||||
if let Ok(clpi_data) = udf_fs.read_file(session, &clpi_path) {
|
if let Ok(clpi_data) = udf_fs.read_file(session, &clpi_path) {
|
||||||
if let Ok(clip_info) = clpi::parse(&clpi_data) {
|
if let Ok(clip_info) = clpi::parse(&clpi_data) {
|
||||||
// Size from source packet count (192 bytes per packet)
|
pkt_count = clip_info.source_packet_count;
|
||||||
total_size += clip_info.source_packet_count as u64 * 192;
|
total_size += pkt_count as u64 * 192;
|
||||||
|
|
||||||
// EP map extents for ripping (sector ranges on disc)
|
|
||||||
let clip_extents = clip_info.get_extents(play_item.in_time, play_item.out_time);
|
let clip_extents = clip_info.get_extents(play_item.in_time, play_item.out_time);
|
||||||
extents.extend(clip_extents);
|
extents.extend(clip_extents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clips.push(Clip {
|
||||||
|
clip_id: play_item.clip_id.clone(),
|
||||||
|
in_time: play_item.in_time,
|
||||||
|
out_time: play_item.out_time,
|
||||||
|
duration_secs: clip_dur,
|
||||||
|
source_packets: pkt_count,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build streams from STN table
|
// Build streams from STN table
|
||||||
@@ -727,7 +752,7 @@ impl Disc {
|
|||||||
playlist_id,
|
playlist_id,
|
||||||
duration_secs,
|
duration_secs,
|
||||||
size_bytes: total_size,
|
size_bytes: total_size,
|
||||||
clip_count,
|
clips,
|
||||||
streams,
|
streams,
|
||||||
extents,
|
extents,
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -87,5 +87,5 @@ pub use profile::{DriveProfile, Chipset};
|
|||||||
pub use platform::{Platform, DriveStatus};
|
pub use platform::{Platform, DriveStatus};
|
||||||
pub use scsi::ScsiTransport;
|
pub use scsi::ScsiTransport;
|
||||||
pub use speed::DriveSpeed;
|
pub use speed::DriveSpeed;
|
||||||
pub use disc::{Disc, DiscFormat, Title, Stream, StreamKind, Codec, HdrFormat, ColorSpace,
|
pub use disc::{Disc, DiscFormat, Title, Clip, Stream, StreamKind, Codec, HdrFormat, ColorSpace,
|
||||||
Extent, ContentReader, AacsState, KeySource, ScanOptions};
|
Extent, ContentReader, AacsState, KeySource, ScanOptions};
|
||||||
|
|||||||
Reference in New Issue
Block a user