-
-
Save Type-kun/e7e5f1f49cf22d907da3 to your computer and use it in GitHub Desktop.
This file contains 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 'rmagick' | |
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 either 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" | |
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 | |
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") | |
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" | |
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