work on ugoira converter
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -43,6 +43,7 @@ gem 'capistrano'
|
|||||||
gem 'capistrano-ext'
|
gem 'capistrano-ext'
|
||||||
gem 'radix62', '~> 1.0.1'
|
gem 'radix62', '~> 1.0.1'
|
||||||
gem 'streamio-ffmpeg'
|
gem 'streamio-ffmpeg'
|
||||||
|
gem 'rubyzip'
|
||||||
|
|
||||||
# needed for looser jpeg header compat
|
# needed for looser jpeg header compat
|
||||||
gem 'ruby-imagespec', :require => "image_spec", :git => "https://github.com/r888888888/ruby-imagespec.git", :branch => "exif-fixes"
|
gem 'ruby-imagespec', :require => "image_spec", :git => "https://github.com/r888888888/ruby-imagespec.git", :branch => "exif-fixes"
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ GEM
|
|||||||
ref (1.0.5)
|
ref (1.0.5)
|
||||||
rmagick (2.13.3)
|
rmagick (2.13.3)
|
||||||
ruby-prof (0.14.2)
|
ruby-prof (0.14.2)
|
||||||
|
rubyzip (1.1.6)
|
||||||
safe_yaml (1.0.2)
|
safe_yaml (1.0.2)
|
||||||
sanitize (2.1.0)
|
sanitize (2.1.0)
|
||||||
nokogiri (>= 1.4.4)
|
nokogiri (>= 1.4.4)
|
||||||
@@ -263,6 +264,7 @@ DEPENDENCIES
|
|||||||
rmagick
|
rmagick
|
||||||
ruby-imagespec!
|
ruby-imagespec!
|
||||||
ruby-prof
|
ruby-prof
|
||||||
|
rubyzip
|
||||||
sanitize
|
sanitize
|
||||||
sass-rails (~> 4.0.0)
|
sass-rails (~> 4.0.0)
|
||||||
shoulda
|
shoulda
|
||||||
|
|||||||
122
app/logical/pixiv_ugoira_converter.rb
Normal file
122
app/logical/pixiv_ugoira_converter.rb
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
class PixivUgoiraConverter
|
||||||
|
attr_reader :agent, :url, :write_path
|
||||||
|
|
||||||
|
def initialize(agent, url, write_path)
|
||||||
|
@agent = agent
|
||||||
|
@url = url
|
||||||
|
@write_path = write_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(format)
|
||||||
|
folder = unpack(fetch_zipped_body)
|
||||||
|
|
||||||
|
if format == :gif
|
||||||
|
write_gif(folder)
|
||||||
|
elsif format == :webm
|
||||||
|
write_webm(folder)
|
||||||
|
elsif format == :apng
|
||||||
|
write_apng(folder)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_gif(folder)
|
||||||
|
anim = Magick::ImageList.new
|
||||||
|
delay_sum = 0
|
||||||
|
folder.each_with_index do |file, i|
|
||||||
|
image_blob = file.get_input_stream.read
|
||||||
|
image = Magick::Image.from_blob(image_blob).first
|
||||||
|
image.ticks_per_second = 1000
|
||||||
|
delay = frame_data[i]["delay"]
|
||||||
|
rounded_delay = (delay_sum + delay).round(-1) - delay_sum.round(-1)
|
||||||
|
image.delay = rounded_delay
|
||||||
|
delay_sum += delay
|
||||||
|
anim << image
|
||||||
|
end
|
||||||
|
|
||||||
|
anim = anim.optimize_layers(Magick::OptimizeTransLayer)
|
||||||
|
anim.write(write_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_webm(folder)
|
||||||
|
Dir.mktmpdir do |tmpdir|
|
||||||
|
FileUtils.mkdir_p("#{tmpdir}/images")
|
||||||
|
folder.each_with_index do |file, i|
|
||||||
|
path = File.join(tmpdir, file.name)
|
||||||
|
image_blob = file.get_input_stream.read
|
||||||
|
File.open(path, "wb") do |f|
|
||||||
|
f.write(image_blob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
delay_sum = 0
|
||||||
|
timecodes_path = File.join(tmpdir, "timecodes.tc")
|
||||||
|
File.open(timecodes_path, "w+") do |f|
|
||||||
|
f.write("# timecode format v2\n")
|
||||||
|
frame_data.each do |img|
|
||||||
|
f.write("#{delay_sum}\n")
|
||||||
|
delay_sum += img["delay"]
|
||||||
|
end
|
||||||
|
f.write("#{delay_sum}\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
ext = folder.first.name.match(/\.(.+)$/)[1]
|
||||||
|
system("ffmpeg -i #{tmpdir}/images/%06d.#{ext} -codec:v libvpx -crf 4 -b:v 5000k -an #{tmpdir}/tmp.webm")
|
||||||
|
system("mkvmerge -o #{tmpdir}/output.webm --timecodes 0:#{tmpdir}/timecodes.tc #{tmpdir}/tmp.webm")
|
||||||
|
|
||||||
|
FileUtils.rm_rf(tmpdir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_apng(folder)
|
||||||
|
FileUtils.mkdir_p("pixiv_anim_#{pixiv_id}")
|
||||||
|
folder.each_with_index do |file, i|
|
||||||
|
frame_path = File.join("pixiv_anim_#{pixiv_id}", "frame#{"%03d" % i}.png")
|
||||||
|
delay_path = File.join("pixiv_anim_#{pixiv_id}", "frame#{"%03d" % i}.txt")
|
||||||
|
image_blob = file.get_input_stream.read
|
||||||
|
delay = frame_data[i]["delay"]
|
||||||
|
image = Magick::Image.from_blob(image_blob).first
|
||||||
|
image.format="PNG"
|
||||||
|
image.write(frame_path)
|
||||||
|
File.open(delay_path, "wb") do |f|
|
||||||
|
f.write("delay=#{delay}/1000")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
system("apngasm pixiv_anim_#{pixiv_id}.png pixiv_anim_#{pixiv_id}/frame*.png")
|
||||||
|
FileUtils.rm_rf("pixiv_anim_#{pixiv_id}")
|
||||||
|
|
||||||
|
puts "Animation successfully created as pixiv_anim_#{pixiv_id}.png"
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpack(zipped_body)
|
||||||
|
Zip::CentralDirectory.new.read_from_stream(StringIO.new(zipped_body))
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_zipped_body
|
||||||
|
zip_uri, frame_data = fetch_frames
|
||||||
|
|
||||||
|
zip_body = Net::HTTP.start(zip_uri.host) do |http|
|
||||||
|
http.get(zip_uri.path, {"Referer" => "http://pixiv.net"}).body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_frames
|
||||||
|
agent.get(url) do |page|
|
||||||
|
# Get the zip url and frame delay by parsing javascript contained in a <script> tag on the page.
|
||||||
|
# Not an neat solution, but I haven't found any other location that has the frame delays listed.
|
||||||
|
scripts = page.search("body script").find_all do |node|
|
||||||
|
node.text =~ /_ugoira600x600\.zip/
|
||||||
|
end
|
||||||
|
|
||||||
|
if scripts.any?
|
||||||
|
javascript = scripts.first.text
|
||||||
|
json = javascript.match(/;pixiv\.context\.ugokuIllustData\s+=\s+(\{.+\});(?:$|pixiv\.context)/)[1]
|
||||||
|
data = JSON.parse(json)
|
||||||
|
zip_uri = URI(data["src"].sub("_ugoira600x600.zip", "_ugoira1920x1080.zip"))
|
||||||
|
frame_data = data["frames"]
|
||||||
|
return [zip_uri, frame_data]
|
||||||
|
else
|
||||||
|
raise "Can't find javascript with frame data"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user