videojoiner 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +28 -0
- data/README.rdoc +20 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/features/create_joined_video.feature +24 -0
- data/features/delete_joined_video.feature +18 -0
- data/features/step_definitions/join_steps.rb +59 -0
- data/features/support/env.rb +26 -0
- data/features/support/hooks.rb +18 -0
- data/features/support/sample.mp4 +0 -0
- data/features/support/unknown_format.mp4 +2 -0
- data/features/support/zero_duration.mp4 +0 -0
- data/lib/videojoiner.rb +6 -0
- data/lib/videojoiner/ffmpeg/joiner_process.rb +29 -0
- data/lib/videojoiner/joiner.rb +150 -0
- data/lib/videojoiner/rvideo_patch.rb +36 -0
- data/log/.gitkeep +0 -0
- data/video_root/.gitkeep +0 -0
- data/videojoiner.gemspec +110 -0
- metadata +362 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rspec", "~> 2.8.0"
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "cucumber", ">= 1.2.1"
|
12
|
+
gem "bundler", "~> 1.2.0"
|
13
|
+
gem "jeweler", "~> 1.8.4"
|
14
|
+
gem "rcov", ">= 0"
|
15
|
+
gem "fattr"
|
16
|
+
gem "arrayfields"
|
17
|
+
gem "simplecov", "~> 0.6.4"
|
18
|
+
gem "map", '4.7.1'
|
19
|
+
gem "churn", '~> 0.0.19'
|
20
|
+
gem "chronic", '0.3.0'
|
21
|
+
gem "metrical", "~> 0.1.0"
|
22
|
+
gem "rvideo", "~> 0.9.3"
|
23
|
+
gem "yard", "~> 0.8.3"
|
24
|
+
end
|
25
|
+
|
26
|
+
gem "runnable", "~> 0.3.1"
|
27
|
+
gem "wait_for", "~> 0.1.1"
|
28
|
+
gem "activesupport", "~> 3.2.8"
|
data/README.rdoc
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
= videojoiner
|
2
|
+
|
3
|
+
A gem with methods for joining a set of video files into a single one.
|
4
|
+
Uses FFMpeg for te video concatenation.
|
5
|
+
|
6
|
+
== Contributing to videojoiner
|
7
|
+
|
8
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
9
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
10
|
+
* Fork the project.
|
11
|
+
* Start a feature/bugfix branch.
|
12
|
+
* Commit and push until you are happy with your contribution.
|
13
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
14
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2013 Rafael Carlos Perez Jimenez. See LICENSE.txt for
|
19
|
+
further details.
|
20
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "videojoiner"
|
18
|
+
gem.homepage = "https://projects.nosolosoftware.biz/mwengine/videojoiner"
|
19
|
+
gem.license = "GPLv3"
|
20
|
+
gem.summary = %Q{Gem to join sets of video files into a single video file using ffmpeg}
|
21
|
+
gem.description = %Q{This gem contains modules to create a single video file using a set of videos as input, via ffmpeg concat demuxer.}
|
22
|
+
gem.email = ["rperez@nosolosoftware.biz", "lciudad@nosolosoftware.biz"]
|
23
|
+
gem.authors = ["Rafael Perez", "Luis Ciudad"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'yard'
|
40
|
+
YARD::Rake::YardocTask.new(:doc)
|
41
|
+
|
42
|
+
require 'cucumber/rake/task'
|
43
|
+
Cucumber::Rake::Task.new(:features)
|
44
|
+
|
45
|
+
task :default => :spec
|
46
|
+
|
47
|
+
require 'rdoc/task'
|
48
|
+
Rake::RDocTask.new do |rdoc|
|
49
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "videojoiner #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Join a set of videos into a single file
|
2
|
+
In order to obtain a single video from a set of video files
|
3
|
+
As an operator
|
4
|
+
I want to join the video set into a single file
|
5
|
+
|
6
|
+
Scenario: Create a joined video file from a set of valid videos
|
7
|
+
Given I have a set of correct video files
|
8
|
+
When I create the video joiner job with id "valid_test"
|
9
|
+
And the video joiner job has finished
|
10
|
+
Then there should be a valid video file
|
11
|
+
And it should report all included videos as valid
|
12
|
+
|
13
|
+
Scenario: Create a joined video file from a set of invalid videos
|
14
|
+
Given I have a set of invalid videos
|
15
|
+
When I create the video joiner job with id "invalid_test"
|
16
|
+
And the video joiner job has finished
|
17
|
+
Then it should report that a video from the set is invalid
|
18
|
+
And there should be a valid video file
|
19
|
+
|
20
|
+
Scenario: Create a joined video file with a duplicated id
|
21
|
+
Given I have a set of correct video files
|
22
|
+
When I create the video joiner job with id "duplicated_test"
|
23
|
+
And I create the video joiner job with id "duplicated_test"
|
24
|
+
Then it should report that the job creation was unsucessful
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Feature: Delete a joined video
|
2
|
+
In order to discard a joined video from a set of video files
|
3
|
+
As an operator
|
4
|
+
I want delete the joined video from that set of video files
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I have a set of correct video files
|
8
|
+
|
9
|
+
Scenario: Delete a video joiner job
|
10
|
+
Given I create the video joiner job with id "delete_test"
|
11
|
+
When I delete the video joiner job with id "delete_test"
|
12
|
+
Then the joiner video job with id "delete_test" should not exist
|
13
|
+
|
14
|
+
Scenario: Delete a unexistent video joiner job
|
15
|
+
Given I create the video joiner job with id "delete_unexistent_test"
|
16
|
+
When I delete the video joiner job with id "delete_unexistent_test"
|
17
|
+
And I delete the video joiner job with id "delete_unexistent_test"
|
18
|
+
Then it should report that the job deletion was unsucessful
|
@@ -0,0 +1,59 @@
|
|
1
|
+
Given /^I have a set of correct video files$/ do
|
2
|
+
for i in 1..3 do
|
3
|
+
FileUtils.copy( "features/support/sample.mp4", "/tmp/#{i}.mp4" )
|
4
|
+
end
|
5
|
+
|
6
|
+
@video_routes = [ "/tmp/1.mp4", "/tmp/2.mp4", "/tmp/3.mp4" ]
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I create the video joiner job with id "(.*?)"$/ do | id |
|
10
|
+
@joiner_job = Videojoiner::FFMpeg::Joiner.new( id, @video_routes )
|
11
|
+
@joiner_job.create( { filename: "video_root/#{id}_#{Time.now.to_i}" } )
|
12
|
+
end
|
13
|
+
|
14
|
+
When /^the video joiner job has finished$/ do
|
15
|
+
wait_for( 5 ){
|
16
|
+
@joiner_job.finished?
|
17
|
+
}.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^there should be a valid video file$/ do
|
21
|
+
File.exist?( @joiner_job.output_file )
|
22
|
+
file = RVideo::Inspector.new( :file => @joiner_job.output_file )
|
23
|
+
file.should be_valid
|
24
|
+
end
|
25
|
+
|
26
|
+
Then /^it should report all included videos as valid$/ do
|
27
|
+
job = Videojoiner::FFMpeg::Joiner.fetch( @joiner_job.id )
|
28
|
+
job.video_list.each_value{ |value| value.should == "valid" }
|
29
|
+
end
|
30
|
+
|
31
|
+
Given /^I have a set of invalid videos$/ do
|
32
|
+
for i in 1..2 do
|
33
|
+
FileUtils.copy( "features/support/sample.mp4", "/tmp/#{i}.mp4" )
|
34
|
+
end
|
35
|
+
FileUtils.copy( "features/support/unknown_format.mp4", "/tmp/3.mp4" )
|
36
|
+
FileUtils.copy( "features/support/zero_duration.mp4", "/tmp/4.mp4" )
|
37
|
+
@video_routes = [ "/tmp/1.mp4", "/tmp/2.mp4", "/tmp/3.mp4", "/tmp/4.mp4" ]
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^it should report that a video from the set is invalid$/ do
|
41
|
+
job = Videojoiner::FFMpeg::Joiner.fetch( @joiner_job.id )
|
42
|
+
job.video_list.to_s.count( "invalid" ) == 2
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^it should report that the job creation was unsucessful$/ do
|
46
|
+
@status.should be_false
|
47
|
+
end
|
48
|
+
|
49
|
+
When /^I delete the video joiner job with id "(.*?)"$/ do | id |
|
50
|
+
@status = @joiner_job.delete
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /^the joiner video job with id "(.*?)" should not exist$/ do | id |
|
54
|
+
Videojoiner::FFMpeg::Joiner.exist?( id ).should be_false
|
55
|
+
end
|
56
|
+
|
57
|
+
Then /^it should report that the job deletion was unsucessful$/ do
|
58
|
+
@status.should be_false
|
59
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter 'lib/videojoiner/rvideo_patch.rb'
|
5
|
+
add_filter 'features/'
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH << File.expand_path( '../../../lib/', __FILE__ )
|
9
|
+
$LOAD_PATH << File.expand_path( '../', __FILE__ )
|
10
|
+
|
11
|
+
require 'bundler'
|
12
|
+
require 'wait_for'
|
13
|
+
require 'videojoiner'
|
14
|
+
require 'rspec/expectations'
|
15
|
+
|
16
|
+
begin
|
17
|
+
Bundler.setup(:default, :development)
|
18
|
+
rescue Bundler::BundlerError => e
|
19
|
+
$stderr.puts e.message
|
20
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
21
|
+
exit e.status_code
|
22
|
+
end
|
23
|
+
|
24
|
+
Videojoiner::FFMpeg::Joiner.configure do |c|
|
25
|
+
c.config_path = "/tmp/"
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
After do |scenario|
|
2
|
+
Dir.new( "video_root" ).each do |filename|
|
3
|
+
if /.*\.(mp4|avi|mpg)/ =~ filename
|
4
|
+
FileUtils.rm( "video_root/#{filename}" )
|
5
|
+
end
|
6
|
+
end
|
7
|
+
Dir.new( "/tmp/" ).each do |filename|
|
8
|
+
if /.*\.(ffmpeg|mp4)/ =~ filename
|
9
|
+
FileUtils.rm( "/tmp/#{filename}" )
|
10
|
+
end
|
11
|
+
end
|
12
|
+
Dir.new( "log/" ).each do |filename|
|
13
|
+
if /.*\.(log)/ =~ filename
|
14
|
+
FileUtils.rm( "log/#{filename}" )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
Videojoiner::FFMpeg::Joiner.clear_list
|
18
|
+
end
|
Binary file
|
Binary file
|
data/lib/videojoiner.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Videojoiner
|
2
|
+
module FFMpeg
|
3
|
+
class JoinerProcess
|
4
|
+
include Runnable
|
5
|
+
|
6
|
+
executes :ffmpeg
|
7
|
+
|
8
|
+
define_command( :start ){ |id, config_file, options|
|
9
|
+
"-loglevel verbose
|
10
|
+
-f concat
|
11
|
+
-i #{config_file}
|
12
|
+
-vcodec #{options[:vcodec]}
|
13
|
+
-acodec #{options[:acodec]}
|
14
|
+
-strict
|
15
|
+
-2
|
16
|
+
#{options[:filename]}.#{options[:extension]}"
|
17
|
+
}
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
def make_ffmpeg_config( video_list, config_file )
|
22
|
+
File.open( "#{config_file}","w" ) do |file|
|
23
|
+
video_list.each_pair{ |source, status| file.puts( "file '#{source}'" ) unless status == "invalid" }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Videojoiner
|
2
|
+
module FFMpeg
|
3
|
+
class Joiner
|
4
|
+
|
5
|
+
class ConfigurationError < RuntimeError; end
|
6
|
+
|
7
|
+
attr_reader :id, :video_list, :output_file, :config_file, :process
|
8
|
+
|
9
|
+
# Initialize a new joiner object
|
10
|
+
# @param id [string] The joiner job ID
|
11
|
+
# @param video_sources [array] An array of video files
|
12
|
+
def initialize( id, video_sources )
|
13
|
+
@id = id
|
14
|
+
@video_list = self.class.probe_videos( video_sources )
|
15
|
+
@output_file = ""
|
16
|
+
@process = Videojoiner::FFMpeg::JoinerProcess.new
|
17
|
+
@process.log_path = "log/"
|
18
|
+
# Path to a text file with the list of videos to be used for ffmpeg's concat demuxer
|
19
|
+
@config_file = "#{self.class.config_path}#{id}_#{Time.now.to_i}.ffmpeg"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Starts the video joiner job and adds it to the job list
|
23
|
+
# @param opts [hash] A hash with configuration options for the transcoder
|
24
|
+
def create( opts = {} )
|
25
|
+
# FFMpeg options hash
|
26
|
+
options = {
|
27
|
+
:vcodec => $1 || "copy",
|
28
|
+
:acodec => $2 || "copy",
|
29
|
+
:filename => $3 || "#{@id}_#{Time.now.to_i}",
|
30
|
+
:extension => $4 || "mp4"
|
31
|
+
}.merge( opts )
|
32
|
+
|
33
|
+
if self.class.exist?( @id )
|
34
|
+
false
|
35
|
+
else
|
36
|
+
Videojoiner::FFMpeg::JoinerProcess.make_ffmpeg_config( @video_list, @config_file )
|
37
|
+
@process.start( @id, @config_file, options )
|
38
|
+
self.class.add_job( @id, self )
|
39
|
+
@output_file = "#{options[:filename]}.#{options[:extension]}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Delete a previously created video joiner job and remove it from the job list
|
44
|
+
# @return true if the job removal was sucessful
|
45
|
+
# @return false otherwise
|
46
|
+
def delete
|
47
|
+
if self.class.exist?( @id )
|
48
|
+
self.class.remove_job( @id )
|
49
|
+
File.delete( "#{@config_file}" ) unless !File.exist?( "#{@config_file}" )
|
50
|
+
File.delete( "#{@output_file}" ) unless !File.exist?( "#{@output_file}" )
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Method that check if the join process has finished
|
57
|
+
# @return true if the job has finished
|
58
|
+
# @return false otherwise
|
59
|
+
def finished?
|
60
|
+
output = @process.std_err
|
61
|
+
output.include?( "No more output streams to write to, finishing." ) && output.include?( "muxing overhead" )
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
|
66
|
+
attr_accessor :config_path
|
67
|
+
|
68
|
+
# Check that the video sources passed has parameters are valid for the joining process
|
69
|
+
# @param video_sources [array] an array with the source videos
|
70
|
+
# @return a hash with the source videos and its validation status
|
71
|
+
def probe_videos( video_sources )
|
72
|
+
video_list = Hash.new
|
73
|
+
|
74
|
+
video_sources.each do |source|
|
75
|
+
inspector = RVideo::Inspector.new( :file => "#{source}" )
|
76
|
+
if ( inspector.valid? ) && ( inspector.duration > 0 )
|
77
|
+
video_list.store( source, 'valid' )
|
78
|
+
else
|
79
|
+
video_list.store( source, 'invalid' )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
video_list
|
83
|
+
end
|
84
|
+
|
85
|
+
# Check if the job list is empty
|
86
|
+
# @return true if the list is empty
|
87
|
+
# @return false otherwise
|
88
|
+
def empty?
|
89
|
+
joiner_list.empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Fetches a job from the job list by it ID
|
93
|
+
# @param id [string] the job ID
|
94
|
+
# @return the job with that ID
|
95
|
+
def fetch( id )
|
96
|
+
joiner_list.fetch( id ) unless empty?
|
97
|
+
end
|
98
|
+
|
99
|
+
# Check if a job exist in the job list
|
100
|
+
# @param id [string] the job ID
|
101
|
+
# @return true if the job exist
|
102
|
+
# @return false otherwise
|
103
|
+
def exist?( id )
|
104
|
+
joiner_list.has_key?( id ) unless empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Clears the job list
|
108
|
+
def clear_list
|
109
|
+
joiner_list.clear unless empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
# Adds a video joiner job to the job list
|
113
|
+
# @param id [string] the job ID
|
114
|
+
# @param joiner [Videojoiner::FFMpeg::Joiner] the joiner job object
|
115
|
+
def add_job( id, joiner )
|
116
|
+
joiner_list.store( id, joiner )
|
117
|
+
end
|
118
|
+
|
119
|
+
# Removes a video joiner job from the job list
|
120
|
+
# @param id [string] the job ID
|
121
|
+
def remove_job( id )
|
122
|
+
joiner_list.fetch( id ).process.stop unless joiner_list.fetch( id ).process.pid == nil
|
123
|
+
joiner_list.delete( id )
|
124
|
+
end
|
125
|
+
|
126
|
+
# Yields the current object to configure some required parameters
|
127
|
+
def configure( &block )
|
128
|
+
yield self
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
# Return the joiner job list, or generate an empty list if it don't exist already
|
134
|
+
# It requires the setting of the config_path parameter previously
|
135
|
+
def joiner_list
|
136
|
+
raise ConfigurationError, "Missing configuration: please check Joiner.config_path" unless configured?
|
137
|
+
raise ConfigPathError, "Config path not exist" unless Dir.exist?( config_path )
|
138
|
+
@joiner_list ||= Hash.new
|
139
|
+
end
|
140
|
+
|
141
|
+
# Return the configuration status of the object
|
142
|
+
# @return the configuration path
|
143
|
+
# @return false otherwise
|
144
|
+
def configured?
|
145
|
+
config_path
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class RVideo::Inspector
|
2
|
+
def initialize(options = {})
|
3
|
+
if options[:raw_response]
|
4
|
+
@raw_response = options[:raw_response]
|
5
|
+
elsif options[:file]
|
6
|
+
if options[:ffmpeg_binary]
|
7
|
+
@ffmpeg_binary = options[:ffmpeg_binary]
|
8
|
+
raise RuntimeError, "ffmpeg could not be found (trying #{@ffmpeg_binary})" unless FileTest.exist?(@ffmpeg_binary)
|
9
|
+
else
|
10
|
+
# assume it is in the unix path
|
11
|
+
raise RuntimeError, 'ffmpeg could not be found (expected ffmpeg to be found in the Unix path)' unless FileTest.exist?(`which ffmpeg`.chomp)
|
12
|
+
@ffmpeg_binary = "ffmpeg"
|
13
|
+
end
|
14
|
+
|
15
|
+
file = options[:file]
|
16
|
+
@filename = File.basename(file)
|
17
|
+
@path = File.dirname(file)
|
18
|
+
@full_filename = file
|
19
|
+
raise ArgumentError, "File not found (#{file})" unless FileTest.exist?(file.gsub("\"",""))
|
20
|
+
@raw_response = `#{@ffmpeg_binary} -i #{@full_filename} 2>&1`
|
21
|
+
else
|
22
|
+
raise ArgumentError, "Must supply either an input file or a pregenerated response" if options[:raw_response].nil? and file.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
metadata = /(Input \#.*)\n/m.match(@raw_response)
|
26
|
+
|
27
|
+
if /Unknown format/i.match(@raw_response) || metadata.nil?
|
28
|
+
@unknown_format = true
|
29
|
+
elsif /Duration: N\/A|bitrate: N\/A/im.match(@raw_response)
|
30
|
+
@unreadable_file = true
|
31
|
+
@raw_metadata = metadata[1] # in this case, we can at least still get the container type
|
32
|
+
else
|
33
|
+
@raw_metadata = metadata[1]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/log/.gitkeep
ADDED
File without changes
|
data/video_root/.gitkeep
ADDED
File without changes
|
data/videojoiner.gemspec
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "videojoiner"
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Rafael Perez", "Luis Ciudad"]
|
12
|
+
s.date = "2013-01-24"
|
13
|
+
s.description = "This gem contains modules to create a single video file using a set of videos as input, via ffmpeg concat demuxer."
|
14
|
+
s.email = ["rperez@nosolosoftware.biz", "lciudad@nosolosoftware.biz"]
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".rspec",
|
21
|
+
"Gemfile",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"features/create_joined_video.feature",
|
26
|
+
"features/delete_joined_video.feature",
|
27
|
+
"features/step_definitions/join_steps.rb",
|
28
|
+
"features/support/env.rb",
|
29
|
+
"features/support/hooks.rb",
|
30
|
+
"features/support/sample.mp4",
|
31
|
+
"features/support/unknown_format.mp4",
|
32
|
+
"features/support/zero_duration.mp4",
|
33
|
+
"lib/videojoiner.rb",
|
34
|
+
"lib/videojoiner/ffmpeg/joiner_process.rb",
|
35
|
+
"lib/videojoiner/joiner.rb",
|
36
|
+
"lib/videojoiner/rvideo_patch.rb",
|
37
|
+
"log/.gitkeep",
|
38
|
+
"video_root/.gitkeep",
|
39
|
+
"videojoiner.gemspec"
|
40
|
+
]
|
41
|
+
s.homepage = "https://projects.nosolosoftware.biz/mwengine/videojoiner"
|
42
|
+
s.licenses = ["GPLv3"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.rubygems_version = "1.8.24"
|
45
|
+
s.summary = "Gem to join sets of video files into a single video file using ffmpeg"
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_runtime_dependency(%q<runnable>, ["~> 0.3.1"])
|
52
|
+
s.add_runtime_dependency(%q<wait_for>, ["~> 0.1.1"])
|
53
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.2.8"])
|
54
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
55
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
56
|
+
s.add_development_dependency(%q<cucumber>, [">= 1.2.1"])
|
57
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.2.0"])
|
58
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
59
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<fattr>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<arrayfields>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<simplecov>, ["~> 0.6.4"])
|
63
|
+
s.add_development_dependency(%q<map>, ["= 4.7.1"])
|
64
|
+
s.add_development_dependency(%q<churn>, ["~> 0.0.19"])
|
65
|
+
s.add_development_dependency(%q<chronic>, ["= 0.3.0"])
|
66
|
+
s.add_development_dependency(%q<metrical>, ["~> 0.1.0"])
|
67
|
+
s.add_development_dependency(%q<rvideo>, ["~> 0.9.3"])
|
68
|
+
s.add_development_dependency(%q<yard>, ["~> 0.8.3"])
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<runnable>, ["~> 0.3.1"])
|
71
|
+
s.add_dependency(%q<wait_for>, ["~> 0.1.1"])
|
72
|
+
s.add_dependency(%q<activesupport>, ["~> 3.2.8"])
|
73
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
74
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
75
|
+
s.add_dependency(%q<cucumber>, [">= 1.2.1"])
|
76
|
+
s.add_dependency(%q<bundler>, ["~> 1.2.0"])
|
77
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
78
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
79
|
+
s.add_dependency(%q<fattr>, [">= 0"])
|
80
|
+
s.add_dependency(%q<arrayfields>, [">= 0"])
|
81
|
+
s.add_dependency(%q<simplecov>, ["~> 0.6.4"])
|
82
|
+
s.add_dependency(%q<map>, ["= 4.7.1"])
|
83
|
+
s.add_dependency(%q<churn>, ["~> 0.0.19"])
|
84
|
+
s.add_dependency(%q<chronic>, ["= 0.3.0"])
|
85
|
+
s.add_dependency(%q<metrical>, ["~> 0.1.0"])
|
86
|
+
s.add_dependency(%q<rvideo>, ["~> 0.9.3"])
|
87
|
+
s.add_dependency(%q<yard>, ["~> 0.8.3"])
|
88
|
+
end
|
89
|
+
else
|
90
|
+
s.add_dependency(%q<runnable>, ["~> 0.3.1"])
|
91
|
+
s.add_dependency(%q<wait_for>, ["~> 0.1.1"])
|
92
|
+
s.add_dependency(%q<activesupport>, ["~> 3.2.8"])
|
93
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
94
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
95
|
+
s.add_dependency(%q<cucumber>, [">= 1.2.1"])
|
96
|
+
s.add_dependency(%q<bundler>, ["~> 1.2.0"])
|
97
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
98
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
99
|
+
s.add_dependency(%q<fattr>, [">= 0"])
|
100
|
+
s.add_dependency(%q<arrayfields>, [">= 0"])
|
101
|
+
s.add_dependency(%q<simplecov>, ["~> 0.6.4"])
|
102
|
+
s.add_dependency(%q<map>, ["= 4.7.1"])
|
103
|
+
s.add_dependency(%q<churn>, ["~> 0.0.19"])
|
104
|
+
s.add_dependency(%q<chronic>, ["= 0.3.0"])
|
105
|
+
s.add_dependency(%q<metrical>, ["~> 0.1.0"])
|
106
|
+
s.add_dependency(%q<rvideo>, ["~> 0.9.3"])
|
107
|
+
s.add_dependency(%q<yard>, ["~> 0.8.3"])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
metadata
ADDED
@@ -0,0 +1,362 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: videojoiner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rafael Perez
|
9
|
+
- Luis Ciudad
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-01-24 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: runnable
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.3.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 0.3.1
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: wait_for
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 0.1.1
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.1.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: activesupport
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.2.8
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 3.2.8
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rspec
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ~>
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 2.8.0
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.8.0
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: rdoc
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ~>
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '3.12'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ~>
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '3.12'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: cucumber
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.2.1
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.2.1
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: bundler
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ~>
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.2.0
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ~>
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: 1.2.0
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: jeweler
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ~>
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 1.8.4
|
135
|
+
type: :development
|
136
|
+
prerelease: false
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ~>
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: 1.8.4
|
143
|
+
- !ruby/object:Gem::Dependency
|
144
|
+
name: rcov
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
146
|
+
none: false
|
147
|
+
requirements:
|
148
|
+
- - ! '>='
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
type: :development
|
152
|
+
prerelease: false
|
153
|
+
version_requirements: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: fattr
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ! '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
type: :development
|
168
|
+
prerelease: false
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
171
|
+
requirements:
|
172
|
+
- - ! '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
- !ruby/object:Gem::Dependency
|
176
|
+
name: arrayfields
|
177
|
+
requirement: !ruby/object:Gem::Requirement
|
178
|
+
none: false
|
179
|
+
requirements:
|
180
|
+
- - ! '>='
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '0'
|
183
|
+
type: :development
|
184
|
+
prerelease: false
|
185
|
+
version_requirements: !ruby/object:Gem::Requirement
|
186
|
+
none: false
|
187
|
+
requirements:
|
188
|
+
- - ! '>='
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
- !ruby/object:Gem::Dependency
|
192
|
+
name: simplecov
|
193
|
+
requirement: !ruby/object:Gem::Requirement
|
194
|
+
none: false
|
195
|
+
requirements:
|
196
|
+
- - ~>
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: 0.6.4
|
199
|
+
type: :development
|
200
|
+
prerelease: false
|
201
|
+
version_requirements: !ruby/object:Gem::Requirement
|
202
|
+
none: false
|
203
|
+
requirements:
|
204
|
+
- - ~>
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
version: 0.6.4
|
207
|
+
- !ruby/object:Gem::Dependency
|
208
|
+
name: map
|
209
|
+
requirement: !ruby/object:Gem::Requirement
|
210
|
+
none: false
|
211
|
+
requirements:
|
212
|
+
- - '='
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: 4.7.1
|
215
|
+
type: :development
|
216
|
+
prerelease: false
|
217
|
+
version_requirements: !ruby/object:Gem::Requirement
|
218
|
+
none: false
|
219
|
+
requirements:
|
220
|
+
- - '='
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 4.7.1
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: churn
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
none: false
|
227
|
+
requirements:
|
228
|
+
- - ~>
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: 0.0.19
|
231
|
+
type: :development
|
232
|
+
prerelease: false
|
233
|
+
version_requirements: !ruby/object:Gem::Requirement
|
234
|
+
none: false
|
235
|
+
requirements:
|
236
|
+
- - ~>
|
237
|
+
- !ruby/object:Gem::Version
|
238
|
+
version: 0.0.19
|
239
|
+
- !ruby/object:Gem::Dependency
|
240
|
+
name: chronic
|
241
|
+
requirement: !ruby/object:Gem::Requirement
|
242
|
+
none: false
|
243
|
+
requirements:
|
244
|
+
- - '='
|
245
|
+
- !ruby/object:Gem::Version
|
246
|
+
version: 0.3.0
|
247
|
+
type: :development
|
248
|
+
prerelease: false
|
249
|
+
version_requirements: !ruby/object:Gem::Requirement
|
250
|
+
none: false
|
251
|
+
requirements:
|
252
|
+
- - '='
|
253
|
+
- !ruby/object:Gem::Version
|
254
|
+
version: 0.3.0
|
255
|
+
- !ruby/object:Gem::Dependency
|
256
|
+
name: metrical
|
257
|
+
requirement: !ruby/object:Gem::Requirement
|
258
|
+
none: false
|
259
|
+
requirements:
|
260
|
+
- - ~>
|
261
|
+
- !ruby/object:Gem::Version
|
262
|
+
version: 0.1.0
|
263
|
+
type: :development
|
264
|
+
prerelease: false
|
265
|
+
version_requirements: !ruby/object:Gem::Requirement
|
266
|
+
none: false
|
267
|
+
requirements:
|
268
|
+
- - ~>
|
269
|
+
- !ruby/object:Gem::Version
|
270
|
+
version: 0.1.0
|
271
|
+
- !ruby/object:Gem::Dependency
|
272
|
+
name: rvideo
|
273
|
+
requirement: !ruby/object:Gem::Requirement
|
274
|
+
none: false
|
275
|
+
requirements:
|
276
|
+
- - ~>
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: 0.9.3
|
279
|
+
type: :development
|
280
|
+
prerelease: false
|
281
|
+
version_requirements: !ruby/object:Gem::Requirement
|
282
|
+
none: false
|
283
|
+
requirements:
|
284
|
+
- - ~>
|
285
|
+
- !ruby/object:Gem::Version
|
286
|
+
version: 0.9.3
|
287
|
+
- !ruby/object:Gem::Dependency
|
288
|
+
name: yard
|
289
|
+
requirement: !ruby/object:Gem::Requirement
|
290
|
+
none: false
|
291
|
+
requirements:
|
292
|
+
- - ~>
|
293
|
+
- !ruby/object:Gem::Version
|
294
|
+
version: 0.8.3
|
295
|
+
type: :development
|
296
|
+
prerelease: false
|
297
|
+
version_requirements: !ruby/object:Gem::Requirement
|
298
|
+
none: false
|
299
|
+
requirements:
|
300
|
+
- - ~>
|
301
|
+
- !ruby/object:Gem::Version
|
302
|
+
version: 0.8.3
|
303
|
+
description: This gem contains modules to create a single video file using a set of
|
304
|
+
videos as input, via ffmpeg concat demuxer.
|
305
|
+
email:
|
306
|
+
- rperez@nosolosoftware.biz
|
307
|
+
- lciudad@nosolosoftware.biz
|
308
|
+
executables: []
|
309
|
+
extensions: []
|
310
|
+
extra_rdoc_files:
|
311
|
+
- README.rdoc
|
312
|
+
files:
|
313
|
+
- .document
|
314
|
+
- .rspec
|
315
|
+
- Gemfile
|
316
|
+
- README.rdoc
|
317
|
+
- Rakefile
|
318
|
+
- VERSION
|
319
|
+
- features/create_joined_video.feature
|
320
|
+
- features/delete_joined_video.feature
|
321
|
+
- features/step_definitions/join_steps.rb
|
322
|
+
- features/support/env.rb
|
323
|
+
- features/support/hooks.rb
|
324
|
+
- features/support/sample.mp4
|
325
|
+
- features/support/unknown_format.mp4
|
326
|
+
- features/support/zero_duration.mp4
|
327
|
+
- lib/videojoiner.rb
|
328
|
+
- lib/videojoiner/ffmpeg/joiner_process.rb
|
329
|
+
- lib/videojoiner/joiner.rb
|
330
|
+
- lib/videojoiner/rvideo_patch.rb
|
331
|
+
- log/.gitkeep
|
332
|
+
- video_root/.gitkeep
|
333
|
+
- videojoiner.gemspec
|
334
|
+
homepage: https://projects.nosolosoftware.biz/mwengine/videojoiner
|
335
|
+
licenses:
|
336
|
+
- GPLv3
|
337
|
+
post_install_message:
|
338
|
+
rdoc_options: []
|
339
|
+
require_paths:
|
340
|
+
- lib
|
341
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
342
|
+
none: false
|
343
|
+
requirements:
|
344
|
+
- - ! '>='
|
345
|
+
- !ruby/object:Gem::Version
|
346
|
+
version: '0'
|
347
|
+
segments:
|
348
|
+
- 0
|
349
|
+
hash: -128888713428271127
|
350
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
351
|
+
none: false
|
352
|
+
requirements:
|
353
|
+
- - ! '>='
|
354
|
+
- !ruby/object:Gem::Version
|
355
|
+
version: '0'
|
356
|
+
requirements: []
|
357
|
+
rubyforge_project:
|
358
|
+
rubygems_version: 1.8.24
|
359
|
+
signing_key:
|
360
|
+
specification_version: 3
|
361
|
+
summary: Gem to join sets of video files into a single video file using ffmpeg
|
362
|
+
test_files: []
|