cutcut 1.1.0
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 +15 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile +3 -0
- data/Guardfile +17 -0
- data/LICENSE +20 -0
- data/README.md +35 -0
- data/bin/cutcut +41 -0
- data/cutcut.gemspec +24 -0
- data/lib/cutcut/base.rb +26 -0
- data/lib/cutcut/helpers.rb +20 -0
- data/lib/cutcut/media.rb +64 -0
- data/lib/cutcut/timelapse.rb +42 -0
- data/lib/cutcut.rb +6 -0
- data/spec/cutcut/base_spec.rb +20 -0
- data/spec/cutcut/helpers_spec.rb +7 -0
- data/spec/cutcut/media_spec.rb +66 -0
- data/spec/cutcut/timelapse_spec.rb +43 -0
- data/spec/fixtures/example +0 -0
- data/spec/fixtures/timelapse/G0023026.JPG +0 -0
- data/spec/fixtures/timelapse/G0023027.JPG +0 -0
- data/spec/spec_helper.rb +26 -0
- metadata +173 -0
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
data/.rubocop.yml
ADDED
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
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
|
data/lib/cutcut/base.rb
ADDED
@@ -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
|
data/lib/cutcut/media.rb
ADDED
@@ -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,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,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
|
Binary file
|
Binary file
|
data/spec/spec_helper.rb
ADDED
@@ -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
|