cutcut 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 75111491536a777f1518aa51a025c2bb87356ef9
4
+ data.tar.gz: 70b2fc148ed07dd389e27c39e68f0d8d1e0fedab
5
+ SHA512:
6
+ metadata.gz: 4ac1b04f0962ae62ddca62be5cce326a5c8de92500cc842244ae7c760ade07494aae97dcb582202ec76eb5422b72a834dd68b91cbe43d2449e5fcc29162dc037
7
+ data.tar.gz: 539a48f89ff804c4372005a62e41b9e2e2a5eb5d6752f508415d51cf8bd66a1887005eb0736f272a8f578b8aade8cf97f4b8dd950bbabbbd537297b460531307
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ *~
2
+ coverage/*
3
+ *.sqlite3
4
+ .bundle
5
+ rdoc/*
6
+ pkg
7
+ log
8
+ test/tmp/*
9
+ spec/tmp/*
10
+ spec/fixtures/*.MP4
11
+ spec/fixtures/*.jpg
12
+ spec/fixtures/*.png
13
+ dummy
14
+ *.gem
15
+ Gemfile.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ Metrics/LineLength:
2
+ Max: 500
data/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ Changelog
2
+ =========
3
+
4
+ Version 1.1.0 (March 17, 2016)
5
+ -----------------------------
6
+
7
+ * Add Timelapse support
8
+ * Add --copy-metadata option
9
+ * Add --scale option
10
+ * Add --timelapse-fps option
11
+
12
+ Version 1.0.0 (February 11, 2016)
13
+ -----------------------------
14
+
15
+ * Use hash options on initialize
16
+ * Add `speec` to convert method
17
+
18
+ Version 0.0.2 (February 8, 2016)
19
+ -----------------------------
20
+
21
+ * Remove Gemfile.lock
22
+ * Add basename and fps to extract_screenshots
23
+ * Add `media.cut`
24
+
25
+ Version 0.0.1 (February 5, 2016)
26
+ -----------------------------
27
+
28
+ * Add Media model
29
+ * Add convert media action
30
+ * Add extract_screenshot action
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,17 @@
1
+ guard :rspec, cmd: 'bundle exec rspec' do
2
+ require 'guard/rspec/dsl'
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ rspec = dsl.rspec
6
+ watch(rspec.spec_helper) { rspec.spec_dir }
7
+ watch(rspec.spec_support) { rspec.spec_dir }
8
+ watch(rspec.spec_files)
9
+
10
+ ruby = dsl.ruby
11
+ dsl.watch_spec_files_for(ruby.lib_files)
12
+ end
13
+
14
+ guard :rubocop do
15
+ watch(/.+\.rb$/)
16
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
17
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Jhimy Fernandes Villar.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # cutcut
2
+
3
+ Trim/Cut/Screenshot videos
4
+
5
+ # Usage
6
+
7
+ ```
8
+ gem install cutcut git: git@github.com:stjhimy/cutcut.git
9
+ ```
10
+
11
+ ```ruby
12
+ media = CutCut::Media.new(input: 'path_to_file.mp4')
13
+ media.convert(scale: '1280:720')
14
+ media.convert(scale: '1280:720', copy_metadata: true)
15
+ media.extract_screenshots
16
+ media.cut(start: '00:00', time: 10)
17
+
18
+ timelapse = CutCut::Timelapse.new(input: 'path_to_folder')
19
+ media.convert(scale: '1280:720')
20
+ media.convert(scale: '1280:720', fps: 60)
21
+ ```
22
+
23
+ ```
24
+ cutcut --help
25
+ Usage: cutcut [options]
26
+ --convert Convert all videos in a folder
27
+ --copy-metadata Copy original video metadata
28
+ --scale SCALE_RESOLUTION
29
+ --timelapse-fps FPS Timelapse FPS
30
+ --extract-screenshots NUMBER Screenshots per second
31
+ ```
32
+
33
+ ## License
34
+
35
+ MIT License. Copyright 2016 Jhimy Fernandes Villar.
data/bin/cutcut ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'cutcut'
4
+ require 'optparse'
5
+ require 'ruby-progressbar'
6
+
7
+ options = {}
8
+ OptionParser.new do |opt|
9
+ opt.on('--convert', 'Convert all videos in a folder') { options[:convert] = true }
10
+ opt.on('--copy-metadata', 'Copy original video metadata') { options[:copy_metadata] = true }
11
+ opt.on('--scale', 'SCALE_RESOLUTION') { |e| options[:scale] = e }
12
+ opt.on('--timelapse-fps FPS', 'Timelapse FPS') { |e| options[:timelapse_fps] = e }
13
+ opt.on('--extract-screenshots NUMBER', 'Screenshots per second') { |e| options[:extract_screenshots] = e }
14
+ end.parse!
15
+
16
+ pwd = ENV['PWD']
17
+ options[:input_dir] ||= pwd
18
+ options[:copy_metadata] ||= false
19
+
20
+ files = Dir[File.join(options[:input_dir], '/*.mp4')]
21
+ folders = Dir.glob(File.join(options[:input_dir], '/*')).select { |e| File.directory?(e) }
22
+ progressbar = ProgressBar.create(title: 'Converting', starting_at: 0, total: files.count + folders.count)
23
+
24
+ puts "#{files.count + folders.count} files found"
25
+
26
+ files.each do |file|
27
+ media = CutCut::Media.new(input: file)
28
+ scale = options[:scale] || '1920:1080'
29
+ media.convert(copy_metadata: options[:copy_metadata], scale: scale)
30
+ media.extract_screenshots(fps: options[:extract_screenshots], copy_metadata: true) if options[:extract_screenshots]
31
+ progressbar.increment
32
+ end
33
+
34
+ folders.each do |folder|
35
+ timelapse = CutCut::Timelapse.new(input: folder, output: File.join(options[:input_dir], File.basename(folder) + '.mp4'))
36
+ scale = options[:scale] || '1920:1080'
37
+ timelapse.convert(fps: options[:timelapse_fps] || 30, scale: scale)
38
+ progressbar.increment
39
+ end
40
+
41
+ puts "\n Finished!"
data/cutcut.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'cutcut'
3
+ s.authors = ['Jhimy Fernandes Villar']
4
+ s.email = ['stjhimy@gmail.com']
5
+ s.homepage = 'http://github.com/stjhimy/cutcut'
6
+ s.license = 'MIT'
7
+ s.summary = 'Trim/Cut/Screenshot videos'
8
+ s.description = 'CLI for working with videos'
9
+ s.version = '1.1.0'
10
+
11
+ s.executables = ['cutcut']
12
+ s.files = `git ls-files`.split("\n")
13
+ s.require_paths = ['lib']
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+
16
+ s.add_dependency 'activesupport', '~> 4.2'
17
+ s.add_dependency 'mini_exiftool', '~> 2.5'
18
+ s.add_dependency 'ruby-progressbar', '~> 1.7'
19
+
20
+ s.add_development_dependency 'guard', '~> 2.13'
21
+ s.add_development_dependency 'guard-rspec', '~> 4.6'
22
+ s.add_development_dependency 'guard-rubocop', '~> 1.2'
23
+ s.add_development_dependency 'rspec', '~> 3.4'
24
+ end
@@ -0,0 +1,26 @@
1
+ module CutCut
2
+ # Base
3
+ class Base
4
+ attr_reader :input, :output
5
+
6
+ def initialize(options = {})
7
+ @input = options.delete(:input)
8
+ @output = options.delete(:output)
9
+ end
10
+
11
+ def original_date_time
12
+ exif = MiniExiftool.new(@input)
13
+ exif.date_time_original || exif.create_date || exif.modify_date
14
+ end
15
+
16
+ private
17
+
18
+ def execute_ffmpeg_command(options = {})
19
+ system("ffmpeg #{options[:raw_options].try(:[], :input)} \
20
+ -i #{options.delete(:input) || @input} \
21
+ #{options[:raw_options].try(:[], :output)} \
22
+ #{options.delete(:output) || @output} \
23
+ -y > /dev/null 2>&1")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ module CutCut
2
+ # Helpers
3
+ class Helpers
4
+ def self.longest_common_substring(*strings)
5
+ short = strings.min_by(&:length)
6
+ max = short.length
7
+ max.downto(0) do |len|
8
+ 0.upto(max - len) do |start|
9
+ substr = short[start, len]
10
+ return substr if strings.all? { |str| str.include? substr }
11
+ end
12
+ end
13
+ end
14
+
15
+ def self.copy_metadata(origin, target)
16
+ exif = MiniExiftool.new(target)
17
+ exif.copy_tags_from(origin, '*')
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,64 @@
1
+ module CutCut
2
+ # Media
3
+ class Media < Base
4
+ attr_reader :output_path
5
+
6
+ def initialize(options = {})
7
+ @input = options.delete(:input)
8
+ @output = options.delete(:output)
9
+ @output_path = options.delete(:output_path) || File.dirname(@input)
10
+ end
11
+
12
+ def convert(options = {})
13
+ scale = options[:scale]
14
+ speed = "-filter:v \"setpts=#{options[:speed]}*PTS\"" if options[:speed]
15
+ copy_metadata = options[:copy_metadata] || false
16
+
17
+ output = options[:output] || @output || File.join(@output_path, '__' + File.basename(@input))
18
+ raw_options = "-movflags +faststart -vf scale=#{scale} -c:v libx264 -crf 20 -preset ultrafast #{speed}"
19
+ execute_ffmpeg_command(input: @input, output: output, raw_options: { output: raw_options })
20
+
21
+ Helpers.copy_metadata(@input, output) if copy_metadata
22
+ output
23
+ end
24
+
25
+ def extract_screenshots(options = {})
26
+ fps = options[:fps] || 1
27
+ basename = options[:basename] || File.basename(@input, '.MP4') + '_screenshot'
28
+ copy_metadata = options[:copy_metadata]
29
+
30
+ execute_ffmpeg_command(
31
+ input: @input,
32
+ output: "#{output_path}/#{basename}%d.jpg",
33
+ raw_options: { output: "-vf fps=#{fps}" }
34
+ )
35
+
36
+ copy_metadata_to_screenshots(basename, fps) if copy_metadata
37
+ end
38
+
39
+ def cut(options = {})
40
+ starts_at = options[:start] || '00:00'
41
+ time = options[:time] || 1
42
+ output = options[:output] || "#{File.basename(@input, '.MP4')}_#{starts_at}.mp4"
43
+
44
+ execute_ffmpeg_command(
45
+ input: @input,
46
+ output: "#{output_path}/#{output}",
47
+ raw_options: { output: "-ss #{starts_at} -t #{time}" }
48
+ )
49
+ end
50
+
51
+ def copy_metadata_to_screenshots(basename, fps)
52
+ Dir.glob("#{output_path}/#{basename}*.jpg").sort.each do |file|
53
+ seconds = screenshot_basename_time(basename, file, fps)
54
+ exif = MiniExiftool.new(file)
55
+ exif.create_date = original_date_time + seconds.seconds
56
+ exif.save
57
+ end
58
+ end
59
+
60
+ def screenshot_basename_time(basename, file, fps)
61
+ (1.0 / fps.to_f) * File.basename(file, '.jpg').gsub(basename, '').to_i - (1.0 / fps.to_f)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,42 @@
1
+ module CutCut
2
+ # Media
3
+ class Timelapse < Base
4
+ def files
5
+ Dir.glob(File.join(input, '*.JPG'))
6
+ end
7
+
8
+ def basenames
9
+ files.map { |e| File.basename(e, '.JPG').to_s }
10
+ end
11
+
12
+ def common_substring
13
+ Helpers.longest_common_substring(*basenames)
14
+ end
15
+
16
+ def start_number
17
+ basenames.first.gsub(common_substring, '')
18
+ end
19
+
20
+ def input_basename
21
+ common_substring + "%#{start_number.chars.count}d.JPG"
22
+ end
23
+
24
+ def original_date_time
25
+ exif = MiniExiftool.new(files.first)
26
+ exif.date_time_original || exif.create_date || exif.modify_date
27
+ end
28
+
29
+ def convert(options = {})
30
+ fps = options[:fps] || 30
31
+ execute_ffmpeg_command(input: File.join(@input, input_basename), output: @output,
32
+ raw_options: {
33
+ input: "-f image2 -start_number #{start_number} -framerate #{fps}",
34
+ output: '-c:v libx264 -r 30 -vf scale=-1:1080 -crf 23 -preset ultrafast -pix_fmt yuv420p'
35
+ }
36
+ )
37
+
38
+ Helpers.copy_metadata(files.first, @output)
39
+ @output
40
+ end
41
+ end
42
+ end
data/lib/cutcut.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'active_support/all'
2
+ require 'cutcut/base'
3
+ require 'cutcut/helpers'
4
+ require 'cutcut/timelapse'
5
+ require 'cutcut/media'
6
+ require 'mini_exiftool'
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe CutCut::Base do
4
+ before(:all) do
5
+ system("rm -rf #{File.join(File.dirname(__FILE__), '../fixtures/*.MP4')}")
6
+ system("rm -rf #{File.join(File.dirname(__FILE__), '../fixtures/*.mp4')}")
7
+ system("cp #{File.join(File.dirname(__FILE__), '../fixtures/example')} #{File.join(File.dirname(__FILE__), '../fixtures/example.MP4')}")
8
+ end
9
+
10
+ it 'has input and output' do
11
+ base = CutCut::Base.new(input: 'input', output: 'output')
12
+ expect(base.input).to eq('input')
13
+ expect(base.output).to eq('output')
14
+ end
15
+
16
+ it 'has original_date_time' do
17
+ base = CutCut::Base.new(input: File.join(File.dirname(__FILE__), '../fixtures/example.MP4'), output: 'output')
18
+ expect(base.original_date_time).to eq('2016-01-25 20:15:35 -0200')
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe CutCut::Helpers do
4
+ it 'return longest comon string' do
5
+ expect(CutCut::Helpers.longest_common_substring('foobar', 'foo')).to eq('foo')
6
+ end
7
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe CutCut::Media do
4
+ let(:media) do
5
+ CutCut::Media.new(input: File.join(File.dirname(__FILE__), '../fixtures/example.MP4'))
6
+ end
7
+
8
+ describe 'initialize' do
9
+ it 'initialize accessors' do
10
+ expect(media.input).to_not eq(nil)
11
+ expect(media.output).to eq(nil)
12
+ expect(media.output_path).to_not eq(nil)
13
+ end
14
+
15
+ it 'return create_date tag' do
16
+ expect(media.original_date_time).to eq('2016-01-25 20:15:35 -0200')
17
+ end
18
+ end
19
+
20
+ describe 'convert' do
21
+ it 'convert media' do
22
+ expect(File.exist?(media.convert(scale: '1920:1080'))).to eq true
23
+ end
24
+
25
+ it 'copy metadata' do
26
+ source = MiniExiftool.new(media.input)
27
+ target = MiniExiftool.new(media.convert(scale: '1920:1080', copy_metadata: true))
28
+ expect(source.create_date).to eq(target.create_date)
29
+ end
30
+ end
31
+
32
+ describe 'cut' do
33
+ it 'cut the video' do
34
+ media.cut(starts_at: '00:00', time: '0.5')
35
+ expect(File.exist?(File.join(File.dirname(__FILE__), '../fixtures/example_00:00.mp4'))).to eq(true)
36
+ end
37
+ end
38
+
39
+ describe 'extract_screenshots' do
40
+ it 'default file to _screenshot' do
41
+ expect(Dir.glob(File.join(File.dirname(__FILE__), '../fixtures/*_screenshot*.jpg')).count).to eq(0)
42
+ media.extract_screenshots(copy_metadata: false)
43
+ expect(Dir.glob(File.join(File.dirname(__FILE__), '../fixtures/*_screenshot*.jpg')).count).to eq(2)
44
+ end
45
+
46
+ it 'allow to save screenshots with a basename' do
47
+ expect(Dir.glob(File.join(File.dirname(__FILE__), '../fixtures/out*.jpg')).count).to eq(0)
48
+ media.extract_screenshots(basename: 'out', copy_metadata: false)
49
+ expect(Dir.glob(File.join(File.dirname(__FILE__), '../fixtures/out*.jpg')).count).to eq(2)
50
+ end
51
+
52
+ it 'extract screenshots based on fps' do
53
+ media.extract_screenshots(fps: 3, copy_metadata: false)
54
+ files = Dir.glob(File.join(File.dirname(__FILE__), '../fixtures/*_screenshot*.jpg'))
55
+ expect(files.count).to eq(4)
56
+ end
57
+
58
+ it 'copy_metadata' do
59
+ media.extract_screenshots(copy_metadata: true)
60
+ files = Dir.glob(File.join(File.dirname(__FILE__), '../fixtures/*_screenshot*.jpg'))
61
+ files.each do |file|
62
+ expect(MiniExiftool.new(file).create_date).to be >= media.original_date_time
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe CutCut::Timelapse do
4
+ let(:timelapse) do
5
+ CutCut::Timelapse.new(
6
+ input: File.join(File.dirname(__FILE__), '../fixtures/timelapse/'),
7
+ output: File.join(File.dirname(__FILE__), '../fixtures/timelapse.mp4')
8
+ )
9
+ end
10
+
11
+ it 'has input and output' do
12
+ expect(timelapse.input).to_not eq(nil)
13
+ expect(timelapse.output).to_not eq(nil)
14
+ end
15
+
16
+ it 'return list of files' do
17
+ expect(timelapse.files.count).to eq(2)
18
+ timelapse.start_number
19
+ end
20
+
21
+ it 'return list of file basenames' do
22
+ expect(timelapse.basenames.count).to eq(2)
23
+ expect(timelapse.basenames.first).to eq('G0023026')
24
+ end
25
+
26
+ it 'return start_number based on first file name' do
27
+ expect(timelapse.start_number).to eq('6')
28
+ end
29
+
30
+ it 'return input_basename based on common string and start_number' do
31
+ expect(timelapse.input_basename).to eq('G002302%1d.JPG')
32
+ end
33
+
34
+ it 'should convert and create timelapse' do
35
+ expect(File.exist?(timelapse.output)).to eq false
36
+ expect(File.exist?(timelapse.convert(fps: '1'))).to eq true
37
+ expect(MiniExiftool.new(timelapse.output).duration).to eq('1.03 s')
38
+ end
39
+
40
+ it 'has original_date_time and copy from first file' do
41
+ expect(timelapse.original_date_time).to eq(MiniExiftool.new(timelapse.output).date_time_original)
42
+ end
43
+ end
Binary file
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'cutcut'
5
+
6
+ RSpec.configure do |config|
7
+ config.raise_errors_for_deprecations!
8
+
9
+ config.before(:all) do
10
+ system("rm -rf #{File.join(File.dirname(__FILE__), '/fixtures/*.MP4')}")
11
+ system("rm -rf #{File.join(File.dirname(__FILE__), '/fixtures/*.mp4')}")
12
+ system("cp #{File.join(File.dirname(__FILE__), '/fixtures/example')} #{File.join(File.dirname(__FILE__), '/fixtures/example.MP4')}")
13
+ end
14
+
15
+ config.before(:each) do
16
+ %w(jpg JPG png PNG).each do |extension|
17
+ system("rm -rf #{File.join(File.dirname(__FILE__), '/fixtures/*.' + extension)}")
18
+ end
19
+ end
20
+
21
+ config.after(:all) do
22
+ %w(jpg JPG png PNG mp4 MP4).each do |extension|
23
+ system("rm -rf #{File.join(File.dirname(__FILE__), '/fixtures/*.' + extension)}")
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cutcut
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jhimy Fernandes Villar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mini_exiftool
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-progressbar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.13'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.2'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.4'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.4'
111
+ description: CLI for working with videos
112
+ email:
113
+ - stjhimy@gmail.com
114
+ executables:
115
+ - cutcut
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rubocop.yml"
121
+ - CHANGELOG.md
122
+ - Gemfile
123
+ - Guardfile
124
+ - LICENSE
125
+ - README.md
126
+ - bin/cutcut
127
+ - cutcut.gemspec
128
+ - lib/cutcut.rb
129
+ - lib/cutcut/base.rb
130
+ - lib/cutcut/helpers.rb
131
+ - lib/cutcut/media.rb
132
+ - lib/cutcut/timelapse.rb
133
+ - spec/cutcut/base_spec.rb
134
+ - spec/cutcut/helpers_spec.rb
135
+ - spec/cutcut/media_spec.rb
136
+ - spec/cutcut/timelapse_spec.rb
137
+ - spec/fixtures/example
138
+ - spec/fixtures/timelapse/G0023026.JPG
139
+ - spec/fixtures/timelapse/G0023027.JPG
140
+ - spec/spec_helper.rb
141
+ homepage: http://github.com/stjhimy/cutcut
142
+ licenses:
143
+ - MIT
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubyforge_project:
161
+ rubygems_version: 2.4.5.1
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: Trim/Cut/Screenshot videos
165
+ test_files:
166
+ - spec/cutcut/base_spec.rb
167
+ - spec/cutcut/helpers_spec.rb
168
+ - spec/cutcut/media_spec.rb
169
+ - spec/cutcut/timelapse_spec.rb
170
+ - spec/fixtures/example
171
+ - spec/fixtures/timelapse/G0023026.JPG
172
+ - spec/fixtures/timelapse/G0023027.JPG
173
+ - spec/spec_helper.rb