video2gif 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -7
- data/lib/video2gif.rb +40 -72
- data/lib/video2gif/version.rb +1 -1
- data/video2gif.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9033455c4d54099067dd61428f3190e75bc52783ab0ee110effa5cb9efedf688
|
4
|
+
data.tar.gz: fd1092a4ef5ffa4c280861fa084444ed2d3b260139062488824c71c3de0957ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19a4bfdfb4abd9f13c9c0b8f6a558ef9c1563c620c864e70b3ed96f59b2dd344e153c7900561b92298b18fdc3094ea8a8feb7f4fc2d1f67b2ba90adbc95f757a
|
7
|
+
data.tar.gz: f7c4f43908ca23a75651a73c3cff2cd30407024296ad3fab227f6dba331d0276228e54032458cf7d80739c0b6b4bd8f440508ef101ce5d6edd788e9f0d680084
|
data/README.md
CHANGED
@@ -3,10 +3,10 @@ video2gif
|
|
3
3
|
|
4
4
|
`video2gif` eases converting any video into a GIF.
|
5
5
|
|
6
|
-
It uses [FFMpeg]
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
It uses [FFMpeg], so it understands any video that [FFMpeg] does. It has
|
7
|
+
an array of options to allow you to select the part of the video you
|
8
|
+
want, crop it automatically, overlay text, and manipulate the color and
|
9
|
+
brightness.
|
10
10
|
|
11
11
|
|
12
12
|
Installation
|
@@ -17,9 +17,6 @@ available in the system `$PATH`. If you can run `ffmpeg` from the
|
|
17
17
|
command line, you're probably good. If not, use your favorite package
|
18
18
|
manager to install it.
|
19
19
|
|
20
|
-
If you install [ImageMagick], further optimization will automatically
|
21
|
-
take place on the resulting GIF.
|
22
|
-
|
23
20
|
Note that some features may not be available by default. For example,
|
24
21
|
tonemapping (used for HDR videos) requires `libzimg` support, not
|
25
22
|
included by default in the [FFMpeg] supplied by [Homebrew].
|
data/lib/video2gif.rb
CHANGED
@@ -93,12 +93,6 @@ module Video2gif
|
|
93
93
|
options[:cropdetect] = c || 24
|
94
94
|
end
|
95
95
|
|
96
|
-
parser.on('-o',
|
97
|
-
'--[no-]optimize',
|
98
|
-
'Attempt to optimize GIF size with ImageMagick (default yes if available)') do |o|
|
99
|
-
options[:optimize] = o
|
100
|
-
end
|
101
|
-
|
102
96
|
parser.on('--contrast CONTRAST',
|
103
97
|
'Apply contrast adjustment, scaled from -2.0 to 2.0 (default 1)') do |c|
|
104
98
|
options[:contrast] = c
|
@@ -239,19 +233,13 @@ module Video2gif
|
|
239
233
|
exit
|
240
234
|
end
|
241
235
|
|
242
|
-
if !is_executable?('convert') && options[:optimize]
|
243
|
-
logger.warn('ImageMagick isn\'t available! Optimization will be'\
|
244
|
-
' disabled!') unless options[:quiet]
|
245
|
-
options[:optimize] = false
|
246
|
-
end
|
247
|
-
|
248
236
|
options
|
249
237
|
end
|
250
238
|
|
251
239
|
def self.build_filter_complex(options)
|
252
|
-
fps
|
253
|
-
|
254
|
-
width
|
240
|
+
fps = options[:fps] || 15
|
241
|
+
max_colors = options[:palette] ? "max_colors=#{options[:palette]}:" : ''
|
242
|
+
width = options[:width] # default is not to scale at all
|
255
243
|
|
256
244
|
# create filter elements
|
257
245
|
fps_filter = "fps=#{fps}"
|
@@ -275,17 +263,17 @@ module Video2gif
|
|
275
263
|
end
|
276
264
|
eq_filter = if options[:eq]
|
277
265
|
'eq=' + %W[
|
278
|
-
contrast=#{options[:contrast] || 1}
|
279
|
-
brightness=#{options[:brightness] || 0}
|
280
|
-
saturation=#{options[:saturation] || 1}
|
281
|
-
gamma=#{options[:gamma] || 1}
|
282
|
-
gamma_r=#{options[:gamma_r] || 1}
|
283
|
-
gamma_g=#{options[:gamma_g] || 1}
|
284
|
-
gamma_b=#{options[:gamma_b] || 1}
|
266
|
+
contrast=#{ options[:contrast] || 1 }
|
267
|
+
brightness=#{ options[:brightness] || 0 }
|
268
|
+
saturation=#{ options[:saturation] || 1 }
|
269
|
+
gamma=#{ options[:gamma] || 1 }
|
270
|
+
gamma_r=#{ options[:gamma_r] || 1 }
|
271
|
+
gamma_g=#{ options[:gamma_g] || 1 }
|
272
|
+
gamma_b=#{ options[:gamma_b] || 1 }
|
285
273
|
].join(':')
|
286
274
|
end
|
287
|
-
palettegen_filter = "palettegen
|
288
|
-
paletteuse_filter = 'paletteuse=dither=
|
275
|
+
palettegen_filter = "palettegen=#{max_colors}stats_mode=diff"
|
276
|
+
paletteuse_filter = 'paletteuse=dither=floyd_steinberg:diff_mode=rectangle'
|
289
277
|
drawtext_filter = if options[:text]
|
290
278
|
count_of_lines = options[:text].scan(/\\n/).count + 1
|
291
279
|
|
@@ -321,10 +309,10 @@ module Video2gif
|
|
321
309
|
# before applying the palettegen so that we accurately predict the
|
322
310
|
# final palette
|
323
311
|
filter_complex << fps_filter
|
324
|
-
filter_complex << crop_filter
|
325
|
-
filter_complex << scale_filter
|
312
|
+
filter_complex << crop_filter if crop_filter
|
313
|
+
filter_complex << scale_filter if options[:width] unless options[:tonemap]
|
326
314
|
filter_complex << tonemap_filters if options[:tonemap]
|
327
|
-
filter_complex << eq_filter
|
315
|
+
filter_complex << eq_filter if options[:eq]
|
328
316
|
filter_complex << drawtext_filter if options[:text]
|
329
317
|
|
330
318
|
# then generate the palette (and label this filter stream)
|
@@ -336,10 +324,10 @@ module Video2gif
|
|
336
324
|
# affected by scaling)
|
337
325
|
filter_complex << '[0:v][palette]' + paletteuse_filter
|
338
326
|
filter_complex << fps_filter
|
339
|
-
filter_complex << crop_filter
|
340
|
-
filter_complex << scale_filter
|
327
|
+
filter_complex << crop_filter if crop_filter
|
328
|
+
filter_complex << scale_filter if options[:width] unless options[:tonemap]
|
341
329
|
filter_complex << tonemap_filters if options[:tonemap]
|
342
|
-
filter_complex << eq_filter
|
330
|
+
filter_complex << eq_filter if options[:eq]
|
343
331
|
filter_complex << drawtext_filter if options[:text]
|
344
332
|
|
345
333
|
filter_complex.join(',')
|
@@ -349,51 +337,38 @@ module Video2gif
|
|
349
337
|
if args[1]
|
350
338
|
args[1].end_with?('.gif') ? args[1] : args[1] + '.gif'
|
351
339
|
else
|
352
|
-
File.join(File.dirname(args[0]),
|
353
|
-
File.basename(args[0], '.*') + '.gif')
|
340
|
+
File.join(File.dirname(args[0]), File.basename(args[0], '.*') + '.gif')
|
354
341
|
end
|
355
342
|
end
|
356
343
|
|
357
|
-
def self.
|
358
|
-
command = []
|
359
|
-
command << 'ffmpeg'
|
360
|
-
command << '-y' # always overwrite
|
344
|
+
def self.build_ffmpeg_cropdetect_command(args, options, logger)
|
345
|
+
command = ['ffmpeg']
|
361
346
|
command << '-analyzeduration' << '2147483647' << '-probesize' << '2147483647'
|
362
347
|
command << '-nostdin'
|
363
348
|
command << '-ss' << options[:seek] if options[:seek]
|
364
349
|
command << '-t' << options[:time] if options[:time]
|
365
350
|
command << '-i' << args[0]
|
366
|
-
command << '-filter_complex' <<
|
367
|
-
command << '-f' << '
|
368
|
-
|
369
|
-
# if we're not optimizing, we won't send to stdout
|
370
|
-
command << (options[:optimize] ? '-' : build_output_filename(args))
|
371
|
-
|
372
|
-
logger.info(command.join(' ')) if options[:verbose] unless options[:quiet]
|
373
|
-
|
374
|
-
command
|
375
|
-
end
|
376
|
-
|
377
|
-
def self.build_convert_optimize_command(args, options, logger)
|
378
|
-
command = []
|
379
|
-
command << 'convert' << '-' << '-layers' << 'Optimize' << build_output_filename(args)
|
351
|
+
command << '-filter_complex' << "cropdetect=limit=#{options[:cropdetect]}"
|
352
|
+
command << '-f' << 'null'
|
353
|
+
command << '-'
|
380
354
|
|
381
355
|
logger.info(command.join(' ')) if options[:verbose] unless options[:quiet]
|
382
356
|
|
383
357
|
command
|
384
358
|
end
|
385
359
|
|
386
|
-
def self.
|
387
|
-
command = []
|
388
|
-
command << '
|
360
|
+
def self.build_ffmpeg_gif_command(args, options, logger)
|
361
|
+
command = ['ffmpeg']
|
362
|
+
command << '-y' # always overwrite
|
389
363
|
command << '-analyzeduration' << '2147483647' << '-probesize' << '2147483647'
|
390
364
|
command << '-nostdin'
|
391
365
|
command << '-ss' << options[:seek] if options[:seek]
|
392
366
|
command << '-t' << options[:time] if options[:time]
|
393
367
|
command << '-i' << args[0]
|
394
|
-
command << '-filter_complex' <<
|
395
|
-
command << '-
|
396
|
-
command << '-'
|
368
|
+
command << '-filter_complex' << build_filter_complex(options)
|
369
|
+
command << '-gifflags' << '+transdiff' # enabled by default
|
370
|
+
command << '-f' << 'gif'
|
371
|
+
command << build_output_filename(args)
|
397
372
|
|
398
373
|
logger.info(command.join(' ')) if options[:verbose] unless options[:quiet]
|
399
374
|
|
@@ -405,7 +380,7 @@ module Video2gif
|
|
405
380
|
options = parse_args(ARGV, logger)
|
406
381
|
|
407
382
|
if options[:cropdetect]
|
408
|
-
Open3.popen3(*build_ffmpeg_cropdetect_command(ARGV, options, logger)) do |stdin, stdout, stderr,
|
383
|
+
Open3.popen3(*build_ffmpeg_cropdetect_command(ARGV, options, logger)) do |stdin, stdout, stderr, thread|
|
409
384
|
stdin.close
|
410
385
|
stdout.close
|
411
386
|
stderr.each(chomp: true) do |line|
|
@@ -414,26 +389,19 @@ module Video2gif
|
|
414
389
|
end
|
415
390
|
stderr.close
|
416
391
|
|
417
|
-
raise "Process #{
|
392
|
+
raise "Process #{thread.pid} failed! Try again with --verbose to see error." unless thread.value.success?
|
418
393
|
end
|
419
394
|
end
|
420
395
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
write_io.close
|
427
|
-
if options[:verbose]
|
428
|
-
read_io.each(chomp: true) { |line| logger.info(line) unless options[:quiet] }
|
429
|
-
else
|
430
|
-
read_io.read(1024) until read_io.eof?
|
396
|
+
Open3.popen3(*build_ffmpeg_gif_command(ARGV, options, logger)) do |stdin, stdout, stderr, thread|
|
397
|
+
stdin.close
|
398
|
+
stdout.close
|
399
|
+
stderr.each(chomp: true) do |line|
|
400
|
+
logger.info(line) if options[:verbose] unless options[:quiet]
|
431
401
|
end
|
432
|
-
|
402
|
+
stderr.close
|
433
403
|
|
434
|
-
|
435
|
-
raise "Process #{t.pid} failed! Try again with --verbose to see error." unless t.value.success?
|
436
|
-
end
|
404
|
+
raise "Process #{thread.pid} failed! Try again with --verbose to see error." unless thread.value.success?
|
437
405
|
end
|
438
406
|
end
|
439
407
|
end
|
data/lib/video2gif/version.rb
CHANGED
data/video2gif.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
# the 'allowed_push_host' to allow pushing to a single host or delete
|
18
18
|
# this section to allow pushing to any host.
|
19
19
|
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata['allowed_push_host'] = 'https://rubygems.org
|
20
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
21
21
|
spec.metadata['homepage_uri'] = spec.homepage
|
22
22
|
spec.metadata['source_code_uri'] = "https://github.com/emilyst/video2gif"
|
23
23
|
# spec.metadata['changelog_uri'] = "https://github.com/emilyst/video2gif"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: video2gif
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emily St.
|
@@ -93,7 +93,7 @@ homepage: https://github.com/emilyst/video2gif
|
|
93
93
|
licenses:
|
94
94
|
- CC0
|
95
95
|
metadata:
|
96
|
-
allowed_push_host: https://rubygems.org
|
96
|
+
allowed_push_host: https://rubygems.org
|
97
97
|
homepage_uri: https://github.com/emilyst/video2gif
|
98
98
|
source_code_uri: https://github.com/emilyst/video2gif
|
99
99
|
post_install_message:
|