Paepoi

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

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

# 最終更新日 2024.08.16
-- Kaby Lake 以降及び Fedora 40 に合わせて書き換え
-- 一部変更するプロパティが変わったので書き換え
-- ついでに現在の設定書き出し、ほんのチョッピリ機能追加

MPV はデフォルトで入っていませんが多機能な動画プレイヤーです。
Lua スクリプトで拡張できる範囲がとても多いのでプログラミング趣味な人には最適です。
実際に筆者は動画プレイヤーを自作していましたがコレを知ったので自作するのをヤメました。
ffmpeg
MPV は ffmpeg を使います、Linux ではもちろん共有ライブラリです。
しかし Fedora デフォルトで入っている ffmpeg は機能限定版です。
sudo dnf install --allowerasing ffmpeg
でフル版が入り VAAPI によるアクセラレーション等が使えるようになります。

準備
MPV は Fedora でもハードウエアアクセラレーションが使えます。
下記は Intel HD graphics の場合です、コレしか手持ちが無いので。
以下を入れて mpv.conf で指定するだけで使えます。
sudo dnf install libva-intel-driver
sudo dnf install libva-intel-hybrid-driver
sudo dnf install intel-media-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 でもハードウエアアクセラレーションが利用できる。
# ハードウエア・アクセラレーション
vo=gpu
hwdec=vaapi

# よくフリーズする場合はコレを有効に
#framedrop=decoder

# 最高画質らしいが Skylake 以前では指定しないほうがいい
#profile=gpu-hq

# 色々試したけどコレが一番安定かつ滑らか
scale=oversample
deband=yes

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

# OSD フォントのサイズ決め打ち
osd-font-size=28

gpu-hq はどういうプロファイルなのかは以下のコマンドで見ることができます。
Skylake 以前の環境では H.265 がソフトウエア対応になりまともに再生できない。
mpv --show-profile=gpu-hq
Kaby Lake なら内蔵 GPU でも再生できますが少しカクつきます。
大半の人が筆者マシンより高性能でしょうから oversample 指定は一例です。

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
seekbarhandlesize=0.9
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/ 以下に置くだけで勝手に適用されます。
ショートカットが重複する場合は最終行を書き換えてお試しください。

一発リサイズはバリエーションを増やしました。
-- ~/.config/mpv/scripts/mpv_fhd.lua

-- 指定サイズににリサイズ
-- しかし 4K 動画を WQHD ディスプレイ等では最大化で再生開始になる
-- その場合に最大化解除させるのが面倒なので自動化するスクリプト
-- サイズバリエーションと元に戻す処理を追加しました

-- FHD
function on_fhd()
    mp.set_property('autofit', '1920x1080')
    if mp.get_property_bool('window-maximized') then
        mp.set_property('window-maximized', 'no')
    end
end

-- HD
function on_hd()
    mp.set_property('autofit', '1280x960')
    if mp.get_property_bool('window-maximized') then
        mp.set_property('window-maximized', 'no')
    end
end

-- VGA
function on_vga()
    mp.set_property('autofit', '540x480')
    if mp.get_property_bool('window-maximized') then
        mp.set_property('window-maximized', 'no')
    end
end

-- 元に戻す
function on_fit()
    mp.set_property('autofit', '')
    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)
mp.add_key_binding('Ctrl+2', 'hd_func', on_hd)
mp.add_key_binding('Ctrl+3', 'vga_func', on_vga)
mp.add_key_binding('Ctrl+4', 'fit_func', on_fit)

やっぱりディレクトリ内の次ファイルを即再生する機能は欲しいですよね。
-- ~/.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 というオリジナルコマンドが必要
-- リストアップを OSD 表示する機能追加

local utils = require 'mp.utils'

function on_listup()
    local hit = false
    local lst = '::PlayList::'
    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')
            lst = lst..'\n'..filename
        else
            hit = filename == fn
        end
    end
    pfile:close()
    mp.osd_message(lst)
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 でアスペクト比は変更できますけど
-- スマホの縦動画用が足りないので独自に作成
-- 変更するプロパティが変わっています、環境によってはコメントアウト側に変更
-- Ctrl+2 指定だったけどリサイズに割り当てたので Ctrl+t の変更
-- ついでに Shift 追加で逆順変更を追加

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

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

function on_change_aspectrate_r()
    aspect_num = aspect_num - 1
    if aspect_num == -1 then
        aspect_num = #aspects
    end
    if aspect_num == 0 then
        mp.set_property('video-aspect-override', '-1')
        mp.osd_message('Aspect Rate @ Default')
    else
        mp.set_property('video-aspect-override', aspects[aspect_num])
        mp.osd_message('Aspect Rate @ '..aspects[aspect_num])
    end
end

mp.add_key_binding('Ctrl+t', 'aspectrate_func', on_change_aspectrate)
mp.add_key_binding('Ctrl+Shift+t', 'aspectrate_r_func', on_change_aspectrate_r)

おまけ
上記 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.