bit sized

Just another WordPress.com weblog

‘Programming’ カテゴリーのアーカイブ

Ubiquityを使って英単語を調べる – smart.fm (iknow)経由

1件のコメント

以前、ubiquityを使って英単語を調べるというエントリを書きましたが、mozilla labの開発スピードに負けて使えなくなっていた事を発見。新しいUbiquityのapiにアップデートしてみました。

ついでに、フォントの色も変えたら少し見やすくなった気がします。

ubiquity smart.fm

ubiquity smart.fm

Ubiquityのコマンドエディターに”共有”というのがあったので、クリックするとGitHubのGist(コード共有サービス)にアップロードされました。多分Firefoxの人なら http://gist.github.com/170912 に行くとインストールできると思います。楽ちんでいいですね

UbiquityのAPIについては、 Parser 2 API Conversion Tutorial に解説があるので、それに従って変更しました。関連があったのは、CmdUtilsの使い方で下記の二点。

  • 2行目 – コマンドの名前の指定(以前はnameというキーに対応する文字列だったが、現在はnamesというキー対応する文字列の配列に変更された。つまりコマンド名が複数持てる。またコマンドの名前に空白が入れれるようになった。)
  • 9行目,その他 – previewおよびexecuteの引数の指定(今まではtakesやmodifiersだったが、argumentsというキーで指定する。

2番目の変更はちょっとむずかしいですが、引数と動詞(コマンド)の関係を指定するSemantic Roleというのが導入されたようです。たとえば”A地点からB地点まで〜を移動させる”というようなコマンドでは、”移動させる”がコマンド(動詞) で、”〜を”は直接目的語です。ただ、この文には”A地点から”や”B地点まで”という修飾語もあり、それらがコマンドの引数として渡される場合もある訳 です。なので、Parser 2では引数が動詞に対して、どのような役割を果たすかを示すために、roleというパラメーターをargumentsに指定し、goal(目的地)や source(出発点)など引数として渡す値とコマンドの関係性を指定できるらしいです。言語間でのこうした関係性の表現の違い(日本語は”〜から”とい う後置詞、英語の場合は”from”という前置詞)を吸収するためにsemantic roleを導入しているようです。

このコマンドは引数に動詞(コマンド)の目的語(つまり検索する文字列)しかとらないので、roleは そのまま(direct) objectです。

  1. CmdUtils.CreateCommand({
  2.   names: [‘iknow search’],
  3.   icon: "http://example.com/example.png",
  4.   homepage: "http://bitsized.wordpress.com/",
  5.   author: { name: "Taku Okawa", email: "xxxxx@gmail.com"},
  6.   license: "GPL",
  7.   description: "search words or pharase in iknow",
  8.   help: "iknow",
  9.   arguments: [{role: "object",nountype: noun_arb_text}],
  10.   preview: function( pblock, arguments ) {
  11.    
  12.     var baseUrl = "http://api.iknow.co.jp/items/matching/${word}.${format}";
  13.     var tempUrl = {"word":arguments.object.text,"format":"json"}
  14.     var params = {include_sentences:"true"};
  15.     pblock.innerHTML = "requesting … " + CmdUtils.renderTemplate(baseUrl,tempUrl) + "<br/>";
  16.    
  17.     jQuery.getJSON(CmdUtils.renderTemplate(baseUrl,tempUrl),params,function(resp){
  18.       pblock.innerHTML = "";
  19.       jQuery.each(resp,function(i){
  20.         pblock.innerHTML += "<span style=’color:#ff0000′>" + this.responses[0].text + "</span>" + "<br/>";
  21.         pblock.innerHTML += "[例文] " + "<span style=’color:#0066ff’>" + this.sentences[0].text + "</span>" + "<br/>";
  22.         pblock.innerHTML += "[和訳] " + this.sentences[0].translations[0].text + "<br/>";
  23.       });
  24.     });
  25.    
  26.   },
  27.   execute: function(arguments) {
  28.     var baseUrl ="http://www.iknow.co.jp/items/search?keyword=${QUERY}";
  29.     var tempUrl = {"QUERY":arguments.object.text};
  30.     var urlString = CmdUtils.renderTemplate(baseUrl, tempUrl);
  31.     Utils.openUrlInBrowser(urlString);
  32.   }
  33. });

投稿者: t4ku

8月 20, 2009 9:21 am

Programming への投稿

Ubiquityを使って英単語を調べる

2件のコメント

FireFoxのプラグインでUbiquityというのがあります。他のWebサイトの機能をコマンドとして登録して使用できる、User MashUpを可能にするFirefox上の環境です。将来的にはFirefoxのURLバーに実装されるかも知れません(URLをWebのドキュメントがある場所であると考えるのではなく、Web上のリソースに対するリクエストような考えると割と自然な進歩?)。

ブラウザの新境地? Ubiquityが変える衝撃のブラウザ体験 (1/2):
http://www.itmedia.co.jp/enterprise/articles/0808/31/news003.html

まあ、そういうわけでどういうものかちょっと気になって見てみて、最近APIが公開されたiknowという英語学習SNSの単語検索を呼び出すコマンドを作成してみました。

iknow-search

iknow-search

コード

CmdUtils.CreateCommand({
  name: "iknow_search",
  icon: "http://example.com/example.png",
  homepage: "http://example.com/",
  author: { name: "xxxx", email: "xxxxx@gmail.com"},
  license: "GPL",
  description: "search words or pharase in iknow",
  help: "iknow",
  takes: {"input": noun_arb_text},
  preview: function( pblock, input ) {

    var baseUrl = "http://api.iknow.co.jp/items/matching/${word}.${format}";
    var tempUrl = {"word":input.text,"format":"json"}
    var params = {include_sentences:"true"};

    pblock.innerHTML = "";

    jQuery.getJSON(CmdUtils.renderTemplate(baseUrl,tempUrl),params,function(resp){
      jQuery.each(resp,function(i){
        pblock.innerHTML += this.responses[0].text + "<br/>";
        pblock.innerHTML += this.sentences[0].text + "<br/>";
        pblock.innerHTML += this.sentences[0].translations[0].text + "<br/>";
      });
    });

  },
  execute: function(input) {
    var baseUrl ="http://www.iknow.co.jp/items/search?keyword=${QUERY}";
    var tempUrl = {"QUERY":input.text};
    var urlString = CmdUtils.renderTemplate(baseUrl, tempUrl);
    Utils.openUrlInBrowser(urlString);
  }
});

Ubiquityコマンドの作成
Ubiquityコマンド作成の情報は、現状下記のリンクかソースを見るしかないくらい少なく、しかも上記のコマンドはまだプレビューで訳を表示しEnterキーでiKnowのサイトで該当のアイテム検索結果を表示するのみですが、行数の割には用を足しているかと思います。Ubiquity導入後、Command Editorを使用して上記のコードを貼付ければ使用できます。

Labs/Ubiquity/Ubiquity 0.1 Author Tutorial
https://wiki.mozilla.org/Labs/Ubiquity/Ubiquity_0.1_Author_Tutorial

上のチュートリアルに載っている事ですが簡単に作り方を説明したいと思います。

CmdUtils.CreateCommand({
  name: "hello-world",
  execute: function() {
    displayMessage( "Hello, World!" );
  }
})

ubiquityコマンドは、cmd_<コマンド名>というfunctionがあればそれを参照するのですが、普通はCmdUtils.CreateCommand()というユーティリティ関数を使用して作成します。上記のコードはその最も単純な例です。

CreateCommandに渡すオブジェクトのnameとexecuteというプロパティに、それぞれコマンド名の文字列と実行する関数を指定します。execute(コマンドの実行)はUbiquityのコンソールにコマンド名を入力しEnterを押した時点で実行されます。displayMessage()を実行すると、MacならGrowl,WindowsならUbiquityのコマンドの下に引数に与えた文字列が表示されます。

Hello-worlddisplay hello world

CmdUtilsにはコマンド作成以外にも、ユーザーがブラウザで選択中の箇所にテキストやHTMLを挿入(setSelection),IPから位置情報を取得(getGeoLocation),文字列のテンプレート処理(renderTemplate)などコマンド作成を楽にする関数が用意されており、これまたドキュメントは少ないのですが、ソースはこちらで見る事ができます。

プレビューの表示
Ubiquityではユーザーの入力に応じて出力されるプレビューウインドウがあり、こちらはCreateCommandに渡すオブジェクトのpreviewプロパティに、HTML文字列か関数を指定する事で実現できます。下記は冒頭のコマンドでのpreview部分です。

preview: function( pblock, input ) {

    var baseUrl = "http://api.iknow.co.jp/items/matching/${word}.${format}";
    var tempUrl = {"word":input.text,"format":"json"}
    var params = {include_sentences:"true"};

    pblock.innerHTML = "";

    jQuery.getJSON(CmdUtils.renderTemplate(baseUrl,tempUrl),params,function(resp){
      jQuery.each(resp,function(i){
        pblock.innerHTML += this.responses[0].text + "<br/>";
        pblock.innerHTML += this.sentences[0].text + "<br/>";
        pblock.innerHTML += this.sentences[0].translations[0].text + "<br/>";
      });
    });

  },.....

previewは第一引数にpreviewエリアに表示するDOM要素を、第二引数にユーザーから渡されるコマンドの引数をとっています。コマンドの引数はtakeプロパティで、どのnoun_typeで取得するか指定できます。

  takes: {"input": noun_arb_text},

引数データの扱い
noun_typeは引数の制約や、コマンドの補完機能(例えば日付データが入力されたら、日付データを引数にとるコマンドをUbiquityがサジェストする)に使用され、下記のようなnoun_typeがあります。また、CmdUtilsを使用してカスタムのnoun_typeを作成する事ができます。

  • noun_type_arb_text
  • noun_type_date
  • noun_type_language
  • noun_type_people
  • noun_type_place
  • noun_type_tab

noun_typeのソースは下記で確認する事ができます
nountypes.js

一方、コマンドの引数としてpreviewやexecute(の第二引数)に渡されるオブジェクトは、下記のようなプロパティを持ちます。

  • inputObject.text // 文字列
  • inputObject.html //タグを含む整形されたHTML
  • inputObject.data // 文字列以外のnoun_typeのデータ
  • inputObject.summary // 長い文字列の要約版

iknow-searchのpreviewで行っているのは、コマンドの引数から(テンプレート機能を使用して)URL文字列を組み立て、JSONでリクエストを取得、preview用のDOMに結果を反映するという一連の処理ですが、uqibuityではjQueryが使用できるため簡単に実装できます。

また同様に、コマンド入力後Enterを押した時に実行されるexecuteでは、Utils.openUrlInBrowser()を使用してブラウザで該当のURL(検索結果ページ)を開くようになっています。

まだまだ発展途上(というかできたばかり)のコンセプトですが、Webの機能をお手軽にコマンドにできるのはこれから重宝しそうです。

追記:Ubiquity のアップデートに合わせて、コードも少し書き換えました

投稿者: t4ku

2月 8, 2009 3:05 pm

Programming, Technology への投稿

Processingでグラフ

コメントなし

前から少し気になっていたProcessingという言語について、ちょうど本屋におもしろそうなオライリー本が出ていたので買ってみました。

ビジュアライジング・データ ―Processingによる情報視覚化手法

簡単な例としてグラフの作成があったので、自分でも日本の人口統計を使ってグラフを書いてみました。

Population growth in Japan

Population growth in Japan

  1. FloatTable data;
  2. PFont plotFont;
  3. float plotX1,plotY1;
  4. float plotX2,plotY2;
  5. float labelX,labelY;
  6. float dataAreaWidth;
  7. float dataAreaHeight;
  8. final float dataOffsetY = 30;
  9. float volumnInterval = 10000;
  10. float dataCeil;
  11. int []years;
  12. int yearMin;
  13. int yearMax;
  14. int yearInterval;
  15. float dataMin;
  16. float dataMax;

  17. void setup(){
  18. plotFont = createFont(“SansSerif”,20);
  19. textFont(plotFont);
  20. //load csv file
  21. data = new FloatTable(“ppl1920-1990-grs-t.csv”);
  22. years = int(data.getRowNames());
  23. yearMin = years[0];
  24. yearMax = years[years.length -1];
  25. yearInterval = 10;
  26. dataMin = data.getColumnMin(0);
  27. dataMax = data.getColumnMax(0);
  28. println(ceil(dataMax/volumnInterval));
  29. dataCeil = ceil(dataMax/volumnInterval) * volumnInterval;
  30. size(600,400);
  31. smooth();
  32. plotX1 = 160;
  33. plotY1 = 80;
  34. plotX2 = width - plotX1/2;
  35. plotY2 = height - plotY1;
  36. labelX = plotX1 -110;
  37. labelY = plotY2 +40;

  38. dataAreaWidth = plotX2 - plotX1;
  39. dataAreaHeight = plotY2- plotY1;
  40. }
  41. void draw(){
  42. background(022); // (1)background color
  43. fill(044);
  44. rectMode(CORNERS);
  45. noStroke();
  46. rect(plotX1,plotY1,plotX2,plotY2);
  47. strokeWeight(0.4);
  48. stroke(254);
  49. drawDataArea();
  50. stroke(112);
  51. strokeWeight(0.1);
  52. drawLabels();
  53. }

  54. void drawLabels(){
  55. drawTitleLabel();
  56. drawAxisLabels();
  57. drawYearLabels();
  58. drawYearGrid();
  59. drawVolumnLabels();
  60. }
  61. void drawTitleLabel( ) {
  62. fill(112);
  63. textSize(15);
  64. textAlign(CENTER,CENTER);
  65. text(“Population growth in Japan\n 1920 to 2000″,(plotX1+plotX2)/2,plotY1 - 40);
  66. }
  67. void drawAxisLabels( ) {
  68. fill(112);
  69. textSize(13);
  70. textLeading(15);
  71. textAlign(CENTER, CENTER);
  72. text(“Total \npopulation\n(Thousand)”, labelX, (plotY1+plotY2)/2);
  73. textAlign(CENTER);
  74. text(“Year”, (plotX1+plotX2)/2, labelY);
  75. }

  76. void drawYearLabels(){
  77. fill(112);
  78. textSize(12);
  79. textAlign(LEFT);
  80. int rowCount = data.getRowCount();
  81. for(int i=0;i < rowCount;i++){
  82. if(years[i] % yearInterval ==0){
  83. String yearText = data.getRowName(i);
  84. float x = map(int(yearText),yearMin,yearMax,plotX1,plotX2);
  85. text(yearText,x,plotY2+12);
  86. }
  87. }
  88. }

  89. void drawYearGrid(){
  90. int rowCount = data.getRowCount();
  91. for(int i=0;i < rowCount;i++){
  92. if(years[i] % yearInterval ==0){
  93. float x = float(data.getRowName(i));
  94. x = map(x,yearMin,yearMax,plotX1,plotX2);
  95. line(x,plotY2,x,plotY1);
  96. }
  97. }
  98. }

  99. void drawVolumnLabels(){
  100. fill(112);
  101. textSize(10);
  102. textAlign(RIGHT);
  103. float x = plotX1;
  104. float y;
  105. for(float v = floor(dataMin/volumnInterval)*volumnInterval;v <= dataCeil;v+= volumnInterval){
  106. y = map(v,floor(dataMin/volumnInterval)*volumnInterval,dataCeil,plotY2,plotY1);
  107. line(x-10,y,x,y);
  108. //println(v);
  109. text(floor(v),x-20,y+4);
  110. }
  111. }

  112. void drawDataArea(){
  113. beginShape();
  114. int rowCount = data.getRowCount();
  115. for(int i=0;i < rowCount;i++){
  116. float value = data.getFloat(i,0);
  117. float x = map(years[i],yearMin,yearMax,plotX1,plotX2);
  118. float y = map(value,floor(dataMin/volumnInterval)*volumnInterval,dataCeil,plotY2,plotY1);
  119. vertex(x,y);
  120. }
  121. endShape();
  122. }

  123. void keyPressed( ) {
  124. saveFrame(“japan_pl_##.png”);
  125. }

processing実行後に何かキーを押すと、連番で”japan_pl_01.png”という画像が保存されます。FloatTableというクラスは本のサンプルコードに含まれているものですが、http://benfry.com/writing/archives/3 からダウンロードできます。人口のCSVは統計局からダウンロードしたものをタブ区切りのcsvにしてます。

こんな感じで

  1. Year    Total
  2. 1920 55963
  3. 1921 56666
  4. 1922 57390
  5. 1923 58119
  6. 1924 58876
  7. 1925 59737
  8. 1926 60740.9
  9. 1927 61659.3
  10. 1928 62595.3
  11. 1929 63460.6
  12. 1930 64450
  13. 1931 65457
  14. 1932 66434
  15. 1933 67432
  16. 1934 68309
  17. 1935 69254
  18. 1936 70114
  19. 1937 70630
  20. 1938 71013
  21. 1939 71380
  22. 1940 71933
  23. 1941 71933
  24. 1942 71933
  25. 1943 71933
  26. 1944 73064
  27. 1945 71998
  28. 1946 73114
  29. 1947 78101
  30. 1948 80002
  31. 1949 81773
  32. 1950 83200
  33. 1951 84573
  34. 1952 85852
  35. 1953 87033
  36. 1954 88293
  37. 1955 89276
  38. 1956 90259
  39. 1957 91088
  40. 1958 92010
  41. 1959 92973
  42. 1960 93419
  43. 1961 94285
  44. 1962 95178
  45. 1963 96156
  46. 1964 97186
  47. 1965 98275
  48. 1966 99054
  49. 1967 100243
  50. 1968 101408
  51. 1969 102648
  52. 1970 103720
  53. 1971 105014
  54. 1972 107332
  55. 1973 108710
  56. 1974 110049
  57. 1975 111940
  58. 1976 110089
  59. 1977 114154
  60. 1978 115174
  61. 1979 116133
  62. 1980 117060
  63. 1981 117884
  64. 1982 118693
  65. 1983 119483
  66. 1984 120235
  67. 1985 121049
  68. 1986 121672
  69. 1987 122264
  70. 1988 122783
  71. 1989 123255
  72. 1990 123611
  73. 1991 124043
  74. 1992 124452
  75. 1993 124764
  76. 1994 125034
  77. 1995 125570
  78. 1996 125864
  79. 1997 126166
  80. 1998 126486
  81. 1999 126686
  82. 2000 126926

Processingを一言で説明しようとすると、Javaの描写機能を使用しやすいインタフェースで提供する言語環境(なのかな?)でしょうが、なんでProcessingに興味をもっていたかというと、これを使用した作品(っていうのがいいですね)に興味深いというか斬新なものが多かったからな訳です。

MusicBox: a truly powerful visualization of your music library:http://www.crunchgear.com/2008/12/15/musicbox-a-truly-powerful-visualization-of-your-music-library/
音楽のライブラリを可視化するソフト。ファイルのメタデータ(作者やジャンルなど)のみではなく、実際の音声データをスキャンしてマッピングする。

Aligning humans and mammals:http://benfry.com/infoseed/
人間とほ乳類の遺伝子配列をならべて描写した画像

Base26:http://toxi.co.uk/p5/base26/
英語の言葉同士の慣例性を表現した図。

仕事ではほとんどCRUDなWEBアプリケーションしか見ないのですが、こういうデータの重要性もさることながらどう見せるかでどれだけ印象や理解度が変わるかという事を考えるとおもしろい。色々遊んでみようと思います。

投稿者: t4ku

2月 3, 2009 3:44 am

Programming, Technology への投稿

タグ:

キーワードで検索した時のgoogleランキングを調べるrubyスクリプト

コメントなし

「SEO対策の有無をチェックするために、あるキーワードで検索して対象のWebサイトが何位くらいか調べる 」 という事をちょっと前仕事でやっていたのだが、数をこなさなければならないのと、対象のWebサイトが多かったのでバッチコマンドっぽく書いてみた

使い方

google_rank.rb  [-l | --limit 検索数 ] 検索文字列

例えば、”bit sized”という文字列で30位までのURLを取得しようとすると

google_rank.rb  -l 30 bit sized

とすればよい、結果は下記のようになる

google_rank.rb  -l 30 bit sized

1,bitsized.wordpress.com/
2,bitsized.wordpress.com/.../
3,en.wikipedia.org/wiki/Drill_bit_sizes
4,www.madison.k12.wi.us/toki/teched/codrills.htm
5,www.imao.us/archives/000960.html
6,www.theoriginalbitfit.com/
7,bitesizestandards.com/
8,www.bitesizebonus.com/
9,www.japaninc.com/article.php?articleID=521
10,www.crypto.rub.de/imperia/md/content/
11,home-and-garden.become.com/
12,www.springerlink.com/index/74HV57D1UH95PHHL.pdf
13,www.hardforum.com/showthread.php?t=967973
14,www.hydracore.com/drill_bit_sizes.htm
15,dic.yahoo.co.jp/dsearch?enc=UTF-8&p=bit&
16,atlaspen.com/search/?item=503438&pv=1
17,www.goodexperience.com/blog/archives/000633.php
18,www.wired.com/wired/archive/15.03/snackminifesto.html
19,search.luky.org/linux-kernel.2001/msg05005.html
20,www.modulaware.com/mdlt76.htm
21,www.patentstorm.us/patents/5501020-description.html
22,answers.yahoo.com/question/
23,www.hechinger.com/hardware/tools/
24,www.gfl-hand-tools.com/product-word/209/Bit-Sets.doc
25,bobmay.astronomy.net/misc/drillchart.htm
26,www.theoriginalbitfit.com/v4/go.gnf?s=bitfit&
27,thatgrrlca.blogspot.com/2007/
28,www.dri.co.jp/auto/report/wf/wfmts340mb07.htm
29,www.ttora.com/forum/showthread.php?t=29091
30,docs.sun.com/app/docs/doc/

準備

HTMLのパースにhpricotというライブラリを使用しているため、あらかじめ準備しておく必要がある。”gem  install  hpricot”でインストールできる

ソースコード

google_rank.rb

require 'rubygems'
require 'optparse'
require 'hpricot'
require 'open-uri'
require 'cgi'   
# class definition
class GoogleRank
    def initialize
      @str_query = ""
      @base_url = "http://www.google.co.jp/search?hl=ja&q="
      @suffix_url = "&lr="
      @srch_url = ""
      @srch_limit = 20
      @current_number = 0
    end

    def encode_query
      @str_query = CGI.escape(@str_query)
    end

    def cat_query
      if @current_number == 0
         @srch_url = @base_url + @str_query + @suffix_url
      else
         @srch_url = @base_url + @str_query + @suffix_url + "&start=" + @current_number.to_s + "&sa=N"
      end
    end

   def get_rank
      doc = Hpricot(open(@srch_url))
      # get the <div> that contains search result
      res = doc.search("//div[@id='res']")
      # <span class="a">hoge.com/target.html</a>
      spans = res.search("//span[@class='a']")

      spans.each_with_index{ |span,i|
         page_info =  CGI.unescape(span.inner_html)
         #puts "page_info:" + page_info
         page_url = page_info.split(/ /)

         # trims html tags
         item_url = page_url[0].gsub(/<[\/]*[a-z]+[\/]*>/,"")
         puts (@current_number + i + 1).to_s + ","+ item_url
      }
    end
    def search_rank
      self.encode_query
      while  @current_number <  @srch_limit
        self.cat_query
        self.get_rank
        @current_number += 10
        #puts "current_number=" + @current_number.to_s
      end
    end
    attr_accessor :str_query
    attr_accessor :srch_limit
end

# bat command process

rank = GoogleRank.new
opt = OptionParser.new

opt.on('-l VAL','--limit VAL'){ |v| rank.srch_limit = v.to_i }

opt.parse!(ARGV)

ARGV.each {|i|

  if rank.str_query == ""
    rank.str_query = i.to_s
  else
    rank.str_query = rank.str_query + " " + i.to_s
  end
}

rank.search_rank

投稿者: t4ku

2月 3, 2008 3:17 pm

Podcast × 速聴 – Podcastを速聴してみる(新刊JP)

コメントなし

Podcast に登録している番組の数が増えてきたというのと、以前ちょっと興味を引かれた速聴なるものを試したいと思い、「じゃあ、Podcastを速聴してみよう」と企ててみました。

速聴については、簡単に言えば

「普通に話しているスピードの3-4倍以上で話を聞くと、普段の言語処理を行う際には使用されない脳の部分が活性化され、記憶力などの脳力向上に役立つ」

というものです。

どうやって、Podcastを3-4倍速で聞こうか?という部分ですが

  1. Podcast再生時にipodの再生速度を変更する機能を使用する
  2. Podcastに登録される前に、mp3の音声データを編集しておく(3-4倍速に変換しておく)

という2種類の方法が考えられると思いますが、1の方法では

  • せいぜい20%くらいの速度変換しかできない
  • オーディオブックしか再生速度を変えられない

という事で、自宅にある余っているLinux+ Apacheを使用して自分用のフィードに変換(mp3 + rssを編集)して、それをPodcastに登録する事にしました(下図)

最近個人的によく聞く、新刊JPで配信している新刊のダイジェスト(火木土更新)での例。

  1. rssファイルを新刊JPのサイトから取得
  2. 1のファイルをparseして、各エントリのmp3ファイルをダウンロードする。
  3. ダウンロードしたmp3を3-4倍速に変換。
  4. 変換済みのmp3ファイルのパスを、1のrssファイルでの各エントリの ファイルとして書き換える
  5. 変換済みのmp3ファイル+編集済みのrssファイルをapacheのディレクトリに配置する。
  6. iTuneから、5のurlを指定してpodcastを登録する

RapidPodcast2

Linuxマシンの方で必要なものは下記ですが、sox以外はデフォルトで使用できるのではないでしょうか?

  • ruby
    • 本体
    • ライブラリ
      • rexml
      • open-uri
  • Linuxコマンド
    • wget
    • sox (mp3編集ツール)

処理としては、上記のツールをつなぎ合わせているだけのようなものなので、
シェルでもできとおもいますが、勉強も兼ねてRubyで書いてみました。

あとは、更新日にあわせてCronに登録しておけばよいでしょう

#!/usr/bin/ruby

require 'open-uri'
require 'rexml/document'

# global variables

$feed_org = "http://www.sinkan.jp/rss.xml"       #新刊JPダイジェストのRSS

$data_dir = "/var/www/xxx/xxx/"                  #Linuxのhttpdディレクトリ

$data_dir_url = "htttp://hostname/xxx/xxx/"      #↑のurl

$username = "username"                           #新刊JPのユーザー名(メールアドレス)

$password = "password"                           #パスワード


class Feed
  def initialize(feed_path)
    @feed_path = feed_path
    @rss_file_name = @feed_path.split(/\//)[-1]

    @feed_local = $data_dir + @rss_file_name
    doc = REXML::Document.new(open(@feed_path).read)
    @rss_root = doc.root
    @feed_items = Array.new
    @rss_root.elements.to_a("/rss/channel/item").each { |i|
    @feed_items.push( FeedItem.new(i))
  }
  end

  def comp_feed_entry(org_idx,stretch_rate)
    @feed_items[org_idx].stretch_rate = stretch_rate
    @feed_items[org_idx].download_mp3($username,$password)
    @feed_items[org_idx].time_stretch
  end

  def comp_all(stretch_rate)
    for i in 0..@feed_items.size - 1
      comp_feed_entry(i,stretch_rate)
    end
    @rss_root.elements["/rss/channel/title"].text = "[" + stretch_rate.to_s + "

]" +  @rss_root.elements["/rss/channel/title"].text + "_"
  end

  def save_feed
    local_doc = File.open(@feed_local,"w")
    @rss_root.write  local_doc
  end

end

class FeedItem
  def initialize(rss_node,stretch_rate = "")
    @rss_node = rss_node
    @stretch_rate = stretch_rate
    # rss node mapping

    @title = @rss_node.elements['title']
    @mp3_path = @rss_node.elements['enclosure'].attributes['url']
    @mp3_name = @mp3_path.split(/\//)[-1]
  end

  def time_stretch
    puts @stretch_rate
    stretched_mp3_name = @mp3_name.sub(/(.+?)\.(.+)/) {|m|  $1 + "_" + @stretch_
rate.to_s + "." + $2 }
    stretched_mp3_path = $data_dir + stretched_mp3_name
    command_line ="sox " + @mp3_path + " " +stretched_mp3_path +  " stretch " + @stretch_rate.to_s
    puts command_line
    system(command_line)
    @title.text =  "[" + @stretch_rate.to_s + "]" +  @title.text
    @rss_node.elements['enclosure'].attributes['url'] = $data_dir_url + stretche
d_mp3_name
  end

  def download_mp3(un = "",pw = "")
    if File.exist?($data_dir + @mp3_name) then
      File.unlink($data_dir + @mp3_name)
    end
    if un != "" || pw != "" then
      command_line = "wget -P " + $data_dir + " --user=" + un + " --password="
+ pw + " " +  @mp3_path
    else
      command_line = "wget -P " + $data_dir + @mp3_path
    end
    system(command_line)
    @mp3_path = $data_dir + @mp3_name
  end

  attr_accessor :rss_node
  attr_accessor :title
  attr_accessor :stretch_rate
end

fd = Feed.new($feed_org)
fd.comp_all(0.3)
fd.save_feed

投稿者: t4ku

9月 28, 2007 12:02 am

Programming, Uncategorized への投稿

タグ: , ,