text: import os
  import glob
  import contextlib
  import clique
  import capture
  
  import pype.maya.lib as lib
  import pype.api
  
  from maya import cmds
  import pymel.core as pm
  
  
  class ExtractPlayblast(pype.api.Extractor):
      """Extract viewport playblast.
  
      Takes review camera and creates review Quicktime video based on viewport
      capture.
  
      """
  
      label = "Extract Playblast"
      hosts = ["maya"]
      families = ["review"]
      optional = True
  
      def process(self, instance):
          self.log.info("Extracting capture..")
  
          # get scene fps
          fps = instance.data.get("fps") or instance.context.data.get("fps")
  
          # if start and end frames cannot be determined, get them
          # from Maya timeline
          start = instance.data.get("frameStartFtrack")
          end = instance.data.get("frameEndFtrack")
          if start is None:
              start = cmds.playbackOptions(query=True, animationStartTime=True)
          if end is None:
              end = cmds.playbackOptions(query=True, animationEndTime=True)
  
          self.log.info("start: {}, end: {}".format(start, end))
  
          # get cameras
          camera = instance.data['review_camera']
          capture_preset = instance.context.data['presets']['maya']['capture']
  
          try:
              preset = lib.load_capture_preset(data=capture_preset)
          except Exception:
              preset = {}
          self.log.info('using viewport preset: {}'.format(preset))
  
          preset['camera'] = camera
          preset['format'] = "image"
          # preset['compression'] = "qt"
          preset['quality'] = 95
          preset['compression'] = "png"
          preset['start_frame'] = start
          preset['end_frame'] = end
          camera_option = preset.get("camera_option", {})
          camera_option["depthOfField"] = cmds.getAttr(
              "{0}.depthOfField".format(camera))
  
          stagingdir = self.staging_dir(instance)
          filename = "{0}".format(instance.name)
          path = os.path.join(stagingdir, filename)
  
          self.log.info("Outputting images to %s" % path)
  
          preset['filename'] = path
          preset['overwrite'] = True
  
          pm.refresh(f=True)
  
          refreshFrameInt = int(pm.playbackOptions(q=True, minTime=True))
          pm.currentTime(refreshFrameInt - 1, edit=True)
          pm.currentTime(refreshFrameInt, edit=True)
  
          with maintained_time():
              filename = preset.get("filename", "%TEMP%")
  
              # Force viewer to False in call to capture because we have our own
              # viewer opening call to allow a signal to trigger between
              # playblast and viewer
              preset['viewer'] = False
  
              # Remove panel key since it's internal value to capture_gui
              preset.pop("panel", None)
  
              path = capture.capture(**preset)
              playblast = self._fix_playblast_output_path(path)
  
          self.log.info("file list  {}".format(playblast))
  
          collected_frames = os.listdir(stagingdir)
          collections, remainder = clique.assemble(collected_frames)
          input_path = os.path.join(
              stagingdir, collections[0].format('{head}{padding}{tail}'))
          self.log.info("input {}".format(input_path))
  
          if "representations" not in instance.data:
              instance.data["representations"] = []
  
          representation = {
              'name': 'png',
              'ext': 'png',
              'files': collected_frames,
              "stagingDir": stagingdir,
              "frameStart": start,
              "frameEnd": end,
              'fps': fps,
              'preview': True,
              'tags': ['review', 'delete']
          }
          instance.data["representations"].append(representation)
  
      def _fix_playblast_output_path(self, filepath):
          """Workaround a bug in maya.cmds.playblast to return correct filepath.
  
          When the `viewer` argument is set to False and maya.cmds.playblast
          does not automatically open the playblasted file the returned
          filepath does not have the file's extension added correctly.
  
          To workaround this we just glob.glob() for any file extensions and
           assume the latest modified file is the correct file and return it.
          """
          # Catch cancelled playblast
          if filepath is None:
              self.log.warning("Playblast did not result in output path. "
                               "Playblast is probably interrupted.")
              return None
  
          # Fix: playblast not returning correct filename (with extension)
          # Lets assume the most recently modified file is the correct one.
          if not os.path.exists(filepath):
              directory = os.path.dirname(filepath)
              filename = os.path.basename(filepath)
              # check if the filepath is has frame based filename
              # example : capture.####.png
              parts = filename.split(".")
              if len(parts) == 3:
                  query = os.path.join(directory, "{}.*.{}".format(parts[0],
                                                                   parts[-1]))
                  files = glob.glob(query)
              else:
                  files = glob.glob("{}.*".format(filepath))
  
              if not files:
                  raise RuntimeError("Couldn't find playblast from: "
                                     "{0}".format(filepath))
              filepath = max(files, key=os.path.getmtime)
  
          return filepath
  
  
  @contextlib.contextmanager
  def maintained_time():
      ct = cmds.currentTime(query=True)
      try:
          yield
      finally:
          cmds.currentTime(ct, edit=True)
  