use std::{default::Default, io::Write};
use anyhow::Context as _;
use log::debug;
use crate::{
export::Exporter,
time::{seconds_to_hhmmss, Period},
video::Id3Metadata,
Result,
};
fn truncate(max: usize, s: &str) -> String {
if s.chars().count() <= max {
s.to_owned()
} else {
let mut result: String = s.chars().take(max - 1).collect();
result.push_str("…");
result
}
}
fn should_merge(p1: Period, p2: Period) -> bool {
if p1.duration() >= 30.0 {
false
} else {
match p1.distance(p2) {
None => true,
Some(d) if d <= 5.0 => true,
_ => false,
}
}
}
#[derive(Debug)]
struct Conv {
period: Period,
text: String,
}
pub fn export_tracks(exporter: &mut Exporter) -> Result<()> {
let mut convs: Vec<Conv> = vec![];
for sub in &exporter.foreign().subtitles.subtitles {
if let Some(prev) = convs.last_mut() {
if should_merge(prev.period, sub.period) {
prev.period = prev.period.union(sub.period);
prev.text.push_str("\n");
prev.text.push_str(&sub.plain_text());
continue;
}
}
convs.push(Conv {
period: sub.period,
text: sub.plain_text(),
});
}
for conv in convs.iter_mut() {
conv.period = conv.period.grow(2.5, 2.5);
}
for i in 1..convs.len() {
if convs[i].period.begin() < convs[i - 1].period.end() {
let overlap =
Period::new(convs[i].period.begin(), convs[i - 1].period.end())
.unwrap();
let midpoint = overlap.midpoint();
debug!(
"Overlap: {:?} {:?} {:?} {}",
&convs[i - 1],
&convs[i],
overlap,
midpoint
);
convs[i - 1].period =
Period::new(convs[i - 1].period.begin(), midpoint).unwrap();
convs[i].period = Period::new(midpoint, convs[i].period.end()).unwrap();
assert!(convs[i].period.begin() >= convs[i - 1].period.end());
}
}
let foreign_lang = exporter.foreign().language;
let mut playlist = vec![];
for (i, conv) in convs.iter().enumerate() {
debug!(
"Conv: {:7.1} -> {:7.1} for {:7.1}",
conv.period.begin(),
conv.period.end(),
conv.period.duration()
);
let name = format!(
"{} {}",
seconds_to_hhmmss(conv.period.begin()),
truncate(32, &conv.text).replace("\n", " ")
);
let metadata = Id3Metadata {
genre: Some("substudy".to_owned()),
album: Some(exporter.file_stem().to_owned()),
track_number: Some((i + 1, convs.len())),
track_name: Some(name),
lyrics: Some(conv.text.clone()),
..Default::default()
};
let path =
exporter.schedule_audio_export_ext(foreign_lang, conv.period, metadata);
writeln!(playlist, "{}", &path)
.context("error serializing playlist to memory")?;
}
exporter.export_data_file("playlist.m3u8", &playlist)?;
exporter.finish_exports()?;
Ok(())
}