require "open-uri"
require "parsedate"
require "date"
require "rexml/document"
require "rubygems"
require "hpricot"
require "mechanize"
# POST rawdata
# based on http://d.hatena.ne.jp/emergent/20070909/1189316316
module WWW
class Mechanize
self.class_eval {
def post_data(url, data='')
cur_page = current_page || Page.new( nil, {'content-type'=>'text/html'})
request_data = data
abs_url = to_absolute_uri(url, cur_page)
request = fetch_request(abs_url, :post)
request.add_field('Content-Length', request_data.size.to_s)
page = fetch_page(abs_url, request, cur_page, [request_data])
add_to_history(page)
page
end
}
end
end
module Nicokit
BASE_URI="http://www.nicovideo.jp/"
API_URI=BASE_URI+"api/"
@ua=WWW::Mechanize.new
@login=false
def self.login mail,pw
url="https://secure.nicovideo.jp/secure/"
f=@ua.get(url).form("login")
f.mail=mail
f.password=pw
@ua.submit(f)
@login=true
end
def self.get url,&block
if !@login
raise "まだログインしてません"
end
block ||= Proc.new {}
block.call(@ua.get(url))
end
def self.post_data url,data,&block
if !@login
raise "まだログインしてません"
end
block ||= Proc.new {}
block.call(@ua.post_data(url,data))
end
class My
def initialize
@_fetched={}
@id=nil
@list=nil
"list".split(/\s+/).each{|var|
self.instance_variable_set("@#{var}",nil)
instance_eval <<-GETTER
def #{var}
_get(:#{var},'list')
end
GETTER
}
end
def _get key,meth
key2=key.to_s.gsub(/^@?/,"@")
result=self.instance_variable_get(key2)
if !result && !@_fetched[meth]
send("_fetch_#{meth}")
@_fetched[meth]=true
end
self.instance_variable_get(key2)
end
def _fetch_list
mylist_url=Nicokit::BASE_URI+"mylistgroup_edit"
list=[]
Nicokit.get(mylist_url){|res|
Hpricot(res.body).search('table#mylistgroups tr[@id $= "row"]'){|tr|
next if tr.attributes["id"].match(/___row$/)
#hash={}
#hash[:thumbnail]=tr.at('td // img[@class="thumb_img_S"]')[:src]
#link=tr.at('td / h3 // a[1]')
#hash[:link]=URI.join(mylist_url,link[:href]).to_s
#hash[:name]=link.inner_html
#hash[:count]=tr.at('td / h3 /strong[1]').inner_html.match(/[0-9]+/)[0]
#hash[:is_public]=(tr.at('td / h3 / a:last')[:class] == "public")
#list << hash
#link=
link=tr.at('td / h3 // a[1]')[:href]
list << Nicokit::VideoList.new(link.match(/[0-9]*$/)[0])
}
info=Hpricot(res.body).at('div.mb16p4')
@title=info.at('h1').inner_html
@description=info.at('p[@class="TXT12][1]')
}
@list=list
end
end
class VideoList
def initialize id
@id=id
@url=Nicokit::BASE_URI+"mylist/#{@id}"
@vodeos=nil
@title=nil
@description=nil
"videos title description".split(/\s+/).each{|var|
self.instance_variable_set("@#{var}",nil)
instance_eval <<-GETTER
def #{var}
_get :#{var}
end
GETTER
}
end
def _get key
key=key.to_s.gsub(/^@?/,"@")
result=self.instance_variable_get(key)
if !result
_fetch
end
self.instance_variable_get(key)
end
def _fetch
list=[]
Nicokit.get(@url){|res|
Hpricot(res.body).search('table#mylists > tr'){|tr|
next if tr.attributes["id"].match(/_/)
# fix strange href
url=URI.join(@url,"/"+tr.at('//h3/a')[:href]).to_s
list << Nicokit::Video.new(url)
}
info=Hpricot(res.body).at('div.mb16p4')
@title=info.at('h1').inner_html
@description=info.at('p[@class="TXT12][2]').inner_html
}
@videos=list
end
end
class Video
attr_reader :id,:url
class FLV
def initialize watch_url,flv
@watch_url=watch_url
@flv=flv
end
def to_s
@flv
end
def save filename
Nicokit.get(@watch_url)
Nicokit.get(@flv){|r|
File.open(filename,"w"){|fp|
fp.puts(r.body)
}
}
end
end
def initialize id
@_fetched={
"info" => false,
"msg" => false,
"comments" => false,
"recommend" => false,
}
if id.match(/^http:/)
@url=id
@id=@url.match(/[a-z]*[0-9]+$/)[0]
else
@id=id.gsub(/^(sm)?/,"sm")
@url=Nicokit::BASE_URI+"watch/#{@id}"
end
"flv msg_server myid thread".split(/\s+/).each{|var|
self.instance_variable_set("@#{var}",nil)
instance_eval <<-GETTER
def #{var}
get(:#{var},'msg')
end
GETTER
}
"title posted mylist comment thumbnail last_comments description tags view".split(/\s+/).each{|var|
self.instance_variable_set("@#{var}",nil)
instance_eval <<-GETTER
def #{var}
get(:#{var},'info')
end
GETTER
}
"comments".split(/\s+/).each{|var|
self.instance_variable_set("@#{var}",nil)
instance_eval <<-GETTER
def #{var}
get(:#{var},'comments')
end
GETTER
}
"recommend".split(/\s+/).each{|var|
self.instance_variable_set("@#{var}",nil)
instance_eval <<-GETTER
def #{var}
get(:#{var},'recommend')
end
GETTER
}
end
def get key,meth
key2=key.to_s.gsub(/^@?/,"@")
result=self.instance_variable_get(key2)
if !result && !@_fetched[meth]
send("_fetch_#{meth}")
@_fetched[meth]=true
end
self.instance_variable_get(key2)
end
def _fetch_msg
api=Nicokit::API_URI+"getflv/#{@id}"
msg={}
Nicokit.get(api){|r|
msg=r.body.split("&").inject({}){|q,param|
tmp=param.split("=")
q[tmp[0].to_sym]=URI.unescape(tmp[1])
q
}
@flv=Nicokit::Video::FLV.new(self.url,msg[:url])
@msg_server=msg[:ms]
@thread=msg[:thread_id]
@myid=msg[:user_id]
}
end
def _fetch_recommend
url=Nicokit::API_URI+"getrelation?video=#{self.id}"
recommend=[]
Nicokit.get(url){|res|
rexml=REXML::Document.new(res.body)
list=Nicokit.xml2hash(REXML::XPath.match(rexml,'//video'))
list.map!{|v|
Nicokit::Video.new(v[:url])
}
recommend = list
}
@recommend=recommend
end
def _fetch_comments
postxml="<thread user_id=\"#{self.myid}\" res_from=\"-1000\" version=\"20061206\" thread=\"#{self.thread}\" />"
Nicokit.post_data(self.msg_server,postxml){|page|
rexml=REXML::Document.new(page.body)
comments=[]
rexml.elements.each( '//chat' ){|n|
hash={}
hash=n.attributes.inject({}){|r,attr|
r[attr[0].to_sym]=attr[1]
r
}
hash[:comment]=n.text
comments << hash
}
@comments=comments
}
end
def _fetch_info
api=Nicokit::API_URI+"getthumbinfo/#{@id}"
Nicokit.get(api){|res|
rexml=REXML::Document.new(res.body)
tmp=Nicokit.xml2hash(REXML::XPath.match(rexml,'//thumb'))[0]
next if !tmp
@title=tmp[:title]
@posted=DateTime.parse(tmp[:first_retrieve])
@mylist=tmp[:mylist_counter].to_i
@comment=tmp[:comment_num].to_i
@thumbnail=tmp[:thumbnail_url]
@last_comments=tmp[:last_res_body]
@description=tmp[:description]
@tags=tmp[:tags][:tag]
@view=tmp[:view_counter].to_i
@url=tmp[:watch_url]
@id=tmp[:video_id]
}
end
end
def self.xml2hash doc
tmp=Proc.new {|item|
hash={}
item.each{|n|
next if n.class.to_s.match(/Text/) # whitespace node?
# DetailPageURL => :detail_page_url
key=n.name.to_s.gsub(/([a-z])([A-Z]+)/,"\\1_\\2").downcase.to_sym
if n.has_elements?
hash[key]=tmp.call(n.elements.to_a)
else
if hash[key]
# <tags> <tag>1</tag><tag>2</tag> </tags>
# to
# hash[:tags]={:tag => [1,2]}
if hash[key].class != [].class
tmp=hash[key]
hash[key]=[tmp]
end
hash[key] << n.text
else
hash[key]=n.text
end
end
}
hash
}
result=[]
doc.each{|n|
result << tmp.call(n)
}
result
end
end