diff --git a/pype/hosts/nuke/lib.py b/pype/hosts/nuke/lib.py index 8fd84b8555..b4795aec13 100644 --- a/pype/hosts/nuke/lib.py +++ b/pype/hosts/nuke/lib.py @@ -978,24 +978,30 @@ class WorkfileSettings(object): self.set_colorspace() def set_favorites(self): - anatomy = get_anatomy() - work_template = anatomy.templates["work"]["path"] - projects_root = anatomy.root_value_for_template(work_template) work_dir = os.getenv("AVALON_WORKDIR") asset = os.getenv("AVALON_ASSET") project = os.getenv("AVALON_PROJECT") - hierarchy = os.getenv("AVALON_HIERARCHY") favorite_items = OrderedDict() # project - favorite_items.update({"Project dir": os.path.join( - projects_root, project).replace("\\", "/")}) - # shot - favorite_items.update({"Shot dir": os.path.join( - projects_root, project, - hierarchy, asset).replace("\\", "/")}) + # get project's root and split to parts + projects_root = os.path.normpath(work_dir.split( + project)[0]) + # add project name + project_dir = os.path.join(projects_root, project) + "/" + # add to favorites + favorite_items.update({"Project dir": project_dir.replace("\\", "/")}) + + # asset + asset_root = os.path.normpath(work_dir.split( + asset)[0]) + # add asset name + asset_dir = os.path.join(asset_root, asset) + "/" + # add to favorites + favorite_items.update({"Shot dir": asset_dir.replace("\\", "/")}) + # workdir - favorite_items.update({"Work dir": work_dir}) + favorite_items.update({"Work dir": work_dir.replace("\\", "/")}) set_context_favorites(favorite_items) @@ -1388,8 +1394,18 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Path: {}".format(self.path)) write_node["file"].setValue(self.path) write_node["file_type"].setValue(self.ext) - write_node["meta_codec"].setValue("ap4h") - write_node["mov64_codec"].setValue("ap4h") + + # Knobs `meta_codec` and `mov64_codec` are not available on centos. + # TODO change this to use conditions, if possible. + try: + write_node["meta_codec"].setValue("ap4h") + except Exception: + self.log.info("`meta_codec` knob was not found") + + try: + write_node["mov64_codec"].setValue("ap4h") + except Exception: + self.log.info("`mov64_codec` knob was not found") write_node["mov64_write_timecode"].setValue(1) write_node["raw"].setValue(1) # connect diff --git a/pype/lib.py b/pype/lib.py index 688c7df3c8..b4d2908fe0 100644 --- a/pype/lib.py +++ b/pype/lib.py @@ -1379,13 +1379,15 @@ class BuildWorkfile: return output -def ffprobe_streams(path_to_file): +def ffprobe_streams(path_to_file, logger=None): """Load streams from entered filepath via ffprobe.""" - log.info( + if not logger: + logger = log + logger.info( "Getting information about input \"{}\".".format(path_to_file) ) args = [ - get_ffmpeg_tool_path("ffprobe"), + "\"{}\"".format(get_ffmpeg_tool_path("ffprobe")), "-v quiet", "-print_format json", "-show_format", @@ -1393,12 +1395,21 @@ def ffprobe_streams(path_to_file): "\"{}\"".format(path_to_file) ] command = " ".join(args) - log.debug("FFprobe command: \"{}\"".format(command)) - popen = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + logger.debug("FFprobe command: \"{}\"".format(command)) + popen = subprocess.Popen( + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) - popen_output = popen.communicate()[0] - log.debug("FFprobe output: {}".format(popen_output)) - return json.loads(popen_output)["streams"] + popen_stdout, popen_stderr = popen.communicate() + if popen_stdout: + logger.debug("ffprobe stdout: {}".format(popen_stdout)) + + if popen_stderr: + logger.debug("ffprobe stderr: {}".format(popen_stderr)) + return json.loads(popen_stdout)["streams"] def source_hash(filepath, *args): diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index bf29e12eab..81d96a09da 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -969,7 +969,7 @@ class ExtractBurnin(pype.api.Extractor): args = [executable, scriptpath, json_data] self.log.debug("Executing: {}".format(args)) - output = pype.api.subprocess(args, shell=True) + output = pype.api.subprocess(args, shell=True, logger=self.log) self.log.debug("Output: {}".format(output)) repre_update = { diff --git a/pype/plugins/global/publish/extract_jpeg.py b/pype/plugins/global/publish/extract_jpeg.py index dd822e9d56..551e57796a 100644 --- a/pype/plugins/global/publish/extract_jpeg.py +++ b/pype/plugins/global/publish/extract_jpeg.py @@ -72,7 +72,7 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): ffmpeg_args = self.ffmpeg_args or {} jpeg_items = [] - jpeg_items.append(ffmpeg_path) + jpeg_items.append("\"{}\"".format(ffmpeg_path)) # override file if already exists jpeg_items.append("-y") # use same input args like with mov diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 87afdd2b20..34fe6f5e10 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -465,7 +465,7 @@ class ExtractReview(pyblish.api.InstancePlugin): audio_filters.append(arg) all_args = [] - all_args.append(self.ffmpeg_path) + all_args.append("\"{}\"".format(self.ffmpeg_path)) all_args.extend(input_args) if video_filters: all_args.append("-filter:v {}".format(",".join(video_filters))) @@ -649,7 +649,9 @@ class ExtractReview(pyblish.api.InstancePlugin): # NOTE Skipped using instance's resolution full_input_path_single_file = temp_data["full_input_path_single_file"] - input_data = pype.lib.ffprobe_streams(full_input_path_single_file)[0] + input_data = pype.lib.ffprobe_streams( + full_input_path_single_file, self.log + )[0] input_width = int(input_data["width"]) input_height = int(input_data["height"]) @@ -1542,7 +1544,7 @@ class ExtractReview(pyblish.api.InstancePlugin): os.mkdir(stg_dir) mov_args = [ - ffmpeg_path, + "\"{}\"".format(ffmpeg_path), " ".join(input_args), " ".join(output_args) ] diff --git a/pype/plugins/global/publish/extract_review_slate.py b/pype/plugins/global/publish/extract_review_slate.py index 2e1fc25ae5..5cf632406c 100644 --- a/pype/plugins/global/publish/extract_review_slate.py +++ b/pype/plugins/global/publish/extract_review_slate.py @@ -26,7 +26,7 @@ class ExtractReviewSlate(pype.api.Extractor): slate_path = inst_data.get("slateFrame") ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") - slate_stream = pype.lib.ffprobe_streams(slate_path)[0] + slate_stream = pype.lib.ffprobe_streams(slate_path, self.log)[0] slate_width = slate_stream["width"] slate_height = slate_stream["height"] @@ -178,7 +178,7 @@ class ExtractReviewSlate(pype.api.Extractor): _remove_at_end.append(slate_v_path) slate_args = [ - ffmpeg_path, + "\"{}\"".format(ffmpeg_path), " ".join(input_args), " ".join(output_args) ] @@ -299,7 +299,7 @@ class ExtractReviewSlate(pype.api.Extractor): try: # Get information about input file via ffprobe tool - streams = pype.lib.ffprobe_streams(full_input_path) + streams = pype.lib.ffprobe_streams(full_input_path, self.log) except Exception: self.log.warning( "Could not get codec data from input.", diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index a78459d37d..3e104a5498 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -616,12 +616,12 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): # copy file with speedcopy and check if size of files are simetrical while True: - import shutil - try: + if not shutil._samefile(src, dst): copyfile(src, dst) - except shutil.SameFileError: - self.log.critical("files are the same {} to {}".format(src, - dst)) + else: + self.log.critical( + "files are the same {} to {}".format(src, dst) + ) os.remove(dst) try: shutil.copyfile(src, dst) diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index dcc87188f3..097761cf4e 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -1024,6 +1024,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): version = pype.api.get_latest_version(asset, subset) if version: version = int(version["name"]) + 1 + else: + version = 1 template_data["subset"] = subset template_data["family"] = "render" diff --git a/pype/plugins/global/publish/validate_ffmpeg_installed.py b/pype/plugins/global/publish/validate_ffmpeg_installed.py index 61127ff96e..2e49650040 100644 --- a/pype/plugins/global/publish/validate_ffmpeg_installed.py +++ b/pype/plugins/global/publish/validate_ffmpeg_installed.py @@ -29,6 +29,6 @@ class ValidateFFmpegInstalled(pyblish.api.ContextPlugin): def process(self, context): ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") self.log.info("ffmpeg path: `{}`".format(ffmpeg_path)) - if self.is_tool(ffmpeg_path) is False: + if self.is_tool("\"{}\"".format(ffmpeg_path)) is False: self.log.error("ffmpeg not found in PATH") raise RuntimeError('ffmpeg not installed.') diff --git a/pype/plugins/harmony/publish/extract_render.py b/pype/plugins/harmony/publish/extract_render.py index 98a47de2f6..7fb8ae79d2 100644 --- a/pype/plugins/harmony/publish/extract_render.py +++ b/pype/plugins/harmony/publish/extract_render.py @@ -90,7 +90,7 @@ class ExtractRender(pyblish.api.InstancePlugin): thumbnail_path = os.path.join(path, "thumbnail.png") ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") args = [ - ffmpeg_path, "-y", + "\"{}\"".format(ffmpeg_path), "-y", "-i", os.path.join(path, list(collections[0])[0]), "-vf", "scale=300:-1", "-vframes", "1", diff --git a/pype/plugins/hiero/publish/extract_review_cutup.py b/pype/plugins/hiero/publish/extract_review_cutup.py index 88b19b7ec8..ace6bc88d3 100644 --- a/pype/plugins/hiero/publish/extract_review_cutup.py +++ b/pype/plugins/hiero/publish/extract_review_cutup.py @@ -130,8 +130,8 @@ class ExtractReviewCutUp(pype.api.Extractor): # check if audio stream is in input video file ffprob_cmd = ( - "{ffprobe_path} -i \"{full_input_path}\" -show_streams " - "-select_streams a -loglevel error" + "\"{ffprobe_path}\" -i \"{full_input_path}\" -show_streams" + " -select_streams a -loglevel error" ).format(**locals()) self.log.debug("ffprob_cmd: {}".format(ffprob_cmd)) @@ -171,7 +171,8 @@ class ExtractReviewCutUp(pype.api.Extractor): # try to get video native resolution data try: resolution_output = pype.api.subprocess(( - "{ffprobe_path} -i \"{full_input_path}\" -v error " + "\"{ffprobe_path}\" -i \"{full_input_path}\"" + " -v error " "-select_streams v:0 -show_entries " "stream=width,height -of csv=s=x:p=0" ).format(**locals())) @@ -274,7 +275,7 @@ class ExtractReviewCutUp(pype.api.Extractor): output_args.append("-y \"{}\"".format(full_output_path)) mov_args = [ - ffmpeg_path, + "\"{}\"".format(ffmpeg_path), " ".join(input_args), " ".join(output_args) ] diff --git a/pype/plugins/photoshop/publish/extract_image.py b/pype/plugins/photoshop/publish/extract_image.py index 38920b5557..af76caf379 100644 --- a/pype/plugins/photoshop/publish/extract_image.py +++ b/pype/plugins/photoshop/publish/extract_image.py @@ -34,8 +34,6 @@ class ExtractImage(pype.api.Extractor): # limit unnecessary calls to client if layer.visible and layer.id not in extract_ids: stub.set_visible(layer.id, False) - if not layer.visible and layer.id in extract_ids: - stub.set_visible(layer.id, True) save_options = [] if "png" in self.formats: diff --git a/pype/plugins/photoshop/publish/extract_review.py b/pype/plugins/photoshop/publish/extract_review.py index 6fb50bba9f..4f2c3c3d23 100644 --- a/pype/plugins/photoshop/publish/extract_review.py +++ b/pype/plugins/photoshop/publish/extract_review.py @@ -38,8 +38,6 @@ class ExtractReview(pype.api.Extractor): # limit unnecessary calls to client if layer.visible and layer.id not in extract_ids: stub.set_visible(layer.id, False) - if not layer.visible and layer.id in extract_ids: - stub.set_visible(layer.id, True) stub.saveAs(output_image_path, 'jpg', True) @@ -56,7 +54,7 @@ class ExtractReview(pype.api.Extractor): # Generate thumbnail. thumbnail_path = os.path.join(staging_dir, "thumbnail.jpg") args = [ - ffmpeg_path, "-y", + "\"{}\"".format(ffmpeg_path), "-y", "-i", output_image_path, "-vf", "scale=300:-1", "-vframes", "1", diff --git a/pype/plugins/standalonepublisher/publish/extract_shot_data.py b/pype/plugins/standalonepublisher/publish/extract_shot_data.py index d5af7638ee..e4eb813bae 100644 --- a/pype/plugins/standalonepublisher/publish/extract_shot_data.py +++ b/pype/plugins/standalonepublisher/publish/extract_shot_data.py @@ -47,7 +47,7 @@ class ExtractShotData(pype.api.Extractor): start += 0.5 args = [ - ffmpeg_path, + "\"{}\"".format(ffmpeg_path), "-ss", str(start / fps), "-i", f"\"{video_file_path}\"", "-t", str(dur / fps) diff --git a/pype/plugins/standalonepublisher/publish/extract_thumbnail.py b/pype/plugins/standalonepublisher/publish/extract_thumbnail.py index fca4039d0e..cabb8be61e 100644 --- a/pype/plugins/standalonepublisher/publish/extract_thumbnail.py +++ b/pype/plugins/standalonepublisher/publish/extract_thumbnail.py @@ -75,7 +75,7 @@ class ExtractThumbnailSP(pyblish.api.InstancePlugin): ffmpeg_args = self.ffmpeg_args or {} jpeg_items = [] - jpeg_items.append(ffmpeg_path) + jpeg_items.append("\"{}\"".format(ffmpeg_path)) # override file if already exists jpeg_items.append("-y") # add input filters from peresets diff --git a/pype/resources/__init__.py b/pype/resources/__init__.py index ba882a84fb..2f7f95e3eb 100644 --- a/pype/resources/__init__.py +++ b/pype/resources/__init__.py @@ -10,12 +10,28 @@ def get_resource(*args): """ return os.path.normpath( os.path.join( - os.path.dirname(__file__), + os.path.dirname(os.path.abspath(__file__)), *args ) ) +def get_liberation_font_path(bold=False, italic=False): + font_name = "LiberationSans" + suffix = "" + if bold: + suffix += "Bold" + if italic: + suffix += "Italic" + + if not suffix: + suffix = "Regular" + + filename = "{}-{}.ttf".format(font_name, suffix) + font_path = get_resource("fonts", font_name, filename) + return font_path + + def pype_icon_filepath(debug=None): if debug is None: debug = bool(os.getenv("PYPE_DEV")) diff --git a/pype/resources/fonts/LiberationSans/LiberationSans-Bold.ttf b/pype/resources/fonts/LiberationSans/LiberationSans-Bold.ttf new file mode 100644 index 0000000000..916abee752 Binary files /dev/null and b/pype/resources/fonts/LiberationSans/LiberationSans-Bold.ttf differ diff --git a/pype/resources/fonts/LiberationSans/LiberationSans-BoldItalic.ttf b/pype/resources/fonts/LiberationSans/LiberationSans-BoldItalic.ttf new file mode 100644 index 0000000000..f3889103dd Binary files /dev/null and b/pype/resources/fonts/LiberationSans/LiberationSans-BoldItalic.ttf differ diff --git a/pype/resources/fonts/LiberationSans/LiberationSans-Italic.ttf b/pype/resources/fonts/LiberationSans/LiberationSans-Italic.ttf new file mode 100644 index 0000000000..53c5a82ea1 Binary files /dev/null and b/pype/resources/fonts/LiberationSans/LiberationSans-Italic.ttf differ diff --git a/pype/resources/fonts/LiberationSans/LiberationSans-Regular.ttf b/pype/resources/fonts/LiberationSans/LiberationSans-Regular.ttf new file mode 100644 index 0000000000..4b2ebce956 Binary files /dev/null and b/pype/resources/fonts/LiberationSans/LiberationSans-Regular.ttf differ diff --git a/pype/resources/fonts/LiberationSans/License.txt b/pype/resources/fonts/LiberationSans/License.txt new file mode 100644 index 0000000000..41751f278a --- /dev/null +++ b/pype/resources/fonts/LiberationSans/License.txt @@ -0,0 +1,77 @@ +LICENSE AGREEMENT AND LIMITED PRODUCT WARRANTY LIBERATION FONT SOFTWARE +This agreement governs the use of the Software and any updates to the +Software, regardless of the delivery mechanism. Subject to the following +terms, Red Hat, Inc. ("Red Hat") grants to the user ("Client") a license to +this collective work pursuant to the GNU General Public License v.2 with the +exceptions set forth below and such other terms as our set forth in this End +User License Agreement. +1. The Software and License Exception. LIBERATION font software (the +"Software") consists of TrueType-OpenType formatted font software for +rendering LIBERATION typefaces in sans serif, serif, and monospaced character +styles. You are licensed to use, modify, copy, and distribute the Software +pursuant to the GNU General Public License v.2 with the following exceptions: +1) As a special exception, if you create a document which uses this font, and +embed this font or unaltered portions of this font into the document, this +font does not by itself cause the resulting document to be covered by the GNU +General Public License.  This exception does not however invalidate any other +reasons why the document might be covered by the GNU General Public License.  +If you modify this font, you may extend this exception to your version of the +font, but you are not obligated to do so. If you do not wish to do so, delete +this exception statement from your version. + +2) As a further exception, any distribution of the object code of the Software +in a physical product must provide you the right to access and modify the +source code for the Software and to reinstall that modified version of the +Software in object code form on the same physical product on which you +received it. +2. Intellectual Property Rights. The Software and each of its components, +including the source code, documentation, appearance, structure and +organization are owned by Red Hat and others and are protected under copyright +and other laws. Title to the Software and any component, or to any copy, +modification, or merged portion shall remain with the aforementioned, subject +to the applicable license. The "LIBERATION" trademark is a trademark of Red +Hat, Inc. in the U.S. and other countries. This agreement does not permit +Client to distribute modified versions of the Software using Red Hat's +trademarks. If Client makes a redistribution of a modified version of the +Software, then Client must modify the files names to remove any reference to +the Red Hat trademarks and must not use the Red Hat trademarks in any way to +reference or promote the modified Software. +3. Limited Warranty. To the maximum extent permitted under applicable law, the +Software is provided and licensed "as is" without warranty of any kind, +expressed or implied, including the implied warranties of merchantability, +non-infringement or fitness for a particular purpose. Red Hat does not warrant +that the functions contained in the Software will meet Client's requirements +or that the operation of the Software will be entirely error free or appear +precisely as described in the accompanying documentation. +4. Limitation of Remedies and Liability. To the maximum extent permitted by +applicable law, Red Hat or any Red Hat authorized dealer will not be liable to +Client for any incidental or consequential damages, including lost profits or +lost savings arising out of the use or inability to use the Software, even if +Red Hat or such dealer has been advised of the possibility of such damages. +5. Export Control. As required by U.S. law, Client represents and warrants +that it: (a) understands that the Software is subject to export controls under +the U.S. Commerce Department's Export Administration Regulations ("EAR"); (b) +is not located in a prohibited destination country under the EAR or U.S. +sanctions regulations (currently Cuba, Iran, Iraq, Libya, North Korea, Sudan +and Syria); (c) will not export, re-export, or transfer the Software to any +prohibited destination, entity, or individual without the necessary export +license(s) or authorizations(s) from the U.S. Government; (d) will not use or +transfer the Software for use in any sensitive nuclear, chemical or biological +weapons, or missile technology end-uses unless authorized by the U.S. +Government by regulation or specific license; (e) understands and agrees that +if it is in the United States and exports or transfers the Software to +eligible end users, it will, as required by EAR Section 740.17(e), submit +semi-annual reports to the Commerce Department's Bureau of Industry & Security +(BIS), which include the name and address (including country) of each +transferee; and (f) understands that countries other than the United States +may restrict the import, use, or export of encryption products and that it +shall be solely responsible for compliance with any such import, use, or +export restrictions. +6. General. If any provision of this agreement is held to be unenforceable, +that shall not affect the enforceability of the remaining provisions. This +agreement shall be governed by the laws of the State of North Carolina and of +the United States, without regard to any conflict of laws provisions, except +that the United Nations Convention on the International Sale of Goods shall +not apply. +Copyright © 2007 Red Hat, Inc. All rights reserved. LIBERATION is a trademark +of Red Hat, Inc. diff --git a/pype/scripts/otio_burnin.py b/pype/scripts/otio_burnin.py index 99611b172c..ef09661f6e 100644 --- a/pype/scripts/otio_burnin.py +++ b/pype/scripts/otio_burnin.py @@ -2,9 +2,10 @@ import os import sys import re import subprocess +import platform import json import opentimelineio_contrib.adapters.ffmpeg_burnins as ffmpeg_burnins -from pype.api import config +from pype.api import config, resources import pype.lib @@ -13,16 +14,16 @@ ffprobe_path = pype.lib.get_ffmpeg_tool_path("ffprobe") FFMPEG = ( - '{} -i "%(input)s" %(filters)s %(args)s%(output)s' + '"{}" -i "%(input)s" %(filters)s %(args)s%(output)s' ).format(ffmpeg_path) FFPROBE = ( - '{} -v quiet -print_format json -show_format -show_streams "%(source)s"' + '"{}" -v quiet -print_format json -show_format -show_streams "%(source)s"' ).format(ffprobe_path) DRAWTEXT = ( - "drawtext=text=\\'%(text)s\\':x=%(x)s:y=%(y)s:fontcolor=" - "%(color)s@%(opacity).1f:fontsize=%(size)d:fontfile='%(font)s'" + "drawtext=fontfile='%(font)s':text=\\'%(text)s\\':" + "x=%(x)s:y=%(y)s:fontcolor=%(color)s@%(opacity).1f:fontsize=%(size)d" ) TIMECODE = ( "drawtext=timecode=\\'%(timecode)s\\':text=\\'%(text)s\\'" @@ -236,13 +237,32 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): } timecode_text = options.get("timecode") or "" text_for_size += timecode_text + data.update(options) + + os_system = platform.system().lower() + data_font = data.get("font") + if not data_font: + data_font = ( + resources.get_liberation_font_path().replace("\\", "/") + ) + elif isinstance(data_font, dict): + data_font = data_font[os_system] + + if data_font: + data["font"] = data_font + options["font"] = data_font + if ffmpeg_burnins._is_windows(): + data["font"] = ( + data_font + .replace(os.sep, r'\\' + os.sep) + .replace(':', r'\:') + ) + data.update( ffmpeg_burnins._drawtext(align, resolution, text_for_size, options) ) - if 'font' in data and ffmpeg_burnins._is_windows(): - data['font'] = data['font'].replace(os.sep, r'\\' + os.sep) - data['font'] = data['font'].replace(':', r'\:') + self.filters['drawtext'].append(draw % data) if options.get('bg_color') is not None: @@ -474,7 +494,7 @@ def burnins_from_data( # Replace with missing key value if frame_start_tc is not set if frame_start_tc is None and has_timecode: has_timecode = False - log.warning( + print( "`frame_start` and `frame_start_tc`" " are not set in entered data." ) @@ -483,7 +503,7 @@ def burnins_from_data( has_source_timecode = SOURCE_TIMECODE_KEY in value if source_timecode is None and has_source_timecode: has_source_timecode = False - log.warning("Source does not have set timecode value.") + print("Source does not have set timecode value.") value = value.replace(SOURCE_TIMECODE_KEY, MISSING_KEY_VALUE) key_pattern = re.compile(r"(\{.*?[^{0]*\})") diff --git a/pype/tools/pyblish_pype/control.py b/pype/tools/pyblish_pype/control.py index 0162848f2b..fecfffd821 100644 --- a/pype/tools/pyblish_pype/control.py +++ b/pype/tools/pyblish_pype/control.py @@ -9,6 +9,7 @@ import os import sys import traceback import inspect +import logging from Qt import QtCore @@ -29,6 +30,7 @@ class IterationBreak(Exception): class Controller(QtCore.QObject): + log = logging.getLogger("PyblishController") # Emitted when the GUI is about to start processing; # e.g. resetting, validating or publishing. about_to_process = QtCore.Signal(object, object) @@ -72,6 +74,8 @@ class Controller(QtCore.QObject): self.instance_toggled.connect(self._on_instance_toggled) def reset_variables(self): + self.log.debug("Resetting pyblish context variables") + # Data internal to the GUI itself self.is_running = False self.stopped = False @@ -113,6 +117,7 @@ class Controller(QtCore.QObject): "nextOrder": None, "ordersWithError": set() } + self.log.debug("Reset of pyblish context variables done") def presets_by_hosts(self): # Get global filters as base @@ -138,6 +143,8 @@ class Controller(QtCore.QObject): return result def reset_context(self): + self.log.debug("Resetting pyblish context object") + self.context = pyblish.api.Context() self.context._publish_states = InstanceStates.ContextType @@ -159,6 +166,8 @@ class Controller(QtCore.QObject): self.context.families = ("__context__",) + self.log.debug("Reset of pyblish context object done") + def reset(self): """Discover plug-ins and run collection.""" @@ -202,6 +211,7 @@ class Controller(QtCore.QObject): self.was_finished.emit() def stop(self): + self.log.debug("Stopping") self.stopped = True def act(self, plugin, action): @@ -346,27 +356,30 @@ class Controller(QtCore.QObject): This process don't stop on one """ def on_next(): + self.log.debug("Looking for next pair to process") try: self.current_pair = next(self.pair_generator) if isinstance(self.current_pair, IterationBreak): raise self.current_pair except IterationBreak: + self.log.debug("Iteration break was raised") self.is_running = False self.was_stopped.emit() return except StopIteration: + self.log.debug("Iteration stop was raised") self.is_running = False # All pairs were processed successfully! return util.defer(500, on_finished) - except Exception: - # This is a bug - exc_type, exc_msg, exc_tb = sys.exc_info() - traceback.print_exception(exc_type, exc_msg, exc_tb) - self.is_running = False - self.was_stopped.emit() + except Exception as exc: + self.log.warning( + "Unexpected exception during `on_next` happened", + exc_info=True + ) + exc_msg = str(exc) return util.defer( 500, lambda: on_unexpected_error(error=exc_msg) ) @@ -376,16 +389,23 @@ class Controller(QtCore.QObject): def on_process(): try: + self.log.debug( + "Processing pair: {}".format(str(self.current_pair)) + ) result = self._process(*self.current_pair) if result["error"] is not None: + self.log.debug("Error happened") self.errored = True + self.log.debug("Pair processed") self.was_processed.emit(result) - except Exception: - # TODO this should be handled much differently - exc_type, exc_msg, exc_tb = sys.exc_info() - traceback.print_exception(exc_type, exc_msg, exc_tb) + except Exception as exc: + self.log.warning( + "Unexpected exception during `on_process` happened", + exc_info=True + ) + exc_msg = str(exc) return util.defer( 500, lambda: on_unexpected_error(error=exc_msg) ) @@ -393,6 +413,10 @@ class Controller(QtCore.QObject): util.defer(10, on_next) def on_unexpected_error(error): + # TODO this should be handled much differently + # TODO emit crash signal to show message box with traceback? + self.is_running = False + self.was_stopped.emit() util.u_print(u"An unexpected error occurred:\n %s" % error) return util.defer(500, on_finished) @@ -443,9 +467,9 @@ class Controller(QtCore.QObject): try: callback(instance, old_value, new_value) except Exception: - print( + self.log.warning( "Callback for `instanceToggled` crashed. {}".format( os.path.abspath(inspect.getfile(callback)) - ) + ), + exc_info=True ) - traceback.print_exception(*sys.exc_info()) diff --git a/pype/tools/standalonepublish/widgets/widget_drop_frame.py b/pype/tools/standalonepublish/widgets/widget_drop_frame.py index a7abe1b24c..339093cf73 100644 --- a/pype/tools/standalonepublish/widgets/widget_drop_frame.py +++ b/pype/tools/standalonepublish/widgets/widget_drop_frame.py @@ -266,7 +266,7 @@ class DropDataFrame(QtWidgets.QFrame): def load_data_with_probe(self, filepath): ffprobe_path = pype.lib.get_ffmpeg_tool_path("ffprobe") args = [ - ffprobe_path, + "\"{}\"".format(ffprobe_path), '-v', 'quiet', '-print_format json', '-show_format', diff --git a/pype/version.py b/pype/version.py index 930e2cd686..8f57b15e91 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.13.0" +__version__ = "2.13.4"