いものやま。

雑多な知識の寄せ集め

ニコニコ動画のダウンロードツールをRubyで書いてみた。(その5)

昨日までの内容は、以下から。

さて、HTTPの動画はダウンロード出来たので、次はRTMPの動画のダウンロード。

gem探し

RTMPプロトコルを読み込んで実装するのはさすがに難しいので、RTMPを扱えるgemを検索。
しかし・・・見つからない!

厳密に言えば、それっぽいgemはいくつか見つかるのだけど、RTMPへの不理解もあって、どのgemを使えばいいのかがよく分からない。
これは困った・・・

rtmpdump

仕方ないので、ちょっと切り口を変えて、RTMPで配信されている動画をダウンロードする方法やツールがないか調べてみたところ、次の情報を発見。

ニコニコ公式アニメポータルのrtmpeプロトコル動画をダウンロードする覚え書き · GitHub

ふむふむ。
rtmpdumpとな。

とりあえずこれを使ってみよう。

インストール

まずはrtmpdumpのインストール。

RTMPDump

自分の場合、Macで開発環境(Xcode)も入ってるので、ソースをダウンロード。
Unix系のOSなら同じ方法で大丈夫なはず。
Windowsの場合は知らない・・・)

$ git clone git://git.ffmpeg.org/rtmpdump
$ cd rtmpdump/

そのあと、ビルドとインストール。

詳細はREADME参照だけど、SYS=platform_nameとしてmakeを行う。
platform_nameは、Macなら"darwin"、Unix系ならposixWindowsMinGWを使っているならmingwとのこと。

自分の場合は、

$ make SYS=darwin
$ sudo make install SYS=darwin

で、ビルドとインストールが出来た。

プログラムの修正

次に、先ほどの情報を元に、プログラムを修正していく。

動画の実際のURLの他に、いくつか情報が必要なようなので、これらをvideo_infoというHashに保存するようにする。

-video_uri = nil
+video_info = nil
 begin
   info = CGI.parse(response.body)
   video_url = info['url'].first
   video_uri = URI.parse(video_url)
+
+  if video_uri.scheme == "http"
+    video_info = {"uri" => video_uri}
+  else
+    fmst2, fmst1 = info['fmst'].first.split(':')
+    video_info = {"uri" => video_uri,
+      "original_uri" => URI::HTTP.build(host: VideoHost, path: video_path),
+      "fmst1" => fmst1, "fmst2" => fmst2}
+  end
 rescue
   raise "Failed to access video information."
 end

これに合わせて、何箇所か修正。

 # set output filename
 
 video_extension = ".flv"
-if match_data = VideoTypeRegexp.match(video_uri.to_s)
+if match_data = VideoTypeRegexp.match(video_info["uri"].to_s)
   if match_data[1] == "s"
     video_extension = ".swf"
-http = Net::HTTP.new(video_uri.host)
-http.request_get(video_uri.request_uri,
-                 {'Cookie' => "user_session=#{user_session}; nicohistory=#{nicohistory};"}) do |response|
-  File.open(output_filename, "wb") do |file|
-    response.read_body do |video_block|
-      file.write(video_block)
+if video_info["uri"].scheme == "http"
+  http = Net::HTTP.new(video_uri.host)
+  http.request_get(video_info["uri"].request_uri,
+                   {'Cookie' => "user_session=#{user_session}; nicohistory=#{nicohistory};"}) do |response|
+    File.open(output_filename, "wb") do |file|
+      response.read_body do |video_block|
+        file.write(video_block)
+      end
     end
   end
+else
+  # ここのRTMPで配信されている動画の
+  # ダウンロードのコード
+  # ...
+end

そして、肝心のRTMPで配信されている動画のダウンロードは、以下のような感じ。

  original_uri = video_info["original_uri"]
  uri = video_info["uri"]
  uri_without_query = URI::Generic.build(scheme: uri.scheme, host: uri.host, path: uri.path)
  playpath = uri.query.split('=')[1]
  fmst1 = video_info["fmst1"]
  fmst2 = video_info["fmst2"]

  done = false
  until done
    done = system <<END_OF_COMMAND
rtmpdump \\
-e \\
-l 2 \\
-a smile \\
-n #{uri.host} \\
-t #{uri_without_query.to_s} \\
-p #{original_uri.to_s} \\
-s http://res.nimg.jp/swf/player/secure_nccreator.swf?t=201111091500 \\
-f "WIN 11,6,602,180" \\
-y #{playpath} \\
-C S:#{fmst1} \\
-C S:#{fmst2} \\
-C S:#{playpath} \\
-o #{output_filename}
END_OF_COMMAND
  end

なお、ループで回しているのは、なぜか1回のダウンロードでダウンロードしきれないから。
-eオプションをつけることで、ダウンロードが中断した箇所からダウンロードが再開されるようになっている。

これで、なんかエラーやらワーニングとかは出ているものの、ダウンロード自体は問題なく出来るようになった。

不要なオプションの削除など

ただ、-sとか-fとかに渡してる値は、参照したページのもので、どうにも怪しげ。
ということで、削除出来るなら削除したいし、もし必要だとしたら正しい値を設定したい。

そんな感じでコマンドラインオプションを修正したあとのコードは、以下。

これでHTTPで配信されている動画もRTMPで配信されている動画もダウンロード出来るようになった。

今日はここまで!