Fix crashes when converting subtitles (#408)

This commit is contained in:
bytedream 2024-05-14 16:11:55 +02:00
parent 53a710a373
commit 48bb7a5ef6
3 changed files with 30 additions and 18 deletions

6
Cargo.lock generated
View file

@ -386,6 +386,7 @@ dependencies = [
"shlex", "shlex",
"sys-locale", "sys-locale",
"tempfile", "tempfile",
"time",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tower-service", "tower-service",
@ -1511,12 +1512,13 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]] [[package]]
name = "rsubs-lib" name = "rsubs-lib"
version = "0.2.1" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dcca2a9560fca05de8f95bc3767e46673d4b4c1f2c7a11092e10efd95bbdf62" checksum = "f43e1a7f184bc76407dbaa67bd2aeea8a15430d7e1e498070963336d03ebedee"
dependencies = [ dependencies = [
"regex", "regex",
"serde", "serde",
"time",
] ]
[[package]] [[package]]

View file

@ -30,7 +30,7 @@ log = { version = "0.4", features = ["std"] }
num_cpus = "1.16" num_cpus = "1.16"
regex = "1.10" regex = "1.10"
reqwest = { version = "0.12", features = ["socks", "stream"] } reqwest = { version = "0.12", features = ["socks", "stream"] }
rsubs-lib = ">=0.2.1" rsubs-lib = "0.3"
rusty-chromaprint = "0.2" rusty-chromaprint = "0.2"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
@ -38,6 +38,7 @@ serde_plain = "1.0"
shlex = "1.3" shlex = "1.3"
sys-locale = "0.3" sys-locale = "0.3"
tempfile = "3.10" tempfile = "3.10"
time = "0.3"
tokio = { version = "1.37", features = ["io-util", "macros", "net", "rt-multi-thread", "time"] } tokio = { version = "1.37", features = ["io-util", "macros", "net", "rt-multi-thread", "time"] }
tokio-util = "0.7" tokio-util = "0.7"
tower-service = "0.3" tower-service = "0.3"

View file

@ -13,17 +13,19 @@ use indicatif::{ProgressBar, ProgressDrawTarget, ProgressFinish, ProgressStyle};
use log::{debug, warn, LevelFilter}; use log::{debug, warn, LevelFilter};
use regex::Regex; use regex::Regex;
use reqwest::Client; use reqwest::Client;
use rsubs_lib::{ssa, vtt}; use rsubs_lib::{SSA, VTT};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::io::Write; use std::io::Write;
use std::ops::Add;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::{env, fs}; use std::{env, fs};
use tempfile::TempPath; use tempfile::TempPath;
use time::Time;
use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader}; use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
use tokio::select; use tokio::select;
use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::unbounded_channel;
@ -929,36 +931,43 @@ impl Downloader {
) -> Result<TempPath> { ) -> Result<TempPath> {
let buf = subtitle.data().await?; let buf = subtitle.data().await?;
let mut ass = match subtitle.format.as_str() { let mut ass = match subtitle.format.as_str() {
"ass" => ssa::parse(String::from_utf8_lossy(&buf).to_string()), "ass" => SSA::parse(String::from_utf8_lossy(&buf))?,
"vtt" => vtt::parse(String::from_utf8_lossy(&buf).to_string()).to_ass(), "vtt" => VTT::parse(String::from_utf8_lossy(&buf))?.to_ssa(),
_ => bail!("unknown subtitle format: {}", subtitle.format), _ => bail!("unknown subtitle format: {}", subtitle.format),
}; };
// subtitles aren't always correct sorted and video players may have issues with that. to // subtitles aren't always correct sorted and video players may have issues with that. to
// prevent issues, the subtitles are sorted // prevent issues, the subtitles are sorted
ass.events // (https://github.com/crunchy-labs/crunchy-cli/issues/208)
.sort_by(|a, b| a.line_start.total_ms().cmp(&b.line_start.total_ms())); ass.events.sort_by(|a, b| a.start.cmp(&b.start));
// it might be the case that the start and/or end time are greater than the actual video // it might be the case that the start and/or end time are greater than the actual video
// length. this might also result in issues with video players, thus the times are stripped // length. this might also result in issues with video players, thus the times are stripped
// to be maxim // to be at most as long as `max_length`
// (https://github.com/crunchy-labs/crunchy-cli/issues/32)
for i in (0..ass.events.len()).rev() { for i in (0..ass.events.len()).rev() {
if ass.events[i].line_end.total_ms() > max_length.num_milliseconds() as u32 { let max_len = Time::from_hms(0, 0, 0)
if ass.events[i].line_start.total_ms() > max_length.num_milliseconds() as u32 { .unwrap()
ass.events[i] .add(Duration::from_millis(max_length.num_milliseconds() as u64));
.line_start
.set_ms(max_length.num_milliseconds() as u32); if ass.events[i].start > max_len {
if ass.events[i].end > max_len {
ass.events[i].start = max_len
} }
ass.events[i] ass.events[i].end = max_len
.line_end
.set_ms(max_length.num_milliseconds() as u32);
} else { } else {
break; break;
} }
} }
// without this additional info, subtitle look very messy in some video player
// (https://github.com/crunchy-labs/crunchy-cli/issues/66)
ass.info
.additional_fields
.insert("ScaledBorderAndShadows".to_string(), "yes".to_string());
let tempfile = tempfile(".ass")?; let tempfile = tempfile(".ass")?;
let path = tempfile.into_temp_path(); let path = tempfile.into_temp_path();
ass.to_file(path.to_string_lossy().to_string().as_str())?; fs::write(&path, ass.to_string())?;
Ok(path) Ok(path)
} }