ffmpeg + deno 批量转换文件夹下的所有匹配文件

in 前后端开发 with 0 comment

概览

如果你是和我一样热爱ACG的人,你一定曾经想过收集资源吧
但是大部分收来的

我不是说他们不好,但是太占地了。试想,你为了人耳几乎听不出来的编码损耗多花了94%的空间
值吗?

对比

然后,你想要转换,却对着大量文件和文件夹干发愁。一个个文件夹转换好麻烦
有没有简单的方法一键转换且简洁方便的呢?

来,我就为此写了一个脚本,不多不多,140行就完美地解决了此问题

然后这是我推荐的格式名单,可以去试试看,空间下降80%不是梦

代码自己下载,记得 安装deno下载ffmpeg,还有依赖包 呦

import { ffmpeg } from 'https://deno.land/x/deno_ffmpeg/mod.ts';
import { normalize } from 'https://deno.land/std/path/normalize.ts'
import { globToRegExp } from "https://deno.land/std/path/glob_to_regexp.ts";
import { basename } from "https://deno.land/std/path/basename.ts";
import { dirname } from "https://deno.land/std/path/dirname.ts";
import ProgressBar from "https://deno.land/x/progressbar/progressbar.ts";
import { percentageWidget } from "https://deno.land/x/progressbar/widgets.ts";
import { ensureDir } from "https://deno.land/std/fs/ensure_dir.ts";
import { delay } from "https://deno.land/std/async/delay.ts";
import { tree, ensure, replaceFmt, arg, help } from './_dep.ts';

export default async function main(){
    help(`
FFTool V1
扫描全部文件(包括子目录),批量转换指定文件到指定格式
需要预先安装ffmpeg

Args available:
    --from <glob>
    --to <format>
    --delete <size?:sizeString>
    --output
    --input
    --ab
    --ac
    --vb
    --vc
    --fps
    --thread
    `);
    if (typeof arg.from != 'string' || typeof arg.to != 'string')
        throw new Error('--from --to 必须定义,详见帮助(--help)');

    if (arg.delete)
        if (!await ensure('将要删除需转换的文件,继续吗?'))
            Deno.exit(1);

    let size = 0;
    if (typeof arg.delete == 'number') size = arg.delete;

    let progress = 0;

    for await (const file of tree(arg.input || './', globToRegExp(arg.from))) {
        const app = ffmpeg({
            input: file,
            ffmpegDir: 'ffmpeg'
        });
        if (arg.ab && typeof arg.ab == 'number') app.audioBitrate(arg.ab);
        if (arg.vb && typeof arg.vb == 'string') app.videoBitrate(arg.vb);
        if (arg.vc && typeof arg.vc == 'string') app.videoCodec(arg.vc);
        if (arg.ac && typeof arg.ac == 'string') app.audioCodec(arg.ac);
        if (arg.thread && typeof arg.thread == 'number') app.threads(arg.thread);
        if (arg.fps && typeof arg.fps == 'number') app.outputFPS(arg.fps);
        
        await ensureDir(dirname(file));
        progress++;

        console.clear();
        console.log(progress, ') ', '任务:', basename(file), '--> (format)', arg.to);

        const prog = new ProgressBar({
            total: 100,
            widgets: [percentageWidget]
        }), out = arg.output
            ? normalize(arg.output + '/' + basename(replaceFmt(file, arg.to)))
            : replaceFmt(file, arg.to);

        for await (const status of await app.save(out, true))
            await prog.update(status.percentage);

        if (arg.delete) {
            let outSize
            try {
                outSize = await Deno.stat(out);
            } catch {
                console.error('<!> 转换失败,文件未输出');
                await delay(3000);
                continue;
            }

            if (size && size >= outSize.size)
                console.warn('文件大小小于限制,未删除文件');
            else await Deno.remove(file);
        }
    }
}

if(import.meta.main) await main();

Responses