FLYING

/* TODO: 気の利いた説明を書く */

ニコニコ動画のマイリスト動画を一括DL

先日のスクリプトにマイリストのデータを読み込む機能を追加してみた。

うさげ

使い方は nico.rb とほぼ同様です。ただし、引数には動画のIDではなく、マイリストのIDを指定してください。仕様上、短時間に大量のアクセスを行うので、もしかしたらダウンロード中にアクセス規制を食らうかもしれません。ダウンロードはできるだけ人の少ない時間帯に行いましょう。

ruby mylist.rb XXXXXXX

ソース

# mylist.rb

require 'cgi'
require 'uri'
require 'kconv'
require 'net/https'

class Nico
  attr_accessor :video, :video_id, :mylist, :mylist_id, :mylist_array
  
  # ニコニコ動画にログイン
  def initialize(mail, password)
    https = Net::HTTP.new("secure.nicovideo.jp", 443)
    https.use_ssl = true
    https.start {|w|
      data = "site=niconico&next_url=&mail=#{mail}&password=#{password}"
      res = w.post("/secure/login", data)
      @sid = $1 if res["Set-Cookie"] =~ /user_session=user_session([0-9_]+)/
    }
  end
  
  # 動画の情報を取得
  def get_video(video_id)
    @video_id = video_id
    Net::HTTP.start("www.nicovideo.jp", 80) {|w|
      res = w.get("/api/getflv/#{@video_id}", "Cookie" => "user_session=user_session#{@sid}")
      array = CGI.unescape(res.body).split(/&/)
      array.map! {|e| e.split(/=/, 2) }
      @hash = Hash[*array.flatten]
    }
    # ダウンロードに必要なCookieを取得
    Net::HTTP.start("www.nicovideo.jp", 80) {|w|
      res = w.get("/watch/#{@video_id}", "Cookie" => "user_session=user_session#{@sid}")
      @history = $1 if res["Set-Cookie"] =~ /nicohistory=([^;]+)/
      @video = $1 if res.body =~ /<h1.*><a.*>(.*)<\/a><\/h1>/
    }
  end
  
  # マイリストの情報を取得
  def get_mylist(mylist_id)
    @mylist_array = []
    @mylist_id = mylist_id
    res = nil
    Net::HTTP.start("www.nicovideo.jp", 80) {|w|
      res = w.get("/mylist/#{@mylist_id}",
        "Cookie" => "user_session=user_session#{@sid}; nicohistory=#{@history}")
    }
    res.body.each {|line|
      @mylist = $1 if line =~ /<h1.*>(.*)<\/h1>/
      if line =~ /<h3.*><a.* href="watch\/(.*)">(.*)<\/a><\/h3>/ then
        @mylist_array.push($1)
      end
    }
  end
  
  # 動画をダウンロード
  def get_flv
    uri = URI.parse(@hash["url"])
    res = nil
    Net::HTTP.start(uri.host, uri.port) {|w|
      res = w.get("#{uri.path}?#{uri.query}",
        "Cookie" => "user_session=user_session#{@sid}; nicohistory=#{@history}")
    }
    return res.body
  end
  
  # コメントをダウンロード
  def get_xml(comments)
    uri = URI.parse(@hash["ms"])
    res = nil
    Net::HTTP.start(uri.host, uri.port) {|w|
      data = "<thread user_id=\"#{@hash["user_id"]}\" res_from=\"#{comments}\""
      data += " version=\"20061206\" thread=\"#{@hash["thread_id"]}\" />"
      res = w.post(uri.path, data,
        "Cookie" => "user_session=user_session#{@sid}; nicohistory=#{@history}")
    }
    return res.body
  end
end

# 準備
nico = Nico.new("YOUR_MAIL", "YOUR_PASSWORD")
nico.get_mylist(ARGV[0])
print "mylist: #{nico.mylist.tosjis}\n"
# 各動画を処理
nico.mylist_array.each {|video_id|
  nico.get_video(video_id)
  title = nico.video.tosjis.gsub(/[\\:*?"<>|\/]/, "_")
  print "saving #{title} ... "
  open("#{title}.flv", "wb") {|f| f.print nico.get_flv }
  open("#{title}.xml", "w") {|f| f.print nico.get_xml(-500) }
  print "done.\n"
}

追記 2008-04-06

動画個別ページの仕様変更に対応。