video2gif 0.0.21 → 0.0.22
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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/video2gif/cli.rb +30 -1
- data/lib/video2gif/ffmpeg/subtitles.rb +40 -0
- data/lib/video2gif/ffmpeg.rb +33 -5
- data/lib/video2gif/options.rb +21 -8
- data/lib/video2gif/utils.rb +9 -0
- data/lib/video2gif/version.rb +1 -1
- data/lib/video2gif.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f906c9980bbe4a0dacb015c1a6b0a1af786d1f82b252ea8963d71ff46a53acc9
|
4
|
+
data.tar.gz: 6d948e892703ff81ecce6395074efc6486a97f7348c3378369c2712476d8fb35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f83e62d9f5ee2f592d1bf9e5ea6b5ea93bf1e5fea7ad004399f611312985ba540c63fd1ee9e29d7b3e74017140cb55630f4f66aa2c4833de091d231b40c2ebce
|
7
|
+
data.tar.gz: c36ffa22c9fa87c45f318210eff0603d02d4be084d29d0f141c8e49fcdcf9e2e20db73edfd76984da3434dafe923beb586b38ac8653ae47f2d04ca28814b7dae
|
data/Gemfile.lock
CHANGED
data/lib/video2gif/cli.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
3
4
|
require 'logger'
|
4
5
|
require 'open3'
|
5
6
|
require 'video2gif'
|
@@ -11,18 +12,45 @@ module Video2gif
|
|
11
12
|
logger = Logger.new(STDOUT)
|
12
13
|
options = Video2gif::Options.parse(ARGV)
|
13
14
|
|
15
|
+
if options[:subtitles]
|
16
|
+
lines = []
|
17
|
+
|
18
|
+
Open3.popen2e(*Video2gif::FFmpeg.ffprobe_command(options, logger)) do |stdin, stdout_stderr, thread|
|
19
|
+
stdin.close
|
20
|
+
stdout_stderr.each do |line|
|
21
|
+
logger.info(line.chomp) if options[:verbose] unless options[:quiet]
|
22
|
+
lines << line
|
23
|
+
end
|
24
|
+
stdout_stderr.close
|
25
|
+
|
26
|
+
unless thread.value.success?
|
27
|
+
# TODO: more info, output lines with errors?
|
28
|
+
raise "Process #{thread.pid} failed! Try again with --verbose to see error."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
options[:probe_infos] = JSON.parse(lines.join, symbolize_names: true)
|
33
|
+
|
34
|
+
if options[:subtitles] && !options[:probe_infos][:streams].any? do |s|
|
35
|
+
s[:codec_type] == 'subtitle'
|
36
|
+
end
|
37
|
+
logger.warn('Could not find subtitles in the file, they will be omitted') unless options[:quiet]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
14
41
|
if options[:autocrop]
|
15
42
|
Open3.popen2e(*Video2gif::FFmpeg.cropdetect_command(options, logger)) do |stdin, stdout_stderr, thread|
|
16
43
|
stdin.close
|
17
44
|
stdout_stderr.each do |line|
|
18
45
|
logger.info(line.chomp) if options[:verbose] unless options[:quiet]
|
19
46
|
if line.include?('Parsed_cropdetect')
|
20
|
-
options[:autocrop] = line.match(
|
47
|
+
options[:autocrop] = line.match(Video2Gif::FFmpeg::CROP_REGEX)
|
21
48
|
end
|
22
49
|
end
|
23
50
|
stdout_stderr.close
|
24
51
|
|
25
52
|
unless thread.value.success?
|
53
|
+
# TODO: more info, output lines with errors?
|
26
54
|
raise "Process #{thread.pid} failed! Try again with --verbose to see error."
|
27
55
|
end
|
28
56
|
end
|
@@ -36,6 +64,7 @@ module Video2gif
|
|
36
64
|
stdout_stderr.close
|
37
65
|
|
38
66
|
unless thread.value.success?
|
67
|
+
# TODO: more info, output lines with errors?
|
39
68
|
raise "Process #{thread.pid} failed! Try again with --verbose to see error."
|
40
69
|
end
|
41
70
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
module Video2gif
|
5
|
+
module FFmpeg
|
6
|
+
module Subtitles
|
7
|
+
KNOWN_TEXT_FORMATS = %w[
|
8
|
+
ass
|
9
|
+
dvb_teletext
|
10
|
+
eia_608
|
11
|
+
hdmv_text_subtitle
|
12
|
+
jacosub
|
13
|
+
microdvd
|
14
|
+
mov_text
|
15
|
+
mpl2
|
16
|
+
pjs
|
17
|
+
realtext
|
18
|
+
sami
|
19
|
+
srt
|
20
|
+
ssa
|
21
|
+
stl
|
22
|
+
subrip
|
23
|
+
subviewer
|
24
|
+
subviewer1
|
25
|
+
text
|
26
|
+
ttml
|
27
|
+
vplayer
|
28
|
+
webvtt
|
29
|
+
]
|
30
|
+
|
31
|
+
KNOWN_BITMAP_FORMATS = %w[
|
32
|
+
dvb_subtitle
|
33
|
+
dvd_subtitle
|
34
|
+
hdmv_pgs_subtitle
|
35
|
+
xsub
|
36
|
+
]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/video2gif/ffmpeg.rb
CHANGED
@@ -3,14 +3,30 @@
|
|
3
3
|
|
4
4
|
module Video2gif
|
5
5
|
module FFmpeg
|
6
|
+
CROP_REGEX = /crop=([0-9]+\:[0-9]+\:[0-9]+\:[0-9]+)/
|
7
|
+
|
6
8
|
def self.filtergraph(options)
|
7
9
|
filtergraph = []
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
if options[:subtitles] && options[:probe_infos][:streams].any? do |s|
|
12
|
+
s[:codec_type] == 'subtitle'
|
13
|
+
end
|
14
|
+
video_info = options[:probe_infos][:streams].find { |s| s[:codec_type] == 'video' }
|
15
|
+
subtitle_info = options[:probe_infos][:streams].find_all { |s| s[:codec_type] == 'subtitle' }[options[:subtitle_index]]
|
16
|
+
|
17
|
+
if Video2gif::FFmpeg::Subtitles::KNOWN_TEXT_FORMATS.include?(subtitle_info[:codec_name])
|
18
|
+
filtergraph << "setpts=PTS+#{Video2gif::Utils.duration_to_seconds(options[:seek])}/TB"
|
19
|
+
filtergraph << "subtitles='#{options[:input_filename]}':si=#{options[:subtitle_index]}"
|
20
|
+
filtergraph << 'setpts=PTS-STARTPTS'
|
21
|
+
elsif Video2gif::FFmpeg::Subtitles::KNOWN_BITMAP_FORMATS.include?(subtitle_info[:codec_name])
|
22
|
+
filtergraph << "[0:s:#{options[:subtitle_index]}]scale=" + %W[
|
23
|
+
flags=lanczos
|
24
|
+
sws_dither=none
|
25
|
+
width=#{video_info[:width]}
|
26
|
+
height=#{video_info[:height]}
|
27
|
+
].join(':') + '[subs]' if options[:width]
|
28
|
+
filtergraph << "[0:v][subs]overlay=format=auto"
|
29
|
+
end
|
14
30
|
end
|
15
31
|
|
16
32
|
# Set 'fps' filter first, drop unneeded frames instead of
|
@@ -114,6 +130,18 @@ module Video2gif
|
|
114
130
|
filtergraph << "[paletteuse][palette]paletteuse=dither=#{options[:dither] || 'floyd_steinberg'}:diff_mode=rectangle"
|
115
131
|
end
|
116
132
|
|
133
|
+
def self.ffprobe_command(options, logger, executable: 'ffprobe')
|
134
|
+
command = [executable]
|
135
|
+
command << '-v' << 'error'
|
136
|
+
command << '-show_entries' << 'stream'
|
137
|
+
command << '-print_format' << 'json'
|
138
|
+
command << '-i' << options[:input_filename]
|
139
|
+
|
140
|
+
logger.info(command.join(' ')) if options[:verbose] unless options[:quiet]
|
141
|
+
|
142
|
+
command
|
143
|
+
end
|
144
|
+
|
117
145
|
def self.ffmpeg_command(options, executable: 'ffmpeg')
|
118
146
|
command = [executable]
|
119
147
|
command << '-y'
|
data/lib/video2gif/options.rb
CHANGED
@@ -148,14 +148,27 @@ module Video2gif
|
|
148
148
|
options[:tonemap] = t || 'hable'
|
149
149
|
end
|
150
150
|
|
151
|
-
parser.on('--subtitles [
|
152
|
-
'Attempt to use the
|
153
|
-
'
|
154
|
-
'
|
155
|
-
'
|
156
|
-
'
|
157
|
-
|
158
|
-
|
151
|
+
parser.on('--subtitles [INDEX]',
|
152
|
+
'(Experimental, requires ffprobe) Attempt to use the',
|
153
|
+
'subtitles built into the video to overlay text on the',
|
154
|
+
'resulting GIF. Takes an optional integer value to',
|
155
|
+
'choose the subtitle stream (defaults to the first',
|
156
|
+
'subtitle stream, index 0)') do |s|
|
157
|
+
unless Video2gif::Utils.is_executable?('ffprobe')
|
158
|
+
puts 'ERROR: Requires FFmpeg utils to be installed (for ffprobe)!'
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
|
162
|
+
options[:subtitles] = s || true
|
163
|
+
options[:subtitle_index] = if options[:subtitles].is_a?(TrueClass) # default to first stream
|
164
|
+
0
|
165
|
+
elsif options[:subtitles].match?(/\A\d+\z/) # select stream by index
|
166
|
+
options[:subtitles].to_i
|
167
|
+
elsif options[:subtitles].is_a?(String) # open subtitles file
|
168
|
+
puts 'ERROR: Selecting subtitles by filename is not yet supported!'
|
169
|
+
exit 1
|
170
|
+
end
|
171
|
+
end
|
159
172
|
|
160
173
|
parser.separator ''
|
161
174
|
parser.separator 'Text overlay options (only used if text is defined):'
|
data/lib/video2gif/utils.rb
CHANGED
@@ -10,5 +10,14 @@ module Video2gif
|
|
10
10
|
end
|
11
11
|
end.flatten.any?
|
12
12
|
end
|
13
|
+
|
14
|
+
# Convert "[-][HH:]MM:SS[.m...]" to "[-]S+[.m...]".
|
15
|
+
# https://ffmpeg.org/ffmpeg-utils.html#time-duration-syntax
|
16
|
+
def self.duration_to_seconds(duration)
|
17
|
+
return duration unless duration.include?(?:)
|
18
|
+
m = duration.match(/(?<sign>-)?(?<hours>\d+:)?(?<minutes>\d+):(?<seconds>\d+)(?<millis>\.\d+)?/)
|
19
|
+
seconds = m[:hours].to_i * 60 * 60 + m[:minutes].to_i * 60 + m[:seconds].to_i
|
20
|
+
duration = "#{m[:sign]}#{seconds}#{m[:millis]}"
|
21
|
+
end
|
13
22
|
end
|
14
23
|
end
|
data/lib/video2gif/version.rb
CHANGED
data/lib/video2gif.rb
CHANGED
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.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emily St.
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- lib/video2gif.rb
|
104
104
|
- lib/video2gif/cli.rb
|
105
105
|
- lib/video2gif/ffmpeg.rb
|
106
|
+
- lib/video2gif/ffmpeg/subtitles.rb
|
106
107
|
- lib/video2gif/options.rb
|
107
108
|
- lib/video2gif/utils.rb
|
108
109
|
- lib/video2gif/version.rb
|