media_processing_tool 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/README.md +2 -0
- data/bin/catalog +181 -0
- data/bin/catalog_assets +187 -0
- data/bin/fcp_xml_parser +41 -0
- data/bin/mig +44 -0
- data/bin/mig_http +52 -0
- data/bin/xml_processor +51 -0
- data/config/default/xml_processor_config +49 -0
- data/lib/axml.rb +59 -0
- data/lib/cli.rb +88 -0
- data/lib/final_cut_pro.rb +31 -0
- data/lib/final_cut_pro/sequence_processor.rb +135 -0
- data/lib/final_cut_pro/xml_parser.rb +15 -0
- data/lib/final_cut_pro/xml_parser/common.rb +121 -0
- data/lib/final_cut_pro/xml_parser/document.rb +18 -0
- data/lib/final_cut_pro/xml_parser/fcpxml/version_1.rb +28 -0
- data/lib/final_cut_pro/xml_parser/xmeml/version_5.rb +234 -0
- data/lib/itunes/xml_parser.rb +51 -0
- data/lib/media_processing_tool/publisher.rb +52 -0
- data/lib/media_processing_tool/xml_parser.rb +30 -0
- data/lib/media_processing_tool/xml_parser/document.rb +38 -0
- data/lib/media_processing_tool/xml_parser/identifier.rb +43 -0
- data/lib/media_processing_tool/xml_processor.rb +132 -0
- data/lib/mig.rb +158 -0
- data/lib/mig/http.rb +54 -0
- data/lib/mig/modules/common.rb +333 -0
- data/lib/mig/modules/exiftool.rb +26 -0
- data/lib/mig/modules/ffmpeg.rb +225 -0
- data/lib/mig/modules/media_type.rb +23 -0
- data/lib/mig/modules/mediainfo.rb +91 -0
- data/lib/timecode_methods.rb +108 -0
- data/lib/udam_utils/publish_map_processor.rb +710 -0
- metadata +111 -0
data/bin/mig_http
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
if %w(start stop restart reload run zap status).include?((command = ARGV.first) ? command.downcase : command)
|
3
|
+
require 'daemons'
|
4
|
+
Daemons.run($0)
|
5
|
+
exit
|
6
|
+
end
|
7
|
+
|
8
|
+
lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
9
|
+
$:.unshift(lib_path) if !$:.include?(lib_path) and File.exists?(lib_path)
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
require 'cli'
|
13
|
+
require 'mig'
|
14
|
+
require 'mig/http'
|
15
|
+
|
16
|
+
options[:binding] = '0.0.0.0'
|
17
|
+
options[:local_port] = 4567
|
18
|
+
options[:ffmpeg_cmd_path] = FFMPEG::DEFAULT_EXECUTABLE_PATH
|
19
|
+
options[:mediainfo_cmd_path] = Mediainfo::DEFAULT_EXECUTABLE_PATH
|
20
|
+
options[:exiftool_cmd_path] = ExifTool::DEFAULT_EXECUTABLE_PATH
|
21
|
+
|
22
|
+
op = common_option_parser
|
23
|
+
op.banner = "Usage: #{File.basename(__FILE__)} [options] \n\t#{File.basename(__FILE__)} [start|stop|status] [daemon options] -- [application options]"
|
24
|
+
op.on('--ffmpeg-bin-path PATH', 'The path to the FFMPEG executable.', "\tdefault: #{options[:ffmpeg_cmd_path]}") { |v| options[:ffmpeg_cmd_path] = v }
|
25
|
+
op.on('--mediainfo-bin-path PATH', 'The path to the MediaInfo executable.', "\tdefault: #{options[:mediainfo_cmd_path]}") { |v| options[:mediainfo_cmd_path] = v }
|
26
|
+
op.on('--exiftool-bin-path PATH', 'The path to the exiftool executable.', "\tdefault: #{options[:exiftool_cmd_path]}") { |v| options[:exiftool_cmd_path] = v }
|
27
|
+
op.on('--binding BINDING', 'The address to bind the server to.',
|
28
|
+
"\tdefault: #{options[:binding]}") do |v|
|
29
|
+
options[:binding] = v
|
30
|
+
end
|
31
|
+
op.on('--port PORT', 'The port that the server should listen on.',
|
32
|
+
"\tdefault: #{options[:local_port]}") do |v|
|
33
|
+
options[:local_port] = v
|
34
|
+
end
|
35
|
+
op.on('--[no-]options-file [FILENAME]', "\tdefault: #{options[:options_file_path]}" ) { |v| options[:options_file_path] = v }
|
36
|
+
add_common_options
|
37
|
+
op.parse_common
|
38
|
+
|
39
|
+
logger = Logger.new(options[:log_to] || STDERR)
|
40
|
+
logger.level = Logger::DEBUG
|
41
|
+
options[:logger] = logger
|
42
|
+
|
43
|
+
mig = MediaInformationGatherer.new(options)
|
44
|
+
|
45
|
+
app = MediaInformationGatherer::HTTP
|
46
|
+
app.set(:logger, logger)
|
47
|
+
app.set(:bind, options.delete(:binding))
|
48
|
+
app.set(:port, options.delete(:local_port))
|
49
|
+
app.set(:mig, mig)
|
50
|
+
app.set(:initial_options, options)
|
51
|
+
app.run!
|
52
|
+
|
data/bin/xml_processor
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
$:.unshift(lib_path) if !$:.include?(lib_path) and File.exists?(lib_path)
|
4
|
+
require 'optparse'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
require 'media_processing_tool/xml_processor'
|
8
|
+
|
9
|
+
options_file_path = nil
|
10
|
+
config_file_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'default', "#{File.basename(__FILE__, '.*')}_config"))
|
11
|
+
options = { }
|
12
|
+
ARGV << '--help' if ARGV.empty?
|
13
|
+
op = OptionParser.new
|
14
|
+
op.banner = "#{File.basename(__FILE__)} [options] file"
|
15
|
+
options[:config_file_path] = config_file_path
|
16
|
+
op.on('--config-file-path PATH', 'The path to the configuration file containing the @publish_map variable definition.', "default: #{config_file_path}") { |v| options[:config_file_path] = v }
|
17
|
+
op.on('--[no-]output-results', 'Outputs the Result as a JSON object.') { |v| options[:output_results] = v }
|
18
|
+
op.on('--[no-]pretty-print', 'Pretty Prints the Results.') { |v| options[:pretty_print] = v }
|
19
|
+
#op.on('--path-to-mig PATH', '') { |v| options[:path_to_mig] = v }
|
20
|
+
options[:log_level] = Logger::WARN
|
21
|
+
op.on('--log-level LEVEL', [:debug, :DEBUG, :info, :INFO, :warn, :WARN, :error, :ERROR, :fatal, :FATAL], 'The logging level.', 'default: WARN') do |v|
|
22
|
+
v = v.upcase # Symbol doesn't support upcase!
|
23
|
+
options[:log_level] = Logger.const_defined?(v) ? Logger.const_get(v) : Logger::WARN
|
24
|
+
end
|
25
|
+
options[:log_to] = STDERR
|
26
|
+
op.on('--log-to PATH', 'Log output location (ex: stderr, stdout, device path, file path)', "\tdefault: STDERR") do |v|
|
27
|
+
options[:log_to] = case v.downcase
|
28
|
+
when 'stderr'; STDERR
|
29
|
+
when 'stdout'; STDOUT
|
30
|
+
else; v
|
31
|
+
end
|
32
|
+
end
|
33
|
+
op.on('--help', 'Display this menu.') { puts op; exit }
|
34
|
+
op.load(options_file_path)
|
35
|
+
op.parse!
|
36
|
+
|
37
|
+
xp = MediaProcessingTool::XMLProcessor.new(options)
|
38
|
+
|
39
|
+
file_paths = ARGV
|
40
|
+
results = file_paths.map { |file_path| { file: file_path, results: xp.process(file_path) } }
|
41
|
+
results = results.pop[:results] if file_paths.length == 1
|
42
|
+
|
43
|
+
if options[:output_results]
|
44
|
+
json_options = { allow_nan: true }
|
45
|
+
|
46
|
+
if options[:pretty_print]
|
47
|
+
puts JSON.pretty_generate(results, json_options)
|
48
|
+
else
|
49
|
+
puts JSON.fast_generate(results, json_options)
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# The Following Instance Variables are Available During Evaluation
|
2
|
+
#
|
3
|
+
# @object The Hash that is being Processed
|
4
|
+
# @full_file_path_field_name defaults to :path_on_file_system
|
5
|
+
# @full_file_path = object[@full_file_path_field_name]
|
6
|
+
#
|
7
|
+
# @metadata_sources = object.fetch(:metadata_sources, { })
|
8
|
+
# @exiftool = @metadata_sources[:exiftool] ||= { }
|
9
|
+
# @mediainfo = @metadata_sources[:mediainfo] ||= { }
|
10
|
+
# @ffmpeg = @metadata_sources[:ffmpeg] ||= { }
|
11
|
+
# @filemagic = @metadata_sources[:filemagic] ||= { }
|
12
|
+
# @media = @metadata_sources[:filemagic] ||= { }
|
13
|
+
# @common_media_info = @metadata_sources[:common] ||= { }
|
14
|
+
#
|
15
|
+
# @media = entity.fetch('media', { })
|
16
|
+
# @media_type = @media[:type] || @media['type']
|
17
|
+
# @media_subtype = @media[:subtype] || @media['subtype']
|
18
|
+
@publish_maps = [
|
19
|
+
{
|
20
|
+
type: :glob,
|
21
|
+
map: {
|
22
|
+
'*.*' => {
|
23
|
+
publish_executable: 'echo',
|
24
|
+
eval_publish_executable: false,
|
25
|
+
publish_arguments: %q("#{@full_file_path} #{@media_type}/#{@media_subtype} >> /tmp/mpt_xml_processor_test"),
|
26
|
+
eval_publish_arguments: true,
|
27
|
+
#workflow: {
|
28
|
+
# name: 'WORKFLOW_ONLY',
|
29
|
+
# parameters: {
|
30
|
+
#
|
31
|
+
# } # parameters
|
32
|
+
#} # workflow
|
33
|
+
}
|
34
|
+
} # map
|
35
|
+
},
|
36
|
+
{
|
37
|
+
type: :eval,
|
38
|
+
map: {
|
39
|
+
'true' => {
|
40
|
+
workflow: {
|
41
|
+
name: 'WORKFLOW_ONLY',
|
42
|
+
parameters: {
|
43
|
+
|
44
|
+
} # parameters
|
45
|
+
} # workflow
|
46
|
+
}
|
47
|
+
} # map
|
48
|
+
},
|
49
|
+
] # @publish_maps
|
data/lib/axml.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Abstracted XML Module
|
2
|
+
module AXML
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'libxml'
|
6
|
+
XML_PARSER = :libxml unless defined?(XML_PARSER)
|
7
|
+
rescue LoadError
|
8
|
+
require 'rexml/document'
|
9
|
+
XML_PARSER = :rexml unless defined?(XML_PARSER)
|
10
|
+
end
|
11
|
+
|
12
|
+
module REXMLAbstraction
|
13
|
+
class << self
|
14
|
+
def self.xml_as_document(xml)
|
15
|
+
if xml.is_a?(String)
|
16
|
+
xml_clean = xml.chomp.strip.chomp
|
17
|
+
unless xml_clean.start_with?('<')
|
18
|
+
file_name = File.expand_path(xml)
|
19
|
+
xml_document = REXML::Document.new(File.new(file_name)) if !file_name.start_with?('<') and File.exists?(file_name)
|
20
|
+
end
|
21
|
+
xml_document ||= REXML::Document.new(xml)
|
22
|
+
else
|
23
|
+
xml_document = xml
|
24
|
+
end
|
25
|
+
xml_document
|
26
|
+
end # xml_as_document
|
27
|
+
end # self
|
28
|
+
end
|
29
|
+
|
30
|
+
module LibXMLAbstraction
|
31
|
+
|
32
|
+
def self.xml_as_document(xml)
|
33
|
+
if xml.is_a?(String)
|
34
|
+
xml_clean = xml.chomp.strip.chomp
|
35
|
+
unless xml_clean.start_with?('<')
|
36
|
+
file_name = File.expand_path(xml)
|
37
|
+
xml_document = LibXML::XML::Document.file(file_name) if File.exists?(file_name)
|
38
|
+
end
|
39
|
+
xml_document ||= LibXML::XML::Document.string(xml)
|
40
|
+
else
|
41
|
+
xml_document = xml
|
42
|
+
end
|
43
|
+
xml_document
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
# if XML_PARSER == :libxml
|
49
|
+
# #puts 'Including LibXML'
|
50
|
+
# include LibXMLAbstraction
|
51
|
+
# else
|
52
|
+
# #puts 'Including REXML'
|
53
|
+
# include REXMLAbstraction
|
54
|
+
# end
|
55
|
+
|
56
|
+
# Force LibXML Usage
|
57
|
+
def self.xml_as_document(xml); LibXMLAbstraction.xml_as_document(xml); end # self.xml_as_document
|
58
|
+
|
59
|
+
end # AXML
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'optparse'
|
3
|
+
module CLI
|
4
|
+
|
5
|
+
LOGGING_LEVELS = {
|
6
|
+
:debug => Logger::DEBUG,
|
7
|
+
:info => Logger::INFO,
|
8
|
+
:warn => Logger::WARN,
|
9
|
+
:error => Logger::ERROR,
|
10
|
+
:fatal => Logger::FATAL
|
11
|
+
}
|
12
|
+
|
13
|
+
class <<self
|
14
|
+
|
15
|
+
attr_accessor :options
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class CommonOptionParser < ::OptionParser
|
20
|
+
|
21
|
+
attr_accessor :options
|
22
|
+
|
23
|
+
#def options=(value)
|
24
|
+
# #puts "Setting #{self.class.name}[#{self.object_id}] options => (#{value.class.name}[#{value.object_id}]) #{value}"
|
25
|
+
# @options = value
|
26
|
+
#end
|
27
|
+
#
|
28
|
+
#def options
|
29
|
+
# #puts "Getting #{self.class.name}[#{self.object_id}] options. #{@options}"
|
30
|
+
# @options
|
31
|
+
#end
|
32
|
+
|
33
|
+
def parse_common
|
34
|
+
#puts "Parsing #{self.class.name}[#{self.object_id}] options. #{@options}"
|
35
|
+
parse!(ARGV.dup)
|
36
|
+
|
37
|
+
options_file_path = options[:options_file_path]
|
38
|
+
# Make sure that options from the command line override those from the options file
|
39
|
+
parse!(ARGV.dup) if options_file_path and load(options_file_path)
|
40
|
+
|
41
|
+
check_required_arguments
|
42
|
+
end
|
43
|
+
|
44
|
+
def required_arguments; @required_arguments ||= [ ] end
|
45
|
+
def add_required_argument(*args) [*args].each { |arg| required_arguments << arg } end
|
46
|
+
alias :add_required_arguments :add_required_argument
|
47
|
+
|
48
|
+
def missing_required_arguments
|
49
|
+
puts "Options #{options}"
|
50
|
+
required_arguments.dup.delete_if { |a| options.has_key?(a.is_a?(Hash) ? a.keys.first : a) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_required_arguments
|
54
|
+
_missing_arguments = missing_required_arguments
|
55
|
+
unless _missing_arguments.empty?
|
56
|
+
abort "Missing Required Arguments: #{_missing_arguments.map { |v| (v.is_a?(Hash) ? v.values.first : v).to_s.sub('_', '-')}.join(', ')}\n#{self.to_s}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end # CommonOptionParser
|
60
|
+
|
61
|
+
def self.new_common_option_parser(*args)
|
62
|
+
op = CommonOptionParser.new(*args)
|
63
|
+
op.options = options
|
64
|
+
op
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
@options = options ||= { } #HashTap.new
|
70
|
+
def options; @options end
|
71
|
+
def common_option_parser
|
72
|
+
@common_option_parser ||= begin
|
73
|
+
CLI.options ||= options
|
74
|
+
CLI.new_common_option_parser
|
75
|
+
end
|
76
|
+
end
|
77
|
+
def add_common_options
|
78
|
+
options[:log_level] ||= 1
|
79
|
+
common_option_parser.on('--[no-]options-file [FILENAME]', "\tdefault: #{options[:options_file_path]}" ) { |v| options[:options_file_path] = v }
|
80
|
+
common_option_parser.on('--log-to FILENAME', 'Log file location.', "\tdefault: STDERR") { |v| options[:log_to] = v }
|
81
|
+
common_option_parser.on('--log-level LEVEL', CLI::LOGGING_LEVELS.keys, "Logging level. Available Options: #{CLI::LOGGING_LEVELS.keys.join(', ')}",
|
82
|
+
"\tdefault: #{CLI::LOGGING_LEVELS.invert[options[:log_level]]}") { |v| options[:log_level] = CLI::LOGGING_LEVELS[v] }
|
83
|
+
common_option_parser.on('-h', '--help', 'Show this message.') { puts common_option_parser; exit }
|
84
|
+
end
|
85
|
+
|
86
|
+
default_options_file_path = File.join(File.expand_path('.'), "#{File.basename($0, '.rb')}_options")
|
87
|
+
options[:options_file_path] = default_options_file_path if File.exists?(default_options_file_path)
|
88
|
+
options[:options_file_path] ||= File.expand_path(File.basename($0, '.*'), '~/.options')
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module FinalCutPro
|
3
|
+
|
4
|
+
attr_writer :logger
|
5
|
+
|
6
|
+
# @param [Hash] options
|
7
|
+
# @option options [Object|nil] :logger (Logger)
|
8
|
+
# @option options [String|Object] :log_to (STDERR)
|
9
|
+
# @option options [Fixnum] :log_level (3)
|
10
|
+
def self.process_options_for_logger(options = { })
|
11
|
+
_logger = options[:logger]
|
12
|
+
unless _logger
|
13
|
+
if options[:log_to] or options[:log_level]
|
14
|
+
_logger = Logger.new(options[:log_to] || STDERR)
|
15
|
+
_logger.level = options[:log_level] if options[:log_level]
|
16
|
+
else
|
17
|
+
_logger = logger
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@logger ||= _logger
|
21
|
+
_logger
|
22
|
+
end # process_options_for_logger
|
23
|
+
|
24
|
+
def self.logger
|
25
|
+
return @logger if @logger
|
26
|
+
@logger = Logger.new(STDERR)
|
27
|
+
@logger.level = Logger::ERROR
|
28
|
+
@logger
|
29
|
+
end # self.logger
|
30
|
+
|
31
|
+
end # FinalCutPro
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'uri'
|
3
|
+
require 'timecode_methods'
|
4
|
+
|
5
|
+
module FinalCutPro
|
6
|
+
class SequenceProcessor
|
7
|
+
|
8
|
+
class << self
|
9
|
+
include TimecodeMethods
|
10
|
+
|
11
|
+
attr_writer :logger
|
12
|
+
def logger; @logger ||= Logger.new(STDOUT) end
|
13
|
+
|
14
|
+
def parse_xml(xml)
|
15
|
+
doc = FinalCutPro::XMLParser.load(xml)
|
16
|
+
sequences = doc.parse_sequences(xml)
|
17
|
+
process(sequences)
|
18
|
+
end
|
19
|
+
|
20
|
+
def process(sequences)
|
21
|
+
files = { }
|
22
|
+
clips = [ ]
|
23
|
+
_sequences = { }
|
24
|
+
[*sequences].each do |sequence|
|
25
|
+
sequence_id = sequence[:id]
|
26
|
+
clip_items = sequence[:clip_items]
|
27
|
+
next unless clip_items
|
28
|
+
clip_item_counter = 0
|
29
|
+
clip_items_count = clip_items.count
|
30
|
+
clip_items.each do |clip_item|
|
31
|
+
clip_item_counter += 1
|
32
|
+
#logger.debug { "Clip Item #{clip_item_counter} of #{clip_items_count}"}
|
33
|
+
|
34
|
+
clip_rate = clip_item[:rate]
|
35
|
+
clip_time_base = clip_rate[:timebase].to_f
|
36
|
+
clip_rate_ntsc = clip_rate[:ntsc] == 'TRUE' ? true : false
|
37
|
+
clip_frame_rate = convert_time_base(clip_time_base, clip_rate_ntsc)
|
38
|
+
|
39
|
+
clip_duration_frames = clip_item[:duration].to_i
|
40
|
+
clip_duration_seconds = clip_duration_frames / clip_frame_rate
|
41
|
+
|
42
|
+
clip_start_frame = clip_item[:start].to_i
|
43
|
+
clip_end_frame = clip_item[:end].to_i
|
44
|
+
|
45
|
+
clip_in_frame = clip_item[:in].to_i
|
46
|
+
clip_out_frame = clip_item[:out].to_i
|
47
|
+
|
48
|
+
|
49
|
+
# File tags may just be a reference to an earlier file tag. A reference only includes an id attribute which we
|
50
|
+
# can use to lookup the file from previous parsing.
|
51
|
+
file = clip_item[:file] || { }
|
52
|
+
file_id = file[:id]
|
53
|
+
if file.keys.count == 1
|
54
|
+
# We only got the id with this file tag so look it up in our file cache
|
55
|
+
file = files[file_id]
|
56
|
+
else
|
57
|
+
# This file has more than just an ID so it's not a reference to a previous file. Store it in the file cache
|
58
|
+
files[file_id] = file
|
59
|
+
end
|
60
|
+
next unless file
|
61
|
+
|
62
|
+
file_timecode = file[:timecode] || { }
|
63
|
+
#file_timecode_value = file_timecode[:string] || '00:00:00:00'
|
64
|
+
file_timecode_frame = file_timecode[:frame].to_i
|
65
|
+
|
66
|
+
file_rate = file[:rate] || { }
|
67
|
+
file_time_base = file_rate[:timebase]
|
68
|
+
file_rate_ntsc = (file_rate[:ntsc] == 'TRUE') ? true : false
|
69
|
+
file_frame_rate = convert_time_base(file_time_base, file_rate_ntsc)
|
70
|
+
#puts "TB: #{file_time_base} NTSC: #{file_rate_ntsc} FPS: #{file_frame_rate}"
|
71
|
+
file_in_frame = clip_in_frame # + file_timecode_frame
|
72
|
+
file_out_frame = clip_out_frame # + file_timecode_frame
|
73
|
+
|
74
|
+
# Use the time base and not the frame rate
|
75
|
+
file_to_clip_rate_ratio = (file_time_base.to_f / clip_time_base.to_f)
|
76
|
+
|
77
|
+
#file_in_frame_at_clip_frame_rate = clip_in_frame * file_to_clip_rate_ratio
|
78
|
+
#file_out_frame_at_clip_frame_rate = clip_out_frame * file_to_clip_rate_ratio
|
79
|
+
|
80
|
+
file_in_timecode = frames_to_timecode(file_in_frame, clip_frame_rate, clip_rate_ntsc, ':')
|
81
|
+
file_out_timecode = frames_to_timecode(file_out_frame, clip_frame_rate, clip_rate_ntsc, ':')
|
82
|
+
|
83
|
+
#file_in_timecode_with_offset = TimecodeHelper.timecode_calculator(file_in_timecode, file_out_timecode, file_timecode_value, file_frame_rate)
|
84
|
+
file_in_seconds = (file_in_frame/clip_frame_rate) # TimecodeHelper.convert_frames_to_seconds(file_frame_rate, file_in_frame)
|
85
|
+
file_out_seconds = (file_out_frame/clip_frame_rate) # TimecodeHelper.convert_frames_to_seconds(file_frame_rate, file_out_frame)
|
86
|
+
|
87
|
+
file_path_url = file[:pathurl] || ''
|
88
|
+
file_path = URI.unescape(file_path_url).scan(/.*:\/\/\w*(\/.*)/).flatten.first
|
89
|
+
|
90
|
+
|
91
|
+
file_in_frame_with_offset = file_timecode_frame + (file_in_frame * file_to_clip_rate_ratio)
|
92
|
+
file_out_frame_with_offset = file_timecode_frame + (file_out_frame * file_to_clip_rate_ratio)
|
93
|
+
file_in_timecode_with_offset = frames_to_timecode(file_in_frame_with_offset, file_frame_rate, file_rate_ntsc, ';')
|
94
|
+
file_out_timecode_with_offset = frames_to_timecode(file_out_frame_with_offset, file_frame_rate, file_rate_ntsc, ';')
|
95
|
+
|
96
|
+
clips << {
|
97
|
+
:start_frame => clip_start_frame,
|
98
|
+
:end_frame => clip_end_frame,
|
99
|
+
:timebase => clip_time_base,
|
100
|
+
:ntsc => clip_rate[:ntsc],
|
101
|
+
:frame_rate => clip_frame_rate,
|
102
|
+
:sequence => { :id => sequence_id },
|
103
|
+
:duration_in_seconds => clip_duration_seconds,
|
104
|
+
:duration_in_frames => clip_duration_frames,
|
105
|
+
:file => {
|
106
|
+
:id => file_id,
|
107
|
+
:pathurl => file_path_url,
|
108
|
+
:path => file_path,
|
109
|
+
:timecode => file_timecode,
|
110
|
+
:timebase => file_time_base,
|
111
|
+
:ntsc => file_rate_ntsc,
|
112
|
+
:frame_rate => file_frame_rate,
|
113
|
+
|
114
|
+
:in_frame => file_in_frame,
|
115
|
+
:out_frame => file_out_frame,
|
116
|
+
:in_seconds => file_in_seconds,
|
117
|
+
:out_seconds => file_out_seconds,
|
118
|
+
:in_timecode => file_in_timecode,
|
119
|
+
:out_timecode => file_out_timecode,
|
120
|
+
:in_frame_with_offset => file_in_frame_with_offset,
|
121
|
+
:out_frame_with_offset => file_out_frame_with_offset,
|
122
|
+
:in_timecode_with_offset => file_in_timecode_with_offset,
|
123
|
+
:out_timecode_with_offset => file_out_timecode_with_offset,
|
124
|
+
},
|
125
|
+
}
|
126
|
+
_sequences[sequence_id] = clips
|
127
|
+
end
|
128
|
+
end
|
129
|
+
_sequences
|
130
|
+
end # process
|
131
|
+
|
132
|
+
end # << self
|
133
|
+
|
134
|
+
end # SequenceProcessor
|
135
|
+
end # FinalCutPro
|