Files
2025-09-29 00:52:08 +02:00

380 lines
12 KiB
Ruby
Executable File

# Convert timecode
# Read EDL FIle input
# REF
# 000001 JH_ENDSCENE_PT1_ALT1.AVI V C 01:00:05:09 01:00:13:14 01:00:00:00 01:00:09:20
path = File.expand_path $0
path = File.dirname(path)
require "#{ENV['RS_TOOLSROOT']}/lib/pipeline/util/string"
DEBUG = true
class Time
attr_reader :SourceIN
attr_writer :SourceIN
attr_reader :SourceOUT
attr_writer :SourceOUT
attr_reader :EditIN
attr_writer :EditIN
attr_reader :EditOUT
attr_writer :EditOUT
attr_reader :SourceDuration
attr_writer :SourceDuration
def initialize()
@SourceIN = Timeformat.new()
@SourceOUT = Timeformat.new()
@EditIN = Timeformat.new()
@EditOUT = Timeformat.new()
@EditDuration = 0
end
class Timeformat
attr_reader :Time
attr_writer :Time
attr_reader :Frames
attr_writer :Frames
def initialize()
@Time = 0
@Frames = 0
end
end
end
class EDLEntry
attr_reader :Index
attr_writer :Index
attr_reader :Name
attr_writer :Name
attr_reader :AudioVideo
attr_writer :AudioVideo
attr_reader :Cut
attr_writer :Cut
attr_reader :TimeCode
attr_writer :TimeCode
attr_reader :MediaType
attr_writer :MediaType
attr_reader :RangeType
attr_writer :RangeType
def initialize(tokens)
@Index = tokens[0].to_i
@Name = filterFilename(tokens[1])
@MediaType = filterMediaType(tokens[1])
@AudioVideo = tokens[2]
@Cut = tokens[3]
@RangeType = "RANGE"
timecode = Time.new()
timecode.SourceIN.Time = tokens[tokens.length-4]
timecode.SourceIN.Frames = retStartRange(timecode.SourceIN.Time).to_i
timecode.SourceOUT.Time = tokens[tokens.length-3]
timecode.SourceOUT.Frames = retEndRange(timecode.SourceOUT.Time).to_i
timecode.EditIN.Time = tokens[tokens.length-2]
timecode.EditIN.Frames = retStartRange(timecode.EditIN.Time).to_i
timecode.EditOUT.Time = tokens[tokens.length-1]
timecode.EditOUT.Frames = retEndRange(timecode.EditOUT.Time).to_i
timecode.SourceDuration = timecode.SourceOUT.Frames - timecode.SourceIN.Frames+1
@TimeCode = timecode
end
def filterFilename(rawName)
if rawName.include? "$"
finalName = rawName.split('$')[0]
elsif
finalName = rawName.split('.')[0]
else
finalName = rawName
end
return finalName
end
def filterMediaType(filename)
if filename.include? "."
mediaType = filename.split('.')[1]
if mediaType.length < 3
mediaType = "MOV" #Default media type in case some is cut off by avid
end
elsif filename.include? "$"
mediaType = "MOV" #Default media type if we have a $ but no .
else
mediaType = "NONE" #Shouldn't ever come up, but just covering everything
end
return mediaType
end
def retStartRange(sTimecode)
timeArray = retTimeArray(sTimecode)
returnTimeFrames(timeArray)
end
def retEndRange(sTimecode)
timeArray = retTimeArray(sTimecode)
returnTimeFrames(timeArray)-1
end
private
def retTimeArray(sTimecode)
timearray = Array.new()
tokens = sTimecode.split(':')
(0..tokens.size-1).each { |i|
timearray.push(tokens[i].to_i)
}
timearray
end
# return the time from the time array
def returnTimeFrames(aTimeArray)
#if aTimeArray.length != 4 then
fHours = 0#aTimeArray[0]*60*60*29.97
fMins = aTimeArray[1]*60*30
fSeconds = aTimeArray[2]*30
fFrames = aTimeArray[3]
#@log.Message("#{fHours} #{fMins} #{fSeconds} #{fFrames}")
# Total
mTotalFrames = fHours + fMins + fSeconds + fFrames # THis is tha start oe end
#puts mTotalFrames
mDropFrames = ((mTotalFrames) + 500) / 1000
puts mTotalFrames
#@log.Message("Total drop frames = #{mDropFrames}")
#@log.Message("Inintal frames = #{mTotalFrames}")
return mTotalFrames+mDropFrames
end
end
class EDL
# Constructors
attr_reader :TITLE
attr_writer :TITLE
attr_reader :FCM
attr_writer :FCM
attr_reader :REELLIST
attr_writer :REELLIST
def initialize()
@REELLIST = Array.new()
end
end
class EDLParser
def initialize(log)
@log = log
@previousEntry = nil
@extraEntry = nil
@rangeTypeNames = ["LEADIN", "LEADOUT", "INGAME", "MULTISTART", "BLENDOUT", "FADEIN", "FADEOUT", "BLACK"]
end
def readEDL(filename)
#file
edlEntry = EDL.new()
backupFrames = 0
backupDuration = 0
fadeinDuration = 0
blackDuration = 0
justDidFakeEditFix = false
File::open( filename, 'r' ) do |fp|
fp.each do |line|
# Skip empty lines
next if 0 == line.size
tokens = line.split(' ')
if line.starts_with( 'TITLE' ) then
edlEntry.TITLE = tokens[1]
elsif line.starts_with( 'FCM' ) then
edlEntry.FCM = tokens[1]
elsif line.starts_with( '00' ) and tokens.length > 7 then
# check we have a avi/mov referenced in teh Entry
fileTokens = tokens[1].split('.')
if fileTokens.length < 2
fileTokens = tokens[1].split('$')
end
if fileTokens.length == 2 then
if tokens[2].starts_with( 'V' ) then
"""
=========================== NIGHTMARE EDL RANGING LOGIC 4.0 ===========================
By Blake Buck
There is probably a much smarter way of doing this, but I've just continued adding checks
and exceptions for all the various special case scenarios that seem to keep coming up.
Latest Updates 4/7/14: Added fadein/fadeout/black range detection, and updated exceptions.
===========================================================================================
"""
# CHECK IF THIS IS THE FIRST ENTRY
# If setup properly in Avid, the first clip will never be a LEADIN/MULTI/BLENDOUT
if @previousEntry == nil
currentType = tokens[1].split('.')[0]
# CHECK FOR FADEIN
if currentType == "FADEIN"
tempEntry = EDLEntry.new(tokens)
fadeinDuration = tempEntry.TimeCode.SourceDuration
next
end
# IF WE HAVE A FADEIN - Create new extra clip first with correct ranges and type
if fadeinDuration != 0
fadeEntry = EDLEntry.new(tokens)
fadeEntry.TimeCode.SourceIN.Frames = fadeEntry.TimeCode.SourceIN.Frames - fadeinDuration
fadeEntry.TimeCode.SourceOUT.Frames = fadeEntry.TimeCode.SourceIN.Frames + fadeinDuration - 1
fadeEntry.TimeCode.SourceDuration = fadeinDuration
fadeEntry.RangeType = "FADEIN"
edlEntry.REELLIST.push(fadeEntry)
fadeinDuration = 0
end
# THEN CREATE OUR FIRST 'REAL' RANGE
newEntry = EDLEntry.new(tokens) #We create a new entry
@previousEntry = newEntry #And set it to previous entry
edlEntry.REELLIST.push(newEntry)
else
# CHECKS IF THIS RANGE IS A 'LABEL RANGE' (LEADIN/MULTI/BLENDOUT)
currentName = tokens[1].split('.')[0]
tempEntry = EDLEntry.new(tokens) #create a temp version of the range without actually adding it to the REELLIST
# CHECK FOR A FADEOUT TO FADEIN EXCEPTION - Set durations for fade and 'black' frames
# I believe we will always have at least 1 frame of 'black' between fadeouts and fadeins.
if (@previousEntry.RangeType == "FADEOUT") and currentName == ("FADEIN")
fadeinDuration = tempEntry.TimeCode.SourceDuration
blackDuration = tempEntry.TimeCode.EditIN.Frames - @previousEntry.TimeCode.EditOUT.Frames
justDidFakeEditFix = false
# NEW LABEL EXCEPTION TO FAKE EDIT FIX (This is way to goddamn complicated.)
# If we just did a fake edit fix, but now know it was a sequential new label, we correct the old
# ranges (technically 2 ranges back now) and create a new range using the saved @extraEntry
elsif (@rangeTypeNames.include? currentName) and (justDidFakeEditFix == true)
@previousEntry.TimeCode.SourceOUT.Frames = backupFrames
@previousEntry.TimeCode.SourceDuration = backupDuration
@extraEntry.RangeType = currentName #And updated the rangetype to the correct label
edlEntry.REELLIST.push(@extraEntry)
@previousEntry = @extraEntry
justDidFakeEditFix = false
# IF THIS IS A REGULAR LABEL (and not a fake edit exception)
elsif (@rangeTypeNames.include? currentName) and (justDidFakeEditFix == false)
@previousEntry.RangeType = currentName #We change the type of the previous entry
justDidFakeEditFix = false
# RANGE IS A REGULAR RANGE AND NOT A LABEL
# CHECK IF WE HAVE A 'FAKE EDIT' (2 immediately sequential 'regular' ranges)
elsif (@previousEntry.RangeType == "RANGE") and (tempEntry.RangeType == "RANGE") and ((@previousEntry.TimeCode.SourceOUT.Frames + 1) == (tempEntry.TimeCode.SourceIN.Frames))
backupFrames = @previousEntry.TimeCode.SourceOUT.Frames
backupDuration = @previousEntry.TimeCode.SourceDuration
@extraEntry = tempEntry
@previousEntry.TimeCode.SourceOUT.Frames = tempEntry.TimeCode.SourceOUT.Frames
@previousEntry.TimeCode.SourceDuration = @previousEntry.TimeCode.SourceDuration + tempEntry.TimeCode.SourceDuration
justDidFakeEditFix = true
else
#OR WE JUST HAVE A VALID NEW RANGE
# CHECK FOR BLACK RANGES FIRST
if blackDuration != 0
blackEntry = EDLEntry.new(tokens)
blackEntry.TimeCode.SourceIN.Frames = blackEntry.TimeCode.SourceIN.Frames - blackDuration - fadeinDuration
blackEntry.TimeCode.SourceOUT.Frames = blackEntry.TimeCode.SourceIN.Frames + blackDuration - 1
blackEntry.TimeCode.SourceDuration = blackDuration
blackEntry.RangeType = "BLACK"
edlEntry.REELLIST.push(blackEntry)
blackDuration = 0
end
# CHECK FOR FADEIN RANGES NEXT
if fadeinDuration != 0
fadeEntry = EDLEntry.new(tokens)
fadeEntry.TimeCode.SourceIN.Frames = fadeEntry.TimeCode.SourceIN.Frames - fadeinDuration
fadeEntry.TimeCode.SourceOUT.Frames = fadeEntry.TimeCode.SourceIN.Frames + fadeinDuration - 1
fadeEntry.TimeCode.SourceDuration = fadeinDuration
fadeEntry.RangeType = "FADEIN"
edlEntry.REELLIST.push(fadeEntry)
fadeinDuration = 0
end
# THEN CREATE A BASIC CLIP (Regardless if fadein / black ranges are created or not)
newEntry = EDLEntry.new(tokens) #Create a new entry
@previousEntry = newEntry #Set it to previous entry
edlEntry.REELLIST.push(newEntry)
justDidFakeEditFix = false
end
end
end
end
end
end
end
# DEbug Output
if DEBUG then
puts
edlEntry.REELLIST.each do |entry|
tab = "\t"
if entry.Name.length < 24 then
tab = "\t\t"
end
puts "#{entry.Name}#{tab}#{entry.TimeCode.SourceIN.Frames}\t#{entry.TimeCode.SourceOUT.Frames}\t#{entry.TimeCode.SourceDuration}\t#{entry.RangeType}"
end
end
return edlEntry
end
end
#require 'RSG.Base.dll'
#require 'RSG.Base.Configuration.dll'
#require 'RSG.Base.Windows.dll'
#require 'RSG.SourceControl.Perforce'
# Core
#include RSG::Base::Configuration
#include RSG::Base::Logging
#include RSG::Base::Logging::Universal
#include RSG::Base::OS
#include RSG::SourceControl::Perforce
#-----------------------------------------------------------------------------
# Entry-Point
#-----------------------------------------------------------------------------
#if ( __FILE__ == $0 ) then
#g_Options = OS::Options::new( OPTIONS )
#Need to enable the initlaize on the TOOLS NG Branch
# LogFactory.Initialize()
# LogFactory.CreateApplicationConsoleLogTarget( )
# @log = LogFactory.ApplicationLog
# @log.message("Using Server: #{"cheese"}")
# filename = 'X:/rdr3/assets/non_final/cutscene/EDL/UTP1MCS1.EDL' #TRV5INT.EDL
# parser = EDLParser.new{ }
# parser.readEDL(filename)
# end