Skip to content

Instantly share code, notes, and snippets.

@tianjiaoshanzai
Created May 26, 2023 01:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tianjiaoshanzai/3cc25da5d11c64973e3c2c324341f490 to your computer and use it in GitHub Desktop.
Save tianjiaoshanzai/3cc25da5d11c64973e3c2c324341f490 to your computer and use it in GitHub Desktop.
最终执行的主类是ListFilesUtil
import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
// 在目录 JAVA_HOME\jre\lib\rt.jar 中,编译不一定能加载,需要单设path
import com.sun.xml.internal.ws.spi.db.BindingContextFactory;
/*
目的是批量将字幕插入视频
*/
public class InsertSubtitle {
File f = null;//获取当前执行文件的所在路径
public InsertSubtitle(){
try{
//注意IDEA默认编译的.class文件在out目录中
f = new File(Objects.requireNonNull(this.getClass().getResource("")).toURI().getPath());
}catch (URISyntaxException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
//TODO 获取所有视频和字幕文件,合成输出。“中间后缀”可以指定,默认全部输出,不同中间后缀对应的目录不同
InsertSubtitle insertSubtitle=new InsertSubtitle();
String fileName="[Kamigami&VCB-Studio] Boku dake ga Inai Machi [01][Ma10p_1080p][x265_flac_ac3].mkv";
String subName="[Kamigami&VCB-Studio] Boku dake ga Inai Machi [01][Ma10p_1080p][x265_flac_ac3].chs&jpn.ass";
File file=new File(insertSubtitle.f,fileName);
File sub=new File(insertSubtitle.f,subName);
try{
insertSubtitle.insertSoftSub(null, file, sub, null);
}catch (IOException | InterruptedException e){
e.printStackTrace();
}
String fileName2="[Kamigami&VCB-Studio] Boku dake ga Inai Machi [02][Ma10p_1080p][x265_flac_ac3].mkv";
String subName2="[Kamigami&VCB-Studio] Boku dake ga Inai Machi [02][Ma10p_1080p][x265_flac_ac3].chs&jpn.ass";
File file2=new File(insertSubtitle.f,fileName2);
File sub2=new File(insertSubtitle.f,subName2);
try{
insertSubtitle.insertSoftSub(null,file2,sub2,null);
}catch (IOException | InterruptedException e){
e.printStackTrace();
}
System.out.println("完成");
}
String getParameterForm(String row){
String os = System.getProperty("os.name");
//Windows操作系统
if (os != null && os.toLowerCase().startsWith("windows")) {
// System.out.println(String.format("当前系统版本是:%s", os));
return "\""+row+"\"";
} else if (os != null && os.toLowerCase().startsWith("linux")) {//Linux操作系统
// System.out.println(String.format("当前系统版本是:%s", os));
return "‘"+row+"’";
} else { //其它操作系统
// System.out.println(String.format("当前系统版本是:%s", os));
return "‘"+row+"’";
}
}
/*
为视频添加软字幕,仅支持mkv封装
添加字幕非常快,几乎不消耗性能
示例命令:ffmpeg -i test_1280x720_3.mp4 -i test_1280x720_3.srt -c copy output.mkv
*/
public void insertSoftSub(String ffmpegPath,File file,File sub,File out) throws IOException, InterruptedException {
//Todo 将分隔符改为根据系统切换
//TODO error log 想想办法
String fileName,subName,outPutPath;
if(file!=null){
fileName=file.getName();
}else {
System.out.println("无转换对象");
return;
}
if(sub!=null){
subName=sub.getName();
}else {
System.out.println("无转换对象");
return;
}
if(out!=null){
outPutPath=out.getAbsolutePath();
}else {
outPutPath="output";
// File output=new File()
out=new File(f,outPutPath);
}
if(ffmpegPath==null){
ffmpegPath="ffmpeg";
}
List<String> command = new ArrayList<>();
command.add(ffmpegPath);
command.add("-i");
// command.add(getParameterForm(file.getAbsolutePath()));
command.add(file.getAbsolutePath());
command.add("-i");
// command.add(getParameterForm(sub.getAbsolutePath()));
command.add(sub.getAbsolutePath());
command.add("-c");
command.add("copy");
// command.add(getParameterForm((new File(out,fileName)).getAbsolutePath()));
command.add((new File(out,fileName)).getAbsolutePath());
// 执行操作
// System.out.println("command is:"+command);
BindingContextFactory.LOGGER.info("command is"+command.toString());
ProcessBuilder builder = new ProcessBuilder(command);
builder.directory(f);
Process process = builder.start();
mointor(process);
Thread.sleep(15000);
}
/**
* 检测进程
*
* @param process
*/
public static void mointor(Process process) {
// 读取进程标准输出
new Thread(() -> {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line = null;
while ((line = bufferedReader.readLine()) != null) {
BindingContextFactory.LOGGER.info(line);
}
} catch (IOException e) {
}
}).start();
// 读取进程异常输出
new Thread(() -> {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line = null;
while ((line = bufferedReader.readLine()) != null) {
BindingContextFactory.LOGGER.info(line);
}
} catch (IOException e) {
}
}).start();
}
}
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
//外部依赖 从 https://commons.apache.org/proper/commons-io/download_io.cgi 下载的binary code,最终要生成可执行文件需要注意
import org.apache.commons.io.FilenameUtils;
public class ListFilesUtil {
File f = null;//获取当前执行文件的所在路径
static List<String> videoFormat=new ArrayList<>(Arrays.asList(".mp4",".avi",".mov",".mkv"));
static List<String> subFormat=new ArrayList<>(Arrays.asList(".ass",".srt","sub"));
private List<File> videoList=new ArrayList<>();
//获取字幕名称和“字幕注释” 例子:Home Alone.spanish.srt-->spanish;Home Alone.spa.forced.srt-->spa.forced
//List object[] index0是String,注释名 index1是File 字幕文件。
private Map<String,List<Object[]>> subMap=new HashMap<>();
public static void main(String[] args) {
ListFilesUtil listFilesUtil=new ListFilesUtil();
File dir =listFilesUtil.f;
File outDir=new File(dir,"output");
//遍历目标目录,将所有视频和字幕和字幕归档
listFilesUtil.scanDir(dir);
List<File> videoList=listFilesUtil.getVideoList();
Map<String,List<Object[]>> subMap=listFilesUtil.getSubMap();
for(File video:videoList){
if(subMap.containsKey(FilenameUtils.removeExtension(video.getName()))){
List<Object[]> subObjList=subMap.get(FilenameUtils.removeExtension(video.getName()));
for(Object[] objArr:subObjList){
//TODO if符合筛选标准(比如简体中文,双字等等),进行压制工作
File outAnnotationDir=new File(outDir,(String) objArr[0]);
if(!outAnnotationDir.exists()){
//TODO 判断创建失败与否
outAnnotationDir.mkdirs();
}
InsertSubtitle insertSubtitle=new InsertSubtitle();
try {
insertSubtitle.insertSoftSub(null,video,(File)objArr[1],outAnnotationDir);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public void scanDir(File dir) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile() && isVideoFile(file)) {
videoList.add(file);
}else if(file.isFile() && isSubFile(file)){
String subAnnotation=getSubAnnotation(file.getName());
String originName=getSubOriginName(file.getName());
List<Object[]> annotationList=subMap.getOrDefault(originName,new ArrayList<>());
Object[] objArr=new Object[2];
objArr[0]=subAnnotation;
objArr[1]=file;
annotationList.add(objArr);
subMap.put(originName,annotationList);
}else if (file.isDirectory()) {
//listFilesAndDirectories(file);
scanDir(file);
}
}
}
}
public List<File> getVideoList(){
return videoList;
}
public Map<String,List<Object[]>> getSubMap(){
return subMap;
}
private static String getSubOriginName(String fileName){
String fileNameWithoutEx= FilenameUtils.removeExtension(fileName);
return fileNameWithoutEx.substring(0,fileNameWithoutEx.indexOf("."));
}
private static String getSubAnnotation(String fileName){
String fileNameWithoutEx= FilenameUtils.removeExtension(fileName);
return fileNameWithoutEx.substring(fileNameWithoutEx.indexOf(".")+1);
}
private static boolean isVideoFile(File file) {
String name = file.getName().toLowerCase();
for(String vf:videoFormat){
if(name.endsWith(vf))
return true;
}
return false;
}
private static boolean isSubFile(File file) {
String name = file.getName().toLowerCase();
for(String sf:subFormat){
if(name.endsWith(sf))
return true;
}
return false;
}
public ListFilesUtil(){
try{
//注意IDEA默认编译的.class文件在out目录中
f = new File(Objects.requireNonNull(this.getClass().getResource("")).toURI().getPath());
}catch (URISyntaxException e){
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment