[pitivi] render: Rework the way the file to render to is chosen



commit 56caf8b735c6c4dcfb686cbfff833361a1452ab7
Author: Thibault Saunier <tsaunier igalia com>
Date:   Fri Jan 8 12:38:58 2021 -0300

    render: Rework the way the file to render to is chosen
    
    Instead of having a atypical way of selecting on one side the directory
    and on the other side the filename, use a standard GtkFileChooser dialog
    to select the file where to render, allowing the user to also manually
    specify the file path in a text entry.

 data/ui/renderingdialog.ui |  89 ++++++++++++++++---------------------
 pitivi/medialibrary.py     |  36 +--------------
 pitivi/render.py           | 106 +++++++++++++++++++++++++++------------------
 pitivi/utils/misc.py       |  77 ++++++++++++++++++++++++++++++++
 pitivi/utils/ui.py         |  34 +++++++++++++++
 tests/test_medialibrary.py |  15 -------
 tests/test_render.py       |  12 ++---
 tests/test_utils.py        |  16 +++++++
 8 files changed, 236 insertions(+), 149 deletions(-)
---
diff --git a/data/ui/renderingdialog.ui b/data/ui/renderingdialog.ui
index 5bbdc5d4c..c94a5e860 100644
--- a/data/ui/renderingdialog.ui
+++ b/data/ui/renderingdialog.ui
@@ -219,57 +219,6 @@
                 <property name="top_attach">0</property>
               </packing>
             </child>
-            <child>
-              <object class="GtkFileChooserButton" id="filebutton">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="hexpand">True</property>
-                <property name="action">select-folder</property>
-                <signal name="current-folder-changed" handler="_current_folder_changed_cb" swapped="no"/>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="top_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel" id="label1">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="halign">end</property>
-                <property name="valign">center</property>
-                <property name="label" translatable="yes">Folder:</property>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkEntry" id="fileentry">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="activates_default">True</property>
-                <signal name="changed" handler="_filename_changed_cb" swapped="no"/>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="top_attach">4</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel" id="label5">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="halign">end</property>
-                <property name="valign">center</property>
-                <property name="label" translatable="yes">File name:</property>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">4</property>
-              </packing>
-            </child>
             <child>
               <object class="GtkExpander" id="advanced_expander">
                 <property name="name">Advanced</property>
@@ -838,6 +787,44 @@ This option is a good trade-off between quality of the rendered video and stabil
                 <property name="top_attach">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkButton" id="filebutton">
+                <property name="label" translatable="yes">Select file</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <signal name="clicked" handler="_select_file_clicked_cb" swapped="no"/>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">end</property>
+                <property name="valign">center</property>
+                <property name="label" translatable="yes">File path:</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="fileentry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="activates-default">True</property>
+                <signal name="changed" handler="_fileentry_changed_cb" swapped="no"/>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
             <child>
               <placeholder/>
             </child>
diff --git a/pitivi/medialibrary.py b/pitivi/medialibrary.py
index 0e307aa63..4f6b1a6c0 100644
--- a/pitivi/medialibrary.py
+++ b/pitivi/medialibrary.py
@@ -53,10 +53,10 @@ from pitivi.utils.misc import quote_uri
 from pitivi.utils.misc import show_user_manual
 from pitivi.utils.proxy import get_proxy_target
 from pitivi.utils.proxy import ProxyingStrategy
-from pitivi.utils.proxy import ProxyManager
 from pitivi.utils.ui import beautify_asset
 from pitivi.utils.ui import beautify_eta
 from pitivi.utils.ui import FILE_TARGET_ENTRY
+from pitivi.utils.ui import filter_unsupported_media_files
 from pitivi.utils.ui import fix_infobar
 from pitivi.utils.ui import info_name
 from pitivi.utils.ui import LARGE_THUMB_WIDTH
@@ -104,28 +104,6 @@ class AssetStoreItem(GObject.GObject):
         self.thumb_decorator = thumb_decorator
 
 
-# This whitelist is made from personal knowledge of file extensions in the wild,
-# from gst-inspect |grep demux,
-# http://en.wikipedia.org/wiki/Comparison_of_container_formats and
-# http://en.wikipedia.org/wiki/List_of_file_formats#Video
-# ...and looking at the contents of /usr/share/mime
-SUPPORTED_FILE_FORMATS = {
-    "video": ("3gpp", "3gpp2", "dv", "mp2t", "mp2t", "mp4", "mpeg", "ogg",
-              "quicktime", "webm", "x-flv", "x-matroska", "x-mng", "x-ms-asf",
-              "x-ms-wmp", "x-ms-wmv", "x-msvideo", "x-ogm+ogg", "x-theora+ogg"),
-    "application": ("mxf",),
-    "audio": ("aac", "ac3", "basic", "flac", "mp2", "mp4", "mpeg", "ogg",
-              "opus", "webm", "x-adpcm", "x-aifc", "x-aiff", "x-aiffc",
-              "x-ape", "x-flac+ogg", "x-m4b", "x-matroska", "x-ms-asx",
-              "x-ms-wma", "x-speex", "x-speex+ogg", "x-vorbis+ogg", "x-wav"),
-    "image": ("jp2", "jpeg", "png", "svg+xml")}
-
-SUPPORTED_MIMETYPES = []
-for category, mime_types in SUPPORTED_FILE_FORMATS.items():
-    for mime in mime_types:
-        SUPPORTED_MIMETYPES.append(category + "/" + mime)
-
-
 class OptimizeOption(IntEnum):
     UNSUPPORTED_ASSETS = 0
     ALL = 1
@@ -760,16 +738,6 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
         project.connect("proxying-error", self._proxying_error_cb)
         project.connect("settings-set-from-imported-asset", 
self.__project_settings_set_from_imported_asset_cb)
 
-    def _filter_unsupported(self, filter_info):
-        """Returns whether the specified item should be displayed."""
-        if filter_info.mime_type not in SUPPORTED_MIMETYPES:
-            return False
-
-        if ProxyManager.is_proxy_asset(filter_info.uri):
-            return False
-
-        return True
-
     def show_import_assets_dialog(self):
         """Pops up the "Import Sources" dialog box."""
         dialog = Gtk.FileChooserDialog()
@@ -794,7 +762,7 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
         file_filter = Gtk.FileFilter()
         file_filter.set_name(_("Supported file formats"))
         file_filter.add_custom(Gtk.FileFilterFlags.URI | Gtk.FileFilterFlags.MIME_TYPE,
-                               self._filter_unsupported)
+                               filter_unsupported_media_files)
         for formatter in GES.list_assets(GES.Formatter):
             for extension in formatter.get_meta("extension").split(","):
                 if not extension:
diff --git a/pitivi/render.py b/pitivi/render.py
index 7d090890c..6772babe2 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -34,6 +34,7 @@ from pitivi import configure
 from pitivi.check import MISSING_SOFT_DEPS
 from pitivi.utils.loggable import Loggable
 from pitivi.utils.misc import cmp
+from pitivi.utils.misc import is_pathname_valid
 from pitivi.utils.misc import path_from_uri
 from pitivi.utils.misc import show_user_manual
 from pitivi.utils.ripple_update_group import RippleUpdateGroup
@@ -41,6 +42,7 @@ from pitivi.utils.ui import AUDIO_CHANNELS
 from pitivi.utils.ui import AUDIO_RATES
 from pitivi.utils.ui import beautify_eta
 from pitivi.utils.ui import create_frame_rates_model
+from pitivi.utils.ui import filter_unsupported_media_files
 from pitivi.utils.ui import get_combo_value
 from pitivi.utils.ui import set_combo_value
 from pitivi.utils.widgets import GstElementSettingsDialog
@@ -814,17 +816,11 @@ class RenderDialog(Loggable):
         self._create_ui()
 
         # Directory and Filename
-        self.filebutton.set_current_folder(self.app.settings.lastExportFolder)
         if not self.project.name:
             self._update_filename(_("Untitled"))
         else:
             self._update_filename(self.project.name)
 
-        # Add a shortcut for the project folder (if saved)
-        if self.project.uri:
-            shortcut = os.path.dirname(self.project.uri)
-            self.filebutton.add_shortcut_folder_uri(shortcut)
-
         self._setting_encoding_profile = False
 
         # We store these so that when the user tries various container formats,
@@ -1133,35 +1129,30 @@ class RenderDialog(Loggable):
 
     def _check_filename(self):
         """Displays a warning if the file path already exists."""
-        path = self.filebutton.get_current_folder()
-        if not path:
-            # This happens when the window is initialized.
-            return
-
-        filename = self.fileentry.get_text()
-
-        # Characters that cause pipeline failure.
-        blacklist = ["/"]
-        invalid_chars = "".join([ch for ch in blacklist if ch in filename])
-
-        warning_icon = "dialog-warning"
-        self._is_filename_valid = True
-        if not filename:
+        filepath = self.fileentry.get_text()
+        if not filepath:
             tooltip_text = _("A file name is required.")
             self._is_filename_valid = False
-        elif os.path.exists(os.path.join(path, filename)):
-            tooltip_text = _("This file already exists.\n"
-                             "If you don't want to overwrite it, choose a "
-                             "different file name or folder.")
-        elif invalid_chars:
-            tooltip_text = _("Remove invalid characters from the filename: %s") % invalid_chars
-            self._is_filename_valid = False
         else:
-            warning_icon = None
-            tooltip_text = None
+            filepath = os.path.realpath(filepath)
+            if os.path.isdir(filepath):
+                tooltip_text = _("A file name is required.")
+                self._is_filename_valid = False
+            elif os.path.exists(filepath):
+                tooltip_text = _("This file already exists.\n"
+                                 "If you don't want to overwrite it, choose a "
+                                 "different file name or folder.")
+                self._is_filename_valid = True
+            elif not is_pathname_valid(filepath):
+                tooltip_text = _("Invalid file path")
+                self._is_filename_valid = False
+            else:
+                tooltip_text = None
+                self._is_filename_valid = True
 
-        self.fileentry.set_icon_from_icon_name(1, warning_icon)
-        self.fileentry.set_icon_tooltip_text(1, tooltip_text)
+        warning_icon = "dialog-warning" if tooltip_text else None
+        self.fileentry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, warning_icon)
+        self.fileentry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, tooltip_text)
         self.__update_render_button_sensitivity()
 
     def _get_filesize_estimate(self):
@@ -1199,7 +1190,8 @@ class RenderDialog(Loggable):
             name = "%s%s%s" % (basename, os.path.extsep, extension)
         else:
             name = basename
-        self.fileentry.set_text(name)
+
+        self.fileentry.set_text(os.path.join(self.app.settings.lastExportFolder, name))
 
     def _update_valid_restriction_values(self, caps, combo, caps_template,
                                          model, combo_value,
@@ -1489,12 +1481,43 @@ class RenderDialog(Loggable):
             getattr(self.project, media_type + "_profile").set_format(caps)
         self.dialog.window.destroy()
 
+    def _select_file_clicked_cb(self, unused_button):
+        chooser = Gtk.FileChooserNative.new(
+            _("Select file path to render"),
+            self.window,
+            Gtk.FileChooserAction.SAVE,
+            None, None)
+
+        file_filter = Gtk.FileFilter()
+        file_filter.set_name(_("Supported file formats"))
+        file_filter.add_custom(Gtk.FileFilterFlags.URI | Gtk.FileFilterFlags.MIME_TYPE,
+                               filter_unsupported_media_files)
+        chooser.add_filter(file_filter)
+        chooser.set_current_folder(self.app.settings.lastExportFolder)
+        # Add a shortcut for the project folder (if saved)
+        if self.project.uri:
+            shortcut = os.path.dirname(self.project.uri)
+            chooser.add_shortcut_folder_uri(shortcut)
+
+        response = chooser.run()
+        if response == Gtk.ResponseType.DELETE_EVENT:
+            # This happens because Gtk.FileChooserNative is confused because we
+            # added a filter but since it's ignored it complains there is none.
+            # Try again without the filter.
+            chooser.remove_filter(file_filter)
+            response = chooser.run()
+
+        if response == Gtk.ResponseType.ACCEPT:
+            self.app.settings.lastExportFolder = chooser.get_current_folder()
+            self.fileentry.set_text(os.path.join(chooser.get_filename()))
+
     def _render_button_clicked_cb(self, unused_button):
         """Starts the rendering process."""
         self.__replace_proxies()
         self.__unset_effect_preview_props()
-        self.outfile = os.path.join(self.filebutton.get_uri(),
-                                    self.fileentry.get_text())
+        filename = os.path.realpath(self.fileentry.get_text())
+        self.outfile = Gst.filename_to_uri(filename)
+        self.app.settings.lastExportFolder = os.path.dirname(filename)
         self.progress = RenderingProgressDialog(self.app, self)
         # Hide the rendering settings dialog while rendering
         self.window.hide()
@@ -1513,7 +1536,6 @@ class RenderDialog(Loggable):
         self.project.pipeline.connect("position", self._update_position_cb)
         # Force writing the config now, or the path will be reset
         # if the user opens the rendering dialog again
-        self.app.settings.lastExportFolder = self.filebutton.get_current_folder()
         self.app.settings.store_settings()
 
     def _close_button_clicked_cb(self, unused_button):
@@ -1528,10 +1550,7 @@ class RenderDialog(Loggable):
     def _container_context_help_clicked_cb(self, unused_button):
         show_user_manual("codecscontainers")
 
-    def _current_folder_changed_cb(self, *unused_args):
-        self._check_filename()
-
-    def _filename_changed_cb(self, *unused_args):
+    def _fileentry_changed_cb(self, unused_entry):
         self._check_filename()
 
     # Periodic (timer) callbacks
@@ -1539,6 +1558,7 @@ class RenderDialog(Loggable):
         if self._rendering_is_paused:
             # Do nothing until we resume rendering
             return True
+
         if self._is_rendering:
             if self.current_position:
                 timediff = time.time() - self._time_started - self._time_spent_paused
@@ -1549,10 +1569,10 @@ class RenderDialog(Loggable):
                 if estimate:
                     self.progress.update_progressbar_eta(estimate)
             return True
-        else:
-            self._time_estimate_timer = None
-            self.debug("Stopping the ETA timer")
-            return False
+
+        self._time_estimate_timer = None
+        self.debug("Stopping the ETA timer")
+        return False
 
     def _update_filesize_estimate_cb(self):
         if self._rendering_is_paused:
diff --git a/pitivi/utils/misc.py b/pitivi/utils/misc.py
index 9897c2dd4..18c874a3d 100644
--- a/pitivi/utils/misc.py
+++ b/pitivi/utils/misc.py
@@ -15,8 +15,10 @@
 #
 # You should have received a copy of the GNU Lesser General Public
 # License along with this program; if not, see <http://www.gnu.org/licenses/>.
+import errno
 import os
 import subprocess
+import sys
 import threading
 import time
 from gettext import gettext as _
@@ -480,3 +482,78 @@ def asset_get_duration(asset):
 
 def cmp(item1, item2):
     return (item1 > item2) - (item1 < item2)
+
+
+# Windows-specific error code indicating an invalid pathname.
+# See Also
+# ----------
+# https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
+#     Official listing of all such codes.
+ERROR_INVALID_NAME = 123
+
+
+def is_pathname_valid(pathname: str) -> bool:
+    """`True` if the passed pathname is a valid pathname for the current OS `False` otherwise."""
+    # If this pathname is either not a string or is but is empty, this pathname
+    # is invalid.
+    try:
+        if not isinstance(pathname, str) or not pathname:
+            return False
+
+        # Strip this pathname's Windows-specific drive specifier (e.g., `C:\`)
+        # if any. Since Windows prohibits path components from containing `:`
+        # characters, failing to strip this `:`-suffixed prefix would
+        # erroneously invalidate all valid absolute Windows pathnames.
+        _, pathname = os.path.splitdrive(pathname)
+
+        # Directory guaranteed to exist. If the current OS is Windows, this is
+        # the drive to which Windows was installed (e.g., the "%HOMEDRIVE%"
+        # environment variable); else, the typical root directory.
+        root_dirname = os.environ.get('HOMEDRIVE', 'C:') \
+            if sys.platform == 'win32' else os.path.sep
+        assert os.path.isdir(root_dirname)   # ...Murphy and her ironclad Law
+
+        # Append a path separator to this directory if needed.
+        root_dirname = root_dirname.rstrip(os.path.sep) + os.path.sep
+
+        # Test whether each path component split from this pathname is valid or
+        # not, ignoring non-existent and non-readable path components.
+        for pathname_part in pathname.split(os.path.sep):
+            try:
+                os.lstat(root_dirname + pathname_part)
+            # If an OS-specific exception is raised, its error code
+            # indicates whether this pathname is valid or not. Unless this
+            # is the case, this exception implies an ignorable kernel or
+            # filesystem complaint (e.g., path not found or inaccessible).
+            #
+            # Only the following exceptions indicate invalid pathnames:
+            #
+            # * Instances of the Windows-specific "WindowsError" class
+            #   defining the "winerror" attribute whose value is
+            #   "ERROR_INVALID_NAME". Under Windows, "winerror" is more
+            #   fine-grained and hence useful than the generic "errno"
+            #   attribute. When a too-long pathname is passed, for example,
+            #   "errno" is "ENOENT" (i.e., no such file or directory) rather
+            #   than "ENAMETOOLONG" (i.e., file name too long).
+            # * Instances of the cross-platform "OSError" class defining the
+            #   generic "errno" attribute whose value is either:
+            #   * Under most POSIX-compatible OSes, "ENAMETOOLONG".
+            #   * Under some edge-case OSes (e.g., SunOS, *BSD), "ERANGE".
+            except OSError as exc:
+                if hasattr(exc, 'winerror'):
+                    if exc.winerror == ERROR_INVALID_NAME:
+                        return False
+                elif exc.errno in {errno.ENAMETOOLONG, errno.ERANGE}:
+                    return False
+    # If a "TypeError" exception was raised, it almost certainly has the
+    # error message "embedded NUL character" indicating an invalid pathname.
+    except TypeError as exc:
+        return False
+    # If no exception was raised, all path components and hence this
+    # pathname itself are valid. (Praise be to the curmudgeonly python.)
+    else:
+        return True
+    # If any other exception was raised, this is an unrelated fatal issue
+    # (e.g., a bug). Permit this exception to unwind the call stack.
+    #
+    # Did we mention this should be shipped with Python already?
diff --git a/pitivi/utils/ui.py b/pitivi/utils/ui.py
index 10e90874e..35e472de3 100644
--- a/pitivi/utils/ui.py
+++ b/pitivi/utils/ui.py
@@ -892,3 +892,37 @@ AUDIO_RATES = create_model((str, int),
                                48000,
                                96000
                            )])
+
+# This whitelist is made from personal knowledge of file extensions in the wild,
+# from gst-inspect |grep demux,
+# http://en.wikipedia.org/wiki/Comparison_of_container_formats and
+# http://en.wikipedia.org/wiki/List_of_file_formats#Video
+# ...and looking at the contents of /usr/share/mime
+SUPPORTED_FILE_FORMATS = {
+    "video": ("3gpp", "3gpp2", "dv", "mp2t", "mp2t", "mp4", "mpeg", "ogg",
+              "quicktime", "webm", "x-flv", "x-matroska", "x-mng", "x-ms-asf",
+              "x-ms-wmp", "x-ms-wmv", "x-msvideo", "x-ogm+ogg", "x-theora+ogg"),
+    "application": ("mxf",),
+    "audio": ("aac", "ac3", "basic", "flac", "mp2", "mp4", "mpeg", "ogg",
+              "opus", "webm", "x-adpcm", "x-aifc", "x-aiff", "x-aiffc",
+              "x-ape", "x-flac+ogg", "x-m4b", "x-matroska", "x-ms-asx",
+              "x-ms-wma", "x-speex", "x-speex+ogg", "x-vorbis+ogg", "x-wav"),
+    "image": ("jp2", "jpeg", "png", "svg+xml")}
+
+SUPPORTED_MIMETYPES = []
+for category, mime_types in SUPPORTED_FILE_FORMATS.items():
+    for mime in mime_types:
+        SUPPORTED_MIMETYPES.append(category + "/" + mime)
+
+
+def filter_unsupported_media_files(filter_info):
+    """Returns whether the specified item should be displayed."""
+    from pitivi.utils.proxy import ProxyManager
+
+    if filter_info.mime_type not in SUPPORTED_MIMETYPES:
+        return False
+
+    if ProxyManager.is_proxy_asset(filter_info.uri):
+        return False
+
+    return True
diff --git a/tests/test_medialibrary.py b/tests/test_medialibrary.py
index daef7f720..f077126b3 100644
--- a/tests/test_medialibrary.py
+++ b/tests/test_medialibrary.py
@@ -192,21 +192,6 @@ class BaseTestMediaLibrary(common.TestCase):
 
 class TestMediaLibrary(BaseTestMediaLibrary):
 
-    def test_import_dialog_proxy_filter(self):
-        mock_filter = mock.Mock()
-        mock_filter.mime_type = "video/mp4"
-
-        self._custom_set_up()
-        mlib = self.medialibrary
-
-        # Test HQ Proxies are filtered
-        mock_filter.uri = "file:///home/user/Videos/video.mp4.2360382.proxy.mov"
-        self.assertFalse(mlib._filter_unsupported(mock_filter))
-
-        # Test Scaled Proxies are filtered
-        mock_filter.uri = "file:///home/user/Videos/video.mp4.2360382.300x300.scaledproxy.mov"
-        self.assertFalse(mlib._filter_unsupported(mock_filter))
-
     def stop_using_proxies(self, delete_proxies=False):
         sample_name = "30fps_numeroted_frames_red.mkv"
         self.check_import([sample_name])
diff --git a/tests/test_render.py b/tests/test_render.py
index 47aac4dde..6dd98425a 100644
--- a/tests/test_render.py
+++ b/tests/test_render.py
@@ -321,11 +321,10 @@ class TestRender(BaseTestMediaLibrary):
         from pitivi.render import RenderingProgressDialog
         with tempfile.TemporaryDirectory() as temp_dir:
             # Start rendering
-            with mock.patch.object(dialog.filebutton, "get_uri",
-                                   return_value=Gst.filename_to_uri(temp_dir)):
-                with mock.patch.object(dialog.fileentry, "get_text", return_value="outfile"):
-                    with mock.patch.object(RenderingProgressDialog, "__new__"):
-                        dialog._render_button_clicked_cb(None)
+            with mock.patch.object(dialog.fileentry, "get_text",
+                                   return_value=os.path.join(temp_dir, "outfile")):
+                with mock.patch.object(RenderingProgressDialog, "__new__"):
+                    dialog._render_button_clicked_cb(None)
 
             message = dialog._pipeline.get_bus().timed_pop_filtered(
                 Gst.CLOCK_TIME_NONE,
@@ -335,10 +334,11 @@ class TestRender(BaseTestMediaLibrary):
                 dialog._pipeline, Gst.DebugGraphDetails.ALL,
                 "test_rendering_with_profile.dot")
 
-            result_file = Gst.filename_to_uri(os.path.join(temp_dir, "outfile"))
             struct = message.get_structure()
             self.assertEqual(message.type, Gst.MessageType.EOS,
                              struct.to_string() if struct else message)
+
+            result_file = Gst.filename_to_uri(os.path.join(temp_dir, "outfile"))
             asset = GES.UriClipAsset.request_sync(result_file)
             self.assertIsNotNone(asset)
 
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 3b73cec88..fbcaf383e 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -30,6 +30,7 @@ from pitivi.utils.misc import fixate_caps_with_default_values
 from pitivi.utils.ui import beautify_last_updated_timestamp
 from pitivi.utils.ui import beautify_length
 from pitivi.utils.ui import create_frame_rates_model
+from pitivi.utils.ui import filter_unsupported_media_files
 from pitivi.utils.ui import format_audiochannels
 from pitivi.utils.ui import format_audiorate
 from pitivi.utils.ui import format_framerate_value
@@ -294,3 +295,18 @@ class TestCreateFramerateModel(common.TestCase):
                              (130, 1)
                              ]
         self.assertListEqual([(row[1].num, row[1].denom) for row in model], sorted_frameslist)
+
+
+class TestFiltering(common.TestCase):
+
+    def test_filter_unsupported_media_files(self):
+        mock_filter_info = mock.Mock()
+        mock_filter_info.mime_type = "video/mp4"
+
+        # Test HQ Proxies are filtered
+        mock_filter_info.uri = "file:///home/user/Videos/video.mp4.2360382.proxy.mov"
+        self.assertFalse(filter_unsupported_media_files(mock_filter_info))
+
+        # Test Scaled Proxies are filtered
+        mock_filter_info.uri = "file:///home/user/Videos/video.mp4.2360382.300x300.scaledproxy.mov"
+        self.assertFalse(filter_unsupported_media_files(mock_filter_info))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]