feat: origin filename support.

This commit is contained in:
2026-01-04 03:12:57 +08:00
parent 5ce56bda36
commit ae0ba21b11
6 changed files with 97 additions and 19 deletions

2
Cargo.lock generated
View File

@@ -54,7 +54,7 @@ dependencies = [
[[package]]
name = "bilibili-merge"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"clap",
]

View File

@@ -1,6 +1,6 @@
[package]
name = "bilibili-merge"
version = "0.2.0"
version = "0.3.0"
authors = ["licsber <admin@licsber.site>"]
edition = "2021"

View File

@@ -20,6 +20,11 @@ impl ShellScript {
self.lines.push(format!("{}=\"{}\"", name, escaped));
}
pub fn add_variable_str(&mut self, name: &str, value: &str) {
let escaped = value.replace('"', r#"\""#).replace('$', r#"\$"#);
self.lines.push(format!("{}=\"{}\"", name, escaped));
}
pub fn add_command(&mut self, command: &str) {
self.lines.push(command.to_string());
}

View File

@@ -21,17 +21,65 @@ pub fn find_largest_video_file(path: &Path) -> Result<PathBuf> {
}
pub fn find_audio_file(video_path: &Path) -> Result<PathBuf> {
let video_name = video_path.file_name().unwrap().to_str().unwrap();
let video_extension = video_path.extension().unwrap();
let audio_name = format!("{}.m4a", &video_name[..video_name.len() - video_extension.len() - 1]);
let audio_path = video_path.parent().unwrap().join(audio_name);
let parent = video_path.parent().unwrap();
let video_extension = video_path.extension().and_then(|e| e.to_str());
if !audio_path.exists() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Audio file does not exist: {}", audio_path.display())
));
// Try to find .m4a file first (old format)
let video_name = video_path.file_stem().and_then(|n| n.to_str()).unwrap_or("");
let audio_m4a_path = parent.join(format!("{}.m4a", video_name));
if audio_m4a_path.exists() {
return Ok(audio_m4a_path);
}
Ok(audio_path)
// Try to find another .m4s file (new format)
if video_extension == Some("m4s") {
let mut audio_candidates = Vec::new();
for entry in std::fs::read_dir(parent)? {
let entry_path = entry?.path();
if entry_path.is_file() {
if let Some(ext) = entry_path.extension().and_then(|e| e.to_str()) {
if ext == "m4s" && entry_path != video_path {
let size = entry_path.metadata()?.len();
audio_candidates.push((entry_path, size));
}
}
}
}
if !audio_candidates.is_empty() {
// Return the largest .m4s file that's not the video file
audio_candidates.sort_by(|a, b| b.1.cmp(&a.1));
return Ok(audio_candidates[0].0.clone());
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Audio file does not exist. Looked for: {}", audio_m4a_path.display())
))
}
pub fn find_ass_file(path: &Path) -> Result<PathBuf> {
for entry in std::fs::read_dir(path)? {
let entry_path = entry?.path();
if entry_path.is_file() {
if let Some(ext) = entry_path.extension().and_then(|e| e.to_str()) {
if ext == "ass" {
return Ok(entry_path);
}
}
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"ASS subtitle file not found in directory"
))
}
pub fn get_output_name_from_ass(ass_path: &Path) -> String {
ass_path.file_stem()
.and_then(|n| n.to_str())
.unwrap_or("output")
.to_string()
}

View File

@@ -1,6 +1,6 @@
use crate::audio_converter::convert_audio_to_aac;
use crate::dry_run::{print_shell_script_header, ShellScript};
use crate::file_finder::{find_audio_file, find_largest_video_file};
use crate::file_finder::{find_audio_file, find_ass_file, find_largest_video_file, get_output_name_from_ass};
use crate::video_merger::merge_video_and_audio;
use std::io::Result;
use std::path::Path;
@@ -34,13 +34,38 @@ pub fn merge_video_from_path(path: &Path, dry_run: bool, overwrite: bool) -> Res
crate::dry_run::print_shell_script(&script);
}
println!("Step 3: Looking for ASS subtitle file to determine output name...");
let output_name = match find_ass_file(path) {
Ok(ass_path) => {
println!("Found ASS file: {}", ass_path.display());
let name = get_output_name_from_ass(&ass_path);
println!("Output name will be: {}", name);
if dry_run {
let mut script = ShellScript::new();
script.add_variable("ASS_FILE", &ass_path);
script.add_variable_str("OUTPUT_NAME", &name);
crate::dry_run::print_shell_script(&script);
}
name
}
Err(_) => {
println!("No ASS file found, using video file name as output name");
let name = video_path.file_stem()
.and_then(|n| n.to_str())
.unwrap_or("output")
.to_string();
println!("Output name will be: {}", name);
name
}
};
println!();
println!("Step 3: Converting audio to AAC format...");
println!("Step 4: Converting audio to AAC format...");
let aac_path = convert_audio_to_aac(&audio_path, dry_run, overwrite)?;
println!();
println!("Step 4: Merging video and audio...");
let output_path = merge_video_and_audio(&video_path, &aac_path, dry_run, overwrite)?;
println!("Step 5: Merging video and audio...");
let output_path = merge_video_and_audio(&video_path, &aac_path, &output_name, path, dry_run, overwrite)?;
if dry_run {
println!();

View File

@@ -4,11 +4,11 @@ use std::io::Result;
use std::path::{Path, PathBuf};
use std::fs;
pub fn merge_video_and_audio(video_path: &Path, audio_path: &Path, dry_run: bool, overwrite: bool) -> Result<PathBuf> {
pub fn merge_video_and_audio(video_path: &Path, audio_path: &Path, output_name: &str, output_dir: &Path, dry_run: bool, overwrite: bool) -> Result<PathBuf> {
let origin_video_extension = video_path.extension().unwrap().to_str().unwrap();
let origin_filename = format!("original.{}", &origin_video_extension);
let origin_path = video_path.parent().unwrap().join(origin_filename);
let output_path = video_path.with_extension("mp4");
let output_path = output_dir.join(format!("{}.mp4", output_name));
if dry_run {
let mut script = ShellScript::new();
@@ -24,7 +24,7 @@ pub fn merge_video_and_audio(video_path: &Path, audio_path: &Path, dry_run: bool
.input_var("VIDEO_BACKUP")
.input_var("AUDIO_AAC")
.codec("copy")
.output_var("VIDEO_OUTPUT")
.output(&output_path)
.execute(true)?;
return Ok(output_path);
}