From b196bb51dcdecdb2d3e40fe9b8d90c39863432ac Mon Sep 17 00:00:00 2001 From: HarveyNW Date: Thu, 20 Feb 2025 16:39:51 +0800 Subject: [PATCH 1/4] fix some code --- Temp/output_subtitle.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Temp/output_subtitle.js b/Temp/output_subtitle.js index 72d5549..a1706d0 100644 --- a/Temp/output_subtitle.js +++ b/Temp/output_subtitle.js @@ -6,7 +6,7 @@ const videoFolder = './video_res'; //获取视频目录 const subtitleFolder = './subtitle_res'; //获取字幕目录 //检测文件信息 -fs.readdir(videoFolder, (err, files) => { +fs.readdirSync(videoFolder, (err, files) => { if(err) { console.error(err); return; @@ -32,7 +32,7 @@ fs.readdir(videoFolder, (err, files) => { const command = `ffmpeg -v error -i "${inputFilePath}" -map 0:s:${index} -c:s ${stream.codec_name} -y "${outputFilePath}"`; //执行ffmpeg将字幕分离 - exec(command, (err, stdout, stderr) => { + execSync(command, (err, stdout, stderr) => { if(err) { console.error(`处理文件${inputFilePath}出错: `, err.message); } @@ -40,7 +40,4 @@ fs.readdir(videoFolder, (err, files) => { }); }); }); -}); - - - +}); \ No newline at end of file From a417f5093656ea1b12a3db37582ea674f316b790 Mon Sep 17 00:00:00 2001 From: HarveyNW Date: Fri, 21 Feb 2025 00:16:54 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=B0=86=E5=BC=82=E6=AD=A5=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Temp/output_subtitle.js | 54 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/Temp/output_subtitle.js b/Temp/output_subtitle.js index a1706d0..8a72c33 100644 --- a/Temp/output_subtitle.js +++ b/Temp/output_subtitle.js @@ -3,41 +3,39 @@ const path = require('path'); const { execSync } = require('child_process'); const { exec } = require('child_process'); const videoFolder = './video_res'; //获取视频目录 -const subtitleFolder = './subtitle_res'; //获取字幕目录 //检测文件信息 -fs.readdirSync(videoFolder, (err, files) => { - if(err) { - console.error(err); - return; - } +try { + const files = fs.readdirSync(videoFolder); const videoFiles = files.filter(file => { const ext = path.extname(file).toLowerCase(); return ['.mp4', '.mkv', 'avi', '.mov'].includes(ext); }) - videoFiles.forEach(videoFile => { - const inputFilePath = path.join(videoFolder,videoFile); - const probeCommand = `ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of json "${inputFilePath}"`; - const probeOut = JSON.parse(execSync(probeCommand).toString()); - //获取字幕详细 - if (!probeOut.streams || probeOut.streams.length === 0) { - console.log(`视频${videoFile}未找到字幕`); - } //确认是否存在字幕 - - probeOut.streams.forEach((stream,index) => { - const outputFilePath = `./output/${videoFile}${index}.${stream.codec_name === 'subrip' ? 'srt' : 'ass'}`; - //定义输出路径名称及字幕格式后缀 - - const command = `ffmpeg -v error -i "${inputFilePath}" -map 0:s:${index} -c:s ${stream.codec_name} -y "${outputFilePath}"`; - //执行ffmpeg将字幕分离 - - execSync(command, (err, stdout, stderr) => { - if(err) { - console.error(`处理文件${inputFilePath}出错: `, err.message); + videoFiles.forEach(videoFile => { + const inputFilePath = path.join(videoFolder,videoFile); + const probeCommand = `ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of json "${inputFilePath}"`; + const probeOut = JSON.parse(execSync(probeCommand).toString()); + //获取字幕详细 + + if (!probeOut.streams || probeOut.streams.length === 0) { + console.log(`视频${videoFile}未找到字幕`); + } //确认是否存在字幕 + + probeOut.streams.forEach((stream,index) => { + const outputFilePath = `./output/${videoFile}${index}.${stream.codec_name === 'subrip' ? 'srt' : 'ass'}`; + //定义输出路径名称及字幕格式后缀 + + const command = `ffmpeg -v error -i "${inputFilePath}" -map 0:s:${index} -c:s ${stream.codec_name} -y "${outputFilePath}" -f ass -`; + //执行ffmpeg将字幕分离 + try { + const output = execSync(command).toString(); + console.log(`字幕已导出:${outputFilePath},\n字幕内容:\n`,output,'字幕输出已完成'); + } catch (err) { + console.error("输出错误:",err) } - console.log(`字幕已导出: ${outputFilePath}`); }); }); - }); -}); \ No newline at end of file + } catch (error) { + console.error("路径读取错误:",error); + } \ No newline at end of file From 954adfa142ae8ee13b3fbb6f546b79f0efc2edbe Mon Sep 17 00:00:00 2001 From: HarveyNW Date: Thu, 27 Feb 2025 17:34:44 +0800 Subject: [PATCH 3/4] update code --- Temp/output_subtitle.js | 91 +++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/Temp/output_subtitle.js b/Temp/output_subtitle.js index 8a72c33..b3123a7 100644 --- a/Temp/output_subtitle.js +++ b/Temp/output_subtitle.js @@ -1,41 +1,62 @@ +const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); -const { execSync } = require('child_process'); -const { exec } = require('child_process'); -const videoFolder = './video_res'; //获取视频目录 - -//检测文件信息 -try { +//筛选文件后缀 +function isSupportedFiles(videoPath){ + const ext = path.extname(videoPath).toLowerCase(); + return ['.mp4', '.mkv', '.mov', '.avi'].includes(ext); +} +//扫描目录 +function scanVideo(videoFolder) { const files = fs.readdirSync(videoFolder); - const videoFiles = files.filter(file => { - const ext = path.extname(file).toLowerCase(); - return ['.mp4', '.mkv', 'avi', '.mov'].includes(ext); - }) + const videoFiles = []; + + files.forEach( file => { + const videoPath = path.join(videoFolder, file); + const stat = fs.statSync(videoPath); - videoFiles.forEach(videoFile => { - const inputFilePath = path.join(videoFolder,videoFile); - const probeCommand = `ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of json "${inputFilePath}"`; - const probeOut = JSON.parse(execSync(probeCommand).toString()); - //获取字幕详细 - - if (!probeOut.streams || probeOut.streams.length === 0) { - console.log(`视频${videoFile}未找到字幕`); - } //确认是否存在字幕 - - probeOut.streams.forEach((stream,index) => { - const outputFilePath = `./output/${videoFile}${index}.${stream.codec_name === 'subrip' ? 'srt' : 'ass'}`; - //定义输出路径名称及字幕格式后缀 - - const command = `ffmpeg -v error -i "${inputFilePath}" -map 0:s:${index} -c:s ${stream.codec_name} -y "${outputFilePath}" -f ass -`; - //执行ffmpeg将字幕分离 + if(stat.isDirectory()) { + videoFiles.push(...scanVideo(videoPath)); + } else { + if (isSupportedFiles(videoPath)) { + videoFiles.push(videoPath) + } + } + }) + return videoFiles +} +//判断是否兼容字体格式 +function isSupportedSubtitle(codec_name) { + return ['subrip', 'ass'].includes(codec_name); +} +//查看视频信息 +function callFFprobe(inputFilePath) { + const ffprobe_cmd = `ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of json "${inputFilePath}"`; + try { + return JSON.parse(execSync(ffprobe_cmd).toString()); + } catch(err) { + throw new Error(`callFFprobe ${inputFilePath} failure`, { cause:err }); + } +} +//开始读取视频内嵌字幕 +function readSubtitleSync(inputFilePath) { + const ffprobeOut = callFFprobe(inputFilePath); + if(!ffprobeOut.streams || ffprobeOut.streams.lenght === 0) { + console.log(`视频未找到字幕`); + } else { + return [ + ...ffprobeOut.streams.map((stream,index) => { try { - const output = execSync(command).toString(); - console.log(`字幕已导出:${outputFilePath},\n字幕内容:\n`,output,'字幕输出已完成'); - } catch (err) { - console.error("输出错误:",err) + const command = `ffmpeg -v error -i "${inputFilePath}" -map 0:s:${index} -c:s ass -f ass -`; + return { + stream_index: stream.index, + codec_name: stream.codec_name, + ass_raw: isSupportedSubtitle(stream.codec_name) ? execSync(command).toString() : null + } + } catch(err) { + throw new Error(`readSubtitleSync${inputFilePath}failure`, { cause: err}) } - }); - }); - } catch (error) { - console.error("路径读取错误:",error); - } \ No newline at end of file + }) + ] + } +} From 0d2c50b2515b3e3a4c75d75dee2a39d2fb15305c Mon Sep 17 00:00:00 2001 From: HarveyNW Date: Tue, 25 Mar 2025 14:33:27 +0800 Subject: [PATCH 4/4] add ass-compiler --- Temp/output_subtitle.js | 161 ++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 57 deletions(-) diff --git a/Temp/output_subtitle.js b/Temp/output_subtitle.js index b3123a7..3a69fad 100644 --- a/Temp/output_subtitle.js +++ b/Temp/output_subtitle.js @@ -1,62 +1,109 @@ -const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); -//筛选文件后缀 -function isSupportedFiles(videoPath){ - const ext = path.extname(videoPath).toLowerCase(); - return ['.mp4', '.mkv', '.mov', '.avi'].includes(ext); -} -//扫描目录 -function scanVideo(videoFolder) { - const files = fs.readdirSync(videoFolder); - const videoFiles = []; - - files.forEach( file => { - const videoPath = path.join(videoFolder, file); - const stat = fs.statSync(videoPath); - - if(stat.isDirectory()) { - videoFiles.push(...scanVideo(videoPath)); - } else { - if (isSupportedFiles(videoPath)) { - videoFiles.push(videoPath) - } +import { execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import { parse, stringify, compile, decompile } from 'ass-compiler'; +/* +videoFolder为目录路径 +videoPath为完整路径(包括文件名) +*/ + + function processVideoFolder(videoFolder) { + //筛选文件后缀 + function isSupportedFiles(videoPath){ + const ext = path.extname(videoPath).toLowerCase(); + return ['.mp4', '.mkv', '.mov', '.avi'].includes(ext); } - }) - return videoFiles -} -//判断是否兼容字体格式 -function isSupportedSubtitle(codec_name) { - return ['subrip', 'ass'].includes(codec_name); -} -//查看视频信息 -function callFFprobe(inputFilePath) { - const ffprobe_cmd = `ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of json "${inputFilePath}"`; - try { - return JSON.parse(execSync(ffprobe_cmd).toString()); - } catch(err) { - throw new Error(`callFFprobe ${inputFilePath} failure`, { cause:err }); - } -} -//开始读取视频内嵌字幕 -function readSubtitleSync(inputFilePath) { - const ffprobeOut = callFFprobe(inputFilePath); - if(!ffprobeOut.streams || ffprobeOut.streams.lenght === 0) { - console.log(`视频未找到字幕`); - } else { - return [ - ...ffprobeOut.streams.map((stream,index) => { - try { - const command = `ffmpeg -v error -i "${inputFilePath}" -map 0:s:${index} -c:s ass -f ass -`; - return { - stream_index: stream.index, - codec_name: stream.codec_name, - ass_raw: isSupportedSubtitle(stream.codec_name) ? execSync(command).toString() : null + + //判断是否兼容字体格式 + function isSupportedSubtitle(codec_name) { + return ['subrip', 'ass'].includes(codec_name); + } + + //扫描目录 + function scanVideoSync(videoFolder) { + const files = fs.readdirSync(videoFolder); + const videoFiles = []; + + files.forEach( file => { + const videoPath = path.join(videoFolder, file); + const stat = fs.statSync(videoPath); + + if(stat.isDirectory()) { + videoFiles.push(...scanVideoSync(videoPath)); + } else { + if (isSupportedFiles(videoPath)) { + videoFiles.push(videoPath) } - } catch(err) { - throw new Error(`readSubtitleSync${inputFilePath}failure`, { cause: err}) } }) - ] + return videoFiles + } + + //查看视频信息 + function callFFprobe(videoPath) { + const ffprobe_cmd = `ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of json "${videoPath}"`; + try { + return JSON.parse(execSync(ffprobe_cmd).toString()); + } catch(err) { + throw new Error(`callFFprobe ${videoPath} failure`, { cause:err }); + } + } + //开始读取视频内嵌字幕 + function readSubtitleSync(videoPath) { + const ffprobeOut = callFFprobe(videoPath); + if(!ffprobeOut.streams || ffprobeOut.streams.length === 0) { + return []; + } else { + return [ + ...ffprobeOut.streams.map((stream,index) => { + try { + const command = `ffmpeg -v error -i "${videoPath}" -map 0:s:${index} -c:s ass -f ass -`; + const options = {}; + const commandToStr = execSync(command).toString(); + const compilationASS = parse(commandToStr,options) + return { + stream_index: stream.index, + codec_name: stream.codec_name, + ass_raw: isSupportedSubtitle(stream.codec_name) ? compilationASS : null + } + } catch(err) { + throw new Error(`readSubtitleSync${videoPath}failure`, { cause: err}) + } + }) + ] + } + } + + const videoFiles = scanVideoSync(videoFolder) + + return videoFiles.map(videoFile => { + const subtitles = readSubtitleSync(videoFile); + return { + videoPath: videoFile, + subtitles: subtitles.map(sub => + ( + { + stream_index: sub.stream_index, + codec_name: sub.codec_name, + ass_raw: sub.ass_raw + } + )) + } + }) } -} + +const target = processVideoFolder('./video_res'); +target.forEach(video => { + process.stdout.write(`读取文件:${video.videoPath}`); + const sub = JSON.stringify(video.subtitles.ass_raw) + const sub_info = video.subtitles.map((sub) => { + return `${sub.stream_index} - ${sub.codec_name} - ${JSON.stringify(sub.ass_raw)}` + }).join('、'); + + process.stdout.write('\n'); + + process.stdout.write(`${ + video.subtitles.length ? '字幕:' + sub_info : '无字幕' + }`) + process.stdout.write('\n'); +})