Programming」カテゴリーアーカイブ

mpv @ Lua to Javascript

mpv を Lua で拡張するのが楽しい皆様。
こんなトコを見つけました。

GitHub – samhippo/mpv-scripts

参考になります、いやコピペでそのまま使ってもいいんだけど。
Windows 限定のコードが所々あるので macOS や Fedora の人は注意です。

そういえばこの人は複数拡張子のマッチをどうやっているんだろう?

mpv-scripts/next-file.lua at master ? samhippo/mpv-scripts ? GitHub

拡張子文字列で連想配列を作り値を全部 true に。

lua_nil

存在しなければエラーではなく nil になる、なるほど。
酷い手段だけど Lua 自体が色々とショボいからしかたがないか。

いやまて、この方法でディレクトリ内を列挙できるんだよな。
ls コマンドが不要なら正規表現が強力な Javascript を使うという手が。
とりあえず mp.utils を Javascript ではどう呼び出すか試す。

// ~/.config/mpv/scripts/test.js

function on_test() {
    //let dir = mp.utils.split_path(mp.get_property('path')); // ES5...
    var dir = mp.utils.split_path(mp.get_property('path'));
    mp.osd_message('Playng File: ' + dir[1]);
}
mp.add_key_binding('Ctrl+3', 'test_func', on_test);

なんだ、ドットで普通に使えるヤン。
しかし let 宣言でエラー、まさか今更 ECMAScript 5…

mpv.io

mpv.io にも書いていた、for-of とかも当然使えないな。
ES5 でも正規表現は同じはずだ、使ってみる。
以前書いたディレクトリ内で次のファイルを js で丸ごと書き換え。

// ~/.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() == '') {
        // for-of が使いたい...
        var len = ls.length;
        var ex = false;
        for (var i=0; i<len; i++) {
            var f = ls[i];
            if (re.test(f) && 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', 'nextfile_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 && prevfn != '') {
                    mp.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);

動くジャン。
ただ ls と違ってソートしないとよくワカラン順番になるみたい。

loadfile は半角スペース付きでもダブルクォートで囲む必要は無かったのね。
というか URI にする必要すら無かった、とほほ。

とにかくコレで完璧な拡張子判別ができるようになった。
慣れた言語はやっぱり楽だね、言語仕様を調べる必要が無いし。
自由に改造して使ってください、Windows でも多分動くと思う。

mpv scripts

今日も mpv を Lua で弄る。

で、例によってディレクトリ内巡回機能なんですが。
筆者は再生終了で本体を終了しないように指定している。
再生中に「次のファイル」とすると普通に上手く切り替わるんだけど。
EOF 状態からだとファイルは切り替わるけどポーズされた状態で始まる。

GStreamer は EOS(End of Stream) という表現だったけど。
mpv では EOF(End of File) なんですね、同じ意味だしどうでもいいけど。

試しに途中でポーズして切り替えだと完全に同じだ。
つまり mpv の EOF は単にポーズされた状態。
もしポーズだったら解除する、というコードを追加すればいい。

mp.command('loadfile "file://'..directory..filename..'"')
-- これでもいい、true|false を yes|no の文字列で
--if mp.get_property('pause') == 'yes' then
if mp.get_property_bool('pause') then
    mp.set_property_bool('pause', false)
    -- これでもいい、true|false を yes|no の文字列で
    --mp.set_property('pause', 'no')
end
break

mpv の property アクセスは全部文字列。
UNIX 系を知っているなら macOS を含んでありがちだと解りますね。
コレを直接 Lua から使える BOOL 値で取り出せるのが _bool 付き関数。

ところで。
~/.config/mpv/scripts
にソースを放り込めば全部が起動時に読み込みされ適用されていくんだね。
これは機能ごとに分割したほうが色々と都合がいい。

そういえばアスペクト比変更もデフォルトでは割り付けされていない。
色々割り付けても筆者はキーを忘れるので Ctrl+2 でサイクルがいいな。
-1 を突っ込むとデフォルトになるので一周させるようにすれば完璧。
OSD(On Screen Document) で表示もあると良さげ。

-- ~/.config/mpv/scripts/mpv_aspect_rate.lua

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

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)

これを放り込んで、よし動いた。
グローバル変数はインスタンス毎に記憶してくれるようだ、助かる。
以降 mpv プログラミングはまとめたページを作るか。

いやープログラミングって面白いですね。
Lua は別に面白くないけど書いたら思った通りに動いてくれるのが面白い。
言語はただの道具で重要なのは完成品、実社会と同じ。

not =

Lua で凄い勘違いをしていた!
~= の演算子を bash の =~ と同じものだと思いこんでいた。
bash の != がコレなのね、何を今頃。

ちなみに。

#!/bin/sh

if [[ aaa.mp4 =~ \.(mov|mp4)$ ]]; then
    echo 動画ファイルです
fi

こんな感じで使います。
bash ですら複数拡張子のマッチを調べられるのに Lua ときたら…

いやまあ。
「Lua って != が無いのか、変わった言語だなぁ」
って思いこんで今まで not を使ってたよ。
それどころか。

#!/usr/bin/lua

if 1 ~= 2 then
    print('lua の ~= は bash の != と同じ意味です')
end

if not(nil) then
    print('nil は否です')
end

-- よく考えたら下記で良かった

--if not(extstr:match(ext) == nil) then
if extstr:match(ext) then

スゲェ無駄なコードを昨日まで書いていた。
もう少し勉強しなきゃとも思うけど。
mpv の拡張以外でこの言語を使うか疑問なんだよな。

Lua filename

前回 Lua スクリプトで mpv でのディレクトリ内巡回機能を作った。
まだ気に入っていない、拡張子 mp4 のファイルしか抜き出していない。
LUMIX で動画を撮影したものは mp4 だけど iPhone の動画は mov だ。

2022 年となった今ではこの二つだけでいいかもだけど他の形式も沢山ある。
Lua の正規表現を調べまくっているが複数の拡張子にマッチさせる手段が見つからない。
だからといって複数の拡張子全部の if 文を書くなんてまるで初心者だし。
後で簡単に書き加えなんかもできるようにしたいのでソレは絶対に避けたい。

しかたがない、別の方法でも考えよう。
配列に入れて if-in は、そんなの Python くらいしか出来ない。
Javascript の includes みたいなメソッドも無い、だめだ。

だったら文字列だ、ある文字列が長い文字列の中に含まれているかどうか。
おっとこれならできるみたいだぞ、よしまず拡張子を抜き出す手段を。

#!/usr/bin/lua

fullpath = '/home/testuser/動画/日本語 スペース.mp4'

dir = fullpath:match('.*/')
print(dir)
--> /home/testuser/動画/

file = fullpath:match('.*/(.*)')
print(file)
--> 日本語 スペース.mp4'

ext = fullpath:match('.*%.(.*)')
print(ext)
--> mp4

none = fullpath:match('.*%](.*)')
print(none)
--> nil

拡張子やファイル名を抜き出すにはコレでいいようだ。
どうでもいいけど Gedit では Lua でもシバンには色が付くんだね。

lua_gedit

Lua の # はコメントじゃないのに、Python の len() 相当です。

#!/usr/bin/lua

extstr = 'mp4,m4v,mov'

bool = extstr:match('mov')
print(bool)
--> mov

bool = extstr:match('wmv')
print(bool)
--> nil

とにかく存在しないなら nil になる、コレを利用しよう。

extstr = 'mp4,m4v,mov'

function on_nextfile()
    -- 略
    for filename in pfile:lines() do
        -- 拡張子を抜きだして小文字化して
        ext = filename:match('.*%.(.*)'):lower()
        -- 含まれていなければ nil になる
        if not(extstr:match(ext) == nil) then

ということで書き換え、上手くいったので前回の投稿も書き換えしました。

GExif2 0.10 metadata

カメラを縦向きで撮影した写真は OM SYSTEM も LUMIX と同じだった。
つまり普通の横向き写真の Exif に回転情報を付加したもの。
検索すればすぐ解るけど反転情報を含めて 1 から 8 まである。
macOS の場合はプレビュー.app のインスペクタで「方向」にて確認できる。

1

ちなみにプレビュー.app は画像の回転情報の書き換えが可能。
command+R で回転させて command+S で上書き保存するだけ。

Fedora は Nautilus でも EOG でも確認できない。
よし GExif2 を使ってスクリプトを、と思ったら。

2

get_orientation が非推奨に。
代替え関数は何だ?

Generic image metadata handling: gexiv2 Reference Manual

どうやら try_*** の関数に変更する時に不要とされたみたい。
タグは以下で確認できる。

Exiv2 – Image metadata library and tools

ということで。
説明は macOS のインスペクタと同じになるよう Python で作ってみた。
回転方向はカメラの向きなので人によっては逆で説明している。

#!/usr/bin/env python3

import sys, gi
gi.require_version('GExiv2', '0.10')
from gi.repository import GExiv2

DESC = [
	'無し',
	'標準',
	'水平方向に反転',
	'180°回転',
	'垂直方向に反転',
	'反時計回りに90°回転および垂直方向に反転',
	'反時計回りに90°回転',
	'時計回りに90°回転および垂直方向に反転',
	'時計回りに90°回転']

with open(sys.argv[1], 'rb') as f:
    metadata = GExiv2.Metadata()
    metadata.open_buf(f.read())
    #ori = metadata.get_orientation() # Deprecate
    num = metadata.try_get_tag_long('Exif.Image.Orientation')
    print(f'{num} ({DESC[num]})')

3

時代は OS 標準、足りない機能はスクリプトですよ。
なんかさ、フリーソフトって文化はもう終わっているよね。

しかし M1 Mac って本当に速いんだな。
ハイレゾショット五千万画素写真の表示に一秒かからない。
i5 6500 マシンの Fedora では三秒もかかるのに。

それと OM SYSTEM の RAW も Finder で普通にサムネイル表示。
でもハイレゾショットの巨大な ORF では表示しないようです。

4

いやハイレゾは多分使わないけど、実験してみたかっただけ。
小型で機動力がウリのカメラに三脚必須機能なんてあってもさ。