2026
2025
2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
全31件 (31件中 1-31件目)
1
チャンネル設定と、キャプチャ用の外部コマンドをキックする部分です。基本的には、外部プログラムをsystem()関数で呼ぶだけ。どうも、Windows用のActiveRubyはspawn系が使えないみたいなので、systemで我慢。バックグラウンドでコマンドを起動するには、startコマンドを使えば良いでしょう(多分)。ちょっとコードはわかりにくいが、system()で呼び出すコマンドのパラメーターをhDBというハッシュ変数で指定しています。 @hDB["%1]~@hDB["%9"]はパラメーターの仮引数で、iEPGD#to_s()を用いて一度評価しています。このとき、Windowsでファイル名に使えない文字を全角に変換、半角スペースも'_'に変換しています(to_WInFileName関数)。その上で、system()関数で実際に外部のコマンドを呼び出しています。 今回のプログラムでは、チューナーとキャプチャは別のプログラムで制御してもよいようにしていますが、キャプチャプログラムによっては、キャプチャ開始後にチューナーのチャンネル切り替えが起きるとコケる場合がある一方で、この問題がなければ同時にスタートさせた方が録画の頭が切れてしまう可能性が減ります。そこで、チューナーの制御をシーケンシャルに行うか、別スレッドで並行して行うかを選択できるようにしました。まあ、これも、startコマンドを使えばよいのですが... なお、Windowsでspawnするプログラムhttp://www.longtail.co.jp/pck/とかを発見しました。一応linkしときましたが、中身をちゃんと見たわけではないので、今回の目的にあっているかどうかは知りません。で、肝心のtunerクラスと、VideoRecorderクラスだけれども、ほとんど同じコードになってしまっている。なんか、だっさいな~...こういう時どうするのが、ruby風なんだろ??require( 'IEPGD' )require( 'to_WinFileName' )require('thread') class TVTuner def initialize( hDB , logsw=false ) @hDB = hDB @logSw = logsw if @logSw print( "#{Time.now.to_s} #{__FILE__},#{__LINE__},::initialize()\n" ) end # if if ( cmd = @hDB[ "init_cmd"]) != "" system( cmd ) end end # def initializer( iniFileSpec ) attr_accessor :logSw def tune( iEPGD ) if (cmd = String.new( @hDB["tune_cmd"]) ) != "" 1.upto(9) {|i| stridx = sprintf("%%%d", i) param = iEPGD.to_s( @hDB[ stridx ] ) param = to_WinFileName(param ) cmd = cmd.gsub( /#{stridx}/, param ) } if @logSw print( "#{Time.now.to_s} #{__FILE__},#{__LINE__},::tune() #{cmd}\n" ) end #if if @hDB["Concurrent"] == "true" Thread::start { system( cmd ) } else system( cmd ) end end end # tuneend # class TVTuner
2009.05.29
コメント(0)
少しづつデバッグも始めているのだが、漢字(というか2バイトコード)処理で、やっぱり嵌った。 結局、Windows版のRubyを使う場合でも、c:\>Ruby -Ksのようにオプション-Ksをつけないと、SJISの取り扱いはうまく行かないらしい。SJISはマイクロソフトの大罪とは私は必ずしも思わない(だって当時のヘボハードで扱うには、「現実的」な解だった、と思えるもん)けど、日本語Windowsの原罪ではあると思う。なかなかすすまない。
2009.05.29
コメント(0)
むう…こんなものが発売になったそうだ。 ☆銀行振込特価はお問い合わせください。☆ESOTERIC(エソテリック)SA-50【SACDプレーヤー】「特価はお問い合わせください」ってのは何だが、まあ、さすがに私がおバカでもおいそれとは買えない値段だ… orzエソリテックの製品ページはこちら。要はTEACの高級ブランド。ポイントは32ビットのD/Aコンバータを使用していること。部品は旭化成マイクロ(AKM)製だが、AKMではさらに上位の32ビットD/A、AK4399なんてのも出しているそうな。この領域になると、いわゆるカタログスペックは意味がないと思うけど、SA-50のカタログによれば、S/N比は130dBだそうなので、32ビットにおける理論的なダイナミックレンジが192dBだか、差し引き62dB、ビット換算で10ビットくらいはノイズに埋もれていることになると思うのだけど、意味あるのかな・・・
2009.05.24
コメント(0)
芝の管理の記録3週間ぶりくらいに芝刈りをする。刈高は、はじめ17.5mmにするが、繁茂具合があまりよくないので、途中から20mmに変更。育成の良し悪しがまばら。肥料が不足しているかもしれない。しばらく手入れができなかったので、雑草が生え放題。
2009.05.23
コメント(0)
先週日曜日に帰ってきてから、休みなしで働いていました。機内検疫のあとは、徹底したアルコール消毒が必要ということで、毎晩遅くまで呑んだくれて、ブロ グの更新どころか「つばさ」のキャッチアップもままならない。さすがに「呑み疲れ」。罹患してる人いるわけだから、アクシュミな冗談はこれくらいにしてお きます。 一日早く帰った同僚や、同じ日に別のフライトで帰った同僚たちは、その後の健康状態を問い合わせる電話があったとのことだが、ワタシにはなし(ナゼだー?)。 それはともかく、USB.orgのDevConに行ってきました。場所は、(どの駅からも遠い)フォーシーズンズホテル。庭とか立派なので、外人は好きな んだろうが、不便なホテルだ。ただし、食事は旨かった。(前回のサンノゼのダブルツリーホテルは食事は「がっくし」だったがワインはダブルツリーの方が良 かったかな…) USB3がメインだったが、内容的には前回のサンノゼとあまり変わらず、カンファレンスは退屈だった。その他はOTG2.0の話があったが、やっぱり普及しない気がする。PICT Bridgeに間に合わなかったのが敗因。 ショーケースでは、TIがUSB3.0のASSPを発表していた。また、MCCIがxHCI対応のドライバを Windows2000,XP,Vista,7用に用意できるとのこと。MSはWindows7SP1でドライバを提供するとのことなので、マザーボード やカードにドライバが付属する、というのが初期の形態になるのだろう。 インテル/AMDがさっさとSuperSpeedに対応する、xHCI付きチップセットを出してくれないことには、一向に普及しないだろう。来年後半かな? ところで、xHCIはUSBのLow/Full/Hi/SuperSpeedのすべてに対応するのだが、Windows7の最初のリリースがxHCI用の ドライバを提供しないとすると、どうやってインストールするんだろう?SATA接続のDVDドライブが必須?それとも、EHCI/UHCIもレガシーインターフェースとして必要?
2009.05.23
コメント(0)
さて、NRTでは機内検疫があったのだが、ヒマだってんでどれくらい時間がかかるか測ってみたら、検疫管が機内に入ってから、降機の許可がでるまで、約35分というところでした。私は今回アップグレードで、1列目に座っていたので、ほぼ正確な時間です。基本的には全員のチェック(サーモカメラと問診表)が終わってから降機の許可を出すようです。今回の機材はエアバスA330だったので、747とかではもっと時間がかかると思いますし、擬陽性の乗客がいれば、もっとかかるはずです。ただ、チェックイン・バゲッジは検疫の間にカルーセルに出てきているので、通関までの合計時間はそれほど余計に時間がかかる、というわけでもありませんでした。成田空港は空いてましたねー。連休後だし、豚インフルエンザの影響で渡航者が減っているのでしょう。では、では。
2009.05.17
コメント(0)
こちらは今土曜日の午前。いや~、ジェットラグもやっと収まって、これからバリバリだぜぇ~,というところだが、SFOに居る。日本に帰るんです。帰りのフライトはビジネスクラス~っ♪飛行機のビジネスクラスってのは、金で買えるささやかかつもっとも確実なシアワセと思います。もちろん、身銭で乗ったことはないですが。ここ何年か、毎年一回はアップグレードされているので、「連続試合安打」みたいなものです(笑)。今回はなぜかそういう気持ちにならず、買い物も全然せず。家内に頼まれたGuerlainの化粧水、(これか?)を免税店で買ったくらい。【ゲラン GUERLAIN】スーパーアクア ローション 200ml【59%OFF!!】【楽天安値挑戦中!!】ゲランといえば、メテオリットとか、ミツコ、ヴォルドニュィとかならいいけど、化粧水というと「ババァ・ブランド」(失礼)な気がするわけで、わが愚妻も「そういう歳になったのだなぁ」と変に感心してしまいます。(もちろん家庭平和のためにはそんなことは微塵ももうしませんが)なにはともかく、日本に帰る。ああ、うまい蕎麦が喰いたいです…
2009.05.16
コメント(0)
録画用のソフトとして、XP MCEを主に使っているのですが、バグってるんですねー、これが。。。。もっとも使っているのはEPGだけなんですけどね。いわゆるキーワード録画が主なのですが、どう考えても引っかかるはずのない番組が録画予約に入ることが非常に多いです。でも今回は、重複している番組はないのに、なぜか『7万人探偵「ニトベ」』が「重複あり」で録画できません。先週は気がつかなくて録画に失敗(orz)。今週は、失敗しそうなのがわかっていたので、サブ機の方で録画しました。メイン機は案の定失敗です。さらにナゾなのは、来週は問題なさそうなのです。「これだけ手間かけて、録画してるのは、『ニトベ』かよっ!」というツッコミはなしね(笑)。忽那 汐里は、まあともかくとして、「六角精児」!(っていうか、山地)。同級生なのよ、実は。特に仲がよかった訳でもないし、むこうも覚えちゃいないだろうけど、深夜の30分枠ドラマとはいえ、タイトルロールの最後に名前が載るようになるとは、、、。「鑑識・米沢守~」も観にいかなくちゃ・・・
2009.05.15
コメント(2)
出張中は、通勤時間がほぼゼロなこともあって以外にヒマだ…暇つぶし用に、録画してあるテレビ番組をポータブルのHDDに入れて持ってくるつもりだったが、肝心のHDDを忘れてしまいました。 orzorbもナゼか調子が悪く、うまくビデオの視聴ができません。 orz自宅のPCのファイルはHDのH264.tsなので、出張に持ってきているDell Latitude D410ではファイルは手元に持って来れても、CPUパワーが不足で満足に再生できません。 orznVidea IONとかが普通になって欲しい。。。仕方ないので、teamviewerで自宅のまともなPCにログインし、TMPGEncで、SDのwmvファイルに変換、dropboxで手元のPCに持ってきて観る、というバカみたいなことをしています。でも、これって「貧者のクラウド・コンピューティング」な感じがしないでもありません...
2009.05.15
コメント(0)
データベースの使い道ですが、Windowsのメディアセンターは予約リストに加え、録画の履歴が保存されます。真似をして、録画が終了したデータや、重複など何 らかの理由で失敗したものも記録として残すことにします。ただ、無限に残しておいても仕方ないので、100位残しておけるようにします。これでデバッグが楽になるでしょう(なるといいな)。ま た、進行中の録画に関しても別のデータベースに入れておくことにします。そうすれば、録画中にプログラムを止めなければならないハメになっても、レジュー ムできるでしょう(多分)。 こんな感じ? #---------------------------------------- class SizedReserveDB < ReserveDB def initialize( workFolder, max = 100 ) super( workFolder ) @max = max end # def initialize( workFolder, max = 100 ) def full? length() >= @max end # def full? def push( iEPG ) synchronize { while( full?() ) pop() end super } end # def push( iEPG ) end # class SizedReserveDB < ReserveDB
2009.05.14
コメント(0)
え~、今シリコンバレー出張中で日付も変わってそろそろ寝ないと明日に響くんですけど、「録画人間の末路」さんのところでWOWOWの行く末、みたいな話をよんだので・・・(意味無しTBですみません) ちょうど4年ほど前に引っ越して来た今の家が近所に超高層マンションが複数建っており、いわゆる「難視聴エリア」なわけです。近所もTVアンテナが立って いる家はありません。「難視聴エリア」といっても東京タワーのVHFを受信する場合の話であって、我が家は丘の中腹にあるのですが、丘の上まで行けば UHFのアンテナ(つまり地デジ)は(正確にはアンテナのあるビル)は目視できるので、少し高いアンテナ(屋根から数メートル、10メートルなら確実)を 立てればUHFは問題なく受信できるはず。もっとも、仮に10メートルのアンテナが必要となるとその後のメンテナンスやらなにやらでちょっと問題ありです が。 引っ越してくるときの選択肢としては、難視聴エリア解消のために件の超高層マンションの再配信に加入するか、CATVに加入するかだったのですが、 そのときは前者にしました。多分以前から住んでいる近隣の家はタダだったろうと思いますが、我が家は結構なお金を払って加入しました。ところが、地デジの 正式サービスエリアにはいったころ、この再配信サービスは2011年7月まででその後はサービスを停止するとのことが発覚します。 orz だまされた気分ですが、インターネットの接続やなんやで結局CATVに加入するのが安い、ということでさっさと見切りをつけ、CATVに加入しました。 で、このときの工事料金がまたなぞで、「TV一台にセットトップボックスをつけるために、壁に穴あけとかその他もろもろの工事はキャンペーン中で無料」、 一方「屋内の分配器に接続する場合は有料」でした。ちなみに、引き込みはまったく簡単で、下工事はしてあるので線を通して屋内の分配器に繋ぐだけなのです が…CATV会社のおねーちゃんも「私も『わけわかんない』って社内でいってるんですけど、スミマセン」とか言いだす始末。 さすがに建てたばかりの家の壁に穴を開けるのも気がひけたので、有料の工事してもらいました。 orz で、この辺からが本題なのですが、件のCATVは(当然)CSの再配信ですが、お仕着せのチャンネルのセットがビミョーなんですな、これが… orz スカパーに入りなおすほどではないし、そもそもリアルタイムではほとんどTVを観ない私にとって、録画機能無しのセットトップボックス(当時)は涙出まし た。(で、「PCで録画」に嵌っていくわけです)ちなみにさすがに今はHDD録画機もありますが、月額1000円も高くなりますし、DVD-Rにも焼けない ので未だにチューナーのみの物を使ってます。 Regza(録画機能つきのTV)に買い換えたとき、WOWOWの無料お試し2週間とかで観てたら、「やっぱ『HDで5.1サラウンド』っていいよなー」 だし、たまたま、スタンリー・キューブリックの映画を立て続けに放送してたことに加え、当時はHD-DVDとBDの決着がついていなかったので、モラトリ アムの意味もあってそのまま加入することになります。このとき、セットトップボックスの方に契約を移すことも考えたのですが、「PCで録画」の方は家内に は手に追えないので、結局追加契約しました。 WOWOWのよいところは、「デジタル2視聴目は945円」に尽きます。3契約目はまた元の金額に戻るのがナゾですけど、CATVは単純に2倍ですから、結構「お得感」はあります。 ま、ということで、グダグダになってしまいましたが、CATV一契約(HDチャンネルもそこそこあり)にHD放送前提でチャンネルを追加するとなると、ス カパーよりは、WOWOWデジタル2契約の方が魅力的なんですよ。ちなみに録画時間は(正確に測ってませんが)、WOWOW:CATV:BS:TDが 4:3:2:1くらいですね、多分。 スカパーのHD化が進めば考え直さなければならないですかねぇ…
2009.05.13
コメント(0)
iEPGD関連が終わったところまでで、意外に行数を喰ってしまいました。全体で500行以下はさすがにむりかな…次は予約用のデータベースです。rubyにはdbmとかもあるのですが、ディレ クトリにiEPGDのファイルを保存するだけにします。いまの所、iEPGD情報はせいぜい一週間分ぐらいしか提供されないみたいなので、予約録画件数も 多くても200件ぐらいでしょうから、これでいいことにします。デバッグも楽そうですし… 録画データベースは結局のところ、時間軸で一番直近のものしか興味はないので、Arrayににたインターフェース(メソッド)を用意することにします。 (Rubyの言語レベルの)マルチスレッドに対応するために、排他制御も含めましたが、どうもmutexではしっくりいかないので、monitorクラスをmixinして、排他制御は片っ端から、synchronize()を使うという、力技というか、エレファントな感じになってしまいました。(ま、動けばいい、ということで・・・) ## Class ReserveDB (C)2009 B.O.# refer below for MonitorMixin# http://www.ruby-lang.org/ja/man/html/MonitorMixin.htmlrequire( "monitor")require( "IEPGD")class ReserveDB @@EXT = "tvpid" def initialize( workFolder ) @workFolder = File::expand_path( workFolder) self.extend(MonitorMixin) if ! FileTest::directory?( @workFolder) Dir::mkdir( @workFolder ) end end #def initialize def to_s "ReserveDB[#{@workFolder}]" end #def to_s def clear synchronize { Dir::glob( "#{@workFolder}/*.#{@@EXT}").each {|f| File.delete( f ) } } end #def clear def length synchronize { i = 0 Dir::glob( "#{@workFolder}/*.#{@@EXT}").each {|f| i+= 1 } return i } end # def length def empty? length() == 0 end #def empty? # adding an iEPGD data # needs to check if the same program has existed already def push( iEPGo ) synchronize { hash = Hash.new del( iEPGo) fname = iEPGo.to_s("%D__%T_%eT_%program-title:_%station-name:_%station:.#{@@EXT}" ) fh = File.open("#{@workFolder}/#{fname}",'w') fh.puts( iEPGo.to_s ) fh.close } end # def push( iEPG ) # delete # if successful, returns true # if not returns nil def del( iEPGo ) hash = Hash.new ret = false # gather all data in the DB synchronize { Dir::glob( "#{@workFolder}/*.#{@@EXT}").each {|f| if (arrayiEPG= IEPG.File2Array( f)) != nil # in this case, if not nil, length must be 1 if arrayiEPG[0].sameProgram?( iEPGo ) File.delete( f ) ret = true end end } } return ret end # def _del( oiEPG ) def top if empty? retrun nil end hash = Hash.new ar = Array.new synchronize { # gather all data in the DB Dir::glob( "#{@workFolder}/*.#{@@EXT}").each {|f| if (arrayiEPG= IEPG.File2Array( f)) != nil ar += arrayiEPG end } } ar.sort! return ar[0] end # def top def pop synchronize { if (tmp = top()) == nil return nil else del( tmp ) return tmp end } end #def pop end # class ReserveDB < Array
2009.05.13
コメント(0)
シリコンバレー出張中ですが、巡回コースでFry'sへWD20EADSが$284.99でした(バルクでなく箱入り)。今日のレート(97円22銭くらい)なら、VATを入れても、邦貨換算で3万円を少し切りますね。ちょっと、グラッと来ますが、1台だけでは仕方ないので、思いとどまりました。 価格.comでも31270円というところがあります。今の円高は少し進みすぎな気がしますが、3万円を切るのも時間の問題でしょう。他のモデルが出れば、27000円位まではすぐかな。Hauppauge HD-PVRは$204.99でした。さすがに買い足しても意味がないので、パス。 M/Bコーナーは、Zotac GeForce 9300-ITX WiFiとかないか知らん?と思ったのですが、インテル、Gigabyte、ASUS、JetStarくらいしかなく、D945GCLFは$68.99で日本では、もう「投売り」なってますので意味ナシ。
2009.05.12
コメント(0)
久々に兄弟そろっての出演です。GWにどっかのサイトのまねをして、「イケア」で吊り棚を作ってあげました。上のほうでまったりしまくっているのが、ぶらうんの「じんじゃー」、下の方でちょっとおスマシがブルーの「みんと」。2人とも生後7ヶ月を過ぎ、3.8Kgくらいになりました。性格は相変わらずいイタズラ大好き。でも、家内の家事のあとをついて回っています(あまえんぼう)
2009.05.11
コメント(0)
(いい加減飽きてきましたが)二つの予約が同じかを判定する、メソッド::sameProgram?()を追加。Rubyの習慣に習い、predicateは最後を?にしてみる。個人的には、sameProgramPとしたいところ…Ver1(iEPGDで無い場合)のデータの場合はbuggy... def sameProgram?( other ) format = "" if self[ "version:"] == "2" && other[ "version:"] == "2" format = "%station:%program-id:" else format = "%station:%program-title:%D%T" end return self.to_s( format ) == other.to_s( format ) end #def sameProgram?( other ) to_s()もちょっとまずそうな所に気がついて変更。RubyはShallow copyだった… orz def to_s( format = nil ) if format != nil str = String.new( format ) for key, value in @formatHelper str.gsub!( /#{key}/, value ) end return str end : (あとは同じ)
2009.05.11
コメント(0)
外部のキャプチャコマンドをsystem()関数などでキックするにもiEPGDの情報からからファイル名を生成する必要がありますね。開始時間、番組名、チャンネル名とかで決めうちで作ってもいいような気がしますが、チューナー制御コマンドへの対応も必要でしょう。 相変わらず行き当たりばったりですが、後でプログラムのコードを直さなくてもいいように、要約情報を作成する関数を作ることにします。機能面ではto_s()に似たものになるので、to_s()を拡張します。linuxのdateコマンドのプレースホルダ機能みたいに、引数に %keyword とするとキーワードの値で置き換えてくれるようにしました。録画の日時や時間は、%Dとかにします。 微妙に悩ましいのは、そのためのデータをどうやって保持するかですが、必要なデータはここでガチには決まらないので、例によってhashで持つことにします(えへへ)。前に、開始終了時間をキャッシュ的に持ち、更新時の管理のために::UptoDate()というメソッドを作りましたが、このメソッドも拡張して、hashデータの管理もここでするようにしました。 それにしても、Rubyの文字列操作や正規表現はべんりですねぇ。正規表現をプログラム言語の機能として取り込んだのはなにが最初なんでしょう? bch? Perl? def to_s( format = nil ) if format != nil for key, value in @formatHelper format = format.gsub( /#{key}/, value ) end return format end #to reproduce .tvipd file single content mode このへんは一緒なので省略 end # to_s # make @timeStart, @timeEnd & @formatHelper upto date def UptoDate timeNow = Time.now year = (self[ "year:" ] == "" ) ? timeNow.year : self[ "year:" ].to_i month = (self[ "month:" ] == "" ) ? timeNow.month : self[ "month:" ] day = (self[ "date:" ] == "" ) ? timeNow.month : self[ "date:" ] _start = self["start:"].split(":") _end = self["end:"].split(":") @timeStart = Time.local( year, month, day, _start[0], _start[1] ) @timeEnd = Time.local( year, month, day, _end[0], _end[1] ) if @timeEnd <= @timeStart @timeEnd += 24*60*60 end # for @formatHelper @formatHelper = Hash::new @formatHelper.default = "" # time related @formatHelper[ "%D" ] = @timeStart.strftime("%Y_%m_%d") @formatHelper[ "%eD"] = @timeEnd.strftime("%Y_%m_%d") @formatHelper[ "%T" ] = @timeStart.strftime("%H%M") @formatHelper[ "%eT"] = @timeEnd.strftime("%H%M") # iEPGD keyword related keywords =Array.new if self[ "version:" ] == "1" keywords = @@V1Mandatory + @@V1Axuary else keywords = @@V2Mandatory + @@V2Axuary end keywords.each {|key| @formatHelper[ "%#{key}" ] = self[ key ] } # '%' itself @formatHelper[ "%%"] = "%" end #def UptoDate
2009.05.11
コメント(0)
とりあえず、無事アメリカまで来ました。 multi形式の読み込み用のクラスメソッド IEPG.File2Array()ですが、バグではないけど、処理が重複していたのと、redoとか使っていて、何をやっているのかが自分でもよくわからない(大笑)感じ。そこで、single形式のときと同じように、各行ごとの配列からパースするようにちょっと変えてみました。こうすることで、Boundaryの文字列に挟まれたところを、single形式専用の関数でパースするのを繰り返すようにでき、関数も短くできました。成田エキスプレスのなかで、ちょろっと直しましたが、問題なようです。 def IEPG.File2Array( filespec ) f = open( filespec ) s = f.read f.close lines = s.split(/$/) lines.each {|line| line.strip! } IEPG.Lines2Array( lines ) end #def IEPG.File2Array( filespec ) def IEPG.Lines2Array( lines ) if lines[0] =~ /^Content-type: *application\/#{@@V1SingleContent}|#{@@V2SingleContent}/ # single content if ( (tmp = IEPG::fromLines( lines )) != nil ) return [ tmp ] end elsif lines[0] =~ /^Content-type: *application\/#{@@V1MultiContent}|#{@@V2MultiContent}/ # multi contents ar = Array.new boundary = "" for i in 1..( lines.length() -1 ) # get boundary string if lines[i] =~ /^boundary:(.*)$/ boundary = $1.strip break end end s=0 for i in (i+1)..( lines.length() -1 ) if lines[i] =~ /^#{boundary}/ if s!=0 if ( (tmp = IEPG::fromLines( lines[(s+1)..(i-1)] )) != nil ) ar << tmp end end s=i end if lines[i] =~ /^#{boundary}--/ # really end return ar end end #for return ar # for safty sake else return [] # unknown end # elsif end # def IEPG.Lines2Array( inlines )
2009.05.11
コメント(0)
以前にも何回か紹介しているteamviewerですが、アカウントを登録すればブラウザでアクセスできます。いま、成田空港のノースウェストのラウンジから、家のPCへのアクセスを試みてますが、いまいち。NWAのラウンジのPCはなぜかMacですが、やっぱsafariってのが無理がありますかねぇ。(Mac の仮名漢字変換がcommand+Spaceだとは知りませんでした。) :自分のPC(WindowsXP)に変えました。ちゃんと使えます。これでPCがなくてもブラウザが使える環境にあれば、世界中どこにいても家のPCが使えます。スバラシイ。
2009.05.10
コメント(0)
データの読み込み部分に関して、multi形式への対応をしなければならないので、IEPG.File2Array()メソッドを作ります。これはファイルから読むことにします。最初の行を読んだ時点で、シングル形式かマルチ形式か判断しています。戻り値は、シングルでもマルチでも同じ形式になるように、iEPGDのArrayとして返すようにしています。 def IEPG.File2Array( filespec ) ar = Array.new File::open( filespec ) {|f| if line = f.gets # first line if line =~ /^Content-type: *application\/#{@@V1SingleContent}|#{@@V2SingleContent}/ # single content fromLines = Array.new fromLines << line.strip while line = f.gets fromLines << line.strip end if ( (tmp = IEPG::fromLines( fromLines )) != nil ) ar << IEPG::fromLines( fromLines ) end elsif line =~ /^Content-type: *application\/#{@@V1MultiContent}|#{@@V2MultiContent}/ # multi contents while line = f.gets if line =~ /^boundary:(.*)$/ boundary = $1.strip break end end # while while ( line = f.gets ) if (line =~ /^#{boundary}--/) # to find really end return ar elsif (line =~ /^#{boundary}/) # to find start of a content fromLines = Array.new while line = f.gets if (line =~ /^#{boundary}/) # found end of a content if ( (tmp = IEPG::fromLines( fromLines )) != nil ) ar << IEPG::fromLines( fromLines ) break ; end else fromLines << line.strip end #if-else end # while redo end # elsif end #while ( line = f.gets ) else print( __FILE__ + __LINE__.to_s + "unknown file \n" ) return nil end # else else break end } return ar end # def IEPG.fromFile( filespec )
2009.05.10
コメント(0)
さて大体できてきたので、データを出力するために、iEPGD::to_s()を作ります。出力をファイルに落とせば、そのままiEPGDのデータになるようにします。1行目は必ず、Content-type:になるようにしています。吐き出すフィールドの順序は、keywordsとkeywordsInSelfで管理しています。memo部も内部データでは、他のフィールドと同じような扱いになっていますが、ここでは特別扱いが必要です。 class IEPG < Hash # keywords @@Header = "Content-type:" @@V1SingleContent = "x-tv-program-info" @@V2SingleContent = "x-tv-program-digital-info" @@V1MultiContent = "x-multi-tv-program-info" @@V2MultiContent = "x-multi-tv-program-digital-info" @@V1Mandatory = [ "version:", "station:", \ "start:", "end:" ] @@V1Axuary = [ "year:", "month:" , "date:", "program-title:", \ "program-subtitle:", "performer:", "genre:" , "subgenre:" ] @@V2Mandatory = [ "version:", "station:", "station-name:", \ "program-title:", "program-id:", \ "start:", "end:" ] @@V2Axuary = [ "year:", "month:" , "date:", \ "genre-1:", "subgenre-1:", \ "program-subtitle:", "performer:", "genre:" , "subgenre:" ] @@Memo = "_memo:" # adding memo section, needs to be def to_s( ) #to reproduce .tvipd file single content mode keywords = [ @@Header ] if self[ "version:" ] == "1" keywords = keywords + @@V1Mandatory + @@V1Axuary else keywords = keywords + @@V2Mandatory + @@V2Axuary end keywordsInSelf = self.keys s = "" keywords.each {|key| # p i.to_s + " " + key + " " + self[ key ] s += key + self[ key ] +"\n" keywordsInSelf.delete( key ) } # if there are anything else in hash, put them for safty sake, except _memo: keywordsInSelf.each {|key| if( key != @@Memo ) s += key + self[ key ] +"\n" keywordsInSelf.delete( key ) end } # handle memo section s += "\n" + self[ @@Memo ] +"\n" keywordsInSelf.delete(@@Memo ) return s end # to_s end end #class IEPG < Hash
2009.05.09
コメント(0)
明日からの出張の準備でスーツケースにパッキングをしていたら、ブラウンの方が「お手伝い」に来てくれました(笑)
2009.05.09
コメント(2)
読んだあとのデータの処理についてですが、データベースの作り方にも関連しますが、開始時間順に取り出す必要があるのは、明らかです。開始時間でソートとかは必須でしょうね。iEPGDデータの中には開始・終了時間の情報は入っていますが、このままでは扱いにくいので、Rubyの標準の時間オブジェクトをキャッシュデータみたいな形で持つことにし、これを管理するために、::UptoDateメソッドを定義します。終了時間は年月日を含まず、開始時間より前の場合は次の日を意味するようなので、24時間プラスします。つまり、iEPG/iEPGDでは24時間を越える録画時間はサポートできないのですね。 ついでにソート用に<=>も定義しておきます。 class IEPG < Hash # keywords attr_reader :timeStart, :timeEnd # member method def initialize self.default = "" @timeStart = Time.now @timeEnd = Time.now end #def initialize def <=>( other ) self.timeStart <=> other.timeStart end #def <=>( other ) # ---------------------- # gopher member functions # ---------------------- # make @timeStart, @timeEnd upto-date def UptoDate timeNow = Time.now year = (self[ "year:" ] == "" ) ? timeNow.year : self[ "year:" ].to_i month = (self[ "month:" ] == "" ) ? timeNow.month : self[ "month:" ] day = (self[ "date:" ] == "" ) ? timeNow.month : self[ "date:" ] _start = self["start:"].split(":") _end = self["end:"].split(":") @timeStart = Time.local( year, month, day, _start[0], _start[1] ) @timeEnd = Time.local( year, month, day, _end[0], _end[1] ) if @timeEnd <= @timeStart @timeEnd += 24*60*60 end end #def UptoDate # # class methods #end #class IEPG < Hash
2009.05.09
コメント(0)
悩んでいてもしかたないので、iEPGDのデータを読む部分を作っていきます。まず、multi形式は一旦おいておくことにして、シングル形式を扱うことにします。その上で、直接ファイルから読むのではなく、1行づつ配列に入っているものとします。こうしておけば、multi形式は、比較的簡単に扱えるはずです。問題は、内部表現なのですが、とりあえず解析はしてみたものの、リバースエンジニアリングの悲しさで、どんなキーワードがくるか良くわからないところですね・・・まあ、「フィールド」というか、「キーワード」をキーとして、ハッシュでデータを持つことにしましょう。効率が悪いのはミエミエですが、昨今のCPUというか、PCの性能を信じることにします(=大名プログラミング)。録画の管理という点でみれば重要なのは、開始時間、終了時間、放送局名、プログラムの名前あたりですが、ほかの情報も必要になる可能性が(特に将来、機能の拡充の際)あるので、全部とっておけばそれに越したことはないでしょう。 350ml.netさんの情報を信じるとすれば、ちょっと信じられないのは、日付(year:,month,date)が必須でないことですが、実際には入っています。まあ、これは本当に入っていない場合があったら対応することにしましょう。念のため、iEPGの古いバージョンにも何気にさり気に対応しておきます。文字コードは、とりあえずSJISのみ対応です。メソッド自体は、ちょっと悩んだのですがオブジェクトを返すクラスメソッドとして定義しました。 ## iEPGD Class# (c) 2008-09 B.O# version 2009/04/03# re-written# to support V1 and V2 of iEPG# for V1 reference , http://350ml.net/labo/iepg.html # thanks to "AKINO Yoh, 350ml.net"# class IEPG < Hash # keywords @@Header = "Content-type:" @@V1SingleContent = "x-tv-program-info" @@V2SingleContent = "x-tv-program-digital-info" @@V1MultiContent = "x-multi-tv-program-info" @@V2MultiContent = "x-multi-tv-program-digital-info" @@V1Mandatory = [ "version:", "station:", \ "start:", "end:" ] @@V1Axuary = [ "year:", "month:" , "date:", "program-title:", \ "program-subtitle:", "performer:", "genre:" , "subgenre:" ] @@V2Mandatory = [ "version:", "station:", "station-name:", \ "program-title:", "program-id:", \ "start:", "end:" ] @@V2Axuary = [ "year:", "month:" , "date:", \ "genre-1:", "subgenre-1:", \ "program-subtitle:", "performer:", "genre:" , "subgenre:" ] @@Memo = "_memo:" # adding memo section, needs to be # # class methods # # read one iEPGd from lines # return a iEPGd def IEPG.fromLines( lines) if lines.length < 2 return nil # error end #if if !( lines[0] =~ /^#{@@Header} *(.*)$/ ) return nil # error end contentType = $1.strip # checking 1st line if !(contentType =~ /application\/#{@@V1SingleContent}|#{@@V2SingleContent}/ ) return nil # error end #check charset contentType =~ /charset=(.*)/ if (charset = $1.strip) != "shift_jis" print( __FILE__ + __LINE__.to_s + "Error unexpected charset type:" + charset + "\n" ) return nil end tmp = IEPG.new tmp[@@Header] = contentType i=1 for i in 1..( lines.length() -1 ) if lines[i] =~ /(.*?:)(.*)/ tmp[ "#{$1.strip}" ] = $2.strip else break end end #for # check memo section if lines[i].strip == "" if (i = i +1 ) <= lines.length() -1 tmp[ @@Memo ] = (lines[i..-1].join("\n")) end end return tmp end #def fromLines( lines) end #class IEPG < Hash
2009.05.09
コメント(0)
連休明けだが、夕刻より秋葉原のお客さんを往訪。この不景気のなか、予想外の商談になり、ノリのりで話をする(売れるといいな)。前祝ということではないが、営業の若いの(でもアラフォー)と神田藪そばへ行く。行ったことがない、というので連れて行くが、蕎麦の味は、正直可もなく不可もなく。まあ、「xx番さん、せいろ2ま~い」の声がプロパゲートしていくのを尊ぶのがこの店の楽しみ方、ということをつれも納得する。ああ、うまい蕎麦が食べたい・・・・
2009.05.07
コメント(0)
2009/6/29 さらに直しました まえにも、書いたんですが、ちょっと直しました。ini = IniFile.new( "test.ini")で、ハッシュのハッシュを返します。ini[section][key]でセクション、キーで値を参照できます。存在しない、ハッシュ、キーが指定されると""(ヌル文字列)が返ります。また、ini[section]でセクション全体をハッシュとして返します。存在しないセクションを指定すれば、空のハッシュを返します。iniファイルを書く方も作らなければなりませんね。ま、それはまた今度。## (c)2009 BO#class IniFile < Hash @@ReComment = /^#/ @@ReSection =/\[\s*(.*)\s*\]/ @@ReKeyValue =/\s*(.*)\s*=\s*(.*)\s*/ def initialize( fileSpec ) @fileSpec = fileSpec File::open( fileSpec) {|f| sName = "" tmpHash = Hash::new tmpHash.default = "" while line = f.gets if line =~ @@ReComment elsif line =~ @@ReSection # closing previous section if sName != "" self[ sName ]= tmpHash end # if sName = $1.strip if ( tmpHash = self[ sName ] ) == nil tmpHash = Hash::new tmpHash.default = "" end elsif line =~ @@ReKeyValue tmpHash[ $1.strip ] = $2.strip end end # while # closing final section if sName != "" self[ sName ]= tmpHash end # if } tmpHash = Hash::new tmpHash.default = "" self.default = tmpHash self end #def initialize( fileSpec ) attr_reader :fileSpecend # class IniFile
2009.05.06
コメント(6)
TV王国のデータを適当にダウンロードしてデータを調べています。ざっと見てみた感じでは、こんな感じiEPGiEPGデジタル備考ファイルの拡張子tvpitvpidContent-type: application/x-tv-program-infoapplication/x-tv-program-digital-infoversion:12Station: 放送局名ユニークな局名(半角英数字)Station-name: (なし)放送局名Station:から変更?program-id: (なし)同一放送局内でのユニークな番組番号?genre:ジャンルなしgenre-1:に変更?genre-1:genre-2:(なし)数subgenre:サブジャンルなしsubgenre-1:に変更?subgenre-1:subgenre-2: (なし)数Copycontrol-1:(なし)orzComponent-video-1: (なし)Component-audio-1:(なし) 「Station:」フィールドはいままでいい加減だったのが、英数字列に変更になったようです。ジャンルの数字はなにを意味するかよくわかりません。Copycontrol-1:、Component-video-1:、Component-audio-1:あたりもコントロール、ビデオ、音声の属性なんでしょうが、よくわかりませんね。それよりも、「まとめてiEPG予約」なんてのがあって、ファイルを開いてみると以下のような構造になっています。Content-type: application/x-multi-tv-program-digital-info;charset=shift_jisversion: 2boundary: XXXBOUNDARYXXXmulti-program-title: MyEPG 2009-05-04 12:02:52XXXBOUNDARYXXX(iEPGD情報)XXXBOUNDARYXXX(次のiEPGD情報)XXXBOUNDARYXXX :XXXBOUNDARYXXX--たぶん、boundary:フィールドが各iEPGデジタル情報の区切り行になっていて、それに--がついたものがファイルの終わりなのでしょう。ちなみに、Ver1でもこのmulti形式はあるようです。ぬぅ、なんかパースしにくいフォーマットですね、いっそのことXMLかなんかにしてしまえばよかったと思うのは、私だけ???
2009.05.06
コメント(0)
Orbというサービスについて前にも書いたけど、来週の出張に備えて、最新版( 2.01.0044、英語版は0.045になってる)を入れてみた。もちろんちゃんと動くきます。仕様には書かれてないのだが、OrbのサーバーはDLNAサーバーに対応しているらしく、家庭内のLANに繋がった、Regza Z2000からも、サーバーが見える。動画も再生できるのだが、いかにせん、ビットレートが低すぎて残念ながら、現状では実用には耐えない(残念)。Orb社としてこの方面に興味がどの程度あるのか次第だろうけど、技術的な問題点は少ないような気がする。ところで、Orb社ってどうやって儲けてんだろ、ってんでUSの方のWebページを見たら、Joe CostelloがCEOなのでちょっとびっくり。CostelloはCadenceの最盛期(と言っていいと思う)の社長でカウフマン賞とかも貰ってます。もっとも、カウフマン賞もMoorby(Verilog-HDLの発明者ね)より先にCostelloにあげちゃうくらいだから、どうかとも思うけど・・・で、Costelloは95年のIVC(International Verilog Congerence,OVI、まだAccelleraになってない頃)で基調講演をしたのを聴いたけど、「化粧」とかしてて、カリスマチックではあったけど、どちらかというと「商売人」(悪い意味ではない)って感じだったな~。
2009.05.05
コメント(0)
PVRの構成要素としてはどんなものが必要でしょうか?チューナー、録画機能の制御モジュール予約録画のデータベースiEPGのハンドリングこんなところかな?チューナーと録画機能の制御は単独で動作するコマンドラインベースのプログラムがあるのが前提だから、必要な時間に、system()関数でキックしてやることにします。データベース機能は、SQL(使い方わかんない。笑)とか本格的なものもあるけど、そこまでのものはいらないでしょう。必要な動作は、「新たな録画を追加」と「古い順に取り出す」ですから。Rubyのdbmクラスを使うのも手ですかね…データの要素はiEPGのデータをそのまま使うとすると、Marshal.dumpとかでシリアライズしてやる必要がありますね。単純にディレクトリにファイルにしておくほうがデバッグが楽そうなので、とりあえず最初はそうしておきましょう。iEPGのデータは、iEPGD(iEPGデジタル)を扱うとすると面倒です。情報がないんですよねー。Ver1のiEPGに関しては、350ml.netにあります。iEPGDの情報は見つからないので、SonetのTV王国のデータをすこし眺めてみることにして考えることにしましょう…iEPGDのデータを扱うところがチト厄介な気がしますが、全体で500~600行くらいですむといいなー。
2009.05.04
コメント(0)
え~、突然ですがPVR(=PersonalVideoRecorder、要はPCでテレビを録画するシステム)を作ってみたいと思います。mythTVとか世の中にはあるので、意味がないといえば意味がないですが、キャプチャプログラムと、チューナー制御プログラムを作ったので、中年のボケ防止です。 機能としては、1.EPGの利用2.汎用3.電源管理今回入れないけど将来的に考えたいものは4.ネットワーク録画予約、5.GUI6.延長対応7.複数チューナーの有効利用8.キーワード録画EPGの利用最近は、Web上でiEPGのサービスが提供されていますので、これを録画予約のために使わない手はないですね。汎用まあ、汎用といっても限界がありますが…逆にいえば、録画、チューナー制御をするコマンドラインベースのプログラムがあるのが前提です。電源管理これは、努力目標。最悪電源入れっぱなし。ネットワーク録画メールベースかな、あるいは電源が入れっぱなしならば、TeamViewrで家の外からアクセスでもいいような気がします。GUI苦手なんです。すみません。せめて重複の解消ぐらいはGUIがないとメンドクサそう。延長対応ん~、アイデアはあるんですがね~…すぐは無理かな…複数チューナーの有効利用我が家ではCATVセットトップボックスとデジタル放送用のチューナーがありますが、こういうヘテロな環境をうまく使いこなしたいな、という希望です。キーワード録画iEPGサービスを利用して、クロウラ(最近は「スパイダー」って呼ぶみたいですが)を作ることになりますかね。まあ、TV王国とかはWebサービスとしてキーワードで番組をピックアップする機能があるので、それですかね。とりあえず、企画意図としてはこんな感じかしら?まあ、あんまり気張らず少しづつ進めることにします。
2009.05.03
コメント(0)
![]()
バッファロー PCastTV2対応 PC用学習リモコンキット RemoteStation (PC-OP-RS1)用のRuby用ライブラリとアプリを公開します。こちらからどうぞ。 元ネタは、「スーの道具箱」さんのところです。RemoteStationは最大4chのLEDが扱えるのだけど、1つのLEDでも異なるデータを出せば、複数の機器の制御はできますね。ただ、排他制御をなんとかしないと、異なるアプリからの制御がうまくいかなそう。。。やっぱり、drubyとかでサービスの形にしないとだめかしらん?
2009.05.03
コメント(0)
いや~、参ったね。 連休明けから、米国出張の予定で、EFTAの登録もしたしばっちりだ、と思っていたら、豚インフルエンザ騒ぎ。帰国便は成田で機内検疫になりそう。(どれくらい時間がかかるのかしら?)仕方ないので、マスクでも買っておこうと思ったのだが、近所の薬局は軒並み売り切れ…orz
2009.05.01
コメント(0)
全31件 (31件中 1-31件目)
1