flvedit 0.6.1 → 0.6.2

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/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 6
4
- :patch: 1
4
+ :patch: 2
@@ -6,11 +6,13 @@ module FLV
6
6
  module Edit
7
7
  module Processor
8
8
 
9
+ # Add is a Processor (see Base and desc)
9
10
  class Add < Base
10
11
  desc "Adds tags from the xml or yaml file PATH (default: tags.yaml/xml)", :param => {:name => "[PATH]"}
12
+
13
+ include Dispatcher
11
14
 
12
- def initialize(*)
13
- super
15
+ def on_header(tag)
14
16
  @add = (options[:add_tags] || read_tag_file).sort_by(&:timestamp)
15
17
  end
16
18
 
@@ -1,140 +1,62 @@
1
1
  module FLV
2
2
  module Edit
3
3
  module Processor
4
- # Processors are used to process FLV files. Processors form are chain and the data (header and tags) will 'flow'
5
- # through the chain in sequence. Each processor can inspect the data and change the flow,
6
- # either by modifying the data, inserting new data in the flow or stopping the propagation of data.
4
+ # Base is the base class for all Processors.
5
+ # Processors are used to process FLV files.
6
+ # A FLV file can be seen as an enumeration of chunks, the first one being a Header and the following ones
7
+ # a series of Tags with different types of bodies: Audio, Video or Event.
8
+ # A Processor acts as an IO operation on such enumerations.
9
+ # They therefore take an enumeration of chunks as input and their output is similarly an enumeration of chunks.
10
+ # They can thus be chained together at will.
7
11
  #
8
- # A FLV file can be seen as a sequence of chunks, the first one being a Header and the following ones
9
- # a series of Tags with different types of bodies: Audio, Video or Event. Events store all meta data
10
- # related information: onMetaData, onCuePoint, ...
12
+ # For example:
13
+ # FLV::File.open("x.flv") do |f|
14
+ # Debug.new(Cut.new(f, :cut => "1m-")).first(10)
15
+ # end
16
+ # # ==> reads the file, skips the first minute, prints and returns the first 10 chunks
11
17
  #
12
- # To tap into the flow of chunks, a processor can define any of the following methods:
13
- # on_chunk
14
- # on_header
15
- # on_tag
16
- # on_audio
17
- # on_video
18
- # on_keyframe
19
- # on_interframe
20
- # on_disposable_interframe
21
- # on_event
22
- # on_meta_data
23
- # on_cue_point
24
- # on_other_event
25
- #
26
- # All of these methods will have one argument: the current chunk being processed.
27
- # For example, if the current chunk is an 'onMetaData' event, then
28
- # the following will be called (from the most specialized to the least).
29
- # processor.on_meta_data(chunk)
30
- # processor.on_event(chunk)
31
- # processor.on_chunk(chunk)
32
- # # later on, the next processor will handle it:
33
- # next_processor.on_meta_data(chunk)
34
- # #...
35
- #
36
- # The methods need not return anything. It is assumed that the chunk will continue to flow through the
37
- # processing chain. When the chunk should not continue down the chain, call +absorb+.
38
- # To insert other tags in the flow, call +dispatch_instead+.
39
- # Finally, it's possible to +stop+ the processing of the file completely.
40
- #
41
- # It is possible to look back at already processed chunks (up to a certain limit) with +look_back+
42
- # or even in the future with +look_ahead+
18
+ # Processors acts as Enumerable, but #each, in that context, is an enumeration of chunks
19
+ # for one single file. Some processors act as more than one such enumeration (Reader, Split)
20
+ # To go through all, call #each_source or #process_all
43
21
  #
44
22
  class Base
23
+ include Enumerable
45
24
  attr_reader :options
46
25
 
26
+ # Create a new processor, using the given +source+ and +options+ hash.
27
+ # Valid +options+ depend on the class of Processor.
47
28
  def initialize(source=nil, options={})
48
- @options = options.freeze
29
+ @options = options.reverse_merge(:out => STDOUT)
49
30
  @source = source
50
- on_calls = self.class.instance_methods(false).select{|m| m.to_s.start_with?("on_")}.map(&:to_sym) #Note: to_s needed for ruby 1.9, to_sym for ruby 1.8
51
- unless (potential_errors = on_calls - ALL_EVENTS).empty?
52
- warn "The following are not events: #{potential_errors.join(',')} (class #{self.class})"
53
- end
54
31
  end
55
32
 
56
- def has_next_file?
57
- @source.has_next_file?
58
- end
59
-
60
- def rewind
61
- @source.rewind
62
- end
63
-
64
- def absorb(*) # Note: the (*) is so that we can alias events like on_meta_data
65
- throw :absorb
66
- end
67
-
68
- def stop
69
- throw :stop
70
- end
71
-
72
- def process_all
73
- each{} while has_next_file?
33
+ # Calls the given block once for each source
34
+ def each_source(&block)
35
+ raise "There is no source for #{self}" unless source
36
+ source.each_source(&block)
74
37
  end
75
38
 
39
+ # Calls the given block once for each chunck, passing that chunk as argument
76
40
  def each(&block)
77
- return to_enum(:each_chunk) unless block_given?
78
- @block = block
79
- catch :stop do
80
- process_next_file
81
- end
82
- end
83
-
84
- def process_next_file
85
- dispatch_chunks(@source)
86
- end
87
-
88
- def dispatch_chunks(enum)
89
- enum.each do |chunk|
90
- dispatch_chunk(chunk)
91
- end
92
- end
93
-
94
- def dispatch_chunk(chunk)
95
- evt = chunk.main_event
96
- catch :absorb do
97
- EVENT_TRIGGER_LIST[evt].each do |event|
98
- send(event, chunk) if respond_to?(event)
99
- end
100
- @block.call chunk
101
- end
41
+ raise "There is no source for #{self}" unless source
42
+ source.each(&block)
102
43
  end
103
44
 
104
- def dispatch_instead(*chunks)
105
- chunks.each do |chunk|
106
- @block.call chunk
107
- end
108
- absorb
109
- end
110
-
111
- def stdout
112
- options[:out] || STDOUT
45
+ # Simple utility going through each chunk of each source
46
+ def process_all
47
+ each_source{each{}}
113
48
  end
114
49
 
115
- EVENT_TRIGGER = {
116
- :on_header => :on_chunk,
117
- :on_tag => :on_chunk,
118
- :on_audio => :on_tag,
119
- :on_video => :on_tag,
120
- :on_event => :on_tag,
121
- :on_meta_data => :on_event,
122
- :on_cue_point => :on_event,
123
- :on_last_second => :on_event,
124
- :on_other_event => :on_event,
125
- :on_keyframe => :on_video,
126
- :on_interframe => :on_video,
127
- :on_disposable_interframe => :on_video
128
- }.freeze
129
-
130
- ALL_EVENTS = (EVENT_TRIGGER.keys | EVENT_TRIGGER.values).freeze
131
-
132
- MAIN_EVENTS = ALL_EVENTS.reject{ |k| EVENT_TRIGGER.has_value?(k)}.freeze
133
-
134
- EVENT_TRIGGER_LIST = Hash.new{|h, k| h[k] = [k] + h[EVENT_TRIGGER[k]]}.tap{|h| h[nil] = []; h.values_at(*MAIN_EVENTS)}.freeze
135
-
136
50
  protected
137
-
51
+ attr_reader :source
52
+
53
+ # Processors can be made directly accessible to the command line by calling #desc
54
+ # with a description.
55
+ # Options are:
56
+ # * :shortcut => "x" (defaults to first letter of processor)
57
+ # * :param => pass a hash if the command line should accept a parameter:
58
+ # * :class => class of the parameter (defaults to String)
59
+ # * :name => name of parameter, surrounded with [] if optional (defaults to the name of the class)
138
60
  def self.desc(text, options = {})
139
61
  registry << [self, text, options]
140
62
  end
@@ -143,67 +65,23 @@ module FLV
143
65
  @@registry ||= []
144
66
  end
145
67
 
146
- def self.absorb(*events)
147
- events.each{|evt| alias_method evt, :absorb}
148
- end
149
-
150
68
  end #class Base
151
69
 
70
+ # Utility function to create a chain of Processors.
71
+ # Example:
72
+ # chain(Update, Debug, Cut, options)
73
+ # # ==> Update.new(Debug.new(Cut.new(options),options),options)
152
74
  def self.chain(chain_classes, options = {})
153
75
  next_chain_class = chain_classes.pop
154
76
  next_chain_class.new(chain(chain_classes, options), options) if next_chain_class
155
77
  end
156
-
157
- # Let's extend the different kinds of chunks so that any_chunk.main_event returns
158
- # the desired trigger.
159
-
160
- module MainEvent # :nodoc:
161
- module Header
162
- def main_event
163
- :on_header
164
- end
165
- end
166
-
167
- module Video
168
- MAPPING = {
169
- :keyframe => :on_keyframe,
170
- :interframe => :on_interframe,
171
- :disposable_interframe => :on_disposable_interframe
172
- }.freeze
173
-
174
- def main_event
175
- MAPPING[frame_type]
176
- end
177
- end
178
-
179
- module Audio
180
- def main_event
181
- :on_audio
182
- end
183
- end
184
-
185
- module Event
186
- MAPPING = Hash.new(:on_other_event).merge!(
187
- :onMetaData => :on_meta_data,
188
- :onCuePoint => :on_cue_point,
189
- :onLastSecond => :on_last_second
190
- ).freeze
191
- def main_event
192
- MAPPING[event]
193
- end
194
- end
195
-
196
- module Tag
197
- def main_event
198
- body.main_event
199
- end
200
- end
201
- end #module MainEvent
202
78
  end #module Processor
203
79
  end #module Edit
204
80
 
205
- [Header, Tag, Audio, Video, Event].each do |klass|
206
- klass.class_eval{ include Edit::Processor::MainEvent.const_get(klass.to_s.sub('FLV::', '')) }
81
+ module File
82
+ # A file acts as a single source, so...
83
+ def each_source
84
+ yield
85
+ end
207
86
  end
208
-
209
87
  end #module FLV
@@ -1,19 +1,26 @@
1
1
  module FLV
2
2
  module Edit
3
3
  module Processor
4
+
5
+ # CommandLine is a Processor (see Base) added automatically as the last level
6
+ # for all command line executions
4
7
  class CommandLine < Base
8
+ include Dispatcher
5
9
  def on_header(h)
6
10
  @last = h.path
7
11
  end
8
12
 
9
- def process_all
13
+ def each_source
14
+ return to_enum(:each_source) unless block_given?
10
15
  ok, errors = [], []
11
- begin
12
- each{}
13
- ok << @last
14
- rescue Exception => e
15
- errors << [@last, e]
16
- end while has_next_file?
16
+ super do
17
+ begin
18
+ each{}
19
+ ok << @last
20
+ rescue Exception => e
21
+ errors << [@last, e]
22
+ end
23
+ end
17
24
  puts (["Processed successfully:"] + ok).join("\n") unless ok.empty?
18
25
  puts (["**** Processed with errors: ****"] + errors.map{|path, err| "#{path}: #{err}"}).join("\n") unless errors.empty?
19
26
  end
@@ -1,21 +1,27 @@
1
1
  module FLV
2
2
  module Edit
3
3
  module Processor
4
+
5
+ # Cut is a Processor class (see Base and desc)
4
6
  class Cut < Base
5
- desc ["Cuts file using the given RANGE"],
7
+ desc ["Cuts selects the tags within RANGE.",
8
+ "The timestamps are offset so that the first tag as timestamp 0"],
6
9
  :param => {:class => TimestampRange, :name => "RANGE"}, :shortcut => "x"
7
10
 
8
- def on_header(*)
11
+ include Filter
12
+
13
+ def before_filter
9
14
  @from, @to = options[:cut].begin, options[:cut].end
10
15
  @wait_for_keyframe = options[:keyframe_mode]
11
16
  @first_timestamp = nil
12
17
  end
13
18
 
14
- def on_tag(tag)
19
+ def filter(tag)
20
+ return if tag.is_a? Header
15
21
  if tag.timestamp > @to
16
22
  stop
17
23
  elsif (tag.timestamp < @from) || (@wait_for_keyframe &&= !tag.body.is?(:keyframe))
18
- absorb
24
+ :skip
19
25
  else
20
26
  @first_timestamp ||= tag.timestamp
21
27
  tag.timestamp -= @first_timestamp
@@ -3,27 +3,29 @@ require_relative "printer"
3
3
  module FLV
4
4
  module Edit
5
5
  module Processor
6
+
7
+ # Debug is a Processor class (see Base and desc)
6
8
  class Debug < Base
9
+ include Filter
7
10
  desc ["Prints out the details of all tags. Information that stays the same",
8
11
  "from one tag type to the next will not be repeated.",
9
12
  "A RANGE argument will limit the output to tags within that range;",
10
13
  "similarily, a given TIMESTAMP will limit the output to tags",
11
14
  "within 0.1s of this timestamp."],
12
15
  :param => {:class => TimestampOrTimestampRange, :name => "[RANGE/TS]"}
13
- def on_header(tag)
16
+
17
+ def before_filter
14
18
  @range = self.options[:debug] || TimestampRange.new(0, INFINITY)
15
19
  @range = @range.widen(0.1) unless @range.is_a? Range
16
20
  @last = {}
17
- @printer = Printer.new(stdout)
18
- tag.debug(@printer) if @range.include? 0
21
+ @printer = Printer.new(options[:out])
19
22
  end
20
-
21
- def on_tag(tag)
23
+
24
+ def filter(tag)
22
25
  return unless @range.include? tag.timestamp
23
26
  tag.debug(@printer, @last[tag.body.class])
24
27
  @last[tag.body.class] = tag
25
28
  end
26
-
27
29
  end
28
30
  end
29
31
  end
@@ -0,0 +1,175 @@
1
+ module FLV
2
+ module Edit
3
+ module Processor
4
+
5
+ # Dispatcher can be included in a processor and will makeit easy to
6
+ # process the chunks according to their type.
7
+ #
8
+ # Chunks can be Header or Tags.
9
+ # The latter have different types of bodies: Audio, Video or Event.
10
+ # Events store all meta data related information: onMetaData, onCuePoint, ...
11
+ #
12
+ # To tap into the flow of chunks, a processor can define any of the following methods:
13
+ # on_chunk
14
+ # on_header
15
+ # on_tag
16
+ # on_audio
17
+ # on_video
18
+ # on_keyframe
19
+ # on_interframe
20
+ # on_disposable_interframe
21
+ # on_event
22
+ # on_meta_data
23
+ # on_cue_point
24
+ # on_other_event
25
+ #
26
+ # All of these methods will have one argument: the current chunk being processed.
27
+ # For example, if the current chunk is an 'onMetaData' event, then
28
+ # the following will be called (from the most specialized to the least).
29
+ # on_meta_data(chunk)
30
+ # on_event(chunk)
31
+ # on_chunk(chunk)
32
+ #
33
+ # The methods need not return anything. It is assumed that the chunk should be output.
34
+ # If that's not the case, call +#absorb+.
35
+ # To output other tags instead, call +#dispatch_instead+.
36
+ #
37
+ # Note that both #absorb and #dispatch_instead stop the processing for the current chunk,
38
+ # so if #on_video calls #absorb, for example, then there won't be a call to #on_tag or #on_chunk.
39
+ #
40
+ # Finally, it's possible to +#stop+ the processing of the current source completely.
41
+ #
42
+ module Dispatcher
43
+ def initialize(*)
44
+ super
45
+ on_calls = self.class.instance_methods(false).select{|m| m.to_s.start_with?("on_")}.map(&:to_sym) #Note: to_s needed for ruby 1.9, to_sym for ruby 1.8
46
+ unless (potential_errors = on_calls - ALL_EVENTS).empty?
47
+ warn "The following are not events: #{potential_errors.join(',')} (class #{self.class})"
48
+ end
49
+ end
50
+
51
+ # Stops the processing for the current chunk
52
+ def absorb(*) # Note: the (*) is so that we can alias events like on_meta_data
53
+ throw :absorb
54
+ end
55
+
56
+ # Stops the processing of the current source completely.
57
+ def stop
58
+ throw :stop
59
+ end
60
+
61
+ def each(&block)
62
+ return to_enum unless block_given?
63
+ @block = block
64
+ catch :stop do
65
+ super{|chunk| dispatch_chunk(chunk)}
66
+ end
67
+ end
68
+
69
+ # Call #dispatch_instead with a list of chunks.
70
+ # These chunks will not be processed by the current processor
71
+ # but will be output directly. This stops the processing for
72
+ # the current chunk.
73
+ def dispatch_instead(*chunks)
74
+ chunks.each do |chunk|
75
+ @block.call chunk
76
+ end
77
+ absorb
78
+ end
79
+
80
+ EVENT_TRIGGER = {
81
+ :on_header => :on_chunk,
82
+ :on_tag => :on_chunk,
83
+ :on_audio => :on_tag,
84
+ :on_video => :on_tag,
85
+ :on_event => :on_tag,
86
+ :on_meta_data => :on_event,
87
+ :on_cue_point => :on_event,
88
+ :on_last_second => :on_event,
89
+ :on_other_event => :on_event,
90
+ :on_keyframe => :on_video,
91
+ :on_interframe => :on_video,
92
+ :on_disposable_interframe => :on_video
93
+ }.freeze
94
+
95
+ ALL_EVENTS = (EVENT_TRIGGER.keys | EVENT_TRIGGER.values).freeze
96
+
97
+ MAIN_EVENTS = ALL_EVENTS.reject{ |k| EVENT_TRIGGER.has_value?(k)}.freeze
98
+
99
+ EVENT_TRIGGER_LIST = Hash.new{|h, k| h[k] = [k] + h[EVENT_TRIGGER[k]]}.tap{|h| h[nil] = []; h.values_at(*MAIN_EVENTS)}.freeze
100
+
101
+ module ClassMethods
102
+ # Call give a list of events of #absorb to always absorb chunks of these types.
103
+ def absorb(*events)
104
+ events.each{|evt| alias_method evt, :absorb}
105
+ end
106
+ end
107
+
108
+ def self.included(base) # :nodoc:
109
+ base.extend ClassMethods
110
+ end
111
+ private
112
+ def dispatch_chunk(chunk) # :nodoc:
113
+ evt = chunk.main_event
114
+ catch :absorb do
115
+ EVENT_TRIGGER_LIST[evt].each do |event|
116
+ send(event, chunk) if respond_to?(event)
117
+ end
118
+ @block.call chunk
119
+ end
120
+ end
121
+ end #class Base
122
+
123
+
124
+ # We supplement the basic FLV classes with a #main_event method
125
+ # which returns the most specialize event for that chunk.
126
+ module MainEvent # :nodoc:
127
+ module Header
128
+ def main_event
129
+ :on_header
130
+ end
131
+ end
132
+
133
+ module Video
134
+ MAPPING = {
135
+ :keyframe => :on_keyframe,
136
+ :interframe => :on_interframe,
137
+ :disposable_interframe => :on_disposable_interframe
138
+ }.freeze
139
+
140
+ def main_event
141
+ MAPPING[frame_type]
142
+ end
143
+ end
144
+
145
+ module Audio
146
+ def main_event
147
+ :on_audio
148
+ end
149
+ end
150
+
151
+ module Event
152
+ MAPPING = Hash.new(:on_other_event).merge!(
153
+ :onMetaData => :on_meta_data,
154
+ :onCuePoint => :on_cue_point,
155
+ :onLastSecond => :on_last_second
156
+ ).freeze
157
+ def main_event
158
+ MAPPING[event]
159
+ end
160
+ end
161
+
162
+ module Tag
163
+ def main_event
164
+ body.main_event
165
+ end
166
+ end
167
+ end #module MainEvent
168
+ end #module Processor
169
+ end #module Edit
170
+
171
+ [Header, Tag, Audio, Video, Event].each do |klass|
172
+ klass.class_eval{ include Edit::Processor::MainEvent.const_get(klass.to_s.sub('FLV::', '')) }
173
+ end
174
+
175
+ end #module FLV
@@ -0,0 +1,32 @@
1
+ module FLV
2
+ module Edit
3
+ module Processor
4
+
5
+ # Basic processors can include Filter which
6
+ # provides a pretty simple #each.
7
+ # It will call #before_filter then
8
+ # #filter(chunk) for each chunk.
9
+ # If #filter returns +:skip+, the chunk won't be yielded
10
+ # #filter can also stops altogether the processing
11
+ # of the current source by calling #stop.
12
+ module Filter
13
+ def each
14
+ return to_enum unless block_given?
15
+ before_filter
16
+ catch :stop do
17
+ super do |chunk|
18
+ yield chunk unless filter(chunk) == :skip
19
+ end
20
+ end
21
+ end
22
+ protected
23
+ def before_filter
24
+ end
25
+ def stop
26
+ throw :stop
27
+ end
28
+
29
+ end #module Filter
30
+ end #module Processor
31
+ end #module Edit
32
+ end #module FLV
@@ -1,15 +1,19 @@
1
1
  module FLV
2
2
  module Edit
3
3
  module Processor
4
+
5
+ # Head is a Processor class (see Base and desc)
4
6
  class Head < Base
5
7
  desc "Processes only the first NB tags.", :param => {:class => Integer, :name => "NB"}, :shortcut => "n"
6
- def on_header(header)
7
- @count = self.options[:head]
8
- end
9
8
 
10
- def on_tag(tag)
11
- throw :stop if (@count -= 1) < 0
9
+ def each
10
+ count = options[:head]
11
+ super do |chunk|
12
+ yield chunk
13
+ break if (count -= 1) < 0 # after the yield because we're not counting the header
14
+ end
12
15
  end
16
+
13
17
  end
14
18
  end
15
19
  end
@@ -2,11 +2,21 @@ module FLV
2
2
  module Edit
3
3
  module Processor
4
4
 
5
+ # Join is a Processor class (see Base and desc)
5
6
  class Join < Base
6
- desc "Join the FLV files"
7
+ desc "Joins all the inputs together."
8
+
9
+ include Dispatcher
7
10
 
8
- def process_next_file
9
- dispatch_chunks(@source) while @source.has_next_file?
11
+ def each_source_with_join
12
+ return to_enum(:each_source) unless block_given?
13
+ yield
14
+ end
15
+ alias_method_chain :each_source, :join
16
+
17
+ def each(&block)
18
+ return to_enum unless block_given?
19
+ each_source_without_join { super }
10
20
  end
11
21
 
12
22
  def on_tag(tag)
@@ -1,7 +1,11 @@
1
1
  module FLV
2
2
  module Edit
3
3
  module Processor
4
- class MetaDataMaker < Base
4
+ # MetaDataMaker is a Processor class (see Base) that computes the metadata
5
+ # for its sources. The metadata for the current source is accessible with #meta_data
6
+ # It is used by Update.
7
+ class MetaDataMaker < Base
8
+ include Dispatcher
5
9
  CHUNK_LENGTH_SIZE = 4 # todo: calc instead?
6
10
  TAG_HEADER_SIZE = 11 # todo: calc instead?
7
11
  TOTAL_EXTRA_SIZE_PER_TAG = CHUNK_LENGTH_SIZE + TAG_HEADER_SIZE
@@ -2,10 +2,18 @@ require_relative "printer"
2
2
  module FLV
3
3
  module Edit
4
4
  module Processor
5
+
6
+ # Print is a Processor class (see Base and desc)
5
7
  class Print < Base
6
- desc "Prints out meta data to stdout"
7
- def on_meta_data(tag)
8
- tag.debug(Printer.new(stdout))
8
+ desc "Prints out the meta data"
9
+ include Filter
10
+
11
+ def before_filter
12
+ @printer = Printer.new(options[:out])
13
+ end
14
+
15
+ def filter(tag)
16
+ tag.debug(@printer) if tag.is? :onMetaData
9
17
  end
10
18
  end
11
19
  end
@@ -2,6 +2,7 @@ module FLV
2
2
  module Edit
3
3
  module Processor
4
4
 
5
+ # Printer is a small utility class to print out FLV chunks.
5
6
  class Printer
6
7
  def initialize(io, options={})
7
8
  @io = io
@@ -1,29 +1,34 @@
1
1
  module FLV
2
2
  module Edit
3
3
  module Processor
4
+
5
+ # Reader is a Processor class (see Base) which use <tt>options[:files]</tt> to generate
6
+ # its sources (instead of the passed +source+ which should be nil)
4
7
  class Reader < Base
5
8
  def initialize(*)
6
9
  super
10
+ raise "Invalid filenames: #{options[:files].inspect}" unless options[:files].all?
11
+ raise "Please specify at least one filename" if options[:files].empty?
12
+ raise NotImplemented, "Reader can't have a source (other than options[:files])" if source
7
13
  rewind
8
- raise "Oups, Filenames were #{@options[:files].inspect}" if @options[:files].include? nil
9
- raise "Please specify at least one filename" if @options[:files].empty?
10
14
  end
11
15
 
12
- def has_next_file?
13
- @to_process > 0
14
- end
15
-
16
- def rewind
17
- @to_process = @options[:files].length
16
+ def each_source
17
+ return to_enum(:each_source) unless block_given?
18
+ rewind
19
+ yield until @sources.empty?
18
20
  end
19
-
20
- def process_next_file
21
- raise IndexError, "No more filenames to process" unless has_next_file?
22
- @to_process -= 1
23
- FLV::File.open(@options[:files][-1- @to_process]) do |f|
24
- dispatch_chunks(f)
21
+
22
+ def each
23
+ FLV::File.open(@sources.shift) do |f|
24
+ @source = f
25
+ super
25
26
  end
26
27
  end
28
+
29
+ def rewind
30
+ @sources = options[:files].dup
31
+ end
27
32
  end
28
33
  end
29
34
  end
@@ -1,28 +1,29 @@
1
1
  module FLV
2
2
  module Edit
3
3
  module Processor
4
+
5
+ # Save is a Processor class (see Base and desc)
4
6
  class Save < Base
5
7
  desc "Saves the result to PATH", :param => {:class => String, :name => "PATH"}
6
- def process_next_file
8
+
9
+ def each_source
10
+ return to_enum(:each_source) unless block_given?
11
+ @out = FLV::File::open(options[:save] || (h.path+".temp"), "w+b")
7
12
  super
8
13
  ensure
9
- if @out
10
- @out.close
11
- finalpath = @out.path.sub(/\.temp$/, '')
12
- FileUtils.mv(@out.path, finalpath) unless finalpath == @out.path
13
- end
14
- end
15
-
16
- def on_header(h)
17
- @out = FLV::File::open(options[:save] || (h.path+".temp"), "w+b")
18
- @out << h
14
+ @out.close
15
+ finalpath = @out.path.sub(/\.temp$/, '')
16
+ FileUtils.mv(@out.path, finalpath) unless finalpath == @out.path
19
17
  end
20
18
 
21
- def on_tag(t)
22
- @out << t
19
+ def each
20
+ return to_enum unless block_given?
21
+ super do |chunk|
22
+ @out << chunk
23
+ yield chunk
24
+ end
23
25
  end
24
26
  end
25
-
26
27
  end
27
28
  end
28
29
  end
@@ -3,17 +3,25 @@ require_relative "meta_data_maker"
3
3
  module FLV
4
4
  module Edit
5
5
  module Processor
6
+
7
+ # Update is a Processor class (see Base and desc)
6
8
  class Update < Base
7
- desc "Updates FLV with an onMetaTag event"
9
+ include Dispatcher
10
+ desc "Updates FLV with an onMetaTag event containing all the relevant information."
8
11
  def initialize(source=nil, options={})
9
12
  super
10
13
  @meta_data_maker = MetaDataMaker.new(source.dup, options)
11
14
  end
12
15
 
16
+
13
17
  def each(&block)
14
- @meta_data_maker.each {}
15
- ensure # even if each throws, we better call super otherwise we won't be synchronized anymore!
16
- super
18
+ return to_enum unless block_given?
19
+ begin
20
+ @meta_data_maker.each {}
21
+ ensure # even if each throws, we better call super otherwise we won't be synchronized anymore!
22
+ super rescue nil
23
+ raise if $!
24
+ end
17
25
  end
18
26
 
19
27
  absorb :on_meta_data, :on_last_second
@@ -1,3 +1,3 @@
1
- %w(base add command_line cut debug head join print reader save update).each do |proc|
1
+ %w(base filter dispatcher add command_line cut debug head join print reader save update).each do |proc|
2
2
  require_relative "processor/#{proc}"
3
3
  end
@@ -2,7 +2,7 @@ module FLV
2
2
  module Edit
3
3
  FILE = ::File.dirname(__FILE__) + '/../../../VERSION.yml'
4
4
 
5
- class Version < Struct.new(:major, :minor, :patch)
5
+ class Version < Struct.new(:major, :minor, :patch) # :nodoc:
6
6
  def to_s
7
7
  "#{major}.#{minor}.#{patch}"
8
8
  end
data/lib/flv/file.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  module FLV
2
+ # FLV::File provide methods to read a FLV file easily.
3
+ # Interface is similar to ::File, except that #each will
4
+ # return FLV chunks instead of lines.
2
5
  module File
3
6
  def each(*arg, &block)
4
7
  return super unless arg.empty?
data/lib/flv/header.rb CHANGED
@@ -31,7 +31,15 @@ module FLV
31
31
 
32
32
  def debug(format, *)
33
33
  format.header("Header", path)
34
- format.values(to_h.tap{|h| h.delete(:path)})
34
+ format.values(to_h.tap{|h| [:path, :timestamp, :body].each{|key| h.delete(key)}})
35
+ end
36
+
37
+ def timestamp
38
+ 0
39
+ end
40
+
41
+ def body
42
+ self
35
43
  end
36
44
  end
37
45
  end
data/lib/flv/packing.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'singleton'
2
2
 
3
3
  module FLV
4
- class Event < Hash ; end
5
4
  # FLV files can contain structured data. This modules makes it easy to (un)pack that data.
6
5
  # The packing option +flv_value+ can (un)pack any kind of variable.
7
6
  # It corresponds to +SCRIPTDATAVALUE+ in the official FLV file format spec.
data/lib/flv.rb CHANGED
@@ -1,12 +1,9 @@
1
1
  require 'rubygems'
2
2
  require 'backports'
3
+ require 'packable'
3
4
 
4
- # utilities
5
+ # Base & Utilities
5
6
  require_relative 'flv/util/double_check'
6
-
7
- # packing of FLV objects:
8
- require 'packable'
9
- require_relative 'flv/packing'
10
7
  require_relative 'flv/base'
11
8
 
12
9
  # FLV body of tags
@@ -17,8 +14,11 @@ require_relative 'flv/body'
17
14
 
18
15
  # FLV chunks (tags & header)
19
16
  require_relative 'flv/timestamp'
20
- require_relative 'flv/tag'
21
17
  require_relative 'flv/header'
18
+ require_relative 'flv/tag'
19
+
20
+ # packing of FLV objects:
21
+ require_relative 'flv/packing'
22
22
 
23
23
  # finally:
24
24
  require_relative 'flv/file'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flvedit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Marc-Andr\xC3\xA9 Lafortune"
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-21 00:00:00 -04:00
12
+ date: 2009-04-22 00:00:00 -04:00
13
13
  default_executable: flvedit
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -60,6 +60,8 @@ files:
60
60
  - lib/flv/edit/processor/command_line.rb
61
61
  - lib/flv/edit/processor/cut.rb
62
62
  - lib/flv/edit/processor/debug.rb
63
+ - lib/flv/edit/processor/dispatcher.rb
64
+ - lib/flv/edit/processor/filter.rb
63
65
  - lib/flv/edit/processor/head.rb
64
66
  - lib/flv/edit/processor/join.rb
65
67
  - lib/flv/edit/processor/meta_data_maker.rb