[firefox][備忘録] ニコニコ動画のflvファイルを保存するfirefoxエクステンションを作成

javascriptの勉強がてらfirefoxのエクステンションを初めて作ってみた。車輪の再(再々…)発明なのはみのがして。いろいろハマったので誰かの何かの足しになるかも、というわけで作り方とかを掲載してみることにする。
ちなみに当方の環境はDebian(lenny) + FireFox(Iceweasel) 2.0.0.6


firefoxのエクステンションに関して参考にしたのは以下のサイト
Firefoxエクステンションの書き方
Firefox拡張機能(extension)の作り方


ニコニコ動画からのflvファイル取得については以下のサイトを参考にした。
ニコニコ動画のFLVをダウンロードするGreasemonkey


仕様はこんな感じで。

  • ステータスバー(ウィンドウの右下にあるやつ)にそれっぽいアイコンを表示
  • 動画の再生ページでアイコンをクリックすると動画のflvファイルを取得

パッケージ名は安易に「nicoget」としてみた。

作ったファイルはこれだけ。

~/work/nicoget/chrome.manifest
              /install.rdf
              /chrome/content/nicoget/nicogetOverlay.xul
              /chrome/content/nicoget/nicoget.js
              /chrome/content/nicoget/nicoget.css
              /chrome/content/nicoget/nico.ico
              /chrome/content/nicoget/contents.rdf

各ファイルの説明

chrome.manifest

パッケージをインストールするときにはインストーラが勝手に作ってくれるので、xpiを作る際には必要ないけど開発用firefoxで実行するときには必要。
以下開発用chrome.manifes

content     nicoget    chrome/content/nicoget/
overlay chrome://browser/content/browser.xul chrome://nicoget/content/nicogetOverlay.xul

フォーマットや役割について詳しくは以下のサイトにある。
http://developer.mozilla.org/ja/docs/chrome.manifest

content行はURI(chrome://...)とパッケージの実際のパスとの関連付けをするものだけど、パスの最後が"/"で終らないとダメということを知らずにそうとうハマった。

install.rdf

ここにあるやつをほぼ丸写し。
異なるのはの部分が追加されてるところくらいだけど、この行がインストールするファイルを決定してるっぽいので結構重要。
の部分はLinuxならuuidコマンドがあるのでそいつを使う。

<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">

  <Description about="urn:mozilla:install-manifest">
    <em:id>{a58fa49f-acb8-43f8-b3b5-e69f552f6a7d}</em:id>   <!-- 拡張機能ごとにUUIDを生成(*) -->
    <em:version>0.1</em:version>  <!-- 拡張機能のバージョン -->
    <em:type>2</em:type>  <!-- 2:拡張機能、4:テーマ、8:ロケール、16:プラグイン、32:... -->
    
    <em:file>
      <Description about="urn:mozilla:extension:file:nicoget.jar">
        <em:package>content/nicoget/</em:package>
      </Description>
    </em:file>

    <!-- Target Application this extension can install into, 
         with minimum and maximum supported versions. --> 
    <em:targetApplication>
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>  <!-- Firefox固有のUUID -->
        <em:minVersion>1.5</em:minVersion>
        <em:maxVersion>2.0.0.*</em:maxVersion>
      </Description>
    </em:targetApplication>
   
    <!-- Front End MetaData -->    <!-- 拡張機能の説明(人間用) -->
    <em:name>nicoget</em:name>
    <em:description>get smilevideo extension</em:description>
    <em:creator>test@test</em:creator>
    <!--em:homepageURL>http://localhost/</em:homepageURL-->
  </Description>      
</RDF>
nicogetOverlay.xul

こいつがbrowser.xulを上書き(Overlay)してステータスバーにアイコンを表示させる。

<?xml version="1.0"?>
<?xml-stylesheet href="nicoget.css" type="text/css"?>
<overlay id="sample" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript" src="nicoget.js"/>
  <statusbar id="status-bar">
    <statusbarpanel class="statusbarpanel-iconic" id="nico-panel" label="nicoget" onclick="getflv()"/>
  </statusbar>
</overlay>

見ての通りステータスバーをクリックするとgetflv()が呼ばれる。

nicoget.js

DL処理の本体。エコノミーならファイル名にlowが付くようにしてみた。

function getflv(){
    try{
	getnicoflv()
    }catch(e){
	alert(e);
    }
}

function getnicoflv(){
    var href = window.content.location.href;

    // 動画IDの取得
    try{
	var vid = /http:\/\/www\.nicovideo\.jp\/watch\/(.*)$/.exec(href)[1];
    }catch(e){
	throw Error("not smile video URL.");
    }
    
    var apiurl = "http://www.nicovideo.jp/api/getflv?v="+vid;
    var req = new XMLHttpRequest();
    req.open("GET", apiurl, false);
    req.send(null);

    if(req.status != 200){
	throw Error(req.responseText);
    }

    try{
	var tid=unescape(/thread_id\=(.*?)\&l/.exec(req.responseText)[1]);
	var url=unescape(/\&url\=(.*?)\&/.exec(req.responseText)[1]);
    }catch(e){
	throw Error("get flv failed: " + vid);
    }

    // エコノミーモードの判定
    var bEconomy = /low$/.test(url);

    // ファイル選択ダイヤログの作成
    const nsIFilePicker = Components.interfaces.nsIFilePicker;
    const FP = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);

    FP.init(window, "get flv file", Components.interfaces.nsIFilePicker.modeSave);
    var file_name = vid + (bEconomy? "_low" : "") + ".flv";
    FP.defaultString = file_name;
    
    // ダイヤログ表示
    var result = FP.show();
    if( result == Components.interfaces.nsIFilePicker.returnCancel)
    {
	return;
    }

    var sourceUri = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService).newURI(url, null, null);

    // ダウンロードマネージャ
    var dm = Components.classes["@mozilla.org/download-manager;1"].getService(Components.interfaces.nsIDownloadManager);
    var PERSIST = Components.classes['@mozilla.org/embedding/browser/nsWebBrowserPersist;1'].createInstance(Components.interfaces.nsIWebBrowserPersist);

    // キャッシュを利用するためのコード
    var postData = null;
    try {
	var SH = getWebNavigation().sessionHistory;
	var entry = SH.getEntryAtIndex(SH.index, false).QueryInterface(Components.interfaces.nsISHEntry);
	postData = entry.postData;
    } catch (e) {}

    // ダウンロードマネージャに情報を登録
    var download = dm.addDownload(0, sourceUri, FP.fileURL, file_name, null, null, null, null, PERSIST);

    // ダウンロード進捗のリスナを設定
    PERSIST.progressListener = download;

    // ファイル取得開始
    PERSIST.saveURI(sourceUri, postData, null, null, null, FP.file);

    return;
}
nico.ico

ステータスバーに表示するアイコンにはニコニコ動画サイトのfaviconを使用することにした。再配布する予定はないから問題ないよね。もちろん別の画像でも可。

今回は以下のコマンドで取得した。

$ wget http://www.nicovideo.jp/img/favicon.ico

favicon.iconico.icoにリネームしてやればOK

nicoget.css

ここではstatusbarpanelとnico.icoを関連付けるために必要

statusbarpanel#nico-panel {
  list-style-image: url("nico.ico");
}
contents.rdf

オーバーレイ情報を記述するものらしい。パッケージインストール時にinstall.rdfやこいつをみてchrome.manifestを作っているのだろうか?

<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
<RDF:Seq RDF:about="urn:mozilla:package:root">
  <RDF:li RDF:resource="urn:mozilla:package:nicoget"/>
</RDF:Seq>

<RDF:Seq RDF:about="urn:mozilla:overlays">
  <RDF:li RDF:resource="chrome://browser/content/browser.xul"/>
</RDF:Seq>

<RDF:Seq RDF:about="chrome://browser/content/browser.xul">
  <RDF:li>chrome://nicoget/content/nicogetOverlay.xul</RDF:li>
</RDF:Seq>

<RDF:Description RDF:about="urn:mozilla:package:nicoget"
  chrome:displayName="nicoget"
  chrome:author="papamitra"
  chrome:authorURL="papamitra@hoge"
  chrome:name="nicoget"
  chrome:extension="true"
  chrome:description="get smile video flv file">
</RDF:Description>
</RDF:RDF>

xpiファイルを作ってインストール

あとはここの「簡単にリビルドするには」にあるスクリプトを使えば上の一連のファイルを固めたxpiファイルが作成されるのでそいつをインストールすればOK