## # nicovideo_download.rb ## ## ニコニコ動画の動画ファイルとコメントをダウンロードするRubyスクリプト for Operaとか ## http://bmky.net/diary/log/1576.html ## ## ## 参考 ## ## * http://d.hatena.ne.jp/emergent/20070820/1187539958 ## * http://blog.livedoor.jp/dankogai/archives/50885358.html ## ## ## 使い方 ## ## Operaで使う際は、profile/menu/内の使用している設定ファイルの[Document Popup Menu]部分に以下を追加 ## ## [Document Popup Menu] ## ... ## Item, "全てのコメントをダウンロード" = Execute program, "C:\nicovideo_download.exe", "%u C:\download -c -a premier.yaml" ## Item, "動画とコメントをダウンロード" = Execute program, "C:\nicovideo_download.exe", "%u C:\download -v -c" ## Item, "動画をダウンロード" = Execute program, "C:\nicovideo_download.exe", "%u C:\download -v" ## Item, "コメントをダウンロード" = Execute program, "C:\nicovideo_download.exe", "%u C:\download -c" ## ## さらに認証用の情報を記入したnico.yamlを ## nicovideo_download.exeと同じディレクトリに置く。 ## nico.yamlは以下のように書く。保存時の文字コードはUTF-8で。 ## ## --- ## mail: hogehoge@gmail.com ## password: foobar ## ## ## コマンドラインオプションについて ## ## nicovideo_download.rb [URL or ID] [save dir] (-v -c -a hoge.yaml) ## ## [URL or ID] ## : 必須。動画のURLもしくはwatch/以降を渡す。マイメモリー対応。 ## [save dir] ## : 必須。ダウンロードしたファイルを保存するディレクトリ。 ## -v ## : 動画をダウンロードするなら -v をつける。 ## -c ## : コメントをダウンロードするなら -c をつける。 ## -a ## : 過去全てのコメントをダウンロードする。要プレミアムアカウント。 ## [半角英数字].yaml ## : 指定したyamlをログイン情報として利用する。アカウントの使い分けをしたい時に。 ## ## ## 更新履歴 ## ## 2007/12/25 ## : 動画がエコノミーだった場合、ファイル名に_lowを追加するようにした ## : プレミアムアカウント時のみ、-aオプションにて全コメントのダウンロードを可能にした ## : xmlを要素毎に改行を追加するようにした ## : 認証用ファイルを指定できるようにした ## ## 2007/12/9 ## : リリース #!/usr/bin/env ruby -Ku require 'yaml' require 'rubygems' require 'mechanize' require 'cgi' require 'kconv' URL_LOGIN = "https://secure.nicovideo.jp/secure/login?site=niconico" URL_WATCH = "http://www.nicovideo.jp/watch/" URL_API = "http://www.nicovideo.jp/api/" class String def safe_filename gsub!( /\s/, "_" ) gsub!( /\//, "/" ) gsub!( /\:/, ":" ) gsub!( /\,/, "," ) gsub!( /\;/, ";" ) gsub!( /\*/, "*" ) gsub!( /\?/, "?" ) gsub!( /\/, ">" ) gsub!( /\|/, "|" ) return self end end class NicoServer def initialize( video_id ) @agent = WWW::Mechanize.new @video_id = video_id end def login( login_file ) login_file = "nico.yaml" if login_file == nil @agent.post( URL_LOGIN, YAML.load_file( login_file ) ) html = @agent.get_file( URL_WATCH + @video_id ) content = @agent.get_file( URL_API + "getflv?v=" + @video_id ) params = content.scan( /([^&]+)=([^&]*)/ ).inject( {} ){ |h, v| h[v[0]] = v[1]; h } @video_url = CGI.unescape( params['url'] ) @is_economy = params["url"].index( "low" ) != nil @is_premium = params["is_premium"] == "1" @video_title = html.scan( /.*?‐(.*?)<\/title>/ )[0][0].safe_filename( ) @save_file = @video_title.tosjis + " [" + @video_id + "]" @thread_id = params["thread_id"] @user_id = params["user_id"] if CGI.unescape( params['ms'] ) =~ /http:\/\/(.*?)(\/\d+\/api\/)/ @comment_host = $1 @comment_server_path = $2 end end # -v 動画をダウンロードする def get_video( save_dir ) p "downloading url : " + @video_id File.open( save_dir.sub( /\\?$/, "\\" ) + @save_file + ( @is_economy ? "_low" : "" ) + ".flv", "wb" ) do |f| f.write @agent.get_file( @video_url ) end end # -c コメントをダウンロードする def get_comment( save_dir, all = false ) p "downloading comment : " + @video_id xml = get_xml( ) access_times = ( xml.last_res / 1000 ).floor get_from = xml.get_from xml_header = xml.header xml.gsub!( /<(?!\/?chat).*?>/, "" ) xml.gsub!( /<\/chat>/, "</chat>\n" ) comments = xml # 過去全てのコメントをダウンロード if all and @is_premium while access_times > 0 xml = get_xml( get_from, get_waybackkey( ) ) get_from = xml.get_from xml.gsub!( /<(?!\/?chat).*?>/, "" ) xml.gsub!( /<\/chat>/, "</chat>\n" ) xml.gsub!( /<chat.*?<\/chat>\n$/, "" ) # 重複削除 comments = xml + comments access_times -= 1 end end xml_header.gsub!( />/, ">\n" ) comments = xml_header + comments + "</packet>" File.open( save_dir.sub( /\\?$/, "\\" ) + @save_file + ".xml", "w" ) do |f| f.write comments end end def get_xml( get_from = nil, waybackkey = nil ) waybackkey = " waybackkey='#{waybackkey}'" if waybackkey get_from = " when='#{get_from}'" if get_from sleep 1 xml = Net::HTTP.start( @comment_host, 80 ) do |http| body = "<thread#{get_from}#{waybackkey} user_id='#{@user_id}' res_from='-1000' version='20061206' thread='#{@thread_id}' />" p body response = http.post( @comment_server_path, body ) response.body end xml.instance_eval do @last_res = xml.scan( /last_res="(\d+)"/ )[0][0].to_i def last_res @last_res end end xml.instance_eval do @get_from = xml.scan( /date="(\d+)"/ )[0][0].to_i def get_from @get_from end end xml.instance_eval do @header = xml.scan( /<\?xml.*?<view_counter.*?video="\d+"\/>/ ).first def header @header end end return xml end def get_waybackkey( ) waybackkey = @agent.get_file( URL_API + "getwaybackkey?thread=" + @thread_id ) waybackkey.sub!( /waybackkey=/, "" ) return waybackkey end end video_id = ARGV[0].scan( /(?:sm)?\d+$/ ).first save_dir = ARGV[1] option = ARGV[2..8].join( " " ) get_video = option.index( "-v" ) != nil get_comment = option.index( "-c" ) != nil comment_all = option.index( "-a" ) != nil login_file = option.scan( /[^\s]*?\.ya?ml/ ).first nico = NicoServer.new( video_id ) nico.login( login_file ) nico.get_video( save_dir ) if get_video nico.get_comment( save_dir, comment_all ) if get_comment