diff --git a/app/logical/apng_inspector.rb b/app/logical/apng_inspector.rb new file mode 100644 index 000000000..d8ade47db --- /dev/null +++ b/app/logical/apng_inspector.rb @@ -0,0 +1,70 @@ +class APNGInspector + attr_reader :frames + + PNG_MAGIC_NUMBER = ["89504E470D0A1A0A"].pack('H*') + + def initialize(file_path) + @file_path = file_path + @corrupted = false + @animated = false + end + + #Calls associated block for each PNG chunk + #parameters passed are |chunk_name, chunk_length, file_descriptor| + #returns true if file is read succesfully from start to IEND, false otherwise. + def each_chunk + iend_reached = false + File.open(@file_path, 'rb') do |file| + return false if file.read(8) != PNG_MAGIC_NUMBER + + while chunkheader = file.read(8) + return false if chunkheader.to_s.length != 8 + + chunk_len, chunk_name = chunkheader.unpack("Na4") + current_pos = file.tell + yield chunk_name, chunk_len, file + if chunk_name == "IEND" + iend_reached = true + break + end + file.seek(current_pos+chunk_len+4, IO::SEEK_SET) + end + end + return iend_reached + end + + def inspect! + actl_corrupted = false + + read_success = each_chunk do |name, len, file| + if name == 'acTL' + if len != 8 then + actl_corrupted = true + else + framedata = file.read(4) + if framedata.to_s.length != 4 + actl_corrupted = true + else + framecount = framedata.unpack("N")[0] + if framecount < 1 + actl_corrupted = true + else + @animated = true + @frames = framecount + end + end + end + end + end + @corrupted = !read_success || actl_corrupted + return !@corrupted + end + + def corrupted? + @corrupted + end + + def animated? + !@corrupted && @animated && @frames > 1 + end +end \ No newline at end of file diff --git a/app/models/post.rb b/app/models/post.rb index 437a62f4d..b12e39d08 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -163,6 +163,16 @@ class Post < ActiveRecord::Base return false end end + + def is_animated_png? + if file_ext =~ /png/i + apng = APNGInspector.new(file_path) + apng.inspect! + return apng.animated? + else + return false + end + end def is_flash? file_ext =~ /swf/i @@ -607,7 +617,7 @@ class Post < ActiveRecord::Base def add_automatic_tags(tags) return tags if !Danbooru.config.enable_dimension_autotagging - tags -= %w(incredibly_absurdres absurdres highres lowres huge_filesize animated_gif flash webm mp4) + tags -= %w(incredibly_absurdres absurdres highres lowres huge_filesize animated_gif animated_png flash webm mp4) if has_dimensions? if image_width >= 10_000 || image_height >= 10_000 @@ -639,6 +649,10 @@ class Post < ActiveRecord::Base if is_animated_gif? tags << "animated_gif" end + + if is_animated_png? + tags << "animated_png" + end if is_flash? tags << "flash"