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

MP4 Container get_m4a Nautilus Script

以前 MPEG4 動画から AAC を抜く Nautilus Script にこんなことを書いた
BlackBerry Ubuntu Connect

これだと再生時間が変な表示になるしタグ編集もできない。
理由が解らなかったけど何を今ごろこんなのを見つけた。

iTunesのライブラリにAACファイルが追加できない | OKWave

そうか、素の AAC でなく MP4 コンテナとして抜かないと駄目なのか。
一応動画プレイヤーを作っている人なのにこんなのも知らないでいいのか俺…
とにかく、ならば ffmpeg に -vn オプションを付け拡張子を m4a 指定でイケそうだ。

#FFCOMMAND = "ffmpeg -y -i %s -acodec copy %s.aac"
FFCOMMAND = "ffmpeg -y -i %s -vn -acodec copy %s.m4a"

ついでに debug 用の表示メッセージボックスのコードも入れて
コマンド出力の確認をしたい場合はコメントアウトを外せばダイアログが出る。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
    get_m4a.py Nautilus Script
"""


import os
import commands
import gtk

FFCOMMAND = "ffmpeg -y -i %s -vn -acodec copy %s.m4a"

def debug_message(text, title):
    dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, text)
    dlg.set_title(title)  
    dlg.run()  
    dlg.destroy()

path_array = os.environ["NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"].split("\n")
for f in path_array:
    pos = f.rindex(".")
    r = commands.getoutput(FFCOMMAND % (f, f[:pos]))
    #debug_message(r, "Get AAC Message")

うん、コレなら再生時間も正しいし Rhythmbox でタグ編集も可能になった。
ついでに以前のコマンドで抜いた AAC もそのまま丸ごと m4a に変換できる。

もちろん BlackBerry で再生…
音が飛ぶんだが…

素の AAC なら飛ばないけど再生時間表示が Rhythmbox での算出と違う。
なんだよコレ、get_aac.py は残して追加するほうがいいな。

ついでに以前書いた Nautilus の場所バー直接入力切り替えだが。
Mandriva to Ubuntu p3
Ctrl+L でよかったのね、戻すには Esc でいい。
toggle.sh はもういらないな、つか一度も使ったことが無いような…
Linux に慣れると端末から Tab キー保管が簡単にできるディレクトリ名にしちゃうからね。

SeeMe 5.0.1 and 1.0.5

GtkSharp TreeView Tutorial – Mono

まだ SeeMe for Linux を GTK# で作っていた頃の参考にしたトコ。
よく見ると自作クラスを型として GtkListStore を作成しているコードがあった。
Controlling how the model is used 以下のところね。

こういう型指定って PyGtk でもできるのかな?
可能であればもっと本体のコード量を減らすことができるんだが。
とりあえず Python で同じように SeeMe コードを書きかえてみた。

class Engine():
    def __init__(self, deleted=True, name="New Item", key="", url="", query="",
                post=False, endsp=False, encode="utf8", stype=0, pos=0,
                nameid=0, verb=0, icon="", unique="", usetld=0):
        self.delete = deleted
        self.name = name
        self.key = key
        self.url = url
        self.query = query
        self.post = post
        self.endsep = endsp
        self.encode = encode
        self.stype = stype
        self.pos = pos
        self.nameid = nameid
        self.verb = verb
        self.icon = icon
        self.unique = unique
        self.usetld = usetld

class SeeMe4(gtk.Window):
    def __init__(self, sset):
        gtk.Window.__init__(self)
        #...
        #self.default_liststore = gtk.ListStore(bool, str, str, str, str, bool, bool, str, int, int, int, int, str, str, int)
        self.custome_liststore = gtk.ListStore(Engine)

型指定の時点で駄目ジャン…
property にしてみたり小細工してみたりしたけど無駄な努力だった。
Python では変数宣言しただけでは型が決まっていないので当然なのかも。

gtk.ListStore

ま、公式の Constructor 解説には C 言語と同じ型を全部書く方法しか書かれていない。
できないことは素直に諦めて、せめて GtkListStore への append をもう少し簡単にやれないか?

てか、そうしておかないと後々のメンテで沢山書き換えを行わなければいけなくなる。
後で書き換えが必要だろう箇所が少なければ少ないほどミスが減るのよね。
というかソレがオブジェクト指向最大のメリットなのだし。

よく考えたら SeeMe で GtkListStore の型は一つしか無い。
だったら GtkListStore サブクラスを作ってコンストラクタでとっとと型を指定。
ついでに Engine クラスを受け取る add メソッドを作れば簡単になるかな?

class Engine():
    def __init__(self, deleted=True, name="New Item", key="", url="", query="",
                post=False, endsp=False, encode="utf8", stype=0, pos=0,
                nameid=0, verb=0, icon="", unique="", usetld=0):
        self.delete = deleted
        self.name = name
        self.key = key
        self.url = url
        self.query = query
        self.post = post
        self.endsep = endsp
        self.encode = encode
        self.stype = stype
        self.pos = pos
        self.nameid = nameid
        self.verb = verb
        self.icon = icon
        self.unique = unique
        self.usetld = usetld

class CreateListStore(gtk.ListStore):
    def __init__(self):
        gtk.ListStore.__init__(self, bool, str, str, str, str, bool, bool, str, int, int, int, int, str, str, int)

    def add(self, en):
        return self.append( [en.delete, en.name, en.key, en.url, en.query,
                en.post, en.endsep, en.encode, en.stype, en.pos,
                en.nameid, en.verb, en.icon, en.unique, en.usetld] )

class SeeMe4(gtk.Window):
    def __init__(self, sset):
        gtk.Window.__init__(self)
        #...
        self.custome_liststore = CreateListStore()
        #...

    def read_default_searchini(self):
        self.default_liststore.clear()
        inipath = self.sset.default_path
        lngpath = self.sset.lang_path
        if os.path.exists(inipath):
            ini = inifile8.Inifile(inipath)
            lng = inifile8.InifileReader(lngpath)
            try:
                self.iniver = ini.read_int("Version", "File Version", 0)
                i = 0
                while 1:
                    i += 1
                    s = "Search Engine %i" % i
                    if not ini.section_exists(s):
                        break
                    t = ini.read_str(s, "Key", "")
                    if t == "":
                        continue
                    en = Engine();
                    en.nameid = ini.read_int(s, "Nameid", 0);
                    if en.nameid != 0:
                        # Get View Name from *.lng
                        if en.nameid == 17171:
                            en.name = lng.read_str("Translation", "1632215285", "")
                        elif en.nameid == 17183:
                            en.name = lng.read_str("Translation", "-1971470391", "")
                        elif en.nameid == 71103:
                            en.name = lng.read_str("Translation", "-1453429782", "")
                        else:
                            en.name = lng.read_str("Translation", str(en.nameid), "")
                        if en.name == "":
                            n = -1752227277 - en.nameid;
                            en.name = lng.read_str("Translation", str(n), "")
                    else:
                        en.name = ini.read_str(s, "Name", "")
                    en.key = t
                    en.url = ini.read_str(s, "URL", "")
                    en.query = ini.read_str(s, "Query", "")
                    en.post = ini.read_bool(s, "Is post", False)
                    en.endsep = ini.read_bool(s, "Has endseparator", False)
                    en.encode = ini.read_str(s, "Encoding", "utf-8")
                    en.stype = ini.read_int(s, "Search Type", 0)
                    en.pos = ini.read_int(s, "Position", -1)
                    en.verb = ini.read_int(s, "Verbtext", 0)
                    en.delete = True #!
                    en.unique = ini.read_str(s, "UNIQUEID", "")
                    en.icon = ini.read_str(s, "ICON", "")
                    en.usetld = ini.read_int(s, "UseTLD", 0)
                    """self.default_liststore.append( [
                            en.delete, en.name, en.key, en.url, en.query,
                            en.post, en.endsep, en.encode, en.type, en.pos,
                            en.nameid, en.verb, en.icon, en.unique, en.usetld] )"""
                    self.default_liststore.add(en)

    def on_item_new(self, widget, event=None):
        uid = self.create_uuid()
        num = self.deditor.get_radio_num()
        if num == 2:
            num = -1988219522
        else:
            num = 0
        en = Engine(verb=num, unique= uid)
        #it = self.custome_liststore.append( [True, "", "", "", "", False, False, "utf-8", 0, -1, 0, num, "", uid, 0] )
        it = self.custome_liststore.add(en)

コードは全然短くならないけど Engine を直で渡して展開できるようになった。
デフォルト引数によってアトリビュートの型も大半が気にしないでいいという。
読み込みや新規アイテムで順番を気にする必要が無くなったのはデカい。
これなら後々で順番の入れ替えや追加の必要があっても型側で調節できる。

ただこのコードでは展開するために for ループにすると

for row in self.default_liststore:
    if row[0] == False:
        i += 1
        s = "Search Engine %i" % i
        ini.write_str(s, "UNIQUEID", row[13])

添字アクセスにするしか無いんだなぁこれが、意味ネェ。
イテレーターは整数による順番という概念を使わないことに意義があるのだが。
この部分については WPF は徹底的にやっている感じ、よく考えて作られているよ。
ここをアトリビュートで取り出すサブクラスをどうすれば作れるか考え中。

ということで重複キーチェック追加や不具合修正で Linux 版更新。
キーボードショートカットを増やしたり不具合修正で Windows 版も更新。

「こんなことができると楽」
な方法を考えている時がアプリケーション作りで一番楽しいです。

Mandriva 2010.1?

Mandriva Linux 2010.1 Finally Released – The Spring version of Mandriva Linux 2010 – Softpedia

http://fundawang.lcuc.org.cn/mandriva/official/iso/2010.1/

見つけたけど上記だと転送が 20KB/s くらい、日本の FTP サーバーに来るのを待つか。
ATI Radeon video driver 6.13 って X.Org 7.5 に対応しているのだろうか?
作りによっては Ubuntu から Mandriva One GNOME に戻るけど、どうなんだろう。
実際ココまでリリースが遅れると今後がかなり不安…

BlackBerry Ubuntu Connect

Ubuntu 10.04 を二ヶ月も利用していて今頃気がついた。
Rhythmbox も Totem もボリューム弄くったらまれに落っこちるじゃないの!

Y901x が落ちまくる原因が解らなくて困っていたんだが、そういうことか。
show event 吐いた時点で pulseaudio ボリューム調節しているんだもの。
相性問題なのか pulseaudio か GStreamer のバグなのか、これじゃお手上げ。

とにかく原因だけは解った、pulseaudio か GStreamer の更新を待つだけ。
自分でフロントエンドを作っていると同系アプリを使わないので気がつかなかった。

というか。
Linux をメインで使うようになって三年だが始めて Rhythmbox を使った。
BlackBerry に何故か音楽を入れたくなった、利用するかはやってみた後で考える。

BlackBerry 付属の USB コネクタでパソコンを繋げば普通に認識されるんだね。
ついでに USB 経由の充電も行われるようだ。

いや、「デジタル・オーディオ・プレイヤー」では無いんですけど…
microSD を差し込んでいるとソッチも認識される。

Windows でも試してみたけど同じように認識するようです。
いや普通なら Windows を基準に説明するだろ?はどうでもよくて。
とっとと手持ち CD をリッピング。

mp3 か aac でエンコードしたいんですけど…

編集ボタンを押すとこんなダイアログが出るけど出てこない…
試しに ogg でエンコードしてみたら

遅っ!

Windows の WMP で MP3 にエンコードしました、十倍以上早いんですけど。
やっぱり再生はともかくエンコードや編集は Windows に限るわ。

んで music というディレクトリにディレクトリごとコピーする。
アンマウントして USB コネクタを外しメディアプレイヤーを立ち上げる。
あっさり認識するけど .trash 内にあるファイルまで認識する、ちょっと迷惑。

よしよし、とにかくこれで BlackBerry に音楽が入れられた。

ついでに。
最近の YouTube FLV は普通に再生しても H.264 と AAC なんだね。
ちょっと前まで通常は H.263 と MP3 だったと思うんだけど。
これは簡単に見つかる ffmpeg コマンドで MP3 を抜く方法は使えないな。
AAC でいいなら

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import os

FFCOMMAND = "ffmpeg -y -i %s -acodec copy %s.aac"

path_array = os.environ["NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"].split("\n")
for f in path_array:
    pos = f.rindex(".")
    os.system(FFCOMMAND % (f, f[:pos]))

みたいなのを Nautilus スクリプトを登録して AAC を抜けばいい。
拡張子を aac にしないと上手くいかなかった、いや悪用厳禁で。
やってみたらそのまんま BlackBerry で再生できました。
こういうのは Linux に限るね。

g_utf8_collate_key_for_filename of ctypes

g_utf8_collate_key_for_filename
がいったいどういう変換をしているかもう少し。

ところで端末に長々と打ち込むのが面倒なので GEdit 外部ツールに以下を指定。
GTK+ を使うには pkg-config 指定が必要なのよね、キーは F7 にした。

/*
gedit tool script

#!/bin/sh
gcc $GEDIT_CURRENT_DOCUMENT_PATH `pkg-config --cflags --libs gtk+-2.0`
*/

#include <stdio.h>
#include <gtk/gtk.h>
#include <glib.h>

int main() {
    gchar c[4];
    int i;
    for (i=0; i<105; i++) {
        sprintf(c, "%d", i);
        gchar *s = g_utf8_collate_key_for_filename (c, -1);
        printf("%s\n", s);
        g_free(s);
    }
    return 0;
}

端末だと解り難いにでリダイレクトしてみる。

十進で桁が増える毎にコロンが一つ付加されていくようだ。
なんだかよく解らない変換だけどこれで自然順ソート比較は上手くいくようだ。
色々やってみたけど結局 Python から ctypes を使う。

14.14.1 ctypesチュートリアル

ココに全部書いているんだが、Linux はちびっと面倒なのね。

glibc = ctypes.cdll.LoadLibrary('libglib-2.0.so.0')
cmpstr = glibc.g_utf8_collate_key_for_filename("a")

とやっても int が戻ってくる、実際にはポインタだが Python にはポインタ型が無いので。
つまり restype をキッチリ指定しないと全部 int になるってことですね。
ということで。

#! /usr/bin/env python
# -*- coding: UTF-8 -*-

import ctypes

glibc = ctypes.cdll.LoadLibrary('libglib-2.0.so.0')
cmpstr = glibc.g_utf8_collate_key_for_filename
cmpstr.restype = ctypes.c_char_p
cmpstr.argtypes = [ctypes.c_char_p, ctypes.c_int]

def sort_nicely(l):
    l.sort(lambda x, y : cmp(cmpstr(x, -1), cmpstr(y, -1)))

たまには lambda を使ってみようと思ったので。
ガベージコレクションなのだからコレでいいはずだけど…
とにかくこれでどうだ?

よし Nautilus とはドットファイルを除けば一致するようになった。
隠しファイルはリストアップに含めないようにする予定なのでどうでもいいけど。

でも Mandriva KDE 上で動かしたら何故か数値ソートしてくれなかった。
って ./ を付け忘れで以前のバージョンを起動しただけだった、GNOME と同様になる。
つか Dolphin のソートって以前の関数とまったく同じ結果じゃん!

これじゃ設定でどちらかに振り分けしてもらうしか両対応の方法が無さそう。