video-gif-previewer 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2fe9890ceea6d652191c3d4a719aa052c84a3191a8d181961ea0b07b98a0faff
4
- data.tar.gz: f1d5ca0abd315f740291049e87dca214173150270435c6b1bf3e7acc2e4418ab
3
+ metadata.gz: 292c2cc47f8ede9e3a0ee23a79e971599be7de0cffc81602f2e494a5e13daeb7
4
+ data.tar.gz: 6de21e7755a025814b7343df160ce7348715ed4a47c3aeedf001aea74991de76
5
5
  SHA512:
6
- metadata.gz: c77fa639612714dfd901a986a39b9c7224e20d96b621f4d1ba63677b1386b7276114bc7afacfebf10a272922ec403cd70e8211aaea9293802f4fe8e517d21d07
7
- data.tar.gz: 93d5de03d0badc7fa137a1f19db0c3b87f6e10f7d39c96e00b05cd9e306e5ebc0623836d2ca299bb7632496d15268a9cba5cf13021cba4151dd8e866ce32efeb
6
+ metadata.gz: df66a6f10b8b7ce17f854f550d1485de29aaea08358f4f193622dc8aafb69aaeb6dc4d12262067c7309bbf284740f936b131551cb45656d35d846ccc068276bb
7
+ data.tar.gz: fa832e3f7318c9939d68f73e3bdec938e1ef2c3a239d44b1965af24ad9f390f099aa5a996c45afd56757d60a1917489ed993a41d9caba1e9ff0dfa99537845b0
data/lib/gif_previewer.rb CHANGED
@@ -1,24 +1,109 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'timeout'
4
+
3
5
  class GifPreviewer < ActiveStorage::Previewer::VideoPreviewer
6
+ MAX_PROCESSING_TIME = 60
7
+
4
8
  def preview(**options)
9
+ Rails.logger.info(msg: 'Downloading Blob', blob: @blob)
5
10
  download_blob_to_tempfile do |input|
6
- draw_relevant_frames_from input do |output|
11
+ file_duration = duration(input_file: input)
12
+ Rails.logger.info(msg: 'generation of gif preview starting', blob: @blob)
13
+ draw_relevant_frames_from input, file_duration do |output|
14
+ Rails.logger.info(msg: 'generation of gif preview completed')
7
15
  yield io: output, filename: "#{blob.filename.base}-preview.gif", content_type: "image/gif", **options
8
16
  end
9
17
  end
18
+
19
+ rescue Timeout::Error
20
+ Rails.logger.error(
21
+ msg: "Timeout::Error, processing took too long",
22
+ max_allowed_time: MAX_PROCESSING_TIME,
23
+ record_type: @blob.attachments.first.record_type,
24
+ record_id: @blob.attachments.first.record.id,
25
+ )
26
+
27
+ # If we take too long encoding a gif, fallback to the standard rails VideoPreviewer
28
+ super
29
+ rescue ActiveStorage::PreviewError => e
30
+ Rails.logger.error(msg: "ActiveStorage::PreviewError", message: e.message)
31
+
32
+ # If we hit an issue encoding a gif, fallback to the standard rails VideoPreviewer
33
+ super
10
34
  end
11
35
 
12
36
  private
13
37
 
38
+ def ffprobe_path
39
+ ActiveStorage.paths[:ffprobe] || "ffprobe"
40
+ end
41
+
42
+ # Use ffprobe to analyze the media for duration
43
+ def duration(input_file: nil)
44
+ start_time = Time.now
45
+
46
+ file_duration = if input_file.present?
47
+ duration_command(input_file)
48
+ else
49
+ download_blob_to_tempfile do |file|
50
+ duration_command(file)
51
+ end
52
+ end
53
+
54
+ Rails.logger.info(msg: 'ffprobe for durations finished', file_duration:, elapsed_time: Time.now - start_time)
55
+
56
+ file_duration
57
+ end
58
+
59
+ def duration_command(input_file)
60
+ `#{ffprobe_path} -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 #{input_file.path}`.to_f.floor.to_i
61
+ end
62
+
63
+ # Use file_duration to either pick a random place ~33% into the file, or if the file is too short, use 0
64
+ def start_frame(file_duration:)
65
+ if file_duration > 10
66
+ mid_frame = (file_duration.to_f / 3).floor
67
+ mid_frame > 3 ? mid_frame : 3
68
+ else
69
+ 0
70
+ end
71
+ end
72
+
73
+ # Use file_duration to pick an end frame frame_duration after the start, or if the file is too short, use file_duration
74
+ def end_frame(start_frame_num:, file_duration:)
75
+ expected_end = (start_frame_num + frame_duration)
76
+
77
+ if file_duration < expected_end
78
+ file_duration
79
+ else
80
+ expected_end
81
+ end
82
+ end
83
+
84
+ def frame_duration
85
+ 5
86
+ end
87
+
88
+ def draw_relevant_frames_from(file, file_duration, &block)
89
+ start_frame_num = start_frame(file_duration:)
90
+ end_frame_num = end_frame(start_frame_num:, file_duration:)
14
91
 
15
- def draw_relevant_frames_from(file, &block)
16
- draw self.class.ffmpeg_path,
17
- "-i", file.path,
18
- '-filter_complex', 'fps=10,scale=360:-1[s]; [s]split[a][b]; [a]palettegen[palette]; [b][palette]paletteuse',
19
- '-f', 'gif',
20
- '-ss', '3',
21
- '-to', '8',
22
- "-", &block
92
+ Rails.logger.info(
93
+ msg: "GifPreviewer draw_relevant_frames_from starting",
94
+ file_path: file.path,
95
+ 'start (ss)': start_frame_num,
96
+ 'end (to)': end_frame_num,
97
+ )
98
+ Timeout::timeout(MAX_PROCESSING_TIME) do
99
+ draw self.class.ffmpeg_path,
100
+ '-i', file.path,
101
+ '-filter_complex', "fps=10,scale=360:-1[s]; [s]split[a][b]; [a]palettegen[palette]; [b][palette]paletteuse",
102
+ '-f', 'gif',
103
+ '-ss', start_frame_num.to_s,
104
+ '-to', end_frame_num.to_s,
105
+ '-',
106
+ &block
107
+ end
23
108
  end
24
109
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class VideoGifPreviewer
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: video-gif-previewer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Haberer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-11 00:00:00.000000000 Z
11
+ date: 2025-04-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Generate rails active storage video previews as gifs
14
14
  email: bhaberer@gmail.com
@@ -25,7 +25,6 @@ licenses:
25
25
  - GPL-2.0-only
26
26
  metadata:
27
27
  allowed_push_host: https://rubygems.org/
28
- homepage_uri: https://github.com/bhaberer/video-gif-previewer
29
28
  source_code_uri: https://github.com/bhaberer/video-gif-previewer
30
29
  rubygems_mfa_required: 'true'
31
30
  post_install_message: