-
-
Save ToksT/e778c6080feb9725368c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'zip' # install with "gem install rubyzip" | |
require 'mechanize' | |
require 'json' | |
# usage: ruby pixanim.rb ID FORMAT, where ID is the pixiv id number and FORMAT is gif, webm, or apng | |
# test posts: | |
# http://www.pixiv.net/member_illust.php?mode=medium&illust_id=44305721 | |
# http://www.pixiv.net/member_illust.php?mode=medium&illust_id=44303110 | |
# http://www.pixiv.net/member_illust.php?mode=medium&illust_id=44353714 | |
# http://www.pixiv.net/member_illust.php?mode=medium&illust_id=44349996 | |
# http://www.pixiv.net/member_illust.php?mode=medium&illust_id=44315772 | |
# http://www.pixiv.net/member_illust.php?mode=medium&illust_id=44303325 | |
# http://www.pixiv.net/member_illust.php?mode=medium&illust_id=44343783 | |
if ARGV[0].nil? | |
raise "Must specify pixiv illustration id number" | |
end | |
pixiv_id = ARGV[0] | |
if ARGV[1].nil? | |
raise "Must specify output format" | |
end | |
format = ARGV[1].downcase | |
mech = Mechanize.new | |
source_url = "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=#{pixiv_id}" | |
zip_url = "" | |
frame_data = [] | |
mech.get(source_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_url = data["src"].sub("_ugoira600x600.zip", "_ugoira1920x1080.zip") | |
frame_data = data["frames"] | |
else | |
raise "Can't find javascript with frame data" | |
end | |
end | |
zip_uri = URI(zip_url) | |
zip_blob = "" | |
Net::HTTP.start(zip_uri.host) do |http| | |
resp = http.get(zip_uri.path, {"Referer" => "http://pixiv.net"}) | |
zip_blob = resp.body | |
end | |
folder = Zip::CentralDirectory.new | |
folder.read_from_stream(StringIO.new(zip_blob)) | |
case format | |
when "gif" | |
require 'rmagick' | |
anim = Magick::ImageList.new | |
delay_sum = 0 | |
folder.each_with_index do |file, i| | |
# puts file.name | |
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("./pixiv_anim_#{pixiv_id}.gif") | |
puts "Animation successfully created as pixiv_anim_#{pixiv_id}.gif" | |
when "webm" | |
FileUtils.mkdir_p("pixiv_anim_#{pixiv_id}") | |
folder.each_with_index do |file, i| | |
path = File.join("pixiv_anim_#{pixiv_id}", file.name) | |
image_blob = file.get_input_stream.read | |
File.open(path, "wb") do |f| | |
f.write(image_blob) | |
end | |
end | |
# Duplicate last frame to avoid it being displayed only for a very short amount of time. | |
folder.to_a.last.name =~ /\A(\d{6})(\..+)\Z/ | |
new_last_index = $1.to_i + 1 | |
file_ext = $2 | |
new_last_filename = ("%06d" % new_last_index) + file_ext | |
path_from = File.join("pixiv_anim_#{pixiv_id}", folder.to_a.last.name) | |
path_to = File.join("pixiv_anim_#{pixiv_id}", new_last_filename) | |
FileUtils.cp(path_from, path_to) | |
delay_sum = 0 | |
File.open("pixiv_anim_#{pixiv_id}-timecodes.tc", "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") | |
f.write("#{delay_sum}\n") | |
end | |
ext = folder.first.name.match(/\.(.+)$/)[1] | |
system("ffmpeg -i pixiv_anim_#{pixiv_id}/%06d.#{ext} -codec:v libvpx -crf 4 -b:v 5000k -an pixiv_anim_#{pixiv_id}-tmp.webm") | |
system("mkvmerge -o pixiv_anim_#{pixiv_id}.webm --timecodes 0:pixiv_anim_#{pixiv_id}-timecodes.tc pixiv_anim_#{pixiv_id}-tmp.webm") | |
FileUtils.rm_rf("pixiv_anim_#{pixiv_id}") | |
File.delete("pixiv_anim_#{pixiv_id}-timecodes.tc") | |
File.delete("pixiv_anim_#{pixiv_id}-tmp.webm") | |
puts "Animation successfully created as pixiv_anim_#{pixiv_id}.webm" | |
when "apng" | |
require 'rmagick' | |
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" | |
else | |
raise "Invalid format" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A few notes if someone decides to run it standalone under windows, like I did: