Source code for cgl.core.path.sequence
import glob
import logging
import os
import re
from cgl.core.path.constants import SEQ_SPLIT, SEQ2_SPLIT
[docs]
class Sequence(object):
"""
Class for dealing with "Sequences" of images or files in general as defined within a vfx/animation/games cookbook
"""
sequence = None
globals_info = {}
frame_range = None
start_frame = None
end_frame = None
middle_frame = None
hash_sequence = None
num_sequence = None
star_sequence = None
padding = None
ext = None
num = None
hash = None
def __init__(self, sequence, verbose=False):
self.sequence = sequence
self.verbose = verbose
if not self.is_valid_sequence():
return
self.padding = 4
self.set_frange()
self.set_sequence_strings()
[docs]
def get_project_info(self):
pass
[docs]
def is_valid_sequence(self):
"""
Checks "sequence" to ensure it matches our definition of a sequence:
sequence.####.ext, sequence.%04d.ext, sequence.*.ext
Returns:
bool: True if valid, False if not
"""
self.set_ext()
valid = False
if not self.sequence:
return False
if self.sequence.endswith("#%s" % self.ext):
valid = True
elif "%" in self.sequence:
valid = True
elif self.sequence.endswith("*%s" % self.ext):
valid = True
if not valid and self.verbose:
logging.error("%s is not a valid sequence" % self.sequence)
return valid
[docs]
def set_ext(self):
"""
sets the ext value for the sequence
Returns:
None
"""
_, self.ext = os.path.splitext(self.sequence)
[docs]
def print_info(self):
logging.debug("---------------- sequence info -------------------")
logging.debug("Star: %s" % self.star_sequence)
logging.debug("Hash: %s" % self.hash_sequence)
logging.debug("Num: %s" % self.num_sequence)
logging.debug("Frame Range: %s" % self.frame_range)
logging.debug("Start Frame: %s" % self.start_frame)
logging.debug("End Frame: %s" % self.end_frame)
logging.debug("Middle Frame: %s" % self.middle_frame)
[docs]
def set_sequence_strings(self):
"""
This is a utility function for setting all the various string formats we need for sequences depending
on the software we happen to be working in:
####.exr - hash_sequence
%04d.exr - num_sequence
*.exr - star_sequence
Returns:
None
"""
seq_base = self.split_sequence()
self.star_sequence = "%s*%s" % (seq_base, self.ext)
self.num_sequence = "%s%s%s" % (seq_base, self.num, self.ext)
self.hash_sequence = "%s%s%s" % (seq_base, self.hash, self.ext)
[docs]
def split_sequence(self):
"""
We split the sequence at the delimiter ('*', '%0Nd', or '###') and return the first value.
Example:
::
split_sequence('sequence.####.exr') returns 'sequence'
split_sequence('sequence.%04d.exr') returns 'sequence'
split_sequence('sequence.*.exr') returns 'sequence'
Returns:
str: The first part of the sequence string or None if the sequence is not valid.
"""
frange = None
group = None
if self.sequence.endswith("#%s" % self.ext):
frange = re.search(SEQ_SPLIT, self.sequence)
group = frange.group(0)
elif "%" in self.sequence:
frange = re.search(SEQ2_SPLIT, self.sequence)
group = frange.group(0)
elif self.sequence.endswith("*%s" % self.ext):
frange = True
group = "*."
if frange:
return self.sequence.split(group)[0]
else:
return
[docs]
def set_frange(self):
"""
Sets all information regarding frame range for the sequence
start_frame, end_frame, middle_frame, frame_range are all set by this function
Returns:
None
"""
sframe = None
eframe = None
regex = re.compile(r"\s[\d]+-[\d]+$")
current_sel = self.sequence
frange = re.search(regex, current_sel)
frame_range = ""
if frange:
sframe, eframe = frange.group(0).split("-")
sframe = sframe.replace(" ", "")
else:
# This requires the # form of the sequence
glob_string = ""
if "#" in self.sequence:
glob_string = "%s*%s" % (self.sequence.split("#")[0], self.ext)
elif "%" in self.sequence:
glob_string = "%s*%s" % (self.sequence.split("%")[0], self.ext)
elif "*" in self.sequence:
glob_string = self.sequence
frames = sorted(glob.glob(glob_string))
if frames:
try:
from cgl.core.list_dir import get_frame_range
frame_range = get_frame_range(frames)
sframe, eframe = frame_range.split("-")
# sframe = re.search(SEQ_REGEX, frames[0]).group(0).replace(".", "")
# eframe = re.search(SEQ_REGEX, frames[-1]).group(0).replace(".", "")
except AttributeError:
logging.error(
"problem with file_path: %s and frames: "
"%s and %s in get_frange_from_seq, skipping."
% (self.sequence, frames[0], frames[1])
)
else:
self.start_frame = 1001 # TODO pull this from project_info
self.hash = "#" * self.padding
if self.padding < 10:
self.num = "%0" + str(self.padding) + "d"
else:
self.num = "%" + str(self.padding) + "d"
return
if sframe and eframe:
self.frame_range = frame_range
self.start_frame = sframe
self.end_frame = eframe
self.padding = len(self.start_frame)
self.hash = "#" * self.padding
if self.padding < 10:
self.num = "%0" + str(self.padding) + "d"
else:
self.num = "%" + str(self.padding) + "d"
mid_frame = int((int(eframe) - int(sframe)) / 2) + int(sframe)
self.middle_frame = self.int_as_padded_frame(mid_frame, self.padding)
[docs]
@staticmethod
def int_as_padded_frame(number, padding=None):
"""
Given number, return a string with padding of `padding`.
Example:
Given 3 return string with padding of 4 = 0003::
int_as_padded_frame(3, 4)
Args:
number (int): The number to convert to a string with padding.
padding (int): The number of digits to pad the string with.
Returns:
str: The number as a string with padding.
"""
if padding == 2:
return "%02d" % number
elif padding == 3:
return "%03d" % number
elif padding == 4:
return "%04d" % number
elif padding == 5:
return "%05d" % number
elif padding == 6:
return "%06d" % number
elif padding == 7:
return "%07d" % number
elif padding == 8:
return "%08d" % number
elif padding == 9:
return "%09d" % number
elif padding == 10:
return "%10d" % number
elif padding == 11:
return "%11d" % number