def __init__(self, shotCutList=''): self.fileService = flix.fileServices.FileService() self.fileServiceLocal = flix.fileServices.fileLocal.FileLocal() self.rePath = RepathDefault() # self.shotList = shotCutList if shotCutList != '': self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.kargs = { 'show': self.shotList.show, 'sequence': self.shotList.sequence, 'branch': self.shotList.branch, 'version': self.shotList.version } self.mode = self.shotList.mode self.movDir = self.mode.get("[editorialMOVFolder]") # load the icon iconPath = flix.core2.mode.Mode().get( '[FLIX_FOLDER]') + '/assets/20px-quicktime.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='QuickTime per Shot', icon=icon, tooltip='QuickTime per Shot', group='Export', pluginPath='flixConfig.plugins.toMovPerShot.ToMovPerShot')
def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' # load the icon iconPath = Mode().get('[FLIX_FOLDER]') + '/assets/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='BurnIn per Shot', icon=icon, tooltip='BurnIn per Shot', group='Export', pluginPath='flixConfig.plugins.burnInPerShot.BurnInPerShot')
def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' # load the icon iconPath = Mode().get('[FLIX_CONFIG_FOLDER]')+'/plugins/icons/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init(label ='Log Multiple Shot Edits', icon =icon, tooltip ='Logs latest shotedit from every sequence', group ='Export', pluginPath='flixConfig.plugins.logMultipleShotEdits.LogMultipleShotEdits')
def __init__(self): self.fileService = flix.fileServices.FileService() self.fileServiceLocal = flix.fileServices.fileLocal.FileLocal() self.rePath = RepathDefault() self.shotList = '' # load the icon iconPath = flix.core2.mode.Mode().get( '[FLIX_FOLDER]') + '/assets/20px-quicktime.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='QuickTime per Shot', icon=icon, tooltip='QuickTime per Shot', group='Export', pluginPath='flixConfig.plugins.toMovPerShot.ToMovPerShot')
class LogMultipleShotEdits(PluginDecorator): #-------------------------------------------------------------------------- # object #-------------------------------------------------------------------------- def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' # load the icon iconPath = Mode().get('[FLIX_CONFIG_FOLDER]')+'/plugins/icons/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init(label ='Log Multiple Shot Edits', icon =icon, tooltip ='Logs latest shotedit from every sequence', group ='Export', pluginPath='flixConfig.plugins.logMultipleShotEdits.LogMultipleShotEdits') #-------------------------------------------------------------------------- # executions #-------------------------------------------------------------------------- def execute(self, shotCutList, selection, additionalData=None): self.shotList = flix.core2.shotCutList.ShotCutList.fromFile(shotCutList.defaultPath()) self.show = self.shotList.show sequences = flix.core.sequence.Sequence.sequencesToXMLList(flix.core.sequence.Sequence.getSequences(self.show)) for seq in sequences: seqName = seq.attrib["name"] latestShotEdit = self.getLatestShotCutList(seqName) log("latestShotEdit: %s" % latestShotEdit) def getLatestSequenceVersion(self, sequence, branch="main"): maxEditVersions = flix.versioning.flixVersioning.FlixVersioning().getCurrentFlixEditVersion(self.show, sequence, branch) i = 0 sequenceObj = flix.core.sequence.Sequence(self.show, sequence) for i in range(maxEditVersions, -1, -1): editVersion = str(i) editorialPath = sequenceObj.getShotEditPath(branch, editVersion) if self.fileService.exists(editorialPath): break return i def getLatestShotCutList(self, sequence, branch="main"): editVersion = self.getLatestSequenceVersion(sequence) sequenceObj = flix.core.sequence.Sequence(self.show, sequence) editorialPath = sequenceObj.getShotEditPath(branch, editVersion) if not fileService.exists(editorialPath): flix.logger.log('Could not get the latest shotcutlist %s' % self.rePath.localize(editorialPath), isError=True) return None return flix.core2.shotCutList.ShotCutList.fromFile(editorialPath)
def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.fileServiceLocal = flix.fileServices.fileLocal.FileLocal() self.serverFlixFunctions = ServerFlixFunctions() self.shotList = '' # load the icon iconPath = Mode().get( '[FLIX_CONFIG_FOLDER]') + '/plugins/icons/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init( label='Picture In Picture', icon=icon, tooltip='Picture In Picture', group='Maya', pluginPath='flixConfig.plugins.pictureInPicture.PictureInPicture')
def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' self.sg = None self.sgShow = '' self.sgSeq = '' self.sgShot = '' self.sgTask = '' # load the icon iconPath = Mode().get('[FLIX_FOLDER]') + '/assets/20px-shotgun.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='Shotgun GEAH', icon=icon, tooltip='Shotgun GEAH', group='Editorial', pluginPath='flixConfig.plugins.toShotgunGEAH.ToShotgunGEAH')
def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' # load the icon iconPath = Mode().get('[FLIX_CONFIG_FOLDER]')+'/plugins/icons/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init(label ='Log Shot Markers', icon =icon, tooltip ='Logs what shots, markers and markerlists are', group ='Export', pluginPath='flixConfig.plugins.logShotMarkers.LogShotMarkers')
def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.flixNuke = FlixNuke() self.shotList = '' # load the icon iconPath = Mode().get('[FLIX_FOLDER]')+'/assets/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init(label ='Quick Nuke Render', icon =icon, tooltip ='Quick Nuke Render', group ='Export', pluginPath='flixConfig.plugins.quickNukeRender.QuickNukeRender')
def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' self.serverFlixFunctions = ServerFlixFunctions() # load the icon iconPath = Mode().get( '[FLIX_CONFIG_FOLDER]') + '/plugins/icons/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init( label='Import Dialogue', icon=icon, tooltip='Import dialogue to current edit', group='Export', pluginPath='flixConfig.plugins.importDialogue.ImportDialogue')
class BurnInPerShot(PluginDecorator): #-------------------------------------------------------------------------- # object #-------------------------------------------------------------------------- def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' # load the icon iconPath = Mode().get('[FLIX_FOLDER]') + '/assets/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='BurnIn per Shot', icon=icon, tooltip='BurnIn per Shot', group='Export', pluginPath='flixConfig.plugins.burnInPerShot.BurnInPerShot') #-------------------------------------------------------------------------- # executions #-------------------------------------------------------------------------- def execute(self, shotCutList, selection, additionalData=None): self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.kargs = { 'show': self.shotList.show, 'sequence': self.shotList.sequence, 'branch': self.shotList.branch, 'version': self.shotList.version } self.mode = self.shotList.mode self.movDir = self.mode.get("[editorialMOVFolder]") movieFile = self.rePath.localize(self.__getMovFilePath()) if movieFile is None: raise flix.exceptions.FlixException( msg="Current version does not have an editorial movie.", notify=True) else: log("Movie File: %s" % movieFile) self.addProgress(5) output = OSUtils.runFileBrowser(kBrowseTypeFolderUsingSaveFile, 'Choose a folder', '/', 'burnInPerShot') self.removeProgress(5) # escape output path output = escape(output.decode('utf-8')) if not output or not os.path.isdir(output): raise flix.exceptions.FlixException( error=output, msg='No Valid directory selected.', notify=False) # markerShotLists = self.getMarkerList(self.shotList) markerList = MarkerList.fromShotCutList(self.shotList) self.addProgress(len(markerList)) shotLabels = [] for marker in markerList: shotLabels.append(marker.name) markerShotLists = MarkerList.markerShotCutList(self.shotList, markerList) if not markerShotLists: markerShotLists.append(self.shotList) self.markIn = 1 self.markOut = 0 markerTuples = [] for markerShot in markerShotLists: if markerShot.isMarker: for shot in markerShot: self.markOut += int(round(shot.duration)) markerTuples.append((self.markIn, self.markOut)) self.markIn = self.markOut + 1 self.breakDownMov(movieFile, markerTuples, shotLabels, self.movDir) for shot in shotLabels: toReplace = str(shotLabels.index(shot)).zfill(3) + ".tmp" oldName = self.movDir + "/" + toReplace + ".mov" newName = oldName.replace(toReplace, shot + "_v" + str(self.shotList.version)) try: # self.fileService.rename(oldName, newName) self.addBurnIns(oldName, newName) self.fileService.copy(newName, output) self.fileService.removeFile(oldName) self.fileService.removeFile(newName) except Exception, e: log("Failed to rename, copy or remove the temp mov: %s" % e, isError=True) self.removeProgress(1) # self.removeProgress() OSUtils().revealFiles([output])
class ToShotgunGEAH(ToShotgun): #-------------------------------------------------------------------------- # object #-------------------------------------------------------------------------- def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' self.sg = None self.sgShow = '' self.sgSeq = '' self.sgShot = '' self.sgTask = '' # load the icon iconPath = Mode().get('[FLIX_FOLDER]') + '/assets/20px-shotgun.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='Shotgun GEAH', icon=icon, tooltip='Shotgun GEAH', group='Editorial', pluginPath='flixConfig.plugins.toShotgunGEAH.ToShotgunGEAH') #-------------------------------------------------------------------------- # executions #-------------------------------------------------------------------------- def execute(self, shotCutList, selection, additionalData=None): if not additionalData is None: referenceCutList = additionalData.get('reference', None) comment = additionalData.get('comment', "") else: comment = '' comment = unicode(urllib.unquote_plus(comment)) self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.kargs = { 'show': self.shotList.show, 'sequence': self.shotList.sequence, 'branch': self.shotList.branch, 'version': self.shotList.version } self.mode = self.shotList.mode self.markerShotLists = self.getMarkerList(self.shotList) # Get the editorial movie path self.addProgress(4) self.movDir = self.mode.get("[editorialMOVFolder]") movieFileName = self.shotList.refVideoFilename if movieFileName is None: raise flix.exceptions.FlixException( msg="Current version does not have an editorial movie.", notify=True) else: movieFile = "%s/%s" % (self.movDir, movieFileName) log("Movie File: %s" % movieFile) shotInfoObject = ShotInfo(self.shotList) shotsInfo = shotInfoObject.getShotsInfo() toMovPerShotObject = ToMovPerShot(self.shotList) self.addProgress(len(shotsInfo)) shotMovies = toMovPerShotObject.toMovPerShot(shotsInfo, movieFile) self.removeProgress() self.sg = self._connectToSG(self.mode) self.removeProgress() self.seqUrl = self.getSequenceUrl() # validate that all information for Flix has an equivalent in shotgun self.sgShow = self._validateShow(self.shotList.show) if int(Mode(self.shotList.show).get('[isEpisodic]')): episodeName = self.shotList.sequence.split("_")[0] self.sgEp = self._validateEp(episodeName) sequenceName = "_".join(self.shotList.sequence.split("_")[1:]) self.sgSeq = self._validateSeqs(sequenceName) else: self.sgEp = None self.sgSeq = self._validateSeqs(self.shotList.sequence) self.removeProgress() # check for shots to create movies and upload to shotgun as version # if shots don't exists yet create a movie of the entire sequence and upload to shotgun as version if not self.markerShotLists: self.markerShotLists.append(self.shotList) self.addProgress(len(self.markerShotLists)) for markerShot in self.markerShotLists: index = self.markerShotLists.index(markerShot) if markerShot.isMarker: self.sgShot = self._validateShot(markerShot) self._validateVersion(markerShot) # dialogue = shotsInfo[index][5] movie = shotMovies[index] mp3 = movie.replace(".mov", ".mp3") AudioUtils().localConvertAudio(movie, mp3) if not self.sgShot: # self.sgShot = self.createSgShot(markerShot) self.sgShot = self.createSgShot(markerShot, shotsInfo[index]) sgVersion = self.uploadSeqVersion(self.rePath.localize(movie), self.rePath.localize(mp3), self.sgShot, comment=comment) else: self._validateVersion(markerShot) self.sgTask = self._validateTask() movie = self.createMov(markerShot) mp3 = movie.replace(".mov", ".mp3") AudioUtils().localConvertAudio(movie, mp3) sgVersion = self.uploadSeqVersion(self.rePath.localize(movie), self.rePath.localize(mp3), self.sgSeq, self.sgTask, comment=comment) # Validate that a FLIX playlist exists or else create one self.playlist = self._validatePlaylist() # update the FLIX playlist to add the new sgVersion self.updatePlaylist(self.playlist, sgVersion) self.removeProgress() ToShotgunGEAH.toFile(self, **self.kargs) autoMarkOmittedShots = self.mode['[sgAutoMarkOmittedShots]'] == "1" if autoMarkOmittedShots: self.autoMarkOmittedShots() self.removeProgress() #-------------------------------------------------------------------------- # Shotgun Methods #-------------------------------------------------------------------------- def _validateEp(self, episode): epField = 'sg_flixepname' pFilters = [['project', 'is', self.sgShow], [epField, 'is', episode]] pFields = ['id', 'code', 'sg_flixepname'] sgEp = self.sg.find_one(kParamSgEpisodeEntity, pFilters, pFields) if sgEp: return sgEp else: raise ShotgunExceptions(msg="Could not find '%s' Episode in SG" % episode) #-------------------------------------------------------------------------- def _validateSeqs(self, sequence): if self.sgEp is None: sequenceField = self.mode[KparamSGsequenceField] sFilters = [['project', 'is', self.sgShow], [sequenceField, 'is', sequence]] else: sequenceField = 'sg_flixseqname' sFilters = [['project', 'is', self.sgShow], ['sg_episode', 'is', self.sgEp], [sequenceField, 'is', sequence]] sFields = ['id', 'code', 'content', sequenceField, 'sg_status_list'] sgSeq = self.sg.find_one('Sequence', sFilters, sFields) if sgSeq: return sgSeq else: raise ShotgunExceptions(msg="Could not find '%s' Sequence in SG" % sequence) #-------------------------------------------------------------------------- def _validateShotTaskTemplate(self): """ Gets the Task Template entity that each shot should be assigned (e.g. "Basic shot template") """ tFilters = [['id', 'is', kParamSgShotTaskTemplateId]] tFields = ['id'] sgShotTaskTemplate = self.sg.find_one('TaskTemplate', tFilters, tFields) return sgShotTaskTemplate #-------------------------------------------------------------------------- def createSgShot(self, shotList, shotInfo): """create a new shotgun shot based on marker name """ shotArgs = copy.copy(self.kargs) shotArgs['shot'] = shotList.isMarker shotName = self.mode.get(KparamSGshotName, shotArgs) taskTemplate = self._validateShotTaskTemplate() data = { 'project': self.sgShow, 'sg_sequence': self.sgSeq, 'code': shotName, 'sg_cut_duration': shotInfo['cut duration'], kParamSgDissolveInField: shotInfo['dissolve in'], kParamSgDissolveOutField: shotInfo['dissolve out'], kParamSgDialogueField: shotInfo['dialogue'], 'sg_working_duration': shotInfo['working duration'], 'task_template': taskTemplate } return self.sg.create('Shot', data, ['id', 'code']) #-------------------------------------------------------------------------- def _validateVersionTask(self): """ Gets the Task entity that each version should be assigned (e.g. "Final Animatic") """ tFilters = [['id', 'is', kParamSgVersionTaskId]] tFields = ['id'] sgVersionTask = self.sg.find_one('Task', tFilters, tFields) return sgVersionTask #-------------------------------------------------------------------------- def uploadSeqVersion(self, movPath, mp3Path, entity, sgTask='', comment=u''): """Uploads the given movie path to the specific entity in shotgun adds a link back to FLIX as the comment """ versionArgs = copy.copy(self.kargs) versionArgs['version'] = "%03d" % versionArgs['version'] versionArgs['shot'] = entity.get('code', '') versionName = self.mode.get( "%s_%s_%s" % (self.sgEp["code"], self.sgSeq["code"], versionArgs["shot"]), versionArgs) description = "%s,\n%s %s %s" % (comment, self.sgEp["code"], self.sgSeq["code"], versionArgs["version"]) data = { 'project': self.sgShow, 'code': versionName, 'description': description, 'sg_path_to_movie': movPath, 'entity': entity, 'sg_task': self._validateVersionTask() } if sgTask: data['sg_task'] = sgTask if not self.sgVersion: self.sgVersion = self.sg.create('Version', data, ['id']) else: self.sg.update('Version', self.sgVersion['id'], data) successUpload = False tryIter = 1 while not successUpload: try: self.fileService.refreshCache(movPath) self.sg.upload('Version', self.sgVersion['id'], movPath, 'sg_uploaded_movie') self.sg.upload('Version', self.sgVersion['id'], mp3Path, 'sg_uploaded_audio') successUpload = True return self.sgVersion except urllib2.URLError, err: if tryIter < 3: tryIter += 1 log('Failed to connect\nAttempt %01d of 3 to connect to SG' % int(tryIter), isWarning=True) continue raise err
class PictureInPicture(PluginDecorator): #-------------------------------------------------------------------------- # object #-------------------------------------------------------------------------- def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.fileServiceLocal = flix.fileServices.fileLocal.FileLocal() self.serverFlixFunctions = ServerFlixFunctions() self.shotList = '' # load the icon iconPath = Mode().get( '[FLIX_CONFIG_FOLDER]') + '/plugins/icons/custom_icon.png' icon = self.fileService.loadByteArray(iconPath) self.init( label='Picture In Picture', icon=icon, tooltip='Picture In Picture', group='Maya', pluginPath='flixConfig.plugins.pictureInPicture.PictureInPicture') #-------------------------------------------------------------------------- # executions #-------------------------------------------------------------------------- def execute(self, shotCutList, selection, additionalData=None): setattr(self, 'selection', selection) self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.mode = self.shotList.mode # For each selected panel for p in self.selection: shot = self.shotList[p] if shot.isMarker(): continue # Get the path to the layout panel's jpeg (thumbnail) layoutPanelJpegPath = self._getPanelFilePath(shot) # Find which version is the latest board (i.e. not from Maya) latestBoard = self._getLatestStoryVersion(shot) if latestBoard is None: log("%s has no non-Maya version." % shot) continue log("latestBoard: %s" % latestBoard) # Get the path to the story panel's jpeg (thumbnail) latestBoardJpegPath = self._getPanelFilePath(latestBoard) # Create nk script using the paths to both jpeg files self.renderPip(layoutPanelJpegPath, latestBoardJpegPath, int(self.mode.get('[yResolution]')), int(self.mode.get('[xResolution]'))) # Refresh the cache and thumbnail in Flix self.fileServiceLocal.refreshCache(layoutPanelJpegPath) log('__renderCallback:: Reloading image %s' % self.rePath.localize(layoutPanelJpegPath)) self.serverFlixFunctions.addFeedback("reloadImages", [layoutPanelJpegPath]) #-------------------------------------------------------------------------- # methods #-------------------------------------------------------------------------- def _getPanelFilePath(self, shot): """ Get the path to the shot's thumbnail jpeg """ kargs = { 'beat': shot.beat, 'setup': shot.setup, 'version': shot.version, 'frame': "%04d" % shot.markInFrame } return self.shotList.mode.get('[recipeCompedFile]', kargs) def _getLatestStoryVersion(self, shot): """ Get the latest story version of a panel (i.e. not coming from Maya) """ for v in self._getRelatedVersions(shot): if v["version"] == shot.version: continue s = flix.core2.shot.Shot(shot.show, shot.sequence, v["beat"], v["setup"], v["version"]) # Right now we assume if the panel's got a thirdPartyMetaData.json file, it comes from Maya and if not, it comes from story if not self.fileServiceLocal.exists( s.recipe.getThirdPartyMetaDataPath()): return s return None def _getRelatedVersions(self, shot): """ Get all the related versions for the given panel """ versions = FlixCore().getRelatedVersion(shot) versions.reverse() return versions @flix.remote.autoRemote(flix.remote.kProcGroupNuke) def renderPip(self, read1, read2, yRes, xRes): """ Create the Nuke script and execute it to render the new thumbnail """ # Clear the nk script FlixNuke().clearScript() # Tweak this parameter for the pic in pic to be smaller(down to 0) or bigger (up to 1) scale = 0.25 # Create the read nodes for both the layout and board panels readNode1 = nuke.createNode("Read", "file {%s}" % self.rePath.localize(read1)) readNode2 = nuke.createNode("Read", "file {%s}" % self.rePath.localize(read2)) # Create the transform node to resize and move the board to the bottom right hand corner transformNode = nuke.createNode("Transform") transformNode.setInput(0, readNode2) transformNode["scale"].setValue(scale) ytranslate = yRes * (scale - 1) / 2 xtranslate = xRes * (1 - scale) / 2 transformNode["translate"].setValue([xtranslate, ytranslate]) # Create the merge node that puts the board over the layout panel mergeNode = nuke.createNode("Merge2") mergeNode.setInput(1, transformNode) mergeNode.setInput(0, readNode1) # Create the Write node to render the thumbnail writeNode = nuke.createNode("Write") writeNode["file"].setValue(self.rePath.localize(read1)) # Execute the script to render the thumbnail nuke.execute(writeNode["name"].getValue(), 1, 1, 1) # Comment out to actually save the nk script to troubleshoot or modify # nuke.scriptSave(self.rePath.localize(read1.replace(".jpg", ".nk"))) # Clear the nk script FlixNuke().clearScript()
class ToMovPerShot(PluginDecorator): #-------------------------------------------------------------------------- # object #-------------------------------------------------------------------------- def __init__(self, shotCutList=''): self.fileService = flix.fileServices.FileService() self.fileServiceLocal = flix.fileServices.fileLocal.FileLocal() self.rePath = RepathDefault() # self.shotList = shotCutList if shotCutList != '': self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.kargs = { 'show': self.shotList.show, 'sequence': self.shotList.sequence, 'branch': self.shotList.branch, 'version': self.shotList.version } self.mode = self.shotList.mode self.movDir = self.mode.get("[editorialMOVFolder]") # load the icon iconPath = flix.core2.mode.Mode().get( '[FLIX_FOLDER]') + '/assets/20px-quicktime.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='QuickTime per Shot', icon=icon, tooltip='QuickTime per Shot', group='Export', pluginPath='flixConfig.plugins.toMovPerShot.ToMovPerShot') #-------------------------------------------------------------------------- # executions #-------------------------------------------------------------------------- def execute(self, shotCutList, selection, additionalData=None): self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.kargs = { 'show': self.shotList.show, 'sequence': self.shotList.sequence, 'branch': self.shotList.branch, 'version': self.shotList.version } self.mode = self.shotList.mode self.movDir = self.mode.get("[editorialMOVFolder]") movieFileName = self.shotList.refVideoFilename if movieFileName is None: raise flix.exceptions.FlixException( msg="Current version does not have an editorial movie.", notify=True) else: movieFile = "%s/%s" % (self.movDir, movieFileName) log("Movie File: %s" % movieFile) self.addProgress(5) output = OSUtils.runFileBrowser(kBrowseTypeFolderUsingSaveFile, 'Choose a folder', '/', 'toMovPerShot') self.removeProgress(5) # escape output path output = escape(output.decode('utf-8')) if not output or not os.path.isdir(output): raise flix.exceptions.FlixException( error=output, msg='No Valid directory selected.', notify=False) shotInfoObject = ShotInfo(self.shotList) shots = shotInfoObject.getShotsInfo() self.addProgress(len(shots)) shotMovies = self.toMovPerShot(shots, movieFile) for shotMovie in shotMovies: self.fileService.copy(shotMovie, output) OSUtils().revealFiles([output]) #-------------------------------------------------------------------------- # methods #-------------------------------------------------------------------------- def toMovPerShot(self, shots, movieFile): shotMovies = [] seqName = "%s_v%s_" % (self.shotList.sequence, self.shotList.version) for shot in shots: # If it's a middle shot, cut the movie in 3 and take the middle part segments = str(shot['first frame'] - 1) + "," + str( shot['last frame']) segment = 1 if shot is shots[0]: # If it's the first shot, cut the movie in 2 and take the 1st part segments = str(shot['last frame']) segment = 0 # Get the MOV proc to cut the movie self.breakDownMov(movieFile, segments) # Rename the randomly named shots with the proper shotlabels and copy them to their respective directories toReplace = str(segment).zfill(3) + ".tmp" oldName = self.movDir + "/" + toReplace + ".mov" shotDir = "%s/%s" % (self.movDir, shot['shot label']) self.fileService.createFolders(shotDir) newName = oldName.replace( toReplace, "%s/%s%s" % (shot['shot label'], seqName, shot['shot label'])) self.fileService.rename(oldName, newName) shotMovies.append(newName) self.removeProgress(1) # Delete unused cut segments toDelete = [self.movDir + "/000.tmp.mov", self.movDir + "/002.tmp.mov"] for f in toDelete: self.fileService.removeFile(f) return shotMovies @flix.remote.autoRemote(flix.remote.kProcGroupMov, firstProcOnly=True) def breakDownMov(self, movieFile, segments): ffmpeg = os.environ.get("FLIX_FFMPEG") source = self.rePath.localize(movieFile) movDir = os.path.abspath(os.path.join(source, os.pardir)) destination = movDir + "/%03d.tmp.mov" # ToMov().setLibraryPaths() self.setLibraryPaths() cmd = "%(ffmpeg)s -i %(source)s -f segment -segment_frames %(segments)s -c copy -map 0 -reset_timestamps 1 \"%(destination)s\"" % locals( ) log("ffmpeg cmd: %s" % cmd) flix.utilities.osUtils.OSUtils.quickCall(cmd, cwd=os.path.dirname(ffmpeg)) def setLibraryPaths(self): ffmpegFolder = os.path.dirname(os.environ.get('FLIX_FFMPEG')) ffmpegLibs = "/".join([ffmpegFolder, "lib"]) lameLibs = flix.thirdParty.getLameLibPath() if ffmpegLibs not in os.environ.get('LD_LIBRARY_PATH'): os.environ["LD_LIBRARY_PATH"] = ":".join( [ffmpegLibs, os.environ.get('LD_LIBRARY_PATH')]) if lameLibs not in os.environ.get('LD_LIBRARY_PATH'): os.environ["LD_LIBRARY_PATH"] = ":".join( [lameLibs, os.environ.get('LD_LIBRARY_PATH')])
class ToShotgunCustom(PluginDecorator): #-------------------------------------------------------------------------- # Static #-------------------------------------------------------------------------- @staticmethod def toFile(obj, **kwargs): ''' write out the shotgun publish ''' sgPubPath = obj.shotList.mode.get('[sgPublishFolder]', kwargs) if fileService.exists(sgPubPath): fileService.copyToLocal(sgPubPath) return if not fileService.exists(os.path.dirname(sgPubPath)): fileService.createFolders(os.path.dirname(sgPubPath)) log(sgPubPath, isInfo=True) fileService.saveJsonFile(sgPubPath, kwargs) #-------------------------------------------------------------------------- # object #-------------------------------------------------------------------------- def __init__(self): self.fileService = fileService self.rePath = RepathDefault() self.shotList = '' self.sg = None self.sgShow = '' self.sgSeq = '' self.sgShot = '' self.sgTask = '' # load the icon iconPath = Mode().get('[FLIX_FOLDER]') + '/assets/20px-shotgun.png' icon = self.fileService.loadByteArray(iconPath) self.init( label='Shotgun Publish Custom', icon=icon, tooltip='Shotgun Publish Custom', group='Editorial', pluginPath='flixConfig.plugins.toShotgunCustom.ToShotgunCustom') #-------------------------------------------------------------------------- # executions #-------------------------------------------------------------------------- def shotgunPublish(self, source, output): setattr(self, 'shotList', ShotCutList.fromFile(source)) self.execute(self.shotList, [], output) #-------------------------------------------------------------------------- def execute(self, shotCutList, selection, additionalData=None): if not additionalData is None: referenceCutList = additionalData.get('reference', None) comment = additionalData.get('comment', "") else: comment = '' comment = unicode(urllib.unquote_plus(comment)) self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.kargs = { 'show': self.shotList.show, 'sequence': self.shotList.sequence, 'branch': self.shotList.branch, 'version': self.shotList.version } self.mode = self.shotList.mode self.addProgress(4) markerShotLists = self.getMarkerList(self.shotList) self.removeProgress() self.sg = self._connectToSG(self.mode) self.removeProgress() self.seqUrl = self.getSequenceUrl() # validate that all information for Flix has an equivalent in shotgun self.sgShow = self._validateShow(self.shotList.show) if int(Mode(self.shotList.show).get('[isEpisodic]')): episodeName = self.shotList.sequence.split("_")[0] self.sgEp = self._validateEp(episodeName) sequenceName = self.shotList.sequence.split("_")[1] self.sgSeq = self._validateSeqs(sequenceName) else: self.sgEp = None self.sgSeq = self._validateSeqs(self.shotList.sequence) self.removeProgress() # check for shots to create movies and upload to shotgun as version # if shots don't exists yet create a movie of the entire sequence and upload to shotgun as version if not markerShotLists: markerShotLists.append(self.shotList) self.addProgress(len(markerShotLists)) self.markIn = 1 self.markOut = 0 for markerShot in markerShotLists: if markerShot.isMarker: for shot in markerShot: self.markOut += int(round(shot.duration)) self.sgShot = self._validateShot(markerShot) self._validateVersion(markerShot) tupleMarker = (markerShot, self.getShotsIndex(markerShot)) movie = self.createMov(tupleMarker) self.markIn += self.markOut if not self.sgShot: self.sgShot = self.createSgShot(markerShot) sgVersion = self.uploadSeqVersion(movie, self.sgShot, comment=comment) else: self._validateVersion(markerShot) self.sgTask = self._validateTask() movie = self.createMov(markerShot) sgVersion = self.uploadSeqVersion(movie, self.sgSeq, self.sgTask, comment=comment) # Validate that a FLIX playlist exists or else create one self.playlist = self._validatePlaylist() # update the FLIX playlist to add the new sgVersion self.updatePlaylist(self.playlist, sgVersion) self.removeProgress() ToShotgunCustom.toFile(self, **self.kargs) autoMarkOmittedShots = self.mode['[sgAutoMarkOmittedShots]'] == "1" if autoMarkOmittedShots: self.autoMarkOmittedShots() self.removeProgress() #-------------------------------------------------------------------------- # methods #-------------------------------------------------------------------------- @flix.remote.autoRemote(flix.remote.kProcGroupFile, lockProc=False) def getPublishedVersions(self, show, sequence, branch): """Return an array of the versions that were published to editorial """ matches = Mode(show, sequence).getMatches('[sgPublishFolder]', {'version': '*'}) versions = [] for m in matches: try: versions.append(int(m['version'])) except ValueError: pass return versions #-------------------------------------------------------------------------- @flix.remote.autoRemote(flix.remote.kProcGroupFile, lockProc=False) def getPublishedSetups(self, show, sequence): """Return an array of the versions that were published to editorial """ pass #-------------------------------------------------------------------------- def createMov(self, shotList): """creates a movie of the given shotcutlist """ toMov = ToMov() toMov.compBuild(shotList) return self.rePath.localize(toMov.movieName) #-------------------------------------------------------------------------- def uploadSeqVersion(self, movPath, entity, sgTask='', comment=u''): """Uploads the given movie path to the specific entity in shotgun adds a link back to FLIX as the comment """ versionArgs = copy.copy(self.kargs) versionArgs['version'] = "%03d" % versionArgs['version'] versionArgs['shot'] = entity.get('code', '') versionName = self.mode.get(KparamSGversionName, versionArgs) comment += "\n%s" % self.seqUrl data = { 'project': self.sgShow, 'code': versionName, 'description': comment, 'sg_path_to_movie': movPath, 'entity': entity } if sgTask: data['sg_task'] = sgTask if not self.sgVersion: self.sgVersion = self.sg.create('Version', data, ['id']) else: self.sg.update('Version', self.sgVersion['id'], data) successUpload = False tryIter = 1 while not successUpload: try: self.fileService.refreshCache(movPath) self.sg.upload('Version', self.sgVersion['id'], movPath, 'sg_uploaded_movie') successUpload = True return self.sgVersion except urllib2.URLError, err: if tryIter < 3: tryIter += 1 log('Failed to connect\nAttempt %01d of 3 to connect to SG' % int(tryIter), isWarning=True) continue raise err
class ToMovPerShot(PluginDecorator): #-------------------------------------------------------------------------- # object #-------------------------------------------------------------------------- def __init__(self): self.fileService = flix.fileServices.FileService() self.fileServiceLocal = flix.fileServices.fileLocal.FileLocal() self.rePath = RepathDefault() self.shotList = '' # load the icon iconPath = flix.core2.mode.Mode().get( '[FLIX_FOLDER]') + '/assets/20px-quicktime.png' icon = self.fileService.loadByteArray(iconPath) self.init(label='QuickTime per Shot', icon=icon, tooltip='QuickTime per Shot', group='Export', pluginPath='flixConfig.plugins.toMovPerShot.ToMovPerShot') #-------------------------------------------------------------------------- # executions #-------------------------------------------------------------------------- def execute(self, shotCutList, selection, additionalData=None): self.shotList = flix.core2.shotCutList.ShotCutList.fromFile( shotCutList.defaultPath()) self.kargs = { 'show': self.shotList.show, 'sequence': self.shotList.sequence, 'branch': self.shotList.branch, 'version': self.shotList.version } self.mode = self.shotList.mode self.movDir = self.mode.get("[editorialMOVFolder]") movieFile = self.getMovFilePath() if movieFile is None: raise flix.exceptions.FlixException( msg="Current version does not have an editorial movie.", notify=True) else: log("Movie File: %s" % movieFile) self.addProgress(5) output = OSUtils.runFileBrowser(kBrowseTypeFolderUsingSaveFile, 'Choose a folder', '/', 'toMovPerShot') self.removeProgress(5) # escape output path output = escape(output.decode('utf-8')) if not output or not os.path.isdir(output): raise flix.exceptions.FlixException( error=output, msg='No Valid directory selected.', notify=False) shotsInfo = self.getShotsInfo() shotMovies = self.toMovPerShot(shotsInfo, movieFile) for shotMovie in shotMovies: self.fileService.copy(shotMovie, output) OSUtils().revealFiles([output]) #-------------------------------------------------------------------------- # methods #-------------------------------------------------------------------------- def getMovFilePath(self): movieFileName = self.shotList.refVideoFilename if movieFileName is not None: return "%s/%s" % (self.movDir, movieFileName) def getShotsInfo(self): markerList = MarkerList.fromShotCutList(self.shotList) self.addProgress(len(markerList)) shotLabels = [] for marker in markerList: shotLabels.append(marker.name) markerShotLists = MarkerList.markerShotCutList(self.shotList, markerList) if not markerShotLists: markerShotLists.append(self.shotList) # List of shot info of the form [[shotLabel, firstFrame, lastFrame, dissolveIn, dissolveOut]] newShots = [] firstFrame = 0 lastFrame = 0 dialogue = "" sequencePanelIndex = 0 for markerShot in markerShotLists: if markerShot.isMarker: shotLabel = shotLabels[markerShotLists.index(markerShot)] for shot in markerShot: sequencePanelIndex += 1 duration = self.shotList[sequencePanelIndex].duration lastFrame += int(round(duration)) if shot is markerShot[-1]: dissolveOut = int(round(shot.dissolveOut)) if shot is markerShot[0]: dissolveIn = int(round(shot.dissolveIn)) newDialogue = str(shot.recipe.getPoses()[0]['dialogue']) if newDialogue != "": dialogue = "\n".join([dialogue, newDialogue]) newShots.append([ shotLabel, firstFrame, lastFrame - 1 + dissolveOut, dissolveIn, dissolveOut, dialogue ]) firstFrame = lastFrame - dissolveOut sequencePanelIndex += 1 log("newShots: %s" % newShots) return newShots def toMovPerShot(self, shots, movieFile): shotMovies = [] seqName = "%s_v%s_" % (self.shotList.sequence, self.shotList.version) for shot in shots: # If it's a middle shot, cut the movie in 3 and take the middle part segments = str(shot[1] - 1) + "," + str(shot[2]) segment = 1 if shot is shots[0]: # If it's the first shot, cut the movie in 2 and take the 1st part segments = str(shot[2] + 1) segment = 0 elif shot is shots[-1]: # If it's the last shot, cut the movie in 2 and take the 2nd part segments = str(shot[1]) # Get the MOV proc to cut the movie self.breakDownMov(movieFile, segments) # Rename the randomly named shots with the proper shotlabels and copy them to their respective directories toReplace = str(segment).zfill(3) + ".tmp" oldName = self.movDir + "/" + toReplace + ".mov" shotDir = "%s/%s" % (self.movDir, shot[0]) self.fileService.createFolders(shotDir) newName = oldName.replace(toReplace, "%s/%s%s" % (shot[0], seqName, shot[0])) self.fileService.rename(oldName, newName) shotMovies.append(newName) self.removeProgress(1) # Delete unused cut segments toDelete = [self.movDir + "/000.tmp.mov", self.movDir + "/002.tmp.mov"] for f in toDelete: self.fileService.removeFile(f) return shotMovies @flix.remote.autoRemote(flix.remote.kProcGroupMov, firstProcOnly=True) def breakDownMov(self, movieFile, segments): ffmpeg = os.environ.get("FLIX_FFMPEG") source = self.rePath.localize(movieFile) movDir = os.path.abspath(os.path.join(source, os.pardir)) destination = movDir + "/%03d.tmp.mov" ToMov().setLibraryPaths() cmd = "%(ffmpeg)s -i %(source)s -f segment -segment_frames %(segments)s -c copy -map 0 -reset_timestamps 1 \"%(destination)s\"" % locals( ) log("ffmpeg cmd: %s" % cmd) flix.utilities.osUtils.OSUtils.quickCall(cmd, cwd=os.path.dirname(ffmpeg))