diff --git a/pype/plugins/standalonepublish/publish/collect_context.py b/pype/plugins/standalonepublish/publish/collect_context.py index cbe9df1ef6..6ac2dca936 100644 --- a/pype/plugins/standalonepublish/publish/collect_context.py +++ b/pype/plugins/standalonepublish/publish/collect_context.py @@ -55,10 +55,10 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): instance = context.create_instance(subset) instance.data.update({ - "subset": family + subset, + "subset": subset, "asset": asset_name, - "label": family + subset, - "name": family + subset, + "label": subset, + "name": subset, "family": family, "families": [family, 'ftrack'], }) @@ -74,10 +74,9 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): collections, remainder = clique.assemble(component['files']) if collections: self.log.debug(collections) - range = collections[0].format('{range}') - instance.data['startFrame'] = range.split('-')[0] - instance.data['endFrame'] = range.split('-')[1] - + instance.data['startFrame'] = component['startFrame'] + instance.data['endFrame'] = component['endFrame'] + instance.data['frameRate'] = component['frameRate'] instance.data["files"].append(component) instance.data["representations"].append(component) diff --git a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py b/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py index 8d938bceb0..0dc9bb137c 100644 --- a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py +++ b/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py @@ -57,19 +57,19 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "name": "thumbnail" # Default component name is "main". } elif comp['preview']: - if not instance.data.get('startFrameReview'): - instance.data['startFrameReview'] = instance.data['startFrame'] - if not instance.data.get('endFrameReview'): - instance.data['endFrameReview'] = instance.data['endFrame'] + if not comp.get('startFrameReview'): + comp['startFrameReview'] = comp['startFrame'] + if not comp.get('endFrameReview'): + comp['endFrameReview'] = instance.data['endFrame'] location = ft_session.query( 'Location where name is "ftrack.server"').one() component_data = { # Default component name is "main". "name": "ftrackreview-mp4", "metadata": {'ftr_meta': json.dumps({ - 'frameIn': int(instance.data['startFrameReview']), - 'frameOut': int(instance.data['endFrameReview']), - 'frameRate': 25.0})} + 'frameIn': int(comp['startFrameReview']), + 'frameOut': int(comp['endFrameReview']), + 'frameRate': float(comp['frameRate')]})} } else: component_data = { diff --git a/pype/standalonepublish/widgets/widget_component_item.py b/pype/standalonepublish/widgets/widget_component_item.py index 2e0df9a00c..43aa54a955 100644 --- a/pype/standalonepublish/widgets/widget_component_item.py +++ b/pype/standalonepublish/widgets/widget_component_item.py @@ -10,6 +10,7 @@ class ComponentItem(QtWidgets.QFrame): C_HOVER = '#ffffff' C_ACTIVE = '#4BB543' C_ACTIVE_HOVER = '#4BF543' + signal_remove = QtCore.Signal(object) signal_thumbnail = QtCore.Signal(object) signal_preview = QtCore.Signal(object) @@ -291,4 +292,12 @@ class ComponentItem(QtWidgets.QFrame): 'thumbnail': self.is_thumbnail(), 'preview': self.is_preview() } + + if ('startFrame' in self.in_data and 'endFrame' in self.in_data): + data['startFrame'] = self.in_data['startFrame'] + data['endFrame'] = self.in_data['endFrame'] + + if 'frameRate' in self.in_data: + data['frameRate'] = self.in_data['frameRate'] + return data diff --git a/pype/standalonepublish/widgets/widget_drop_frame.py b/pype/standalonepublish/widgets/widget_drop_frame.py index cffe673152..4e99f697cb 100644 --- a/pype/standalonepublish/widgets/widget_drop_frame.py +++ b/pype/standalonepublish/widgets/widget_drop_frame.py @@ -1,5 +1,6 @@ import os import re +import json import clique import subprocess from pypeapp import config @@ -49,7 +50,7 @@ class DropDataFrame(QtWidgets.QFrame): else: # If path is in clipboard as string try: - path = ent.text() + path = os.path.normpath(ent.text()) if os.path.exists(path): paths.append(path) else: @@ -170,6 +171,13 @@ class DropDataFrame(QtWidgets.QFrame): repr_name = file_ext.replace('.', '') range = collection.format('{ranges}') + # TODO: ranges must not be with missing frames!!! + # - this is goal implementation: + # startFrame, endFrame = range.split('-') + rngs = range.split(',') + startFrame = rngs[0].split('-')[0] + endFrame = rngs[-1].split('-')[-1] + actions = [] data = { @@ -177,39 +185,15 @@ class DropDataFrame(QtWidgets.QFrame): 'name': file_base, 'ext': file_ext, 'file_info': range, + 'startFrame': startFrame, + 'endFrame': endFrame, 'representation': repr_name, 'folder_path': folder_path, 'is_sequence': True, 'actions': actions } - self._process_data(data) - def _get_ranges(self, indexes): - if len(indexes) == 1: - return str(indexes[0]) - ranges = [] - first = None - last = None - for index in indexes: - if first is None: - first = index - last = index - elif (last+1) == index: - last = index - else: - if first == last: - range = str(first) - else: - range = '{}-{}'.format(first, last) - ranges.append(range) - first = index - last = index - if first == last: - range = str(first) - else: - range = '{}-{}'.format(first, last) - ranges.append(range) - return ', '.join(ranges) + self._process_data(data) def _process_remainder(self, remainder): filename = os.path.basename(remainder) @@ -232,39 +216,75 @@ class DropDataFrame(QtWidgets.QFrame): 'is_sequence': False, 'actions': actions } - data['file_info'] = self.get_file_info(data) self._process_data(data) - def get_file_info(self, data): - output = None - if data['ext'] == '.mov': - try: - # ffProbe must be in PATH - filepath = data['files'][0] - args = ['ffprobe', '-show_streams', filepath] - p = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=True - ) - datalines=[] - for line in iter(p.stdout.readline, b''): - line = line.decode("utf-8").replace('\r\n', '') - datalines.append(line) + def load_data_with_probe(self, filepath): + args = [ + 'ffprobe', + '-v', 'quiet', + '-print_format', 'json', + '-show_format', + '-show_streams', filepath + ] + ffprobe_p = subprocess.Popen( + args, + stdout=subprocess.PIPE, + shell=True + ) + ffprobe_output = ffprobe_p.communicate()[0] + if ffprobe_p.returncode != 0: + raise RuntimeError( + 'Failed on ffprobe: check if ffprobe path is set in PATH env' + ) + return json.loads(ffprobe_output)['streams'][0] + + def get_file_data(self, data): + filepath = data['files'][0] + ext = data['ext'] + output = {} + probe_data = self.load_data_with_probe(filepath) + + if ( + ext in self.presets['extensions']['image_file'] or + ext in self.presets['extensions']['video_file'] + ): + if 'frameRate' not in data: + # default value + frameRate = 25 + frameRate_string = probe_data.get('r_frame_rate') + if frameRate_string: + frameRate = int(frameRate_string.split('/')[0]) + + output['frameRate'] = frameRate + + if 'startFrame' not in data or 'endFrame' not in data: + startFrame = endFrame = 1 + endFrame_string = probe_data.get('nb_frames') + + if endFrame_string: + endFrame = int(endFrame_string) + + output['startFrame'] = startFrame + output['endFrame'] = endFrame + + file_info = None + if 'file_info' in data: + file_info = data['file_info'] + elif ext in ['.mov']: + file_info = probe_data.get('codec_name') + + output['file_info'] = file_info - find_value = 'codec_name' - for line in datalines: - if line.startswith(find_value): - output = line.replace(find_value + '=', '') - break - except Exception as e: - pass return output def _process_data(self, data): ext = data['ext'] + # load file data info + file_data = self.get_file_data(data) + for key, value in file_data.items(): + data[key] = value + icon = 'default' for ico, exts in self.presets['extensions'].items(): if ext in exts: