给你的mpv添加制作gif动图,截取音频,裁剪视频的功能

2021-05-22  本文已影响0人  mudssky

mpv是一个很强大的开源跨平台命令行播放器。虽然支持命令行和各种快捷键,但是mpv的gui也很不错,用户界面已经到了可用的水平,ui属于比较简洁的风格。

个人来说使用这款播放器主要是通过快捷键操作,鼠标的用途是滚轮,可以用快进视频,还有右键是暂停视频,还有小窗时移动播放器的位置。

mpv可以配置很多快捷键,也可以配置很多配置项。他最强大的地方是支持用户脚本,这样我们就可以自由地添加自己想要的功能。mpv支持lua和js两种用户脚本。

它的js脚本支持到es5,个人来说更偏好js,因为我们可以用先进的typescript来编码,有很多新的语法糖可以用。而且生态上能用的库比lua多太多了。lua就只有运行快这一个优势了,但是在不用lua-jit的情况下性能差距并没有很明显。

下面介绍的是我编写的mpv-gif.js脚本。

mpv配置文件的最好使用方式是在目录下面创建 portable_config\scripts,然后脚本放进这个目录里面,作为绿色便携版使用。

这是我存放脚本和配置文件的代码仓库,https://github.com/mudssky/mpv-config,脚本都放在scripts文件夹里。也有说明文档。

本文后面是复制了仓库里的说明文档,并且把脚本的内容复制过来了。

mpv-gif.js

给你的mpv添加制作gif,webp动图的功能,除此之外,还能裁剪音频和视频。

使用g指定开始时间,G指定结束时间。

然后就可以用快捷键生成这段时间的gif,视频或是音频。

个人更推荐使用webp动图,如果你上传的平台支持的话,webp动图体积更小画质也更好

运行环境需求

需要ffmepg的命令行工具,并且要放在环境变量里。

feature

配置说明

你可以在js文件内进行配置。

dir目前是没用的,本来是用来指定存放动图的目录,但是个人没有这个需求,就懒得做了。

frameSize你也可以用1280*720这样来指定,我这里的默认设置是原视频的三分之一的长和宽,8帧

其他选项下面有注释

// 用户可配置的选项
const userOptions = {
  dir: mp.get_property('working-directory'),
  //帧大小
  frameSize: 'iw/3:ih/3',
  //帧率 fps: 15,
  fps: 8,
  // 设置动图循环播放次数,0是无限循环播放
  loop: 0,
  // 是否带声音
  audio: false,
}

快捷键说明

键位 作用
g 设置起始时间
G 设置结束时间
Ctrl+g 生成gif动图
Ctrl+G 生成带字幕的gif动图
Ctrl+w 生成webp动图
Ctrl+W 生成带字幕的webp动图
Ctrl+a 截取音频
Ctrl+v 截取视频
"use strict";
;
(function (mp) {
    var userOptions = {
        dir: mp.get_property('working-directory'),
        frameSize: 'iw/3:ih/3',
        fps: 8,
        loop: 0,
        audio: false,
    };
    var envOptions = {
        startTime: -1,
        endTime: -1,
        filename: '',
        basename: '',
        currentSubFilter: '',
        tracksList: [],
        audioCodec: '',
    };
    var animatePicType;
    (function (animatePicType) {
        animatePicType["webp"] = ".webp";
        animatePicType["gif"] = ".gif";
        animatePicType["png"] = ".png";
    })(animatePicType || (animatePicType = {}));
    function validateTime() {
        if (envOptions.startTime === -1 ||
            envOptions.endTime === -1 ||
            envOptions.startTime >= envOptions.endTime) {
            mp.osd_message('Invalid start/end time. ');
            return false;
        }
        return true;
    }
    function initEnvOptions() {
        envOptions.filename = mp.get_property('filename') || '';
        envOptions.basename = mp.get_property('filename/no-ext') || '';
        envOptions.tracksList = JSON.parse(mp.get_property('track-list') || '');
        dump('userOption:', userOptions);
        dump('envOptions', envOptions);
    }
    function setStartTime() {
        envOptions.startTime = mp.get_property_number('time-pos', -1);
        mp.osd_message("GIF Start: " + envOptions.startTime);
    }
    function setEndTime() {
        envOptions.endTime = mp.get_property_number('time-pos', -1);
        mp.osd_message("GIF End: " + envOptions.endTime);
    }
    function pathCorrect(path) {
        return path.replace(/\\/g, '/');
    }
    function getCurrentSub() {
        for (var index in envOptions.tracksList) {
            var currentObj = envOptions.tracksList[index];
            if (currentObj['selected'] && currentObj['type'] === 'sub') {
                if (currentObj['external'] === true) {
                    envOptions.currentSubFilter = "subtitles='" + pathCorrect(currentObj['external-filename']) + "':si=0";
                }
                else {
                    envOptions.currentSubFilter = "subtitles='" + envOptions.filename + "'";
                }
            }
        }
    }
    function getAudioType() {
        for (var index in envOptions.tracksList) {
            var currentObj = envOptions.tracksList[index];
            if (currentObj['selected'] &&
                currentObj['type'] === 'audio' &&
                currentObj['external'] !== true) {
                envOptions.audioCodec = currentObj['codec'];
                return currentObj['codec'];
            }
        }
        return '';
    }
    function geneRateAnimatedPic(picType, hasSubtitles) {
        if (!validateTime()) {
            return;
        }
        mp.osd_message('Creating GIF.');
        getCurrentSub();
        var commands = [];
        if (envOptions.currentSubFilter && hasSubtitles) {
            commands = [
                'ffmpeg',
                '-v',
                'warning',
                '-ss',
                "" + envOptions.startTime,
                '-i',
                "" + envOptions.filename,
                '-to',
                "" + envOptions.endTime,
                '-loop',
                "" + userOptions.loop,
                '-vf',
                "fps=" + userOptions.fps + ",scale=" + userOptions.frameSize + "," + envOptions.currentSubFilter,
                envOptions.basename + "[" + envOptions.startTime.toFixed() + "-" + envOptions.endTime.toFixed() + "]" + picType,
            ];
        }
        else {
            commands = [
                'ffmpeg',
                '-v',
                'warning',
                '-i',
                "" + envOptions.filename,
                '-ss',
                "" + envOptions.startTime,
                '-to',
                "" + envOptions.endTime,
                '-loop',
                "" + userOptions.loop,
                '-vf',
                "fps=" + userOptions.fps + ",scale=" + userOptions.frameSize,
                envOptions.basename + "[" + envOptions.startTime.toFixed() + "-" + envOptions.endTime.toFixed() + "]" + picType,
            ];
        }
        print(commands.join(' '));
        mp.command_native_async({
            name: 'subprocess',
            playback_only: false,
            args: commands,
            capture_stdout: true,
        }, function (success, result, err) {
            if (success) {
                mp.msg.info("generate " + picType + ":" + envOptions.filename + " succeed");
                mp.osd_message("generate " + picType + ":" + envOptions.filename + " succeed");
            }
            else {
                mp.msg.warn("generate " + picType + ":" + envOptions.filename + " failed");
                mp.osd_message("generate " + picType + ":" + envOptions.filename + " failed");
            }
        });
        if (userOptions.audio) {
            cutAudio();
        }
    }
    function getExt(filename) {
        var splitIndex = filename.lastIndexOf('.');
        var res = filename.substring(splitIndex + 1);
        dump('res', res);
        return res;
    }
    function cutAudio() {
        if (!validateTime()) {
            return;
        }
        var AudioType = getAudioType();
        var commands;
        commands = [
            'ffmpeg',
            '-v',
            'warning',
            '-i',
            "" + envOptions.filename,
            '-accurate_seek',
            '-ss',
            "" + envOptions.startTime,
            '-to',
            "" + envOptions.endTime,
            '-vn',
            '-acodec',
            'copy',
            envOptions.basename + ".mka",
        ];
        print(commands.join(' '));
        mp.command_native_async({
            name: 'subprocess',
            playback_only: false,
            args: commands,
            capture_stdout: true,
        }, function (success, result, err) {
            if (success) {
                mp.msg.info("cut audio succeed");
                mp.osd_message("Cut Audio Succeed.");
            }
            else {
                mp.msg.warn("cut audio failed");
                mp.osd_message("Cut Audio Failed.");
            }
        });
    }
    function cutVideo() {
        if (!validateTime()) {
            return;
        }
        var commands = [
            'ffmpeg',
            '-v',
            'warning',
            '-i',
            "" + envOptions.filename,
            '-accurate_seek',
            '-ss',
            "" + envOptions.startTime,
            '-to',
            "" + envOptions.endTime,
            '-c',
            'copy',
            envOptions.basename + "[" + envOptions.startTime.toFixed() + "-" + envOptions.endTime.toFixed() + "]." + getExt(envOptions.filename),
        ];
        print(commands.join(' '));
        mp.command_native_async({
            name: 'subprocess',
            playback_only: false,
            args: commands,
            capture_stdout: true,
        }, function (success, result, err) {
            if (success) {
                mp.msg.info("cut video succeed");
                mp.osd_message("Cut Video Succeed ");
            }
            else {
                mp.msg.warn("cut video failed");
                mp.osd_message("Cut Video failed ");
            }
        });
    }
    function generateGif() {
        geneRateAnimatedPic(animatePicType.gif, false);
    }
    function generateGifWithSub() {
        geneRateAnimatedPic(animatePicType.gif, true);
    }
    function generateWebp() {
        geneRateAnimatedPic(animatePicType.webp, false);
    }
    function generateWebpWithSub() {
        geneRateAnimatedPic(animatePicType.webp, true);
    }
    mp.add_key_binding('g', 'setStartTime', setStartTime);
    mp.add_key_binding('G', 'setEndTime', setEndTime);
    mp.add_key_binding('Ctrl+g', 'generateGif', generateGif);
    mp.add_key_binding('Ctrl+G', 'generateGifWithSub', generateGifWithSub);
    mp.add_key_binding('Ctrl+w', 'generateWebp', generateWebp);
    mp.add_key_binding('Ctrl+W', 'generateWebpWithSub', generateWebpWithSub);
    mp.add_key_binding('Ctrl+a', 'cutAudio', cutAudio);
    mp.add_key_binding('Ctrl+v', 'cutVideo', cutVideo);
    mp.register_event('file-loaded', initEnvOptions);
})(mp);

上一篇下一篇

猜你喜欢

热点阅读