Skip to content

Commit

Permalink
code redesign 6
Browse files Browse the repository at this point in the history
  • Loading branch information
clitic committed Jul 25, 2024
1 parent ebb2548 commit 6f3f11c
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 20 deletions.
36 changes: 16 additions & 20 deletions vsd/src/downloader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,6 @@ pub struct Stream {
pub media_type: MediaType,
}

pub fn progress_bar() -> RichProgress {
RichProgress::new(
tqdm!(unit = " SEG".to_owned(), dynamic_ncols = true),
vec![
Column::Text("[bold blue]?".to_owned()),
Column::Animation,
Column::Percentage(0),
Column::Text("•".to_owned()),
Column::CountTotal,
Column::Text("•".to_owned()),
Column::ElapsedTime,
Column::Text("[cyan]>".to_owned()),
Column::RemainingTime,
Column::Text("•".to_owned()),
Column::Rate,
],
)
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn download(
all_keys: bool,
Expand Down Expand Up @@ -186,7 +167,22 @@ pub(crate) fn download(
// Prepare Progress Bar
// -----------------------------------------------------------------------------------------

let mut pb = progress_bar();
let mut pb = RichProgress::new(
tqdm!(unit = " SEG".to_owned(), dynamic_ncols = true),
vec![
Column::Text("[bold blue]?".to_owned()),
Column::Animation,
Column::Percentage(0),
Column::Text("•".to_owned()),
Column::CountTotal,
Column::Text("•".to_owned()),
Column::ElapsedTime,
Column::Text("[cyan]>".to_owned()),
Column::RemainingTime,
Column::Text("•".to_owned()),
Column::Rate,
],
);

// -----------------------------------------------------------------------------------------
// Prepare Directory & Store Streams Metadata
Expand Down
202 changes: 202 additions & 0 deletions vsd/src/downloader/subtitle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
use crate::{downloader::Stream, playlist::MediaPlaylist, utils};
use anyhow::{anyhow, bail, Result};
use kdam::{term::Colorizer, BarExt, Column, RichProgress};
use reqwest::{blocking::Client, header, Url};
use std::{fs::File, io::Write, path::PathBuf};
use vsd_mp4::text::{ttml_text_parser, Mp4TtmlParser, Mp4VttParser};

enum SubtitleType {
Mp4Vtt,
Mp4Ttml,
SrtText,
TtmlText,
VttText,
}

pub fn download_subtitle_stream(
base_url: Option<Url>,
client: &Client,
directory: &Option<PathBuf>,
stream: &MediaPlaylist,
pb: &mut RichProgress,
temp_files: &mut Vec<Stream>,
) -> Result<()> {
pb.write(format!(
" {} {} stream {}",
"Processing".colorize("bold green"),
stream.media_type,
stream.display_stream().colorize("cyan"),
))?;

let length = stream.segments.len();

if length == 0 {
pb.write(format!(
" {} skipping stream (no segments)",
"Warning".colorize("bold yellow"),
))?;
return Ok(());
}

pb.pb.total = length;

let mut ext = stream.extension();
let mut codec = None;

if let Some(codecs) = &stream.codecs {
match codecs.as_str() {
"vtt" => {
ext = "vtt".to_owned();
codec = Some(SubtitleType::VttText);
}
"wvtt" => {
ext = "vtt".to_owned();
codec = Some(SubtitleType::Mp4Vtt);
}
"stpp" | "stpp.ttml" | "stpp.ttml.im1t" | "stpp.TTML.im1t" => {
ext = "srt".to_owned();
codec = Some(SubtitleType::Mp4Ttml);
}
_ => (),
}
}
let mut temp_file = String::new();

let mut first_run = true;
let mut subtitles_data = vec![];

let stream_base_url = base_url
.clone()
.unwrap_or(stream.uri.parse::<Url>().unwrap());

for segment in &stream.segments {
if let Some(map) = &segment.map {
let url = stream_base_url.join(&map.uri)?;
let mut request = client.get(url);

if let Some(range) = &map.range {
request = request.header(header::RANGE, range.as_header_value());
}

let response = request.send()?;
let bytes = response.bytes()?;
subtitles_data.extend_from_slice(&bytes);
}

let url = stream_base_url.join(&segment.uri)?;
let mut request = client.get(url);

if let Some(range) = &segment.range {
request = request.header(header::RANGE, range.as_header_value());
}

let response = request.send()?;
let bytes = response.bytes()?;
subtitles_data.extend_from_slice(&bytes);

if first_run {
first_run = false;

if subtitles_data.starts_with(b"WEBVTT") {
ext = "vtt".to_owned();
codec = Some(SubtitleType::VttText);
} else if subtitles_data.starts_with(b"1") {
ext = "srt".to_owned();
codec = Some(SubtitleType::SrtText);
} else if subtitles_data.starts_with(b"<?xml") || subtitles_data.starts_with(b"<tt") {
ext = "srt".to_owned();
codec = Some(SubtitleType::TtmlText);
} else if codec.is_none() {
bail!("could'nt determine subtitle codec.");
}

temp_file = stream
.file_path(directory, &ext)
.to_string_lossy()
.to_string();
temp_files.push(Stream {
file_path: temp_file.clone(),
language: stream.language.clone(),
media_type: stream.media_type.clone(),
});
pb.write(format!(
"{} stream to {}",
"Downloading".colorize("bold green"),
temp_file.colorize("cyan")
))?;
}

pb.replace(
0,
Column::Text(format!(
"[bold blue]{}",
utils::format_bytes(subtitles_data.len(), 2).2
)),
);
pb.update(1)?;
}

match codec {
Some(SubtitleType::Mp4Vtt) => {
pb.write(format!(
" {} wvtt subtitles",
"Extracting".colorize("bold cyan"),
))?;

let vtt = Mp4VttParser::parse_init(&subtitles_data)?;
let subtitles = vtt.parse_media(&subtitles_data, None)?;
File::create(&temp_file)?.write_all(subtitles.as_vtt().as_bytes())?;
}
Some(SubtitleType::Mp4Ttml) => {
pb.write(format!(
" {} stpp subtitles",
"Extracting".colorize("bold cyan"),
))?;

let ttml = Mp4TtmlParser::parse_init(&subtitles_data)?;
let subtitles = ttml.parse_media(&subtitles_data)?;
File::create(&temp_file)?.write_all(subtitles.as_srt().as_bytes())?;
}
Some(SubtitleType::TtmlText) => {
pb.write(format!(
" {} ttml+xml subtitles",
"Extracting".colorize("bold cyan"),
))?;

let xml = String::from_utf8(subtitles_data)
.map_err(|_| anyhow!("cannot decode subtitles as valid utf-8 data."))?;
let ttml = ttml_text_parser::parse(&xml).map_err(|x| {
anyhow!(
"couldn't parse xml string as ttml content.\n\n{}\n\n{:#?}",
xml,
x,
)
})?;
File::create(&temp_file)?.write_all(ttml.into_subtitles().as_srt().as_bytes())?;
}
_ => File::create(&temp_file)?.write_all(&subtitles_data)?,
};

pb.write(format!(
" {} stream successfully",
"Downloaded".colorize("bold green"),
))?;
eprintln!();
pb.reset(Some(0));
Ok(())
}

pub fn download_subtitle_streams(
base_url: Option<Url>,
client: &Client,
directory: &Option<PathBuf>,
subtitle_streams: &Vec<MediaPlaylist>,
pb: &mut RichProgress,
temp_files: &mut Vec<Stream>,
) -> Result<()> {
for stream in subtitle_streams {
download_subtitle_stream(base_url.clone(), client, directory, stream, pb, temp_files)?;
}

Ok(())
}

0 comments on commit 6f3f11c

Please sign in to comment.