[gnome-music/wip/jfelder/playlist-export] playlistcontrols: Add a playlist export feature



commit 0d2561f7ea5abca0388473e87113ed844aa0a70b
Author: Jean Felder <jfelder src gnome org>
Date:   Wed Feb 26 20:33:02 2020 +0100

    playlistcontrols: Add a playlist export feature
    
    This allows to export a playlist to an m3u8 file. The export method is
    added to the Playlist object in order to create the m3u8 file content
    and save it.

 data/ui/PlaylistControls.ui                     |  4 +++
 gnomemusic/grilowrappers/grltrackerplaylists.py | 17 ++++++++++
 gnomemusic/widgets/playlistcontrols.py          | 42 +++++++++++++++++++++++--
 3 files changed, 61 insertions(+), 2 deletions(-)
---
diff --git a/data/ui/PlaylistControls.ui b/data/ui/PlaylistControls.ui
index d0db94b0..9591a69d 100644
--- a/data/ui/PlaylistControls.ui
+++ b/data/ui/PlaylistControls.ui
@@ -14,6 +14,10 @@
       <attribute name="label" translatable="yes">_Rename…</attribute>
       <attribute name="action">win.playlist_rename</attribute>
     </item>
+    <item>
+      <attribute name="label" translatable="yes">_Export…</attribute>
+      <attribute name="action">win.playlist_export</attribute>
+    </item>
   </menu>
   <object class="GtkImage" id="_view_more_image">
     <property name="visible">True</property>
diff --git a/gnomemusic/grilowrappers/grltrackerplaylists.py b/gnomemusic/grilowrappers/grltrackerplaylists.py
index f63da4af..357852ae 100644
--- a/gnomemusic/grilowrappers/grltrackerplaylists.py
+++ b/gnomemusic/grilowrappers/grltrackerplaylists.py
@@ -690,6 +690,23 @@ class Playlist(GObject.GObject):
                 query, GLib.PRIORITY_LOW, None, _position_changed_cb,
                 position)
 
+    def export(self, playlist_path):
+        """Save playlist to an m3u8 file.
+
+        :param str playlist_path: filename to write playlist content
+        """
+        song_content = ["#EXTINF:{},{} - {}\n{}\n".format(
+            song.props.duration, song.props.artist,
+            song.props.title, song.props.url)
+            for song in self.model]
+        pl_content = "#EXTM3U\n\n" + "\n".join(song_content)
+
+        try:
+            GLib.file_set_contents(playlist_path, pl_content.encode("utf-8"))
+        except GLib.Error as e:
+            self._log.warning(
+                "Unable to save playlist: {}, {}".format(e.domain, e.message))
+
 
 class SmartPlaylist(Playlist):
     """Base class for smart playlists"""
diff --git a/gnomemusic/widgets/playlistcontrols.py b/gnomemusic/widgets/playlistcontrols.py
index f3a9bb81..7a3eeccd 100644
--- a/gnomemusic/widgets/playlistcontrols.py
+++ b/gnomemusic/widgets/playlistcontrols.py
@@ -23,8 +23,9 @@
 # delete this exception statement from your version.
 
 import gettext
+from gettext import gettext as _
 
-from gi.repository import Gdk, GObject, Gio, Gtk
+from gi.repository import Gdk, GLib, GObject, Gio, Gtk
 
 from gnomemusic.grilowrappers.grltrackerplaylists import Playlist
 from gnomemusic.widgets.notificationspopup import PlaylistNotification
@@ -59,6 +60,9 @@ class PlaylistControls(Gtk.Grid):
         self._rename_action = Gio.SimpleAction.new("playlist_rename", None)
         self._rename_action.connect("activate", self._on_rename_action)
 
+        self._export_action = Gio.SimpleAction.new("playlist_export", None)
+        self._export_action.connect("activate", self._on_export_action)
+
     # FIXME: This is a workaround for not being able to pass the application
     # object via init when using Gtk.Builder.
     @GObject.Property(default=None)
@@ -87,6 +91,38 @@ class PlaylistControls(Gtk.Grid):
         self._window.add_action(self._delete_action)
         self._window.add_action(self._play_action)
         self._window.add_action(self._rename_action)
+        self._window.add_action(self._export_action)
+
+    def _on_export_action(self, action, parameter):
+        export_dialog = Gtk.FileChooserDialog(
+            _("Save Playlist"), self._window, Gtk.FileChooserAction.SAVE,
+            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+             Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT))
+        export_dialog.props.modal = True
+
+        pl_name = self.props.playlist.props.title
+        export_dialog.set_current_name("{}.m3u8".format(pl_name))
+        music_dir = GLib.get_user_special_dir(
+            GLib.UserDirectory.DIRECTORY_MUSIC)
+        export_dialog.set_current_folder_uri(GLib.filename_to_uri(music_dir))
+        export_dialog.set_do_overwrite_confirmation(True)
+
+        filter_pl = Gtk.FileFilter()
+        filter_pl.set_name(_("Playlists"))
+        filter_pl.add_mime_type("audio/x-mpegurl")
+        filter_pl.add_mime_type("audio/mpegurl")
+        export_dialog.add_filter(filter_pl)
+
+        filter_any = Gtk.FileFilter()
+        filter_any.set_name(_("Any files"))
+        filter_any.add_pattern("*")
+        export_dialog.add_filter(filter_any)
+
+        if export_dialog.run() == Gtk.ResponseType.ACCEPT:
+            pl_filename = export_dialog.get_filename()
+            self.props.playlist.export(pl_filename)
+
+        export_dialog.destroy()
 
     def _on_rename_action(self, menuitem, data=None):
         self._enable_rename_playlist(self.props.playlist)
@@ -129,7 +165,9 @@ class PlaylistControls(Gtk.Grid):
             "{} Song", "{} Songs", self.props.playlist.count).format(
                 self.props.playlist.count)
 
-        self._play_action.props.enabled = self.props.playlist.props.count > 0
+        not_empty = self.props.playlist.props.count > 0
+        self._play_action.props.enabled = not_empty
+        self._export_action.props.enabled = not_empty
 
     def _enable_rename_playlist(self, pl_torename):
         """Enables rename button and entry


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