Paepoi

Paepoi » Fedora Tips » Fedora Tips | MPV をスクリプトで拡張

Fedora Tips | MPV をスクリプトで拡張

# 最終更新日 2022.07.24
-- mpv.io へのリンクが無かったので追加
-- スクリプトの変数に local 指定が無い例があったので追記
-- mpv.conf を色々と書き換えをしたので書き出し
-- FHD サイズにリサイズを拡張スクリプト化

MPV はデフォルトで入っていませんが多機能な動画プレイヤーです。
Lua スクリプトで拡張できる範囲がとても多いのでプログラミング趣味な人には最適です。
実際に筆者は動画プレイヤーを自作していましたがコレを知ったので自作するのをヤメました。

準備
MPV は Fedora でもハードウエアアクセラレーションが使えます。
Intel HD graphics の場合です、コレしか手持ちが無いので。
以下を入れて mpv.conf で指定するだけで使えます。
sudo dnf install libva-intel-driver
sudo dnf install libva-intel-hybrid-driver
GPU が NVidia や AMD の人は他の詳しい人を探してください。

それと下記にある拡張 Lua スクリプトは以下の筆者自作コマンドを利用する。
nautilus_ls という名前で保存しパスの通った場所 (~/bin 等) に入れておく。
#!/usr/bin/gjs

/**
 * ディレクトリ内の動画ファイル (ContentType が vodeo/***) を選別する Gjs スクリプト
 * 拡張子で判別するより正確、かつ MPV はそのすべて再生可能です。
 * Nautilus と同じ順番にソートすることが内臓スクリプトでは不可能なので外部コマンドで
 */

if (ARGV.length == 0) {
    print('usage: nautilus_ls {DIRNAME}');
} else {
    const GLib = imports.gi.GLib;
    const Gio = imports.gi.Gio;
    const re = /^video\//;

    let files = [];
    let d = Gio.file_new_for_path(ARGV[0]);
    let ls = d.enumerate_children('standard::content-type', 0, null);
    for (;;) {
        let info = ls.next_file(null);
        if (info == null)
            break;
        let t = info.get_content_type();
        if (re.test(t))
            files.push(info.get_name());
    }
    files.sort((s1, s2)=> {
        let cmpstr1 = GLib.utf8_collate_key_for_filename(s1, -1);
        let cmpstr2 = GLib.utf8_collate_key_for_filename(s2, -1);
        if (cmpstr1 < cmpstr2)
            return -1;
        return 1;
    });
    print(files.join('\n'));
}

// ex: ft=js
面倒な人用にアーカイブも置いておきます。

公式 SDK
mpv.io
上記にすべて書いています、英語ですががんばって解読しましょう。
筆者の例のように OPTIONS を弄くれば楽しく拡張が楽しめます。

mpv.conf
筆者の mpv.conf です、意味は公式かもっと詳しい人の Blog で。
これで Fedora でもハードウエアアクセラレーションが利用できる。
H.265 に対応するかは GPU による、筆者の環境ではソフトウエア対応のようです。
# ~/.config/mpv/mpv.conf

# ハードウエア・アクセラレーション
vo=gpu
hwdec=vaapi

# この指定を入れてからまったくフリーズしなくなった
framedrop=decoder

# i5 6500 GPU ではコレを指定しないほうがいい
#profile=gpu-hq

# 再生終了で本体を終了しない
keep-open=yes

gpu-hq はどういうプロファイルなのかは以下のコマンドで見ることができます。
筆者の環境では H.265 がまともに再生できなくなります。
mpv --show-profile=gpu-hq

input.conf
筆者の input.conf です、とうとうこれだけに減りました。
理想を求めて拡張スクリプトを自作した結果です。
# ~/.config/mpv/input.conf

# Eye of GNOME に合わせる
F11 cycle fullscreen
Ctrl+w quit

osc.conf
筆者の osc.conf です、シークバーとタイトルバーのみにしています。
タイトル名が埋め込まれた動画だとファイル名が表示されず困ることありますよね。
layout は box slimbox bottombar topbar があり好きな形を選べます。
# ~/.config/mpv/script-opts/osc.conf

# 動画に合わせた OSC リサイズ無効
vidscale=no

# 薄くする
boxalpha=180

# 常にトータル時間
timetotal=yes

# タイトルはファイル名 -- タイトル名
title=${filename} -- ${media-title}

# シークバーのみに、弄る時に枠外になるのを防げる
layout=slimbox
seekbarstyle=diamond
valign=0.95

ショートカット覚書
使いそうなのを覚書、すぐ忘れるので。
# シーク
左右キーで 5 秒、Shift 追加で 1 秒
上下キーで 1 分、Shift 追加で 5 秒

# 再生速度
ブラケット「[ と ]」で 10% 追加で再生速度変更
ブレース「{ と }」で上記の倍速で変更、つまり Shift 追加
BackSpace でリセット

# ズーム、パン
Alt+プラス(マイナス)でズーム
Alt+矢印キーでパン
Alt+Backspace でリセット

# A-B 間ループ
ココからココまでを l キーで指定

# プレイリスト関連
F8 で常に表示切り替え
Enter, <, > で次(手前)のファイル

# その他
1,2 でコントラスト
3,4 でブライトネス
5,6 でガンマ
7,8 で彩度(色の濃さ)
9,0 でボリューム

Delete で OSD 常に表示切り替え
Shift+L で無限ループ切り替え

スクリプトの例
まとめてアーカイブも置いておきます。
~/.config/mpv/scripts/ 以下に置くだけで勝手に適用されます。
ショートカットが重複する場合は最終行を書き換えてお試しください。

New! FHD にリサイズも拡張スクリプト化。
-- ~/.config/mpv/scripts/mpv_fhd.lua

-- 1920x1080 サイズにリサイズ
-- しかし 4K 動画は WQHD ディスプレイでは最大化で再生開始になる
-- その場合に最大化解除させるのが面倒なので自動化

function on_fhd()
    mp.set_property('autofit', '1920x1080')
    if mp.get_property_bool('window-maximized') then
        mp.set_property('window-maximized', 'no')
    end
end
mp.add_key_binding('Ctrl+1', 'fhd_func', on_fhd)

やっぱりディレクトリ内の次ファイルを即再生する機能は欲しいですよね。
-- ~/.config/mpv/scripts/mpv_next_prev.lua

-- Ctrl+DOWN(UP) でディレクトリ内の次(前)のファイルに切り替えする
-- nautilus_ls というオリジナルコマンドが必要

local utils = require 'mp.utils'

-- Ctrl+DOWN @ Next File Play
function on_nextfile()
    local hit = false
    local directory, fn = utils.split_path(mp.get_property('path'))
    if directory == '.' then
        directory = utils.getcwd()
    end
    local pfile = io.popen('nautilus_ls  "'..directory..'"')
    for filename in pfile:lines() do
        if hit then
            mp.commandv('loadfile', utils.join_path(directory, filename))
            if mp.get_property_bool('pause') then
                mp.set_property_bool('pause', false)
            end
            break
        end
        hit = filename == fn
    end
    pfile:close()
end
mp.add_key_binding('Ctrl+DOWN', 'nextfile_func', on_nextfile)

-- Ctrl+UP Previous File Play
function on_prevfile()
    local prevfn = ''
    local directory, fn = utils.split_path(mp.get_property('path'))
    if directory == '.' then
        directory = utils.getcwd()
    end
    local pfile = io.popen('nautilus_ls  "'..directory..'"')
    for filename in pfile:lines() do
        if filename == fn and prevfn ~= '' then
            mp.commandv('loadfile', utils.join_path(directory, prevfn))
            if mp.get_property_bool('pause') then
                mp.set_property_bool('pause', false)
            end
            break
        end
        prevfn = filename
    end
    pfile:close()
end
mp.add_key_binding('Ctrl+UP', 'prevfile_func', on_prevfile)

同じ手段で現在再生しているファイルからのプレイリストを作成し連続再生。
-- ~/.config/mpv/scripts/mpv_listup.lua

-- Ctrl+l でディレクトリ内をプレイリスト化して連続再生
-- nautilus_ls というオリジナルコマンドが必要

local utils = require 'mp.utils'

function on_listup()
    local hit = false
    local directory, fn = utils.split_path(mp.get_property('path'))
    if directory == '.' then
        directory = utils.getcwd()
    end
    local pfile = io.popen('nautilus_ls  "'..directory..'"')
    for filename in pfile:lines() do
        if hit then
            mp.commandv('loadfile', utils.join_path(directory, filename), 'append')
        else
            hit = filename == fn
        end
    end
    pfile:close()
end
mp.add_key_binding('Ctrl+l', 'listup_func', on_listup)

イベントはこんな感じで捕まえます。
-- ~/.config/mpv/scripts/mpv_load_event.lua

-- 再生開始時にファイル名を OSD 表示させる例
-- イベントハンドラはこんな感じで捕まえます

local utils = require 'mp.utils'

function on_loaded()
    local directory, fn = utils.split_path(mp.get_property('path'))
    mp.osd_message(fn)
end
mp.register_event('file-loaded', on_loaded)

動画の回転。
-- ~/.config/mpv/scripts/mpv_rotate.lua

-- input.conf に 'Ctrl+r add video-rotate 90' でもいいけど
-- 0-359 以内にしないとエラーなのとキーを Eye of GNOME に合わせたかったので

function on_rotate_right()
    local angle = mp.get_property_number('options/video-rotate')
    local num = angle + 90
    if num > 270 then
        num = 0
    end
    mp.osd_message('angle:'..tostring(num)..'°')
    mp.set_property_number('options/video-rotate', num)
end

function on_rotate_left()
    local angle = mp.get_property_number('options/video-rotate')
    local num = angle - 90
    if num < 0 then
        num = 270
    end
    mp.osd_message('angle:'..tostring(num)..'°')
    mp.set_property_number('options/video-rotate', num)
end

mp.add_key_binding('Ctrl+r', 'rotate_right', on_rotate_right)
mp.add_key_binding('Ctrl+Shift+r', 'rotate_left', on_rotate_left)

GNOME なら Ctrl+Q ですべてのウインドウが終了しないとおかしい。
-- ~/.config/mpv/scripts/mpv_ctrl_q.lua

-- Ctrl+q ですべての mpv を終了させる
-- killall コマンドで強制終了

function on_close_all()
    os.execute('killall mpv')
end
mp.add_key_binding('Ctrl+q', 'close_all_func', on_close_all)

Eye of GNOME のように Esc でスパッと終了させたい。
-- ~/.config/mpv/scripts/mpv_esc.lua

-- Esc 時にフルスクリーンなら解除、そうでなければ終了
-- ようするに Eye of GNOME と同じ動作

function on_escape()
    if mp.get_property_bool('fullscreen') then
        mp.set_property('fullscreen', 'no')
    else
        mp.command('quit')
    end
end
mp.add_key_binding('Esc', 'esc_func', on_escape)

Home キーでファイルの先頭から再生しなおす機能が何故か無い。
-- ~/.config/mpv/scripts/mpv_go_home.lua

-- Home キーで先頭に巻き戻して再生する
-- 最後まで再生しポーズ状態になっていても即再生できるように

function on_go_home()
    mp.set_property_number('time-pos', 0)
    if mp.get_property_bool('pause') then
        mp.set_property_bool('pause', false)
    end
end
mp.add_key_binding('Home', 'go_home_func', on_go_home)

アスペクト比変更機能がデフォルトでは選択肢が少ない。
-- ~/.config/mpv/scripts/mpv_aspect_rate.lua

-- Shift+a でアスペクト比は変更できますけど
-- スマホの縦動画用が足りないので独自に作ってみました

local aspect_num = 0
local aspects = {'4:3', '16:9', '9:16', '2:3', '3:4', '1:1', '3:2'}

function on_change_aspectrate()
    aspect_num = aspect_num + 1
    if aspect_num > #aspects then
        aspect_num = 0
        mp.set_property('video-aspect', '-1')
        mp.osd_message('Aspect Rate @ Default')
    else
        mp.set_property('video-aspect', aspects[aspect_num])
        mp.osd_message('Aspect Rate @ '..aspects[aspect_num])
    end
end
mp.add_key_binding('Ctrl+2', 'aspectrate_func', on_change_aspectrate)

おまけ
上記 nautilus_ls を作る以前に使っていた次のファイルを再生スクリプト。
環境に依存しないので macOS や Windows 版でも使えるはず。
正規表現の関係で Javascript(ECMA Script 5) になっています。
// ~/.config/mpv/scripts/next_prev.js

// 抜き出す拡張子は下記正規表現に追記してください

var re = /\.(mov|m4v|mp4)$/i;

// Ctrl+DOWN でディレクトリ内の次ファイルを再生
function on_nextfile() {
    var dir = mp.utils.split_path(mp.get_property('path'));
    // カレントディレクトリだとドットになるので
    if (dir[0] == '.') dir[0] = mp.utils.getcwd();
    // ls
    var ls = mp.utils.readdir(dir[0], 'files');
    ls.sort();
    if (mp.last_error() == '') {
        var len = ls.length;
        var ex = false;
        for (var i=0; i<len; i++) {
            var f = ls[i];
            if (re.test(f)) {
                if (ex) {
                    mp.commandv('loadfile', mp.utils.join_path(dir[0], f));
                    if (mp.get_property_bool('pause'))
                        mp.set_property_bool('pause', false);
                    break;
                }
            }
            ex = f == dir[1];
        }
    }
}
mp.add_key_binding('Ctrl+DOWN', 'next_func', on_nextfile);

// Ctrl+UP でディレクトリ内の手前ファイルを再生
function on_prevfile() {
    var prevfn = '';
    var dir = mp.utils.split_path(mp.get_property('path'));
    if (dir[0] == '.') dir[0] = mp.utils.getcwd();
    var ls = mp.utils.readdir(dir[0], 'files');
    ls.sort();
    if (mp.last_error() == '') {
        var len = ls.length;
        var ex = false;
        for (var i=0; i<len; i++) {
            var f = ls[i];
            if (re.test(f)) {
                if (dir[1] == f) {
                    if (prevfn != '') {
                        mmp.commandv('loadfile', mp.utils.join_path(dir[0], prevfn));
                        if (mp.get_property_bool('pause'))
                            mp.set_property_bool('pause', false);
                        break;
                    }
                }
                prevfn = f;
            }
        }
    }
}
mp.add_key_binding('Ctrl+UP', 'prevfile_func', on_prevfile)
ただ Finder や Explorer と名前順の順番が同じにはならないと思う。
同じ感じになるソート関数を自作すれば使えるかも、ということで貼っておきます。

Copyright(C) sasakima-nao All rights reserved 2002 --- 2024.