media 0.0.3 → 0.0.4
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.
- data/README.md +22 -35
- data/lib/media.rb +11 -6
- data/lib/media/command/converter.rb +50 -4
- data/lib/media/command/probe.rb +7 -3
- data/lib/media/command/progress.rb +36 -0
- data/lib/media/command/subshell.rb +21 -2
- data/lib/media/container.rb +8 -4
- data/lib/media/filter.rb +26 -5
- data/lib/media/filter/argument.rb +3 -3
- data/lib/media/filter/chain.rb +8 -1
- data/lib/media/filter/graph.rb +9 -2
- data/lib/media/helper.rb +2 -0
- data/lib/media/helper/label.rb +13 -0
- data/lib/media/helper/size.rb +14 -0
- data/lib/media/input.rb +21 -6
- data/lib/media/option.rb +4 -4
- data/lib/media/output.rb +33 -5
- data/lib/media/version.rb +1 -1
- data/test/media/command/test_converter.rb +11 -7
- data/test/media/command/test_probe.rb +4 -4
- data/test/media/command/test_subshell.rb +13 -4
- data/test/media/filter/test_graph.rb +1 -1
- data/test/media/{test_label.rb → helper/test_label.rb} +2 -2
- data/test/media/test_input.rb +2 -2
- data/test/media/test_option.rb +3 -3
- data/test/media/test_output.rb +2 -2
- metadata +14 -22
- data/lib/media/builder/command/converter.rb +0 -55
- data/lib/media/builder/filter.rb +0 -48
- data/lib/media/builder/filter/chain.rb +0 -41
- data/lib/media/builder/filter/graph.rb +0 -43
- data/lib/media/builder/input.rb +0 -46
- data/lib/media/builder/output.rb +0 -51
- data/lib/media/label.rb +0 -11
- data/lib/media/size.rb +0 -12
data/README.md
CHANGED
@@ -4,6 +4,10 @@ An `ffmpeg` or `avconv` wrapper
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
+
Install ffmpeg:
|
8
|
+
|
9
|
+
brew install ffmpeg
|
10
|
+
|
7
11
|
Add this line to your application's Gemfile:
|
8
12
|
|
9
13
|
gem 'media'
|
@@ -18,56 +22,39 @@ Or install it yourself as:
|
|
18
22
|
|
19
23
|
## Usage
|
20
24
|
|
21
|
-
Media.convert do
|
22
|
-
|
25
|
+
conversion = Media.convert do
|
26
|
+
options y: true
|
23
27
|
|
24
|
-
input '
|
25
|
-
|
26
|
-
input 'in2.mov' do
|
27
|
-
option 'foo', 'bar'
|
28
|
+
input 'http://www.google.com/images/srpr/logo3w.png' do
|
29
|
+
options loop: 1, f: 'image2'
|
28
30
|
end
|
29
31
|
|
30
|
-
output '
|
32
|
+
output '/path/to/test2.webm' do
|
33
|
+
options vcodec: 'libvpx', acodec: 'libvorbis', t: 4
|
34
|
+
maps label('video'), label('audio')
|
31
35
|
graph do
|
32
36
|
chain do
|
33
|
-
filter '
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
filter 'fifo'
|
38
|
-
filter 'overlay' do
|
39
|
-
input 'T2'
|
40
|
-
arg '0'
|
41
|
-
arg 'H/2'
|
42
|
-
output 'out'
|
37
|
+
filter 'negate'
|
38
|
+
filter 'hflip' do
|
39
|
+
outputs 'video'
|
43
40
|
end
|
44
41
|
end
|
45
42
|
chain do
|
46
|
-
filter '
|
47
|
-
|
48
|
-
|
49
|
-
filter 'crop' do
|
50
|
-
arg 'iw'
|
51
|
-
arg 'ih/2'
|
52
|
-
arg '0'
|
53
|
-
arg 'ih/2'
|
54
|
-
end
|
55
|
-
filter 'vflip' do
|
56
|
-
output 'T2'
|
43
|
+
filter 'aevalsrc' do
|
44
|
+
arguments 'sin(440*2*PI*t)' => true
|
45
|
+
outputs 'audio'
|
57
46
|
end
|
58
47
|
end
|
59
48
|
end
|
60
|
-
|
61
|
-
map 'out'
|
62
|
-
|
63
|
-
option 'f', 'prores'
|
64
49
|
end
|
65
50
|
end
|
66
|
-
|
67
|
-
Outputs:
|
68
51
|
|
69
|
-
|
52
|
+
conversion.call {|progress| p progress}
|
70
53
|
|
54
|
+
Outputs:
|
55
|
+
|
56
|
+
ffmpeg -v info -y -loop 1 -f image2 -i http://www.google.com/images/srpr/logo3w.png -vcodec libvpx -acodec libvorbis -t 4 -map [video] -map [audio] -filter_complex negate, hflip [video]; aevalsrc=sin(440*2*PI*t) [audio] /path/to/test2.webm
|
57
|
+
|
71
58
|
## Contributing
|
72
59
|
|
73
60
|
1. Fork it
|
data/lib/media.rb
CHANGED
@@ -4,17 +4,22 @@ require_relative 'media/command'
|
|
4
4
|
require_relative 'media/container'
|
5
5
|
require_relative 'media/filter'
|
6
6
|
require_relative 'media/input'
|
7
|
-
require_relative 'media/label'
|
8
7
|
require_relative 'media/option'
|
9
8
|
require_relative 'media/output'
|
10
|
-
require_relative 'media/
|
11
|
-
|
12
|
-
require_relative 'media/builder/command/converter'
|
9
|
+
require_relative 'media/helper'
|
13
10
|
|
14
11
|
module Media
|
15
12
|
extend self
|
16
13
|
|
17
|
-
def convert(&
|
18
|
-
|
14
|
+
def convert(&block)
|
15
|
+
Media::Command::Converter.new(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def size(args)
|
19
|
+
Media::Helper::Size.new(args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def label(name)
|
23
|
+
Media::Helper::Label.new(name: name)
|
19
24
|
end
|
20
25
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require_relative 'subshell'
|
2
|
+
require_relative 'progress'
|
3
|
+
require_relative '../option'
|
2
4
|
|
3
5
|
module Media
|
4
6
|
module Command
|
@@ -6,21 +8,65 @@ module Media
|
|
6
8
|
|
7
9
|
attr_accessor :options, :inputs, :outputs
|
8
10
|
|
9
|
-
def initialize(args={})
|
11
|
+
def initialize(args={}, &block)
|
10
12
|
@options = Array args.fetch(:options, [])
|
11
13
|
@inputs = Array args.fetch(:inputs, [])
|
12
14
|
@outputs = Array args.fetch(:outputs, [])
|
13
15
|
|
14
16
|
@cmd = args.fetch(:cmd, 'ffmpeg')
|
15
17
|
@subshell = args.fetch(:subshell, Subshell)
|
18
|
+
@progress = args.fetch(:progress, Progress)
|
19
|
+
|
20
|
+
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(&blk)
|
24
|
+
progress = @progress.new
|
25
|
+
|
26
|
+
process = @subshell.new(cmd: to_a).call do |line|
|
27
|
+
progress.update(line, &blk)
|
28
|
+
end
|
29
|
+
progress.complete(&blk) if process.success?
|
30
|
+
|
31
|
+
process
|
16
32
|
end
|
17
33
|
|
18
|
-
def
|
19
|
-
|
34
|
+
def to_a
|
35
|
+
[
|
36
|
+
@cmd,
|
37
|
+
(required_options + options).map(&:to_a),
|
38
|
+
inputs.map(&:to_a),
|
39
|
+
outputs.map(&:to_a)
|
40
|
+
].flatten
|
20
41
|
end
|
21
42
|
|
22
43
|
def to_s
|
23
|
-
|
44
|
+
to_a.join(' ')
|
45
|
+
end
|
46
|
+
|
47
|
+
def options(value=nil)
|
48
|
+
return @options unless value
|
49
|
+
|
50
|
+
@options = value.map {|k,v| Option.new(key: k, value: v)}
|
51
|
+
end
|
52
|
+
alias_method :options=, :options
|
53
|
+
|
54
|
+
def add_input(url, &block)
|
55
|
+
@inputs << Input.new(url: url, &block)
|
56
|
+
end
|
57
|
+
alias_method :input, :add_input
|
58
|
+
|
59
|
+
def add_output(url, &block)
|
60
|
+
@outputs << Output.new(url: url, &block)
|
61
|
+
end
|
62
|
+
alias_method :output, :add_output
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def required_options
|
67
|
+
[
|
68
|
+
Option.new(key: 'v', value: 'info')
|
69
|
+
]
|
24
70
|
end
|
25
71
|
end
|
26
72
|
end
|
data/lib/media/command/probe.rb
CHANGED
@@ -13,11 +13,15 @@ module Media
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def call
|
16
|
-
@subshell.new(cmd:
|
16
|
+
@subshell.new(cmd: to_a).call
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
[
|
19
|
+
def to_a
|
20
|
+
[
|
21
|
+
@cmd,
|
22
|
+
@options.map(&:to_a),
|
23
|
+
@input
|
24
|
+
].flatten
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Media
|
2
|
+
module Command
|
3
|
+
class Progress
|
4
|
+
|
5
|
+
DURATION = /Duration: (\d+):(\d+):(\d+.\d+), start: (\d+.\d+)/
|
6
|
+
TIME = /time=(\d+):(\d+):(\d+.\d+)/
|
7
|
+
|
8
|
+
attr_reader :duration, :time
|
9
|
+
|
10
|
+
def initialize(args={})
|
11
|
+
@duration = args.fetch(:duration, 0.0)
|
12
|
+
@time = args.fetch(:time, 0.0)
|
13
|
+
end
|
14
|
+
|
15
|
+
def update(line)
|
16
|
+
case line
|
17
|
+
when DURATION
|
18
|
+
@duration = ($1.to_i * 3600) + ($2.to_i * 60) + $3.to_f + $4.to_f
|
19
|
+
yield self if block_given?
|
20
|
+
when TIME
|
21
|
+
@time = ($1.to_i * 3600) + ($2.to_i * 60) + $3.to_f
|
22
|
+
yield self if block_given?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def complete
|
27
|
+
@time = @duration
|
28
|
+
yield self if block_given?
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_f
|
32
|
+
time / duration rescue ZeroDivisionError 0.0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -10,11 +10,30 @@ module Media
|
|
10
10
|
attr_reader :out, :error, :status
|
11
11
|
|
12
12
|
def initialize(args)
|
13
|
-
@cmd = args.fetch(:cmd)
|
13
|
+
@cmd = Array(args.fetch(:cmd))
|
14
14
|
end
|
15
15
|
|
16
16
|
def call
|
17
|
-
@out, @error, @status = Open3.
|
17
|
+
@out, @error, @status = Open3.popen3(*@cmd) {|stdin,stdout,stderr,thread|
|
18
|
+
|
19
|
+
out = Thread.new do
|
20
|
+
stdout.each_with_object([]) do |line, memo|
|
21
|
+
memo << line
|
22
|
+
yield line.strip if block_given?
|
23
|
+
end.join
|
24
|
+
end
|
25
|
+
|
26
|
+
err = Thread.new do
|
27
|
+
stderr.each_with_object([]) do |line, memo|
|
28
|
+
memo << line
|
29
|
+
yield line.strip if block_given?
|
30
|
+
end.join
|
31
|
+
end
|
32
|
+
|
33
|
+
stdin.close
|
34
|
+
|
35
|
+
[out.value, err.value, thread.value]
|
36
|
+
}
|
18
37
|
self
|
19
38
|
end
|
20
39
|
|
data/lib/media/container.rb
CHANGED
@@ -6,9 +6,13 @@ require_relative 'option'
|
|
6
6
|
|
7
7
|
module Media
|
8
8
|
class Container
|
9
|
+
|
10
|
+
attr_reader :url, :options
|
11
|
+
|
9
12
|
def initialize(args)
|
10
|
-
@
|
11
|
-
@probe = args.fetch(:probe, Command::Probe)
|
13
|
+
@url = args.fetch(:url)
|
14
|
+
@probe = args.fetch(:probe, Command::Probe)
|
15
|
+
@options = args.fetch(:options, []) + required_options
|
12
16
|
end
|
13
17
|
|
14
18
|
def format
|
@@ -29,10 +33,10 @@ module Media
|
|
29
33
|
end
|
30
34
|
|
31
35
|
def probe
|
32
|
-
@probe.new(input: @
|
36
|
+
@probe.new(input: @url, options: options).call
|
33
37
|
end
|
34
38
|
|
35
|
-
def
|
39
|
+
def required_options
|
36
40
|
[
|
37
41
|
Option.new(key: 'print_format', value: 'json'),
|
38
42
|
Option.new(key: 'show_format'),
|
data/lib/media/filter.rb
CHANGED
@@ -1,25 +1,46 @@
|
|
1
|
-
require 'shellwords'
|
2
|
-
|
3
1
|
require_relative 'filter/argument'
|
4
2
|
require_relative 'filter/chain'
|
5
3
|
require_relative 'filter/graph'
|
6
|
-
require_relative 'label'
|
4
|
+
require_relative 'helper/label'
|
7
5
|
|
8
6
|
module Media
|
9
7
|
class Filter
|
10
|
-
attr_reader :name
|
8
|
+
attr_reader :name
|
11
9
|
|
12
|
-
def initialize(args)
|
10
|
+
def initialize(args, &block)
|
13
11
|
@name = args.fetch(:name)
|
14
12
|
@arguments = Array args.fetch(:arguments, [])
|
15
13
|
@inputs = Array args.fetch(:inputs, [])
|
16
14
|
@outputs = Array args.fetch(:outputs, [])
|
15
|
+
|
16
|
+
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_s
|
20
20
|
[inputs, filter, outputs].reject(&:empty?).join(' ')
|
21
21
|
end
|
22
22
|
|
23
|
+
def inputs(*value)
|
24
|
+
return @inputs if value.empty?
|
25
|
+
|
26
|
+
@inputs = value.map {|name| Helper::Label.new(name: name)}
|
27
|
+
end
|
28
|
+
alias_method :inputs=, :inputs
|
29
|
+
|
30
|
+
def outputs(*value)
|
31
|
+
return @outputs if value.empty?
|
32
|
+
|
33
|
+
@outputs = value.map {|name| Helper::Label.new(name: name)}
|
34
|
+
end
|
35
|
+
alias_method :outputs=, :outputs
|
36
|
+
|
37
|
+
def arguments(value=nil)
|
38
|
+
return @arguments unless value
|
39
|
+
|
40
|
+
@arguments = value.map {|k,v| Argument.new(key: k, value: v)}
|
41
|
+
end
|
42
|
+
alias_method :arguments=, :arguments
|
43
|
+
|
23
44
|
private
|
24
45
|
|
25
46
|
def filter
|
@@ -3,12 +3,12 @@ module Media
|
|
3
3
|
class Argument
|
4
4
|
def initialize(args)
|
5
5
|
@key = args.fetch(:key)
|
6
|
-
@value = args.fetch(:value, true)
|
6
|
+
@value = args.fetch(:value, true)
|
7
7
|
end
|
8
8
|
|
9
9
|
def to_s
|
10
10
|
case @value
|
11
|
-
when TrueClass, FalseClass then @key
|
11
|
+
when TrueClass, FalseClass then @key.to_s
|
12
12
|
else "#{@key}=#{value}"
|
13
13
|
end
|
14
14
|
end
|
@@ -16,7 +16,7 @@ module Media
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def value
|
19
|
-
@value.gsub(/([\[\]=;,])/, "\\\\\\1")
|
19
|
+
@value.to_s.gsub(/([\[\]=;,])/, "\\\\\\1")
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
data/lib/media/filter/chain.rb
CHANGED
@@ -3,13 +3,20 @@ module Media
|
|
3
3
|
class Chain
|
4
4
|
attr_reader :filters
|
5
5
|
|
6
|
-
def initialize(args={}, &
|
6
|
+
def initialize(args={}, &block)
|
7
7
|
@filters = args.fetch(:filters, [])
|
8
|
+
|
9
|
+
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
8
10
|
end
|
9
11
|
|
10
12
|
def to_s
|
11
13
|
filters.join(', ')
|
12
14
|
end
|
15
|
+
|
16
|
+
def add_filter(name, &block)
|
17
|
+
@filters << Filter.new(name: name, &block)
|
18
|
+
end
|
19
|
+
alias_method :filter, :add_filter
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|
data/lib/media/filter/graph.rb
CHANGED
@@ -5,13 +5,20 @@ module Media
|
|
5
5
|
class Graph
|
6
6
|
attr_reader :chains
|
7
7
|
|
8
|
-
def initialize(args={}, &
|
8
|
+
def initialize(args={}, &block)
|
9
9
|
@chains = args.fetch(:chains, [])
|
10
|
+
|
11
|
+
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
10
12
|
end
|
11
13
|
|
12
14
|
def to_s
|
13
|
-
|
15
|
+
chains.join('; ')
|
14
16
|
end
|
17
|
+
|
18
|
+
def add_chain(&block)
|
19
|
+
@chains << Filter::Chain.new(&block)
|
20
|
+
end
|
21
|
+
alias_method :chain, :add_chain
|
15
22
|
end
|
16
23
|
end
|
17
24
|
end
|
data/lib/media/helper.rb
ADDED
data/lib/media/input.rb
CHANGED
@@ -3,21 +3,36 @@ require_relative 'filter/graph'
|
|
3
3
|
|
4
4
|
module Media
|
5
5
|
class Input
|
6
|
-
attr_reader :options
|
7
6
|
|
8
|
-
def initialize(args)
|
9
|
-
@url = args.fetch(:url)
|
7
|
+
def initialize(args, &block)
|
8
|
+
@url = args.fetch(:url) { raise ':url required'}
|
10
9
|
@options = Array args.fetch(:options, [])
|
10
|
+
|
11
|
+
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
-
|
14
|
+
def to_a
|
15
|
+
(options << graph << url).compact.map(&:to_a)
|
15
16
|
end
|
16
17
|
|
18
|
+
def options(value=nil)
|
19
|
+
return @options unless value
|
20
|
+
|
21
|
+
@options = value.map {|k,v| Option.new(key: k, value: v)}
|
22
|
+
end
|
23
|
+
alias_method :options=, :options
|
24
|
+
|
25
|
+
def graph(&block)
|
26
|
+
return @graph unless block_given?
|
27
|
+
|
28
|
+
@graph = Option.new(key: 'filter_complex', value: Filter::Graph.new(&block))
|
29
|
+
end
|
30
|
+
alias_method :graph=, :graph
|
31
|
+
|
17
32
|
private
|
18
33
|
|
19
34
|
def url
|
20
|
-
Option.new(key: 'i', value: @url)
|
35
|
+
Option.new(key: 'i', value: @url)
|
21
36
|
end
|
22
37
|
end
|
23
38
|
end
|
data/lib/media/option.rb
CHANGED
@@ -5,11 +5,11 @@ module Media
|
|
5
5
|
@value = args.fetch(:value, true)
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
8
|
+
def to_a
|
9
9
|
case @value
|
10
|
-
when TrueClass then "-#{@key}"
|
11
|
-
when FalseClass then "-no#{@key}"
|
12
|
-
else "-#{@key}
|
10
|
+
when TrueClass then ["-#{@key}"]
|
11
|
+
when FalseClass then ["-no#{@key}"]
|
12
|
+
else ["-#{@key}", @value.to_s]
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
data/lib/media/output.rb
CHANGED
@@ -3,15 +3,43 @@ require_relative 'option'
|
|
3
3
|
module Media
|
4
4
|
class Output
|
5
5
|
|
6
|
-
attr_reader :options
|
6
|
+
attr_reader :options, :maps
|
7
7
|
|
8
|
-
def initialize(args, &
|
9
|
-
@url
|
8
|
+
def initialize(args, &block)
|
9
|
+
@url = args.fetch(:url) { raise ':url required'}
|
10
10
|
@options = Array args.fetch(:options, [])
|
11
|
+
@maps = Array args.fetch(:maps, [])
|
12
|
+
|
13
|
+
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
11
14
|
end
|
12
15
|
|
13
|
-
def
|
14
|
-
|
16
|
+
def to_a
|
17
|
+
(options + maps << graph).compact.map(&:to_a) + [@url]
|
15
18
|
end
|
19
|
+
|
20
|
+
def options(value=nil)
|
21
|
+
return @options unless value
|
22
|
+
|
23
|
+
@options = value.map {|k,v| Option.new(key: k, value: v)}
|
24
|
+
end
|
25
|
+
alias_method :options=, :options
|
26
|
+
|
27
|
+
def maps(*value)
|
28
|
+
return @maps if value.empty?
|
29
|
+
|
30
|
+
@maps = value.map {|v| Option.new(key: 'map', value: v)}
|
31
|
+
end
|
32
|
+
alias_method :maps=, :maps
|
33
|
+
|
34
|
+
def label(name)
|
35
|
+
Media.label(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def graph(&block)
|
39
|
+
return @graph unless block_given?
|
40
|
+
|
41
|
+
@graph = Option.new(key: 'filter_complex', value: Filter::Graph.new(&block))
|
42
|
+
end
|
43
|
+
alias_method :graph=, :graph
|
16
44
|
end
|
17
45
|
end
|
data/lib/media/version.rb
CHANGED
@@ -18,27 +18,31 @@ module Media
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_empty_call
|
21
|
-
|
22
|
-
|
21
|
+
stub = Class.new do
|
22
|
+
def success?; true; end
|
23
|
+
end
|
24
|
+
|
25
|
+
subshell.expect(:new, subshell, [cmd: ['ffmpeg', '-v', 'info']])
|
26
|
+
subshell.expect(:call, stub.new)
|
23
27
|
|
24
28
|
subject.call
|
25
29
|
end
|
26
30
|
|
27
31
|
def test_with_options
|
28
|
-
assert_equal 'ffmpeg
|
32
|
+
assert_equal ['ffmpeg', '-v', 'info', 'option'], subject(options: [['option']]).to_a
|
29
33
|
end
|
30
34
|
|
31
35
|
def test_call_with_inputs
|
32
|
-
assert_equal 'ffmpeg
|
36
|
+
assert_equal ['ffmpeg', '-v', 'info', 'input'], subject(inputs: [['input']]).to_a
|
33
37
|
end
|
34
38
|
|
35
39
|
def test_call_with_outputs
|
36
|
-
assert_equal 'ffmpeg
|
40
|
+
assert_equal ['ffmpeg', '-v', 'info', 'output'], subject(outputs: [['output']]).to_a
|
37
41
|
end
|
38
42
|
|
39
43
|
def test_call_with_all
|
40
|
-
assert_equal 'ffmpeg
|
41
|
-
subject(options: ['
|
44
|
+
assert_equal ['ffmpeg', '-v', 'info', 'option', 'input', 'output'],
|
45
|
+
subject(options: [['option']], inputs: [['input']], outputs: [['output']]).to_a
|
42
46
|
end
|
43
47
|
end
|
44
48
|
end
|
@@ -18,19 +18,19 @@ module Media
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_call
|
21
|
-
subshell.expect(:new, subshell, [cmd: 'ffprobe input'])
|
21
|
+
subshell.expect(:new, subshell, [cmd: ['ffprobe', 'input']])
|
22
22
|
subshell.expect(:call, true)
|
23
23
|
|
24
24
|
subject(input: ['input']).call
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_call_with_input
|
28
|
-
assert_equal 'ffprobe input', subject(input: ['input']).
|
28
|
+
assert_equal ['ffprobe', 'input'], subject(input: ['input']).to_a
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_call_with_all
|
32
|
-
assert_equal 'ffprobe
|
33
|
-
subject(options: ['
|
32
|
+
assert_equal ['ffprobe', 'option', 'input'],
|
33
|
+
subject(options: [['option']], input: ['input']).to_a
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -10,15 +10,15 @@ module Media
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_stdout
|
13
|
-
shell = subject.new(cmd: 'echo hello')
|
13
|
+
shell = subject.new(cmd: ['echo', 'hello'])
|
14
14
|
|
15
15
|
assert_equal("hello\n", shell.call.out)
|
16
16
|
end
|
17
17
|
|
18
18
|
def test_stderr
|
19
|
-
shell = subject.new(cmd: '
|
20
|
-
|
21
|
-
|
19
|
+
shell = subject.new(cmd: ['ffprobe'])
|
20
|
+
|
21
|
+
assert_match(/^ffprobe/, shell.call.error)
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_success
|
@@ -32,6 +32,15 @@ module Media
|
|
32
32
|
|
33
33
|
refute(shell.call.success?)
|
34
34
|
end
|
35
|
+
|
36
|
+
def test_streaming
|
37
|
+
shell = subject.new(cmd: ['echo', "hello\n", 'there'])
|
38
|
+
|
39
|
+
out = []
|
40
|
+
shell.call {|line| out << line}
|
41
|
+
|
42
|
+
assert_equal(['hello', 'there'], out)
|
43
|
+
end
|
35
44
|
end
|
36
45
|
end
|
37
46
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'helper'
|
2
|
-
require 'media/label'
|
2
|
+
require 'media/helper/label'
|
3
3
|
|
4
4
|
module Media
|
5
5
|
class TestLabel < MiniTest::Unit::TestCase
|
6
6
|
|
7
7
|
def test_to_s
|
8
|
-
assert_equal '[foo]', Label.new(name: 'foo').to_s
|
8
|
+
assert_equal '[foo]', Helper::Label.new(name: 'foo').to_s
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
data/test/media/test_input.rb
CHANGED
@@ -5,9 +5,9 @@ module Media
|
|
5
5
|
class TestInput < MiniTest::Unit::TestCase
|
6
6
|
|
7
7
|
def test_to_s
|
8
|
-
input = Input.new(url: 'url', options: '
|
8
|
+
input = Input.new(url: 'url', options: [['option']])
|
9
9
|
|
10
|
-
assert_equal('
|
10
|
+
assert_equal([['option'], ['-i', 'url']], input.to_a)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/test/media/test_option.rb
CHANGED
@@ -9,15 +9,15 @@ module Media
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_option
|
12
|
-
assert_equal '-foo bar', subject.new(key: 'foo', value: 'bar').
|
12
|
+
assert_equal ['-foo', 'bar'], subject.new(key: 'foo', value: 'bar').to_a
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_true_flag
|
16
|
-
assert_equal '-foo', subject.new(key: 'foo').
|
16
|
+
assert_equal ['-foo'], subject.new(key: 'foo').to_a
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_false_flag
|
20
|
-
assert_equal '-nofoo', subject.new(key: 'foo', value: false).
|
20
|
+
assert_equal ['-nofoo'], subject.new(key: 'foo', value: false).to_a
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/test/media/test_output.rb
CHANGED
@@ -5,9 +5,9 @@ module Media
|
|
5
5
|
class TestOutput < MiniTest::Unit::TestCase
|
6
6
|
|
7
7
|
def test_to_s
|
8
|
-
output = Output.new(url: 'url', options: '
|
8
|
+
output = Output.new(url: 'url', options: [['option']])
|
9
9
|
|
10
|
-
assert_equal('
|
10
|
+
assert_equal([['option'], 'url'], output.to_a)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: media
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jamie Hodge
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
prerelease: false
|
@@ -62,24 +62,14 @@ files:
|
|
62
62
|
UmFrZWZpbGU=
|
63
63
|
- !binary |-
|
64
64
|
bGliL21lZGlhLnJi
|
65
|
-
- !binary |-
|
66
|
-
bGliL21lZGlhL2J1aWxkZXIvY29tbWFuZC9jb252ZXJ0ZXIucmI=
|
67
|
-
- !binary |-
|
68
|
-
bGliL21lZGlhL2J1aWxkZXIvZmlsdGVyLnJi
|
69
|
-
- !binary |-
|
70
|
-
bGliL21lZGlhL2J1aWxkZXIvZmlsdGVyL2NoYWluLnJi
|
71
|
-
- !binary |-
|
72
|
-
bGliL21lZGlhL2J1aWxkZXIvZmlsdGVyL2dyYXBoLnJi
|
73
|
-
- !binary |-
|
74
|
-
bGliL21lZGlhL2J1aWxkZXIvaW5wdXQucmI=
|
75
|
-
- !binary |-
|
76
|
-
bGliL21lZGlhL2J1aWxkZXIvb3V0cHV0LnJi
|
77
65
|
- !binary |-
|
78
66
|
bGliL21lZGlhL2NvbW1hbmQucmI=
|
79
67
|
- !binary |-
|
80
68
|
bGliL21lZGlhL2NvbW1hbmQvY29udmVydGVyLnJi
|
81
69
|
- !binary |-
|
82
70
|
bGliL21lZGlhL2NvbW1hbmQvcHJvYmUucmI=
|
71
|
+
- !binary |-
|
72
|
+
bGliL21lZGlhL2NvbW1hbmQvcHJvZ3Jlc3MucmI=
|
83
73
|
- !binary |-
|
84
74
|
bGliL21lZGlhL2NvbW1hbmQvc3Vic2hlbGwucmI=
|
85
75
|
- !binary |-
|
@@ -93,15 +83,17 @@ files:
|
|
93
83
|
- !binary |-
|
94
84
|
bGliL21lZGlhL2ZpbHRlci9ncmFwaC5yYg==
|
95
85
|
- !binary |-
|
96
|
-
|
86
|
+
bGliL21lZGlhL2hlbHBlci5yYg==
|
97
87
|
- !binary |-
|
98
|
-
|
88
|
+
bGliL21lZGlhL2hlbHBlci9sYWJlbC5yYg==
|
89
|
+
- !binary |-
|
90
|
+
bGliL21lZGlhL2hlbHBlci9zaXplLnJi
|
91
|
+
- !binary |-
|
92
|
+
bGliL21lZGlhL2lucHV0LnJi
|
99
93
|
- !binary |-
|
100
94
|
bGliL21lZGlhL29wdGlvbi5yYg==
|
101
95
|
- !binary |-
|
102
96
|
bGliL21lZGlhL291dHB1dC5yYg==
|
103
|
-
- !binary |-
|
104
|
-
bGliL21lZGlhL3NpemUucmI=
|
105
97
|
- !binary |-
|
106
98
|
bGliL21lZGlhL3ZlcnNpb24ucmI=
|
107
99
|
- !binary |-
|
@@ -120,12 +112,12 @@ files:
|
|
120
112
|
dGVzdC9tZWRpYS9maWx0ZXIvdGVzdF9jaGFpbi5yYg==
|
121
113
|
- !binary |-
|
122
114
|
dGVzdC9tZWRpYS9maWx0ZXIvdGVzdF9ncmFwaC5yYg==
|
115
|
+
- !binary |-
|
116
|
+
dGVzdC9tZWRpYS9oZWxwZXIvdGVzdF9sYWJlbC5yYg==
|
123
117
|
- !binary |-
|
124
118
|
dGVzdC9tZWRpYS90ZXN0X2ZpbHRlci5yYg==
|
125
119
|
- !binary |-
|
126
120
|
dGVzdC9tZWRpYS90ZXN0X2lucHV0LnJi
|
127
|
-
- !binary |-
|
128
|
-
dGVzdC9tZWRpYS90ZXN0X2xhYmVsLnJi
|
129
121
|
- !binary |-
|
130
122
|
dGVzdC9tZWRpYS90ZXN0X29wdGlvbi5yYg==
|
131
123
|
- !binary |-
|
@@ -175,12 +167,12 @@ test_files:
|
|
175
167
|
dGVzdC9tZWRpYS9maWx0ZXIvdGVzdF9jaGFpbi5yYg==
|
176
168
|
- !binary |-
|
177
169
|
dGVzdC9tZWRpYS9maWx0ZXIvdGVzdF9ncmFwaC5yYg==
|
170
|
+
- !binary |-
|
171
|
+
dGVzdC9tZWRpYS9oZWxwZXIvdGVzdF9sYWJlbC5yYg==
|
178
172
|
- !binary |-
|
179
173
|
dGVzdC9tZWRpYS90ZXN0X2ZpbHRlci5yYg==
|
180
174
|
- !binary |-
|
181
175
|
dGVzdC9tZWRpYS90ZXN0X2lucHV0LnJi
|
182
|
-
- !binary |-
|
183
|
-
dGVzdC9tZWRpYS90ZXN0X2xhYmVsLnJi
|
184
176
|
- !binary |-
|
185
177
|
dGVzdC9tZWRpYS90ZXN0X29wdGlvbi5yYg==
|
186
178
|
- !binary |-
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require_relative '../../option'
|
2
|
-
require_relative '../../input'
|
3
|
-
require_relative '../../output'
|
4
|
-
|
5
|
-
require_relative '../../builder/input'
|
6
|
-
require_relative '../../builder/output'
|
7
|
-
|
8
|
-
module Media
|
9
|
-
module Builder
|
10
|
-
module Command
|
11
|
-
module Converter
|
12
|
-
|
13
|
-
module ClassMethods
|
14
|
-
|
15
|
-
def build(&blk)
|
16
|
-
converter = new
|
17
|
-
|
18
|
-
if block_given?
|
19
|
-
context = eval('self', blk.binding)
|
20
|
-
converter.instance_variable_set(:@context, context)
|
21
|
-
|
22
|
-
converter.instance_eval(&blk)
|
23
|
-
end
|
24
|
-
converter
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
module InstanceMethods
|
29
|
-
|
30
|
-
def option(key, value)
|
31
|
-
options << Media::Option.new(key: key, value: value)
|
32
|
-
end
|
33
|
-
|
34
|
-
def input(url, &blk)
|
35
|
-
inputs << Media::Input.extend(Builder::Input).build(url, &blk)
|
36
|
-
end
|
37
|
-
|
38
|
-
def output(url, &blk)
|
39
|
-
outputs << Media::Output.extend(Builder::Output).build(url, &blk)
|
40
|
-
end
|
41
|
-
|
42
|
-
def method_missing(method, *args, &blk)
|
43
|
-
@context && @context.send(method, *args, &blk)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.extended(receiver)
|
48
|
-
receiver.extend(ClassMethods)
|
49
|
-
receiver.send(:include, InstanceMethods)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
data/lib/media/builder/filter.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require_relative '../filter/argument'
|
2
|
-
|
3
|
-
module Media
|
4
|
-
module Builder
|
5
|
-
module Filter
|
6
|
-
|
7
|
-
module ClassMethods
|
8
|
-
|
9
|
-
def build(name, &blk)
|
10
|
-
filter = new(name: name)
|
11
|
-
|
12
|
-
if block_given?
|
13
|
-
context = eval('self', blk.binding)
|
14
|
-
filter.instance_variable_set(:@context, context)
|
15
|
-
|
16
|
-
filter.instance_eval(&blk)
|
17
|
-
end
|
18
|
-
filter
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module InstanceMethods
|
23
|
-
|
24
|
-
def input(name)
|
25
|
-
inputs << Media::Label.new(name: name)
|
26
|
-
end
|
27
|
-
|
28
|
-
def output(name)
|
29
|
-
outputs << Media::Label.new(name: name)
|
30
|
-
end
|
31
|
-
|
32
|
-
def argument(key, value=true)
|
33
|
-
arguments << Media::Filter::Argument.new(key: key, value: value)
|
34
|
-
end
|
35
|
-
alias :arg :argument
|
36
|
-
|
37
|
-
def method_missing(method, *args, &blk)
|
38
|
-
@context && @context.send(method, *args, &blk)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.extended(receiver)
|
43
|
-
receiver.extend(ClassMethods)
|
44
|
-
receiver.send(:include, InstanceMethods)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require_relative '../filter'
|
2
|
-
|
3
|
-
module Media
|
4
|
-
module Builder
|
5
|
-
module Filter
|
6
|
-
module Chain
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
|
10
|
-
def build(&blk)
|
11
|
-
chain = new
|
12
|
-
|
13
|
-
if block_given?
|
14
|
-
context = eval('self', blk.binding)
|
15
|
-
chain.instance_variable_set(:@context, context)
|
16
|
-
|
17
|
-
chain.instance_eval(&blk)
|
18
|
-
end
|
19
|
-
chain
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module InstanceMethods
|
24
|
-
|
25
|
-
def filter(name, &blk)
|
26
|
-
filters << Media::Filter.extend(Filter).build(name, &blk)
|
27
|
-
end
|
28
|
-
|
29
|
-
def method_missing(method, *args, &blk)
|
30
|
-
@context && @context.send(method, *args, &blk)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.extended(receiver)
|
35
|
-
receiver.extend(ClassMethods)
|
36
|
-
receiver.send(:include, InstanceMethods)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require_relative '../../filter/chain'
|
2
|
-
|
3
|
-
require_relative 'chain'
|
4
|
-
|
5
|
-
module Media
|
6
|
-
module Builder
|
7
|
-
module Filter
|
8
|
-
module Graph
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
|
12
|
-
def build(&blk)
|
13
|
-
graph = new
|
14
|
-
|
15
|
-
if block_given?
|
16
|
-
context = eval('self', blk.binding)
|
17
|
-
graph.instance_variable_set(:@context, context)
|
18
|
-
|
19
|
-
graph.instance_eval(&blk)
|
20
|
-
end
|
21
|
-
graph
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module InstanceMethods
|
26
|
-
|
27
|
-
def chain(&blk)
|
28
|
-
chains << Media::Filter::Chain.extend(Chain).build(&blk)
|
29
|
-
end
|
30
|
-
|
31
|
-
def method_missing(method, *args, &blk)
|
32
|
-
@context && @context.send(method, *args, &blk)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.extended(receiver)
|
37
|
-
receiver.extend(ClassMethods)
|
38
|
-
receiver.send(:include, InstanceMethods)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/lib/media/builder/input.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require_relative 'filter/graph'
|
2
|
-
|
3
|
-
module Media
|
4
|
-
module Builder
|
5
|
-
module Input
|
6
|
-
|
7
|
-
module ClassMethods
|
8
|
-
|
9
|
-
def build(url, &blk)
|
10
|
-
input = new(url: url)
|
11
|
-
|
12
|
-
if block_given?
|
13
|
-
context = eval('self', blk.binding)
|
14
|
-
input.instance_variable_set(:@context, context)
|
15
|
-
|
16
|
-
input.instance_eval(&blk)
|
17
|
-
end
|
18
|
-
input
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module InstanceMethods
|
23
|
-
|
24
|
-
def option(key, value=true)
|
25
|
-
@options << Media::Option.new(key: key, value: value).to_s
|
26
|
-
end
|
27
|
-
|
28
|
-
def graph(&blk)
|
29
|
-
@options << Option.new(
|
30
|
-
key: 'filter_complex',
|
31
|
-
value: Media::Filter::Graph.extend(Filter::Graph).build(&blk)
|
32
|
-
)
|
33
|
-
end
|
34
|
-
|
35
|
-
def method_missing(method, *args, &blk)
|
36
|
-
@context && @context.send(method, *args, &blk)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.extended(receiver)
|
41
|
-
receiver.extend(ClassMethods)
|
42
|
-
receiver.send(:include, InstanceMethods)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
data/lib/media/builder/output.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
require_relative '../option'
|
2
|
-
require_relative '../filter/graph'
|
3
|
-
|
4
|
-
require_relative 'filter/graph'
|
5
|
-
|
6
|
-
module Media
|
7
|
-
module Builder
|
8
|
-
module Output
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
|
12
|
-
def build(url, &blk)
|
13
|
-
output = new(url: url)
|
14
|
-
|
15
|
-
if block_given?
|
16
|
-
context = eval('self', blk.binding)
|
17
|
-
output.instance_variable_set(:@context, context)
|
18
|
-
|
19
|
-
output.instance_eval(&blk)
|
20
|
-
end
|
21
|
-
output
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module InstanceMethods
|
26
|
-
|
27
|
-
def option(key, value=true)
|
28
|
-
@options << Media::Option.new(key: key, value: value)
|
29
|
-
end
|
30
|
-
|
31
|
-
def map(value)
|
32
|
-
@options << Media::Option.new(key: 'map', value: value)
|
33
|
-
end
|
34
|
-
|
35
|
-
def graph(&blk)
|
36
|
-
@options << Media::Option.new(key: 'filter_complex',
|
37
|
-
value: Media::Filter::Graph.extend(Filter::Graph).build(&blk))
|
38
|
-
end
|
39
|
-
|
40
|
-
def method_missing(method, *args, &blk)
|
41
|
-
@context && @context.send(method, *args, &blk)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.extended(receiver)
|
46
|
-
receiver.extend(ClassMethods)
|
47
|
-
receiver.send(:include, InstanceMethods)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
data/lib/media/label.rb
DELETED