髪を斬ったのだが、美容師に望むのはひとつ、今日の予定を聞くというマニュアル会話はやめて欲しい。
今日は台風だしできる会話はこんなだ。
美容師「今日、このあと予定とかあるんっスか?」
俺「川の様子を見に行く」
2012年9月30日日曜日
髪を斬った
2012年9月27日木曜日
2012年9月25日火曜日
パソコン作った
TVRockを動かしていたマシンがどうもよく再起動するしもうTVRock使いたくないからとレコーダーを作っていたのだ
けれど、だいぶ軌道にのってきたのでレコーダー専用にマシンを作ることにした。
自作するのもう学生以来かと思う。
2012年9月24日月曜日
2012年9月22日土曜日
2012年9月20日木曜日
Process::spawnで返ってくるpid
アイオーエススィックス!
Ruby の Process::spawn で返ってくる pid でハマったので、後世にこのことを残してから死のうと思いました。
2012年9月19日水曜日
2012年9月16日日曜日
2012年9月15日土曜日
2012年9月14日金曜日
2012年9月12日水曜日
2012年9月11日火曜日
2012年9月10日月曜日
予約の登録、待機、実行
レコーダーネタまだ一杯ある。
というわけで、予約を登録して、実行まで待機して、実行するまでの流れについて。
予約の登録
予約はTwitterで「うぽって録画して」みたいにつぶやくだけである。
中はどうなってるかというと Programs テーブルから要求された単語を検索して、見つかったら Reserves テーブルに追加する。
word = "うぽって" descs = Groonga['Programs'].select do |record| (record.description =~ word) & (record.stop > Time.now) end titles = Groonga['Programs'].select do |record| target = record.match_target do |match_record| match_record.title * 5 end (target =~ word) & (record.stop > Time.now) end programs = descs.union!(titles).sort([ {:key => "_score", :order => "descending" }, 'start', { :key => 'channel.type', :order => "descending" } ]) programs.each do |program| unless Groonga['Reserves'].has_key?(program._key) reserve = Groonga['Reserves'].add(program._key, {} ) reserve.title = program.title reserve.start = program.start # 省略 break else # 予約済み end end
ほんとは一連の流れを排他で処理しなきゃならないけどそれはまた今度。まぁこんな感じ。
検索のスコアリングは好き好きあるだろうけどこの例だとタイトルでのヒットは番組詳細に対して5倍のスコアで算出するようにしてる。
予約実行までの待機
予約から実行までの流れをざっくり考えてみる。もっとも単純なのはきっとこう。
- 予約リストを開始時間でソートして1番目の開始時間まで sleep
- sleepを抜けたら別スレッドで録画開始
- 録画開始した予約に実行中フラグを立てて 1 に戻る
でもどう考えてもこんな簡単なのではだめで、予約は任意のタイミングで追加されるし、その都度待機時間を調整しなくてはならない。
それを考慮すると、方法は2つ浮かんできて、
- 予約リストを監視する
- 予約リストが更新されたという通知を受け取り、待機をやめる
ようするにパッシブかアクティブかのどちらか。
予約リストの監視をするにはおそらく 短時間 sleep を挟んだ無限ループで行うことになるが、これはどうもスマートではないし、十分なマージンをとって録画を開始しなきゃならない。(まぁどのみちマージンはあったほうがいいのだが)
では、通知を受け取る方法はどうやるのか。
inotify を使うとかもあるけど、簡単なIPCは名前付きパイプでできるし、pipe のIOをselectで待ってれば、タイムアウト付きで待機できる。
次の録画開始までの待機時間をタイムアウトに設定すればうまいことやれそうである。
実際のコードはこんな感じ
シェルで名前付きパイプをつくっとく
$ mkfifo -m 600 var/run/recorder
Ruby のコード
REC_MERGIN = 10 # 10秒マージン IPC_PIPE = 'var/run/recorder' ipc = open(IPC_PIPE, "r") loop do reserve = Groonga['Reserves'].select do |r| (r.running == false) end.sort(['start']).first wait = reserve.start - Time.now + REC_MERGIN ret = IO::select([ipc], nil, nil, wait > 0 ? wait : 0.1) if ret ipc.gets puts "io event" end reserve = Groonga['Reserves'].select do |r| (r.running == false) end.sort(['start']).first if reserve and reserve.start <= Time.now + REC_MERGIN start_recording(reserve) end end
適当だけどこんな感じ。
別のスレッドやプロセスから "var/run/recorder" に対して "\n" を書けば select が起きて処理が進むし、でなければ次の録画開始時間まで待機する。
start_recording() は中で reserve.running = true にして 別スレッド生成。
案外簡単。
※ 他プロセスが絡まない待機なら timeout(sec) { Queue.pop } や observer なんかでもいいかもしれない。
あとは、待機時間が大変長い時に適当にEPG取得するような処理いれたり(いつ予約が入るかわからないからいつでも殺せるような仕組みで)したらよい。
注意しなきゃならないのは、外部プログラムで録画していると何が起こるかわからないのでちゃんと例外をキャッチして情報を得られるようにしておかないと後で辛い。
あとは例外でプロセスが死なないようにうまく retry したり。
録画開始から終了までの話はまた別エントリで。
2012年9月9日日曜日
2012年9月8日土曜日
PT3 recpt1 epgdump
Linux 録画三種の神器というわけでもないが、コレさえあればあとは自分でなんとかできるツールであります。
しかしながら、recpt1/epgdumpについては亜種が多数存在しておりどれを使っていいかわからないという状況でもあります。
当方はこちらの方々を使用させていただいているというメモ。