video_metadata_extractor.rb 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. # frozen_string_literal: true
  2. class VideoMetadataExtractor
  3. attr_reader :duration, :bitrate, :video_codec, :audio_codec,
  4. :colorspace, :width, :height, :frame_rate
  5. def initialize(path)
  6. @path = path
  7. @metadata = Oj.load(ffmpeg_command_output, mode: :strict, symbol_keys: true)
  8. parse_metadata
  9. rescue Terrapin::ExitStatusError, Oj::ParseError
  10. @invalid = true
  11. rescue Terrapin::CommandNotFoundError
  12. raise Paperclip::Errors::CommandNotFoundError, 'Could not run the `ffprobe` command. Please install ffmpeg.'
  13. end
  14. def valid?
  15. !@invalid
  16. end
  17. private
  18. def ffmpeg_command_output
  19. command = Terrapin::CommandLine.new('ffprobe', '-i :path -print_format :format -show_format -show_streams -show_error -loglevel :loglevel')
  20. command.run(path: @path, format: 'json', loglevel: 'fatal')
  21. end
  22. def parse_metadata
  23. if @metadata.key?(:format)
  24. @duration = @metadata[:format][:duration].to_f
  25. @bitrate = @metadata[:format][:bit_rate].to_i
  26. end
  27. if @metadata.key?(:streams)
  28. video_streams = @metadata[:streams].select { |stream| stream[:codec_type] == 'video' }
  29. audio_streams = @metadata[:streams].select { |stream| stream[:codec_type] == 'audio' }
  30. if (video_stream = video_streams.first)
  31. @video_codec = video_stream[:codec_name]
  32. @colorspace = video_stream[:pix_fmt]
  33. @width = video_stream[:width]
  34. @height = video_stream[:height]
  35. @frame_rate = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate])
  36. end
  37. if (audio_stream = audio_streams.first)
  38. @audio_codec = audio_stream[:codec_name]
  39. end
  40. end
  41. @invalid = true if @metadata.key?(:error)
  42. end
  43. end