media_processing_tool 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +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
|