月別アーカイブ: 2020年1月

open -a URI

macOS の open -a に渡すのは URI でも良かった。

Finder Script を以下に書き換えしても普通に動くね。
Tips ページを又しても書き換えしなきゃ。

#!/usr/bin/osascript

let app = Application.currentApplication();
app.includeStandardAdditions = true;

let uri = Application('Finder').finderWindows()[0].target().url();
//let path = decodeURI(uri).slice(7);
//app.doShellScript(`open -a Terminal "${path}"`);
app.doShellScript(`open -a Terminal "${uri}"`);

ところで筆者はメインマシンが Fedora なので URI と主に書く。
macOS メインの人は URL と同じ意味だと思えばいい。
URLとURIの違いとは? パーツの構造・名称・意味も大解説! | 初代編集長ブログ―安田英久 | Web担当者Forum

ただし macOS 標準アプリは全部対応しているけどアプリによるみたい。
筆者自作の comipoli も対応しなきゃいけないんだけど。

Gst Metadata

スマホ動画等の回転情報の件。
そういう埋め込み情報ってメタデータというんだね。
gstreamer metadata で検索したら公式のサンプルコードが見付かった。

Metadata

上部の Language から JavaScript を選択しても C のままなんだけど。
いやそれは別にいい、C のサンプルがあるだけで親切という世界。

for (let s in OBJECT) print(s);

すれば今の gjs はメソッドの存在は解る、以前はできなかったような?
別の端末で Python を立ち上げて dir していた記憶があるけど、まあいいか。

そんなことより困ったぞ。
GST_CLOCK_TIME_NONE は 18446744073709551615 だ。
JavaScript の Number は 16 桁までしか使えない。
Python なら桁数無制限だから気にしなくてもいいんだけど。

BigInt – JavaScript | MDN

コレが使えるかなと思ったけど現行 gjs は未対応。
そもそも bus.timed_pop_filtered の引数が Number 指定だった。
あぁコイツも PyGObject で作り替えするしかないのか。。。。。
って、gst_bus_timed_pop_filtered の第一引数はタイムアウト指定じゃん。

GstBus

GST_CLOCK_TIME_NONE 固定では無いみたい、一千万ナノ秒にして問題無しだった。
これで Gjs のまま書き換え作業に移れるぞ。

そんなことより、困ったのが GstElement の取得。
ハンドラの中で get_pipeline にて得た変数は当然ガベージコレクションされる。
すると GstElement の参照元まで破棄される、と気がつくのに半日かかった。
SpiderMonkey ってガベージコレクションのタイミングわかり辛ぇ!
コンストラクタで this に付けるという回避策を気がつくのに二日も使った筆者は…

それとタグって結構重複している、値も試したかぎりでは全部同じだった。
Map を使って重複タグは一つにまとめるようにしてみた。

var Y901xWindow = GObject.registerClass({
    GTypeName: 'Y901xWindow'
}, class Y901xWindow extends Gtk.ApplicationWindow {
    _init(app) {
        super._init({application: app});
        // var
        this.player = new ClutterGst.Playback();
        this.pipeline = this.player.get_pipeline();
        // etc...
        this.player.connect('ready', (playbin)=> {
            this.player.set_playing(false);
            // Get Origin size
            let vsink = playbin.get_video_sink();
            // Only ClutterGst
            let frame = vsink.get_frame();
            let d = frame.resolution.par_d;
            let n = frame.resolution.par_n;
            this.src_width = frame.resolution.width * n / d;
            this.src_height = frame.resolution.height;
            /**
             * get Tag
             */
            //let bus = vsink.get_bus(); // Not this.
            //let bus = playbin.get_pipeline().get_bus(); // GC...
            let bus = this.pipeline.get_bus();
            let meta = new Map();
            let t = GLib.path_get_basename(playbin.get_uri());
            meta.set('Title', decodeURI(t));
            for (;;) {
                let msg = bus.timed_pop_filtered(10000000, //Gst.CLOCK_TIME_NONE 
                    Gst.MessageType.ASYNC_DONE | Gst.MessageType.TAG | Gst.MessageType.ERROR);
                if (msg == null) {
                    break;
                } else if (msg.type != Gst.MessageType.TAG) {
                    break;
                }
                let tag_list = msg.parse_tag();
                tag_list.foreach((ls, tag)=> {
                    let num = ls.get_tag_size(tag);
                    for (let i=0; i<num; ++i) {
                        let val = ls.get_value_index(tag, i);
                        if (tag == 'datetime') {
                            let t = val.to_iso8601_string()
                            meta.set(tag, t);
                        } else {
                            meta.set(tag, val);
                        }
                    }
                });
            }
            for (let [key, val] of meta) print(`${key}: ${val}`);

って。

回転情報出てこないジャン!
勉強にはなったけど何も進まず正月休みが終わってしまった。

os.forkpty

あけましておめでとうございます。
元旦といえば、そうプログラミングです。

ということで、Python で sshpass を再現の話。
os.forkpty というものがあるようだ。

python – Using os.forkpty() to create a pseudo-terminal to ssh to a remote server and communicate with it – Stack Overflow

これだよコレ、似たようなことを考える人っているもんだ。
がんばって sshpass と完全に同じコードにしなくてもよかった。
というか ptmx では上手くいかない、擬似端末ならこっちでもいいはず。

Python3 に書き換えしなきゃね。
色々問題はあるけどなんとかなったコードをとりあえず。

#!/usr/bin/env python3

import os, sys, signal, time

# var
HOSTNAME = 'username@hostname'
PASSWORD = '********'

# Ctrl+C
signal.signal(signal.SIGINT, signal.SIG_DFL)

pid, fd = os.forkpty()

def cmd(s):
    '''
        長い出力の場合分割される場合がある
        os.read はこの場合ループにすると値を戻さずフリーズする
        しかたがないので空打ちの場合は残りの読み込みにしている
    '''
    if s:
        os.write(fd, f'{s}\n'.encode('utf-8'))
        time.sleep(1)
        res = os.read(fd, 1024).decode()
        # 一行目を取り除いて表示
        sys.stdout.write(res[res.find('\n')+1 : ])
    else:
        res = os.read(fd, 1024).decode()
        sys.stdout.write(res)
    sys.stdout.flush()

if pid == 0:
    #os.setsid() # Error
    os.execvp('ssh',['ssh', HOSTNAME])
    # 親プロセスに切り替わるので以下は実行されない
    print('execvp Error!')

# レスポンスが遅いと表示されないけどログインは可能
output = os.read(fd, 1024)
sys.stdout.write(output.decode())
sys.stdout.flush()
# パスいワード
os.write(fd, f'{PASSWORD}\n'.encode('utf-8'))
# 只の時間稼ぎ
time.sleep(1)
res = os.read(fd, 1024).decode()
# 最初のプロンプトが表示できないので仮プロンプト
print(f'{res}First Command > ', end='')
# exit で終了
while True:
    s = input()
    if s == 'exit':
        break
    cmd(s)
os.write(fd, 'exit\n'.encode('utf-8'))
print('__DONE__')

よしこれで sshpass 不要でパスワード入力ができる。
しかし time.sleep を駆使するしかないのかな?
今のところこんな感じ。

調子こいて pysshpass みたいなものを作ろうと思ったけど…
os.forkpty では setsid できないし ptmx だと暴走するしetc…
我が Macbook Air の CPU ファン全開音なんて初めて聞いたよ。
ま、必要ないか。

しかし思っていたより Fedora と macOS では使える関数が違う。
os.fsync は macOS で問題ないけど Fedora は何をやってもエラー。
fcntl でノンブロッキングI/O は macOS ではエラー出まくり。
他の Linux や BSD では、なんて考えたくないな。