Nautilus」タグアーカイブ

1200px

PC での twitter の画像観覧が少し前から原寸になった。
twitter の 4 コマ漫画が大好きな筆者は少し困ったことに。

少し前まで普通にコンテキストメニューから落とすと縦 1200px 固定だった。
原寸を落とす方法も知っていたけど cbz にする場合このサイズが都合よかった。

つまりその時作った cbz が沢山ある。
それも完結していない続き物が多い。

続き物のページを cbz に追加する場合は縦 1200px に縮小しないと整合性が。
原寸は巨大なものが多いので表示に時間が掛かるというのもある。
途中のページから急に重く、なんて嫌だよ。

そういえば追加する時に以前追加した最後の名前は何だったか調べるのも面倒。
020.jpg だったら 021.jpg にして追加したい、ずっとそうしているし。

画像を縮小して勝手に cbz ファイルから名前を調べて追加。
なんて定型作業を自動化したいな。

だったら自分で作ればいいじゃないか!

#!/usr/bin/env python3

'''
    ダウンロードした続き物の twitter 4コマまんがを選択して使う
    ダイアログで選択した cbz ファイルの 001.jpg から始まる名前を取得
    名前順の最後になるよう 022.jpg 等の名前を付ける
    縦 1200px に画像をサイズダウンしキャッシュに保存
    それを cbz ファイルに追加
    までを自動化する Nautilus スクリプト
'''

import os, re, zipfile, gi
gi.require_version('Gtk', '3.0')
gi.require_version('GdkPixbuf', '2.0')
from gi.repository import GLib, Gtk, GdkPixbuf

# move ~/.catch
cachedir = GLib.get_user_cache_dir()
os.chdir(cachedir)

dlg = Gtk.FileChooserNative(title='Open', action=Gtk.FileChooserAction.OPEN)
dlg.set_current_folder(GLib.get_home_dir())
ft = Gtk.FileFilter()
ft.set_name('Comic Book Archive')
ft.add_mime_type('application/x-cbz')
ft.add_mime_type('application/vnd.comicbook+zip')
dlg.add_filter(ft)
r = dlg.run()
dlg.destroy()
if r == Gtk.ResponseType.ACCEPT:
    arc_name = dlg.get_file().get_path()
    with zipfile.ZipFile(arc_name, 'a') as z:
        # last name
        s = z.namelist()[-1]
        num = int(os.path.splitext(s)[0])
        # loop
        path_array = os.environ['NAUTILUS_SCRIPT_SELECTED_FILE_PATHS'].split('\n')
        for filepath in path_array:
            try:
                pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
            except:
                continue
            # resize
            p_width = pixbuf.get_width()
            p_height = pixbuf.get_height()
            width = p_width * 1200 // p_height
            smallpix = pixbuf.scale_simple(width, 1200, GdkPixbuf.InterpType.BILINEAR)
            # create name
            ext = os.path.splitext(filepath)[1]
            num += 1
            name = f'{num:03d}{ext}'
            # create jpeg or png
            if re.search(r'\.(jpg|jpeg)$', filepath, re.I):
                smallpix.savev(name, 'jpeg', ['quality'], ['85'])
            elif re.search(r'\.png$', filepath, re.I):
                smallpix.savev(name, 'png', ['compression'], ['9'])
            z.write(name)

作ってみた。

file-roller で一旦 cbz 内のファイル名を調べる必要が無くなった。
1.jpg とか適当な名前の一時保存でも追加時に自動変名、我ながら超便利。
何故今までコレを思いつかなかったのだ俺よ。

ZipFile 作成にうっかり w 指定をして消えてしまったファイルも…
こういうのを作る時はバックアップをしてから、絶対だよ。

しかし特に Node.js 屋で見かけるんだけーが。
「コードを書くのが面倒だから npm でインストール!」
みたいなことを書く輩はいったい何故プログラミングを勉強しているのだ?
定型作業が面倒だからコードを書いて楽をするんじゃないのかと。
コードを書くのが面倒な人がそんなことするとは思えない。
定型作業はタイピング速度で解決、とか考えそう。

CB7

先日 gnome-autoar というのを見つけた。
けれどソースコードには肝心の圧縮展開を行うコードは無かった。

GitHub – GNOME/gnome-autoar: Automatic archives creating and extracting library

中身をよく見ると archive.h を include している。
どうやら圧縮展開は libarchive 依存のミドルウエアってことみたい。

てゆーか libarchive って今は 7z 圧縮に対応しているってことかYO!

libarchive – C library and command-line tools for reading and writing tar, cpio, zip, ISO, and other archive formats @ GitHub

でも CB7 を作っても Nautilus 上でサムネイルされない。
おいおいマジで圧縮展開だけなのかよ。

いや、コミックブックアーカイブって五種類もあったのね。
ACE なんて見たことが無いし TAR はコレだけだと非圧縮だし無視でいいと思うが。

Comic book archive – Wikipedia

Evince でも CBR, CBZ, CB7 の三つをサポートしているみたい。
だけど Evince はどうやら p7zip 依存のようだ。
それに 7z だと file-roller で開くことができない、こいつも同様か。

ということで、現状では p7zip を入れておかないと色々不便だ。

sudo dnf install p7zip

United RPMS は unrar はあるけど rar は無いことに今頃気が付く。
コイツは削除して RARLAB のを自分で入れる。

よし3形式ともサムネイルとプレビューができるようになったぞ。
Evince, file-roller も対応、後は我がアプリ。
CBR と同じ方法でイケた、つーことで更新。

結局 gnome-autoar はどうでもよかった。
サムネイル表示されないのではビューアを作っても虚しいもんね。

file-roller command

忘れるところだった。
以前のように形式自由でアーカイブ可能な Nautilus Script を作らないと!

筆者は「新しくなったことを全否定する老人は死ね」な人間である。
でも今回は否定させて、だってデフォルトの機能では tar.gz が無いんだ。

7z なんて Kivy がソレで配布されていて mac でスゲー困ったし使いたくない。
GNOME はあの3形式以外は使ってほしく無いのかもだが流石に無理。
サルな人はすぐ昔のバージョンに戻そうとする、筆者は作る。

でも多数のアーカイブ形式に対応するって大変そう。
と思うけど実はもうソレをやっているものがあるんです。
file-roller はコマンドとして使えます。

file_roller

–add-to オプションで拡張子を指定すれば自動判別でアーカイブしてくれる。
ということでこんなシェルスクリプトを、拡張子のとうりな形式になります。

#!/bin/sh

path=`pwd`
name=${path##*/}
file-roller --add-to="${name}.tar.gz" "$@"

で。
Nautilus Script にコピーして送ってみる。

targz

これだけでで半角スペース及び日本語に対応。
以前と同じく親ディレクトリ名でアーカイブされる。

コレを Gjs なんかを使って GUI を以前みたく作って実行させればいい。
と思ったけどなんかコレで充分な気がしてきた。
Comipoli もやんなきゃいけないし、後は気が向いたら。

追記

下記だけで以前のダイアログが出せる、何を今頃だけど。

#!/bin/sh
file-roller --add "$@"

Video Cut Nautilus Script

デジカメ動画の編集に筆者は Avidemux を使っていた。
凝ったことはしないし残したい部分を切り張りできれば充分ということで。

しかし古い AMD から Skylake に環境を移したら何故か起動できなくなった。
未対応なのか?てか変に高機能にされると迷惑なだけなんだが。
しかたがないので代わりに ffmpeg コマンドを使う。

FFmpegで素早く正確に動画をカットする自分的ベストプラクティス – Qiita

うーん面倒だ。
やはりこういう編集作業は GUI でやったほうが楽に決まっている。

我が再生アプリに ffmpeg コマンドを送る機能の追加とか…
って同じことを考える人がいるもんだ。

Rosa Media Player 動画の切り取りやMP3の抽出が簡単にできる動画プレイヤー | Ubuntuアプリのいいところ

スクショを見ると編集は編集アプリで分けたほうがやはりよさそう。
そもそも再生が GStreamer で切り出しが ffmpeg って変なのでパス。
GStreamer でも当然編集はできるんだけど。

Fun with videomixer

GStreamer Editing Services 自体が PiTiVi の付属品でありまして。
だったら PiTiVi を使えばいいじゃん、ですよね。

ges

でも PiTiVi じゃ大袈裟なんだよなぁ。
Hello World に Visual Studio や Anjuta を使うくらいアホ臭い。

ということで。
Nautilus Script として使える超簡易な GUI カットアプリを作ってみた。

#!/usr/bin/gjs

// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-

const Gtk = imports.gi.Gtk;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const System = imports.system;

const NumEntry = new Lang.Class({
    Name: 'NumEntry',
    Extends: Gtk.Entry,

    _init: function() {
        this.parent({
            xalign: 1.0
        });
        let buf = this.get_buffer();
        buf.connect("inserted-text", Lang.bind(this, function(buf, position, chars, n_chars) {
            if (isNaN(chars))
                buf.delete_text(position, n_chars);
        }));
    }
});

const FFCut = new Lang.Class({
    Name: 'FFCut',
    Extends: Gtk.ApplicationWindow,

    _init: function(app, basename) {
        this.parent({
            application: app,
            title: basename
        });
        // Grid
        let texts = ["Start", ":", "Stop", ":"];
        let labels = [];
        this.entries = [];
        for (let i=0; i<4; i++) {
            labels[i] = new Gtk.Label({label: texts[i]});
            this.entries[i] = new NumEntry();
        }
        let grid = new Gtk.Grid();
        grid.attach(labels[0], 0, 0, 1, 1);
        grid.attach(labels[1], 2, 0, 1, 1);
        grid.attach(labels[2], 0, 1, 1, 1);
        grid.attach(labels[3], 2, 1, 1, 1);
        grid.attach(this.entries[0], 1, 0, 1, 1);
        grid.attach(this.entries[1], 3, 0, 1, 1);
        grid.attach(this.entries[2], 1, 1, 1, 1);
        grid.attach(this.entries[3], 3, 1, 1, 1);
        // Button
        let button = new Gtk.Button({label: "Cut"});
        button.connect("clicked", Lang.bind(this, function() {
            let ss1 = Number(this.entries[0].get_text()) * 60 + Number(this.entries[1].get_text());
            let ss2 = Number(this.entries[2].get_text()) * 60 + Number(this.entries[3].get_text()) - ss1;
            let cmd = "ffmpeg -ss " + ss1 + " -i " + this.title + " -t " + ss2 + " -vcodec copy -acodec copy out_" + this.title;
            GLib.spawn_command_line_async(cmd);
        }));
        // Pack
        let vbox = new Gtk.Box({
            orientation: Gtk.Orientation.VERTICAL,
            spacing: 5
        });
        vbox.pack_start(grid, true, true, 0);
        vbox.pack_start(button, true, true, 0);
        this.add(vbox);
        this.show_all();
    }
});

const FFApp = new Lang.Class({
    Name: 'FFApp',
    Extends: Gtk.Application,

    _init: function() {
        this.parent({
            application_id: 'org.sasakima.ffcut',
            flags: Gio.ApplicationFlags.HANDLES_OPEN
        });
    },
    vfunc_open: function(files, hint) {
        let basename = files[0].get_basename();
        let w = new FFCut(this, basename);
    },
    vfunc_activate: function() {
        print("Usage: ffcut FILENAME");
    }
});

let argv = [System.programInvocationName];
ARGV.forEach(function(element) {
    if (element.indexOf("//") == -1) {
        argv.push(decodeURIComponent(escape(element)));
    } else {
        argv.push(element);
    }
});
let application = new FFApp();
application.run(argv);

ffcut

コレに ffcut という名前で +x 属性を付けて
~/.local/share/nautilus/scripts に突っ込んで動画ファイルを送る。
切り出し開始と終了時間を入力して [Cut] ボタン。
すると [out_ファイル名] のカットされたファイルが同一ディレクトリに作られる。
~/bin に入れてコマンドでも多分使える。

Gjs は拡張子を付けないと Gedit で色分けしてくれないのでモードラインを入れた。
GNOME さん、そこは見分けてくださいよ。

もう少し改良の余地があるけど私的にはコレで充分だ。
気が向いたらもう少し弄ってアプリとして公開、しないと思うけど。

プログラミング初心者はこんなのから初めたらいいと思う。

Connect of Linux and Mac

実は最近 MacBook Air 13inch を買った。
US キーボードだぜ、これでスタバでドヤ顔できるze!
行かないけど、おまけに中古だけど。

GNOME べったりな筆者に何が起こったのだ?
と思われても困る、ずっと前から欲しかったんだし。

サイトを持っていると Mac Safari での表示確認をしたくなるジャン。
プログラミングネタで Mac でのコンパイル結果も書きたいジャン。
US キーボードでないと打ち間違える筆者にはありがたい豊富な US キー中古。
しかも筆者は iPhone 持ちだ、欲しいに決まっている。

いや、MacBook を買おうか本気で迷って出るのを待っていたんだが。
実物に触りペコペコキーボードに萎えて勢いで中古を選んでしまったオチ。

中古だけどとっとと Yosemite に更新。
Safari で自サイトの表示確認、うん問題無かった。
WebKit だから CSS3 は問題ないと思っていたけど安心した。

さて、筆者がまず行うのは Linux との接続ですよね。
共有設定を開きコンピュータ名を決め [ファイル共有] を有効にする。

mac_setting

Fedora を立ち上げ Nautilus から [ネットワークを表示] をクリック。

nautilus_mba

あら、それしかやっていないのにアッサリ。
W クリックして Mac のユーザー名とパスワードを入力。

mba_connect

AFC だけでなく AFP プロトコルにも対応していたのか。
iPhone mount in GNOME | PaePoi

afp

Nautilus をもう一つ立ち上げ普通にドラッグアンドドロップ。
あたりまえのようにコピーできる、もちろん双方向で。
普通にファイル編集もできる、AFP はかなり柔軟なようで。
Mac からのアクセスは知らないけど別にイイよね、SELinux あるし。

Macintosh HD と表示されて驚いたけど /usr/bin とかは表示されない。
でも普通に Application ディレクトリにはアクセスできるという。

applications_directory

Mac のアプリ実体って実行パーミッションの付いたディレクトリなのね。
だからアプリを削除しただけでアンインストール終了なのか、ふむふむ。
同じ UNIX ベースとはいっても Linux とは随分違うんだな。

ということで、Linux は Mac にも普通にアクセスできる。

しかし、もっと早くコレを買えば良かった。
たまに Windows を使うみたいなイライラがまったく無い。

タッチパッドには驚愕した、マジでマウスはいらない。
二本指タップでコンテキストメニュー、三本指でウインドウの移動。
二本指スクロールは GNOME 同様にアクティブでなくとも使える、素晴らしい。

ウインドウは全部 command+Q で閉じる一貫性も GNOME 同様。
おかげで閉じるボタンが左にあっても困らない、まったく使わない。
Control を command に置き換えればほぼ GNOME アプリと同じに使える。
F11 がフルスクリーンならもっと良かったんだが。

ps, ls, grep 等 UNIX コマンドが一通り使えるのでありがたい。
bc も使える、計算もこれで困らないな。

Mac でさえ Linux 風に使おうとする筆者であった。