Skip to content

Instantly share code, notes, and snippets.

@yamaimo
Created May 16, 2015 01:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yamaimo/a3c28b942e635d533dcb to your computer and use it in GitHub Desktop.
Save yamaimo/a3c28b942e635d533dcb to your computer and use it in GitHub Desktop.
NicovideoDownloader-mini with Ruby (RTMP support)
#!/usr/bin/env ruby
require 'netrc'
require 'net/http'
require 'net/https'
require 'cgi'
LoginURI = URI.parse("https://secure.nicovideo.jp/secure/login?site=niconico")
LoginPostFormat = "current_form=login&mail=%s&password=%s&login_submit=Log+In"
VideoHost = "www.nicovideo.jp"
VideoPathFormat = "/watch/%s"
VideoURLRegexp = %r{^(?:(?:http://)?(?:\w+\.)?(?:nicovideo\.jp/(?:v/|(?:watch(?:\.php)?))?/)?(\w+))}
VideoInfoPathFormat = "/api/getflv?v=%s&as3=1"
VideoTypeRegexp = %r{^http://.*\.nicovideo\.jp/smile\?(.*?)=.*}
if ARGV.size < 1
raise "URL is not specified."
end
url = ARGV[0]
puts "Download #{url}"
# get user info
netrc_info = Netrc.read
user, password = netrc_info["nicovideo"]
if user.nil? || user.empty? || password.nil? || password.empty?
raise "Netrc is invalid."
end
# login
https = Net::HTTP.new(LoginURI.host, LoginURI.port)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
postdata = sprintf(LoginPostFormat, user, password)
response = https.post(LoginURI.request_uri, postdata)
user_session = nil
response.get_fields('set-cookie').each do |cookie|
key, value = cookie.split(';').first.split('=')
if (key == 'user_session') && (value != 'deleted')
user_session = value
break
end
end
if user_session.nil?
raise "Failed to login."
end
# get video id
video_id = nil
if match_data = VideoURLRegexp.match(url)
video_id = match_data[1]
else
raise "URL is invalid. [url=#{url}]"
end
# access video page and get cookie
http = Net::HTTP.new(VideoHost)
video_path = sprintf(VideoPathFormat, video_id)
response = http.get(video_path, {'Cookie' => "user_session=#{user_session};"})
nicohistory = nil
response.get_fields('set-cookie').each do |cookie|
key, value = cookie.split(';').first.split('=')
if key == 'nicohistory'
nicohistory = value
break
end
end
# get video uri
http = Net::HTTP.new(VideoHost)
video_info_path = sprintf(VideoInfoPathFormat, video_id)
response = http.get(video_info_path,
{'Cookie' => "user_session=#{user_session}; nicohistory=#{nicohistory};"})
while response.is_a?(Net::HTTPRedirection)
redirect_uri = URI.parse(response.get_fields('location').first)
http = Net::HTTP.new(redirect_uri.host)
response = http.get(redirect_uri.request_uri,
{'Cookie' => "user_session=#{user_session}; nicohistory=#{nicohistory};"})
end
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_info["uri"].to_s)
if match_data[1] == "s"
video_extension = ".swf"
elsif match_data[1] == "m"
video_extension = ".mp4"
else
video_extension = ".flv"
end
else
video_extension = ".flv"
end
output_filename = "#{video_id}#{video_extension}"
# get video data
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
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 \\
-q \\
-e \\
-r #{uri_without_query.to_s} \\
-t #{uri_without_query.to_s} \\
-p #{original_uri.to_s} \\
-y #{playpath} \\
-C S:#{fmst1} \\
-C S:#{fmst2} \\
-C S:#{playpath} \\
-o #{output_filename}
END_OF_COMMAND
end
end
puts "done."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment