[pitivi] Allow editing nested timelines



commit 268d88f4eb029c5731f69821769cce368d5c9999
Author: Swayamjeet <swayam1998 gmail com>
Date:   Wed Aug 14 23:57:02 2019 +0530

    Allow editing nested timelines
    
    This is done by starting up another Pitivi instance.

 pitivi/editorperspective.py   | 26 ++++++++++++++++++++++++--
 pitivi/medialibrary.py        | 36 ++++++++++++++++++++++++++++++++++++
 pitivi/timeline/elements.py   |  5 +++++
 pitivi/timeline/previewers.py | 42 ++++++++++++++++++++++++++++++++++++++++++
 pitivi/utils/proxy.py         |  2 +-
 5 files changed, 108 insertions(+), 3 deletions(-)
---
diff --git a/pitivi/editorperspective.py b/pitivi/editorperspective.py
index da3386db..793f4538 100644
--- a/pitivi/editorperspective.py
+++ b/pitivi/editorperspective.py
@@ -37,6 +37,7 @@ from pitivi.perspective import Perspective
 from pitivi.project import ProjectSettingsDialog
 from pitivi.settings import GlobalSettings
 from pitivi.tabsmanager import BaseTabs
+from pitivi.timeline.previewers import ThumbnailCache
 from pitivi.timeline.timeline import TimelineContainer
 from pitivi.titleeditor import TitleEditor
 from pitivi.transitions import TransitionsListWidget
@@ -45,8 +46,6 @@ from pitivi.utils.misc import path_from_uri
 from pitivi.utils.ui import beautify_time_delta
 from pitivi.utils.ui import EDITOR_PERSPECTIVE_CSS
 from pitivi.utils.ui import info_name
-from pitivi.utils.ui import PADDING
-from pitivi.utils.ui import SPACING
 from pitivi.viewer.viewer import ViewerContainer
 
 
@@ -102,6 +101,7 @@ class EditorPerspective(Perspective, Loggable):
         """Sets up the UI."""
         self.__setup_css()
         self._createUi()
+        self.app.gui.connect("focus-in-event", self.__focus_in_event_cb)
         self.app.gui.connect("destroy", self._destroyedCb)
 
     def refresh(self):
@@ -116,6 +116,28 @@ class EditorPerspective(Perspective, Loggable):
         style_context.add_provider_for_screen(screen, css_provider,
                                               Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
 
+    def __focus_in_event_cb(self, unused_widget, unused_event):
+        ges_timeline = self.timeline_ui.timeline.ges_timeline
+        if not ges_timeline:
+            # Nothing to work with, Pitivi is starting up.
+            return
+
+        # Commit the timeline so its nested timelines assets are refreshed.
+        ges_timeline.commit()
+
+        # We need to track the changed assets ourselves.
+        changed_files_uris = ThumbnailCache.update_caches()
+        if changed_files_uris:
+            self.medialibrary.update_asset_thumbs(changed_files_uris)
+
+            for ges_layer in ges_timeline.get_layers():
+                for ges_clip in ges_layer.get_clips():
+                    if ges_clip.get_asset().props.id in changed_files_uris:
+                        if ges_clip.ui._audioSource:
+                            ges_clip.ui._audioSource.update_previewer()
+                        if ges_clip.ui._videoSource:
+                            ges_clip.ui._videoSource.update_previewer()
+
     def _destroyedCb(self, unused_main_window):
         """Cleanup before destroying this window."""
         pm = self.app.project_manager
diff --git a/pitivi/medialibrary.py b/pitivi/medialibrary.py
index 6d519a17..5a1082d7 100644
--- a/pitivi/medialibrary.py
+++ b/pitivi/medialibrary.py
@@ -19,6 +19,8 @@
 # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 # Boston, MA 02110-1301, USA.
 import os
+import subprocess
+import sys
 import time
 from gettext import gettext as _
 from gettext import ngettext
@@ -208,6 +210,15 @@ class AssetThumbnail(GObject.Object, Loggable):
         self.src_small, self.src_large = self.__get_thumbnails()
         self.decorate()
 
+    def disregard_previewer(self):
+        if self.__previewer:
+            self.__previewer.disconnect_by_func(self.__done_cb)
+            self.__previewer.stop_generation()
+            self.__previewer = None
+
+        self.refresh()
+        self.emit("thumb-updated")
+
     def __get_thumbnails(self):
         """Gets the base source thumbnails.
 
@@ -801,6 +812,11 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
         filter.add_custom(Gtk.FileFilterFlags.URI |
                           Gtk.FileFilterFlags.MIME_TYPE,
                           self.__filter_unsupported)
+        for formatter in GES.list_assets(GES.Formatter):
+            for extension in formatter.get_meta("extension").split(","):
+                if not extension:
+                    continue
+                filter.add_pattern("*.%s" % extension)
         dialog.add_filter(filter)
 
         # ...and allow the user to override our whitelists
@@ -830,6 +846,11 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
         if self._project.loaded:
             self._flushPendingAssets()
 
+    def update_asset_thumbs(self, asset_uris):
+        for row in self.storemodel:
+            if row[COL_ASSET].props.id in asset_uris:
+                row[COL_THUMB_DECORATOR].disregard_previewer()
+
     def _flushPendingAssets(self):
         self.debug("Flushing %d pending model rows", len(self._pending_assets))
         for asset in self._pending_assets:
@@ -1231,6 +1252,14 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
         parent_path = os.path.dirname(path_from_uri(assets[0].get_id()))
         Gio.AppInfo.launch_default_for_uri(Gst.filename_to_uri(parent_path), None)
 
+    def __edit_nested_clip_cb(self, unused_action, unused_parameter):
+        assets = self.getSelectedAssets()
+        if len(assets) != 1:
+            return
+
+        path = os.path.abspath(path_from_uri(assets[0].get_id()))
+        subprocess.Popen([sys.argv[0], path])
+
     def __createMenuModel(self):
         if self.app.proxy_manager.proxyingUnsupported:
             return None, None
@@ -1249,6 +1278,13 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
             text = _("Open containing folder")
             menu_model.append(text, "assets.%s" % action.get_name().replace(" ", "."))
 
+        if len(assets) == 1 and assets[0].props.is_nested_timeline:
+            action = Gio.SimpleAction.new("edit-nested-clip", None)
+            action.connect("activate", self.__edit_nested_clip_cb)
+            action_group.insert(action)
+            text = _("Edit")
+            menu_model.append(text, "assets.%s" % action.get_name().replace(" ", "."))
+
         image_assets = [asset for asset in assets
                         if asset.is_image()]
 
diff --git a/pitivi/timeline/elements.py b/pitivi/timeline/elements.py
index 0b5de8fd..bd1cf47e 100644
--- a/pitivi/timeline/elements.py
+++ b/pitivi/timeline/elements.py
@@ -636,6 +636,11 @@ class TimelineElement(Gtk.Layout, Zoomable, Loggable):
         # and override that one.
         self.showDefaultKeyframes(lazy_render=True)
 
+    def update_previewer(self):
+        """Refreshes the previewer widget."""
+        if self.__previewer:
+            self.__previewer.refresh()
+
     def release(self):
         if self.__previewer:
             self.__previewer.release()
diff --git a/pitivi/timeline/previewers.py b/pitivi/timeline/previewers.py
index ec9a7fe0..74f35366 100644
--- a/pitivi/timeline/previewers.py
+++ b/pitivi/timeline/previewers.py
@@ -740,6 +740,16 @@ class AssetPreviewer(Previewer, Loggable):
                 self.failures.add(self.position)
                 self.position = -1
             self._schedule_next_thumb_generation()
+        elif message.type == Gst.MessageType.STREAM_COLLECTION and isinstance(message.src, GES.Timeline):
+            # Make sure we only work with the video track when thumbnailing
+            # nested timelines.
+            collection = message.parse_stream_collection()
+            for i in range(collection.get_size()):
+                stream = collection.get_stream(i)
+                if stream.get_stream_type() == Gst.StreamType.VIDEO:
+                    message.src.send_event(Gst.Event.new_select_streams([stream.get_stream_id()]))
+                    break
+
         return Gst.BusSyncReply.PASS
 
     def __preroll_timed_out_cb(self):
@@ -822,6 +832,12 @@ class VideoPreviewer(Gtk.Layout, AssetPreviewer, Zoomable):
         for thumb in self.get_children():
             thumb.props.opacity = opacity
 
+    def refresh(self):
+        """Recreates the thumbnails cache."""
+        self.stop_generation()
+        self.thumb_cache = ThumbnailCache.get(self.uri)
+        self._update_thumbnails()
+
     def _update_thumbnails(self):
         """Updates the thumbnail widgets for the clip at the current zoom."""
         if not self.thumb_width:
@@ -934,6 +950,22 @@ class ThumbnailCache(Loggable):
         thumbs_cache_dir = get_dir(os.path.join(xdg_cache_home(), "thumbs"))
         return os.path.join(thumbs_cache_dir, filename)
 
+    @classmethod
+    def update_caches(cls):
+        """Trashes the obsolete caches, for assets which changed.
+
+        Returns:
+            list[str]: The URIs of the assets which changed.
+        """
+        changed_files_uris = []
+        for uri, cache in cls.caches_by_uri.items():
+            dbfile = cls.dbfile_name(uri)
+            if cache.dbfile != dbfile:
+                changed_files_uris.append(uri)
+        for uri in changed_files_uris:
+            del cls.caches_by_uri[uri]
+        return changed_files_uris
+
     @classmethod
     def get(cls, obj):
         """Gets a ThumbnailCache for the specified object.
@@ -1086,6 +1118,16 @@ class AudioPreviewer(Gtk.Layout, Previewer, Zoomable, Loggable):
         self._num_failures = 0
         self.become_controlled()
 
+    def refresh(self):
+        """Discards the audio samples so they are recreated."""
+        self.stop_generation()
+
+        self.samples = None
+        self.surface = None
+        self.queue_draw()
+
+        self.become_controlled()
+
     def _startLevelsDiscovery(self):
         filename = get_wavefile_location_for_uri(self._uri)
         if os.path.exists(filename):
diff --git a/pitivi/utils/proxy.py b/pitivi/utils/proxy.py
index f823380e..0eae303e 100644
--- a/pitivi/utils/proxy.py
+++ b/pitivi/utils/proxy.py
@@ -85,7 +85,7 @@ class ProxyManager(GObject.Object, Loggable):
         "error-preparing-asset": (GObject.SignalFlags.RUN_LAST, None, (object, object, object)),
     }
 
-    WHITELIST_CONTAINER_CAPS = ["video/quicktime", "application/ogg",
+    WHITELIST_CONTAINER_CAPS = ["video/quicktime", "application/ogg", "application/xges",
                                 "video/x-matroska", "video/webm"]
     WHITELIST_AUDIO_CAPS = ["audio/mpeg", "audio/x-vorbis",
                             "audio/x-raw", "audio/x-flac",


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