video-gif-previewer 0.1.0 → 0.1.1

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: '08e35907ca5d95b4ce59a135cbe9572a1f13240df1c8abdfed340f3ada0fcb28'
4
+ data.tar.gz: 34bc9c5bda01697d00d0d45d7679bf05fd1e26769e8cf4384c0b8d06a2e2342d
5
5
  SHA512:
6
- metadata.gz: c77fa639612714dfd901a986a39b9c7224e20d96b621f4d1ba63677b1386b7276114bc7afacfebf10a272922ec403cd70e8211aaea9293802f4fe8e517d21d07
7
- data.tar.gz: 93d5de03d0badc7fa137a1f19db0c3b87f6e10f7d39c96e00b05cd9e306e5ebc0623836d2ca299bb7632496d15268a9cba5cf13021cba4151dd8e866ce32efeb
6
+ metadata.gz: 9427e2a3edad7c1140e8d00b85c8205cde639167a5316748e2d32a9d10a89a5511ceeae8cabcbe6641833bc2eb08b089aa8ae7e842273111f4896a33089c018d
7
+ data.tar.gz: deafe286cb4ff483b60287fd9898eed29cefff1659b64df98abfc262cf01d894c0fc58fb38c1154290b7d23094d0ebaaff3e8737438c4bf0259a629c7a1752fc
data/lib/gif_previewer.rb CHANGED
@@ -1,24 +1,104 @@
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(msg: "Timeout::Error, processing took too long", max_allowed_time: MAX_PROCESSING_TIME)
21
+
22
+ # If we take too long encoding a gif, fallback to the standard rails VideoPreviewer
23
+ super
24
+ rescue ActiveStorage::PreviewError => e
25
+ Rails.logger.error(msg: "ActiveStorage::PreviewError", message: e.message)
26
+
27
+ # If we hit an issue encoding a gif, fallback to the standard rails VideoPreviewer
28
+ super
10
29
  end
11
30
 
12
31
  private
13
32
 
33
+ def ffprobe_path
34
+ ActiveStorage.paths[:ffprobe] || "ffprobe"
35
+ end
36
+
37
+ # Use ffprobe to analyze the media for duration
38
+ def duration(input_file: nil)
39
+ start_time = Time.now
40
+
41
+ file_duration = if input_file.present?
42
+ duration_command(input_file)
43
+ else
44
+ download_blob_to_tempfile do |file|
45
+ duration_command(file)
46
+ end
47
+ end
48
+
49
+ Rails.logger.info(msg: 'ffprobe for durations finished', file_duration:, elapsed_time: Time.now - start_time)
50
+
51
+ file_duration
52
+ end
53
+
54
+ def duration_command(input_file)
55
+ `#{ffprobe_path} -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 #{input_file.path}`.to_f.floor.to_i
56
+ end
57
+
58
+ # Use file_duration to either pick a random place ~33% into the file, or if the file is too short, use 0
59
+ def start_frame(file_duration:)
60
+ if file_duration > 10
61
+ mid_frame = (file_duration.to_f / 3).floor
62
+ mid_frame > 3 ? mid_frame : 3
63
+ else
64
+ 0
65
+ end
66
+ end
67
+
68
+ # Use file_duration to pick an end frame frame_duration after the start, or if the file is too short, use file_duration
69
+ def end_frame(start_frame_num:, file_duration:)
70
+ expected_end = (start_frame_num + frame_duration)
71
+
72
+ if file_duration < expected_end
73
+ file_duration
74
+ else
75
+ expected_end
76
+ end
77
+ end
78
+
79
+ def frame_duration
80
+ 5
81
+ end
82
+
83
+ def draw_relevant_frames_from(file, file_duration, &block)
84
+ start_frame_num = start_frame(file_duration:)
85
+ end_frame_num = end_frame(start_frame_num:, file_duration:)
14
86
 
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
87
+ Rails.logger.info(
88
+ msg: "GifPreviewer draw_relevant_frames_from starting",
89
+ file_path: file.path,
90
+ 'start (ss)': start_frame_num,
91
+ 'end (to)': end_frame_num,
92
+ )
93
+ Timeout::timeout(MAX_PROCESSING_TIME) do
94
+ draw self.class.ffmpeg_path,
95
+ '-i', file.path,
96
+ '-filter_complex', "fps=10,scale=360:-1[s]; [s]split[a][b]; [a]palettegen[palette]; [b][palette]paletteuse",
97
+ '-f', 'gif',
98
+ '-ss', start_frame_num.to_s,
99
+ '-to', end_frame_num.to_s,
100
+ '-',
101
+ &block
102
+ end
23
103
  end
24
104
  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.1'
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.1
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-15 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
@@ -43,7 +43,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
43
  - !ruby/object:Gem::Version
44
44
  version: '0'
45
45
  requirements: []
46
- rubygems_version: 3.5.23
46
+ rubygems_version: 3.5.11
47
47
  signing_key:
48
48
  specification_version: 4
49
49
  summary: ActiveStorage Gif Video Preview generation