sedmelluq / lavaplayer

Audio player library for Discord
Apache License 2.0
955 stars 248 forks source link

"Unknown file format" Error when loading Nicovideo #32

Open yukukotani opened 7 years ago

yukukotani commented 7 years ago

Hi. An error has been occurred when loading audio from Nicovideo (www.nicovideo.jp).

com.sedmelluq.discord.lavaplayer.tools.FriendlyException: Unknown file format. at com.sedmelluq.discord.lavaplayer.source.ProbingAudioSourceManager.handleLoadResult(ProbingAudioSourceManager.java:21) at com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager.loadItem(HttpAudioSourceManager.java:65) at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager.checkSourcesForItemOnce(DefaultAudioPlayerManager.java:429) at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager.checkSourcesForItem(DefaultAudioPlayerManager.java:415) at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager.lambda$createItemLoader$0(DefaultAudioPlayerManager.java:219) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at com.sedmelluq.discord.lavaplayer.tools.OrderedExecutor$ChannelRunnable.executeQueue(OrderedExecutor.java:98) at com.sedmelluq.discord.lavaplayer.tools.OrderedExecutor$ChannelRunnable.run(OrderedExecutor.java:87) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)

s5bug commented 7 years ago

Unknown file format. This means the site isn't supported yet. I'm sure though that you can extract the video address from a nicovideo page and use that.

abalabahaha commented 7 years ago

The problem is, it should be supported, since there is code for it

yukukotani commented 7 years ago

Like @abalabahaha said, There is AudioSourceManager for Nicovideo, so it should be supported.

yukukotani commented 7 years ago

This is working code written in Ruby from Here.

#!/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_uri = nil
begin
  info = CGI.parse(response.body)
  video_url = info['url'].first
  video_uri = URI.parse(video_url)
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[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

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)
    end
  end
end

puts "done."