video-sprites 0.0.1.pre1
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 +7 -0
- data/.gitignore +16 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +2 -0
- data/bin/video-sprites +252 -0
- data/lib/video/sprites.rb +7 -0
- data/lib/video/sprites/version.rb +5 -0
- data/video-sprites.gemspec +23 -0
- data/video-sprites.iml +18 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 43859ff5512d89e5dbefb3e3eff88c9660af15a9
|
4
|
+
data.tar.gz: e8feed13b4c139db06e452795cf24619a729b52a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 90166c4cc92205cedfe25d6a72bd10b4f96698db08c5eea9d2271b55cbbe6f46cb1ff03c3fb2c428f2faaee67ad3f6e9d48815575a1c07e01f158308da4946e2
|
7
|
+
data.tar.gz: 5d1a34832889aacd3720711e18884da2f980aaf4670e62b958169b7940984e69ee7c67bae068def6001a6449b6b529e85eaa690a90fac4bda4e9b1e3dcfbce4e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jason Ronallo
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Video Sprites
|
2
|
+
|
3
|
+
Exports thumbnail images, thumbnail sprite image, and a WebVTT metadata file with synced media fragment URLs to thumbnails within the sprite.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem install 'video-sprites'
|
9
|
+
```
|
10
|
+
|
11
|
+
## Requirements
|
12
|
+
|
13
|
+
- ffmpeg
|
14
|
+
- imagemagick (montage)
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
```shell
|
19
|
+
video-sprites --help
|
20
|
+
```
|
21
|
+
|
22
|
+
```shell
|
23
|
+
video-sprites --seconds 5 --width 200 --columns 5 --input . --output ./output
|
24
|
+
```
|
25
|
+
|
26
|
+
## Test Media Sources
|
27
|
+
|
28
|
+
https://www.youtube.com/watch?v=dTCEDG9h9AA
|
29
|
+
|
30
|
+
https://www.youtube.com/watch?v=9AGisNPUBqM
|
31
|
+
|
32
|
+
https://www.youtube.com/watch?v=Z9To9NOLEPI
|
33
|
+
|
34
|
+
https://www.youtube.com/watch?v=Ww4WrcjAOlo
|
35
|
+
|
36
|
+
https://www.youtube.com/watch?v=wz-eInv9f7g
|
37
|
+
|
38
|
+
## TODO
|
39
|
+
|
40
|
+
- Consider adding an option to change the output filename.
|
41
|
+
- Optionally allow for scene change detection and variable length cues. How difficult would this be?
|
42
|
+
- Should the first timestamp after the first cue not be on the second but be a fractional second instead?
|
43
|
+
|
44
|
+
## Authors
|
45
|
+
|
46
|
+
- Ashley Blewer
|
47
|
+
- Jay Brown
|
48
|
+
- Jason Ronallo
|
49
|
+
- Nicholas Zoss
|
data/Rakefile
ADDED
data/bin/video-sprites
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'slop'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
# remove all JPEG and VTT files, but leave the directories in place.
|
7
|
+
def remove_files(output_directory, verbose)
|
8
|
+
deadfiles = Dir.glob(File.join(output_directory, '**', '*.jpg'))
|
9
|
+
puts "list of dead files will be #{deadfiles}" if verbose
|
10
|
+
deadfiles.each do | deadfile |
|
11
|
+
puts "removing file #{deadfile}" if verbose
|
12
|
+
File.unlink(deadfile)
|
13
|
+
end
|
14
|
+
deadfiles = Dir.glob(File.join(output_directory, '**', '*.vtt'))
|
15
|
+
puts "list of dead files will be #{deadfiles}" if verbose
|
16
|
+
deadfiles.each do | deadfile |
|
17
|
+
puts "removing file #{deadfile}" if verbose
|
18
|
+
File.unlink(deadfile)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Format a WebVTT timestamp according to the specification using hours, minutes, seconds, and fractional seconds.
|
23
|
+
def timestamp(total_seconds)
|
24
|
+
seconds = total_seconds % 60
|
25
|
+
minutes = (total_seconds / 60) % 60
|
26
|
+
hours = total_seconds / (60 * 60)
|
27
|
+
|
28
|
+
format("%02d:%02d:%02d.000", hours, minutes, seconds)
|
29
|
+
end
|
30
|
+
|
31
|
+
opts = Slop.new strict: true do
|
32
|
+
banner 'Usage: video-sprites [options]'
|
33
|
+
on 'i', 'input=', 'Input file or directory', required: true
|
34
|
+
on 'o', 'output=', 'Output file or directory', argument: :optional
|
35
|
+
on 's', 'seconds=', 'Seconds interval between snapshots', argument: :optional, as: Integer, default: 5
|
36
|
+
on 'w', 'width=', 'Width of each thumbnail', argument: :optional, as: Integer, default: 200
|
37
|
+
on 'c', 'columns=', 'Number of columns in the image sprite', argument: :optional, as: Integer, default: 5
|
38
|
+
on 'r', 'rows=', 'Maximum number of rows to put into each sprite', argument: :optional, as: Integer
|
39
|
+
# on 'k', 'keep', 'Keep all the individual images and other intermediate artifacts.', argument: :optional
|
40
|
+
on 't', 'start', 'Start time to begin from in seconds or hh:mm:ss[.xxx]', argument: :optional
|
41
|
+
on 'd', 'duration', 'Duration of the clip to extract stills from in seconds or hh:mm:ss[.xxx] and can only be set if --start is also set.', argument: :optional
|
42
|
+
on 'u', 'url=', "Base url to use for webvtt", default: 'http://example.com'
|
43
|
+
on 'g', 'gif', "Create a GIF animation as well?"
|
44
|
+
on 'v', 'verbose', 'Enable verbose mode'
|
45
|
+
on 'z', 'clean', 'Clean up the output directory'
|
46
|
+
on 'h', 'help', 'Help!!!'
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
opts.parse
|
51
|
+
rescue Slop::Error => e
|
52
|
+
puts e.message
|
53
|
+
puts opts # print help
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
|
57
|
+
verbose = opts[:verbose]
|
58
|
+
input = File.expand_path(opts[:input])
|
59
|
+
output = opts[:output]
|
60
|
+
seconds = opts[:seconds]
|
61
|
+
columns = opts[:columns]
|
62
|
+
width = opts[:width]
|
63
|
+
clean = opts[:clean]
|
64
|
+
|
65
|
+
unless File.exist?(input)
|
66
|
+
puts 'File or directory does not exist!'
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
|
70
|
+
if verbose
|
71
|
+
puts "Input file: #{input}"
|
72
|
+
end
|
73
|
+
|
74
|
+
if File.directory?(input)
|
75
|
+
# TODO: Only pull in video files with this. What's the best way to do that?
|
76
|
+
# Should we test for whether a file is a video file or use something potentially more
|
77
|
+
# fragile like testing for extensions?
|
78
|
+
files = Dir.glob(File.join(input, '*'))
|
79
|
+
else
|
80
|
+
files = [input]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Since we allow for input to be either a file or a directory we need to set file paths
|
84
|
+
# for use later appropriately.
|
85
|
+
if output
|
86
|
+
output_directory = File.expand_path(output)
|
87
|
+
puts "output dir is #{output_directory}" if verbose
|
88
|
+
if File.directory?(output_directory)
|
89
|
+
remove_files(output_directory, verbose) if clean
|
90
|
+
elsif File.exist?(output_directory)
|
91
|
+
raise "Output directory must be a directory, was: #{output}"
|
92
|
+
else
|
93
|
+
FileUtils.mkdir_p(output_directory)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
output_directory = input
|
97
|
+
if File.directory?(output_directory)
|
98
|
+
puts "output dir is #{output_directory}" if verbose
|
99
|
+
remove_files(output_directory, verbose) if clean
|
100
|
+
else
|
101
|
+
output_directory = File.dirname(input)
|
102
|
+
puts "output dir is #{output_directory}" if verbose
|
103
|
+
remove_files(output_directory, verbose) if clean
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# This is the main loop where we go through video files.
|
108
|
+
files.each do | file |
|
109
|
+
|
110
|
+
# for repeated runs, skip any directories in this directory
|
111
|
+
if File.directory?(file)
|
112
|
+
next
|
113
|
+
end
|
114
|
+
|
115
|
+
puts "Processing file: #{file}" if verbose
|
116
|
+
extension = File.extname(file)
|
117
|
+
basename = File.basename(file, extension)
|
118
|
+
puts "Basename is: #{basename}" if verbose
|
119
|
+
|
120
|
+
output_path = File.join(output_directory, basename)
|
121
|
+
|
122
|
+
if File.directory?(input)
|
123
|
+
FileUtils.mkdir_p(output_path)
|
124
|
+
output_file_path = File.join(output_directory, basename, basename)
|
125
|
+
else
|
126
|
+
output_file_path = File.join(output_directory, basename)
|
127
|
+
end
|
128
|
+
|
129
|
+
puts "#{output_file_path} output file path"
|
130
|
+
# FIXME: check if the files exist already and maybe don't process anything
|
131
|
+
|
132
|
+
# Use ffprobe to discover the frames per second.
|
133
|
+
ffprobe_cmd = 'ffprobe -i "' + "#{file}" + '" 2>&1 | sed -n "s/.*, \(.*\) fp.*/\1/p"'
|
134
|
+
puts ffprobe_cmd if verbose
|
135
|
+
# Frames per second can be fractional so round up
|
136
|
+
frames_per_sec=`#{ffprobe_cmd}`.to_f.ceil.to_i
|
137
|
+
frames_value=frames_per_sec * seconds
|
138
|
+
puts "frames_per_sec #{frames_per_sec}, frames_value #{frames_value}" if verbose
|
139
|
+
|
140
|
+
# Using select allows for more exact selection of a frame at the times we want
|
141
|
+
ffmpeg_cmd = %Q|ffmpeg -i "#{file}" -vf select='lt(mod(n\\,#{frames_value})\\,1),fps=1/#{seconds}' |
|
142
|
+
if opts[:start]
|
143
|
+
ffmpeg_cmd += " -ss #{opts[:start]} "
|
144
|
+
if opts[:duration]
|
145
|
+
ffmpeg_cmd += " -t #{opts[:duration]} "
|
146
|
+
end
|
147
|
+
end
|
148
|
+
ffmpeg_cmd += %Q| "#{output_file_path}-%05d.jpg" |
|
149
|
+
puts ffmpeg_cmd if verbose
|
150
|
+
`#{ffmpeg_cmd}`
|
151
|
+
|
152
|
+
# We drop the first snapshot because it is the first frame which will either be useless or already in our poster
|
153
|
+
# image for the video. This also allows the WebVTT to show the thumbnail of a frame that will come some time after
|
154
|
+
# the time selected/clicked on.
|
155
|
+
jpegs = Dir.glob(output_file_path + '*.jpg').sort
|
156
|
+
FileUtils.rm(jpegs.first)
|
157
|
+
jpegs.shift
|
158
|
+
|
159
|
+
if opts[:gif]
|
160
|
+
`convert -delay 20 -loop 0 "#{output_file_path}*.jpg" #{output_file_path}.gif`
|
161
|
+
end
|
162
|
+
|
163
|
+
# Montage concatenates all the images into an image sprite.
|
164
|
+
# TODO: Instead of creating a single sprite take a slice of the output jpegs and create a number of different
|
165
|
+
# sprites.
|
166
|
+
slice_size = jpegs.length
|
167
|
+
if opts[:rows]
|
168
|
+
slice_size = opts[:columns] * opts[:rows]
|
169
|
+
end
|
170
|
+
jpegs.each_slice(slice_size).with_index do | jpeg_slice, index |
|
171
|
+
montage_files = jpeg_slice.map{|jpeg| %Q|"#{jpeg}"|}.join(" ")
|
172
|
+
padded_index = (index + 1).to_s.rjust(5, "0")
|
173
|
+
montage_cmd = %Q|montage #{montage_files} -tile #{columns}x -geometry #{width}x "#{output_file_path}-sprite-#{padded_index}.jpg"|
|
174
|
+
puts montage_cmd if verbose
|
175
|
+
`#{montage_cmd}`
|
176
|
+
end
|
177
|
+
|
178
|
+
# Prepare to create WebVTT file
|
179
|
+
if verbose
|
180
|
+
puts 'Processing a WebVTT file from these images:'
|
181
|
+
puts jpegs
|
182
|
+
end
|
183
|
+
# place all the cues into an array of hashes like this:
|
184
|
+
# {start: 0, end: 5, x: 0, y: 0, w: 200, h: 150}
|
185
|
+
cues = []
|
186
|
+
start = 0
|
187
|
+
|
188
|
+
# Use the first jpeg to determine the height of the resulting thumbs. We already know the width from the
|
189
|
+
# passed in option or the default width.
|
190
|
+
first_jpeg = jpegs.first
|
191
|
+
|
192
|
+
original_height = `identify -format "%h" -ping "#{first_jpeg}"`
|
193
|
+
original_width = `identify -format "%w" -ping "#{first_jpeg}"`
|
194
|
+
processed_height = (original_height.to_f / original_width.to_f * width).to_i
|
195
|
+
height = processed_height
|
196
|
+
|
197
|
+
# For each jpeg create a cue
|
198
|
+
jpegs.each_slice(slice_size) do | jpeg_slice |
|
199
|
+
jpeg_slice.each_with_index do |jpeg, index|
|
200
|
+
puts "Index: #{index}" if verbose
|
201
|
+
cue = {}
|
202
|
+
|
203
|
+
cue[:start] = start
|
204
|
+
cue[:end] = start + opts[:seconds]
|
205
|
+
|
206
|
+
cue[:x] = ((index % opts[:columns]) * opts[:width])
|
207
|
+
puts "x #{cue[:x]}" if verbose
|
208
|
+
|
209
|
+
cue[:y] = (index.to_f / opts[:columns].to_f).floor * height
|
210
|
+
puts "y #{cue[:y]}" if verbose
|
211
|
+
|
212
|
+
cue[:w] = opts[:width]
|
213
|
+
cue[:h] = height
|
214
|
+
|
215
|
+
start += opts[:seconds]
|
216
|
+
puts if verbose
|
217
|
+
|
218
|
+
cues << cue
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
puts cues if verbose
|
223
|
+
|
224
|
+
# Create a WebVTT file.
|
225
|
+
# TODO: When multiple sprites are created use the correct filename for each sprite.
|
226
|
+
# media fragment order: x,y,w,h
|
227
|
+
webvtt_file_name = output_file_path + '.vtt'
|
228
|
+
puts "Creating WebVTT: #{webvtt_file_name}" if verbose
|
229
|
+
File.open(webvtt_file_name, 'w') do |fh|
|
230
|
+
fh.puts "WEBVTT\n\n"
|
231
|
+
|
232
|
+
cues.each_slice(slice_size).with_index do |cue_slice, index|
|
233
|
+
cue_slice.each do |cue|
|
234
|
+
puts cue if verbose
|
235
|
+
start_timestamp = timestamp(cue[:start])
|
236
|
+
end_timestamp = timestamp(cue[:end])
|
237
|
+
fh.print start_timestamp
|
238
|
+
fh.print ' --> '
|
239
|
+
fh.puts end_timestamp
|
240
|
+
|
241
|
+
padded_index = (index + 1).to_s.rjust(5, "0")
|
242
|
+
|
243
|
+
url = File.join(opts[:url], basename + "-sprite-#{padded_index}.jpg#xywh=#{cue[:x]},#{cue[:y]},#{cue[:w]},#{cue[:h]}")
|
244
|
+
|
245
|
+
fh.puts url
|
246
|
+
fh.puts
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'video/sprites/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "video-sprites"
|
8
|
+
spec.version = Video::Sprites::VERSION
|
9
|
+
spec.authors = ["Ashley Blewer, Jay Brown, Jason Ronallo, Nick Zoss"]
|
10
|
+
spec.email = ["ashley.blewer@gmail.com, jlb1504@gmail.com, jronallo@gmail.com, nickzoss@yahoo.com"]
|
11
|
+
spec.summary = %q{Automatically generated thumbnails for video files.}
|
12
|
+
spec.description = %q{Automatically generated thumbnails and WebVTT with time synced media fragment URLs for video files.}
|
13
|
+
spec.homepage = "https://github.com/jronallo/video-sprites"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").select{|file| file =~ /^test\/videos\// ? false : true}
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "slop", '~> 3.3', '>= 3.3.3'
|
23
|
+
end
|
data/video-sprites.iml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<module type="RUBY_MODULE" version="4">
|
3
|
+
<component name="CompassSettings">
|
4
|
+
<option name="compassSupportEnabled" value="true" />
|
5
|
+
</component>
|
6
|
+
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
7
|
+
<exclude-output />
|
8
|
+
<content url="file://$MODULE_DIR$">
|
9
|
+
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
10
|
+
</content>
|
11
|
+
<orderEntry type="inheritedJdk" />
|
12
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
13
|
+
<orderEntry type="library" scope="PROVIDED" name="bundler (v1.7.3, ruby-2.1.3-p242) [gem]" level="application" />
|
14
|
+
<orderEntry type="library" scope="PROVIDED" name="rake (v10.3.2, ruby-2.1.3-p242) [gem]" level="application" />
|
15
|
+
<orderEntry type="library" scope="PROVIDED" name="slop (v3.3.3, ruby-2.1.3-p242) [gem]" level="application" />
|
16
|
+
</component>
|
17
|
+
</module>
|
18
|
+
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: video-sprites
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ashley Blewer, Jay Brown, Jason Ronallo, Nick Zoss
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: slop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.3'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 3.3.3
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.3'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.3.3
|
47
|
+
description: Automatically generated thumbnails and WebVTT with time synced media
|
48
|
+
fragment URLs for video files.
|
49
|
+
email:
|
50
|
+
- ashley.blewer@gmail.com, jlb1504@gmail.com, jronallo@gmail.com, nickzoss@yahoo.com
|
51
|
+
executables:
|
52
|
+
- video-sprites
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- ".gitignore"
|
57
|
+
- Gemfile
|
58
|
+
- LICENSE.txt
|
59
|
+
- README.md
|
60
|
+
- Rakefile
|
61
|
+
- bin/video-sprites
|
62
|
+
- lib/video/sprites.rb
|
63
|
+
- lib/video/sprites/version.rb
|
64
|
+
- video-sprites.gemspec
|
65
|
+
- video-sprites.iml
|
66
|
+
homepage: https://github.com/jronallo/video-sprites
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.3.1
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.2.2
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Automatically generated thumbnails for video files.
|
90
|
+
test_files: []
|