diff --git a/data/com.github.needleandthread.vocal.gschema.xml b/data/com.github.needleandthread.vocal.gschema.xml index 69f28b3..7a3da90 100644 --- a/data/com.github.needleandthread.vocal.gschema.xml +++ b/data/com.github.needleandthread.vocal.gschema.xml @@ -67,18 +67,18 @@ - "~/vocal" + "~/.local/share/vocal" The directory where podcasts are stored The directory where podcasts are stored - - "" + + [] The podcast and episode that was last playing - The podcast and episode that was last played, in podcast_name,episode_title format + The podcast and episode that was last played. diff --git a/src/Controller.vala b/src/Controller.vala index d152523..501eda4 100644 --- a/src/Controller.vala +++ b/src/Controller.vala @@ -47,7 +47,7 @@ namespace Vocal { public bool first_run = true; public bool newly_launched = true; - public bool should_quit_immediately = true; + public bool should_quit_immediately = false; public bool plugins_are_installing = false; public bool checking_for_updates = false; public bool is_closing = false; @@ -63,7 +63,7 @@ namespace Vocal { /* References, pointers, and containers */ - public Episode current_episode; + private Episode current_episode; public Podcast highlighted_podcast; /* Miscellaneous global variables */ @@ -118,6 +118,9 @@ namespace Vocal { MPRIS mpris = new MPRIS (this); mpris.initialize (); + // Restore last played Episode after MPRIS has been initialized + mpris.initialized.connect (restore_episode); + // Connect the new player position available signal from the player // to set the new progress on the playback box @@ -368,6 +371,60 @@ namespace Vocal { } } + public void set_episode (Episode? e) { + if (current_episode != null) { + library.set_episode_playback_position (current_episode); + } + + current_episode = e; + if (current_episode != null) { + try { + player.set_episode (current_episode); + window.toolbar.playback_box.set_info_title (current_episode.title.replace ("%27", "'"), current_episode.parent.name.replace ("%27", "'")); + window.toolbar.playback_box.set_artwork_image_image (current_episode.parent.coverart_uri); + track_changed (current_episode.title, current_episode.parent.name, current_episode.parent.coverart_uri, (uint64) player.duration); + settings.last_played_media = {current_episode.guid, current_episode.link, current_episode.podcast_uri}; + window.artwork_popover.set_notes_text (current_episode.description); + } catch (Error e) { + warning (e.message); + } + + window.toolbar.show_playback_box (); + } else { + window.toolbar.hide_playback_box (); + } + } + + public Episode get_episode () { + return current_episode; + } + + private void restore_episode () { + if (settings.last_played_media != null && settings.last_played_media.length > 2) { + + info ("Restoring last played media."); + + // Split the media into two different strings + string[] fields = settings.last_played_media; + bool found = false; + foreach (Podcast podcast in library.podcasts) { + + if (!found) { + if (podcast.feed_uri == fields[2]) { + found = true; + + // Attempt to find the matching episode, set it as the current episode, and display the information in the box + foreach (Episode episode in podcast.episodes) { + if (episode.guid == fields[0] && episode.link == fields[1]) { + set_episode (episode); + } + } + } + } + } + } + } + /* * Playback related methods */ @@ -437,25 +494,6 @@ namespace Vocal { player.play (); playback_status_changed ("Playing"); - // Seek if necessary - if (current_episode.last_played_position > 0 && current_episode.last_played_position > player.get_position ()) { - - // If it's a streaming episode, seeking takes longer - // Temporarily pause the track and give it some time to seek - if (current_episode.current_download_status == DownloadStatus.NOT_DOWNLOADED) { - player.pause (); - } - - player.set_position (current_episode.last_played_position); - - // Pause for about a second to give time to catch up - if (current_episode.current_download_status == DownloadStatus.NOT_DOWNLOADED) { - player.pause (); - Thread.usleep (700000); - player.play (); - } - } - var playpause_image = new Gtk.Image.from_icon_name ("media-playback-pause-symbolic", Gtk.IconSize.LARGE_TOOLBAR); window.toolbar.set_play_pause_image (playpause_image); window.toolbar.set_play_pause_text (_ ("Pause")); @@ -551,13 +589,7 @@ namespace Vocal { } } - // Hide the shownotes button - window.toolbar.playback_box.hide_artwork_image (); - window.toolbar.playback_box.hide_volume_button (); - window.toolbar.hide_playlist_button (); - window.show_infobar (_ ("Adding new podcast: " + feed + ""), MessageType.INFO); - window.toolbar.show_playback_box (); var loop = new MainLoop (); bool success = false; @@ -573,6 +605,7 @@ namespace Vocal { }); loop.run (); + window.hide_infobar (); if (success) { @@ -594,8 +627,6 @@ namespace Vocal { gpodder_loop.run (); } - window.toolbar.show_playlist_button (); - if (!player.playing) window.toolbar.hide_playback_box (); diff --git a/src/Library.vala b/src/Library.vala index 848d24e..21da2ba 100644 --- a/src/Library.vala +++ b/src/Library.vala @@ -100,7 +100,7 @@ namespace Vocal { local_library_path = settings.library_location.replace ("~", GLib.Environment.get_home_dir ()); #if HAVE_LIBUNITY - launcher = Unity.LauncherEntry.get_for_desktop_id ("vocal.desktop"); + launcher = Unity.LauncherEntry.get_for_desktop_id ("com.github.needleandthread.vocal.desktop"); launcher.count = new_episode_count; #endif @@ -717,8 +717,12 @@ namespace Vocal { foreach (Podcast p in podcasts) { output_line = - """ - """.printf (p.name.replace ("\"", "'").replace ("&", "and"), p.feed_uri); +""" + + rss + + +""".printf (p.name, p.feed_uri); stream.output_stream.write (output_line.data); } @@ -905,7 +909,7 @@ namespace Vocal { FROM Episode e LEFT JOIN Podcast p on p.feed_uri = e.podcast_uri WHERE podcast_uri = '%s' - ORDER BY e.rowid ASC".printf (podcast.feed_uri); + ORDER BY e.released ASC".printf (podcast.feed_uri); ec = db.prepare_v2 (prepared_query_str, prepared_query_str.length, out stmt); if (ec != Sqlite.OK) { warning ("Error: %d: %s\n", db.errcode (), db.errmsg ()); @@ -1227,7 +1231,7 @@ namespace Vocal { if (settings.library_location == null) { settings.library_location = GLib.Environment.get_user_data_dir () + """/vocal"""; } - local_library_path = settings.library_location.replace ("~", GLib.Environment.get_user_data_dir ()); + local_library_path = settings.library_location.replace ("~", GLib.Environment.get_home_dir ()); // If the new local_library_path has been modified, update the setting if (settings.library_location != local_library_path) { diff --git a/src/MainWindow.vala b/src/MainWindow.vala index f45c85c..7879bd0 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -331,7 +331,11 @@ namespace Vocal { }); new_episodes_view.add_all_new_to_queue.connect ((episodes) => { foreach (Episode e in episodes) { - enqueue_episode (e); + if (controller.get_episode () == null) { + controller.set_episode (e); + } else { + enqueue_episode (e); + } } }); @@ -348,6 +352,7 @@ namespace Vocal { all_flowbox = new Gtk.FlowBox (); all_art = new Gee.ArrayList (); all_flowbox.get_style_context ().add_class ("notebook-art"); + all_flowbox.max_children_per_line = 20; all_flowbox.selection_mode = Gtk.SelectionMode.SINGLE; all_flowbox.activate_on_single_click = true; all_flowbox.child_activated.connect (on_child_activated); @@ -466,7 +471,6 @@ namespace Vocal { toolbar.play_pause_selected.connect (controller.play_pause); toolbar.seek_forward_selected.connect (controller.seek_forward); toolbar.seek_backward_selected.connect (controller.seek_backward); - toolbar.playlist_button.clicked.connect (() => { artwork_popover.queue_box.show_all (); }); toolbar.store_selected.connect (() => { details.pane_should_hide (); @@ -620,58 +624,6 @@ namespace Vocal { all_art.clear (); } - //TODO: Move this to the controller - - info ("Restoring last played media."); - // If the program was just launched, check to see what the last played media was - if (controller.newly_launched) { - - current_widget = all_scrolled; - - if (controller.settings.last_played_media != null && controller.settings.last_played_media.length > 1) { - - // Split the media into two different strings - string[] fields = controller.settings.last_played_media.split (","); - bool found = false; - foreach (Podcast podcast in controller.library.podcasts) { - - if (!found) { - if (podcast.name == fields[1]) { - found = true; - - // Attempt to find the matching episode, set it as the current episode, and display the information in the box - foreach (Episode episode in podcast.episodes) { - if (episode.title == fields[0]) { - controller.current_episode = episode; - toolbar.playback_box.set_info_title (controller.current_episode.title.replace ("%27", "'"), controller.current_episode.parent.name.replace ("%27", "'")); - toolbar.playback_box.set_artwork_image_image (controller.current_episode.parent.coverart_uri); - controller.track_changed (controller.current_episode.title, controller.current_episode.parent.name, controller.current_episode.parent.coverart_uri, (uint64) controller.player.duration); - - try { - - controller.player.set_episode (controller.current_episode); - controller.player.set_position (controller.current_episode.last_played_position); - artwork_popover.set_notes_text (episode.description); - - } catch (Error e) { - warning (e.message); - } - - if (controller.current_episode.last_played_position != 0) { - toolbar.show_playback_box (); - } - else { - toolbar.hide_playback_box (); - } - } - } - } - } - } - } - } - - // Refill the controller.library based on what is stored in the database (if it's not newly launched, in // which case it has already been filled) if (!controller.newly_launched) { @@ -773,16 +725,11 @@ namespace Vocal { */ private void play_episode_from_queue_immediately (Episode e) { - controller.current_episode = e; - artwork_popover.queue_box.hide (); + controller.set_episode (e); + artwork_popover.hide (); controller.library.remove_episode_from_queue (e); controller.play (); - - // Set the shownotes, the media information, and update the last played media in the settings - controller.track_changed (controller.current_episode.title, controller.current_episode.parent.name, controller.current_episode.parent.coverart_uri, (uint64)controller.player.duration); - artwork_popover.set_notes_text (controller.current_episode.description); - controller.settings.last_played_media = "%s,%s".printf (controller.current_episode.title, controller.current_episode.parent.name); } /* @@ -792,19 +739,13 @@ namespace Vocal { // Get the episode if (episode == null) { - controller.current_episode = details.current_episode; + controller.set_episode (details.current_episode); } else { - controller.current_episode = episode; + controller.set_episode (episode); } controller.player.pause (); controller.play (); - - // Set the shownotes, the media information, and update the last played media in the settings - controller.track_changed (controller.current_episode.title, controller.current_episode.parent.name, controller.current_episode.parent.coverart_uri, (uint64) controller.player.duration); - toolbar.playback_box.set_artwork_image_image (controller.current_episode.parent.coverart_uri); - artwork_popover.set_notes_text (controller.current_episode.description); - controller.settings.last_played_media = "%s,%s".printf (controller.current_episode.title, controller.current_episode.parent.name); } /* @@ -964,13 +905,6 @@ namespace Vocal { //If the user selects a file, get the name and parse it if (decision == Gtk.ResponseType.ACCEPT || run_pending_import == true) { - toolbar.show_playback_box (); - - // Hide the shownotes button - toolbar.playback_box.hide_artwork_image (); - toolbar.playback_box.hide_volume_button (); - toolbar.hide_playlist_button (); - if (current_widget == welcome) { switch_visible_page (import_message_box); } @@ -1012,10 +946,6 @@ namespace Vocal { // Make the refresh and export items sensitive now toolbar.export_item.sensitive = true; - toolbar.playback_box.show_artwork_image (); - toolbar.playback_box.show_volume_button (); - toolbar.show_playlist_button (); - if (current_widget == import_message_box) { switch_visible_page (all_scrolled); } @@ -1025,9 +955,9 @@ namespace Vocal { controller.currently_importing = false; if (controller.player.playing) { - toolbar.playback_box.set_info_title (controller.current_episode.title.replace ("%27", "'"), controller.current_episode.parent.name.replace ("%27", "'")); - toolbar.playback_box.set_artwork_image_image (controller.current_episode.parent.coverart_uri); - video_controls.set_info_title (controller.current_episode.title.replace ("%27", "'"), controller.current_episode.parent.name.replace ("%27", "'")); + toolbar.playback_box.set_info_title (controller.get_episode ().title.replace ("%27", "'"), controller.get_episode ().parent.name.replace ("%27", "'")); + toolbar.playback_box.set_artwork_image_image (controller.get_episode ().parent.coverart_uri); + video_controls.set_info_title (controller.get_episode ().title.replace ("%27", "'"), controller.get_episode ().parent.name.replace ("%27", "'")); } loop.quit (); @@ -1066,8 +996,13 @@ namespace Vocal { */ public void switch_visible_page (Gtk.Widget widget) { - if (current_widget != widget) - previous_widget = current_widget; + if (current_widget != widget) { + if (current_widget == welcome) { + previous_widget = all_scrolled; + } else { + previous_widget = current_widget; + } + } if (widget == all_scrolled) { notebook.set_visible_child (all_scrolled); @@ -1141,7 +1076,7 @@ namespace Vocal { info ("GStreamer registry updated, attempting to start playback using the new plugins..."); // Reset the controller.player - controller.player.current_episode = null; + controller.set_episode (null); controller.play (); } @@ -1237,7 +1172,7 @@ namespace Vocal { details.on_single_delete(episode); // Update gpodder.net - controller.gpodder_client.update_episode (controller.current_episode, EpisodeAction.DELETE); + controller.gpodder_client.update_episode (controller.get_episode (), EpisodeAction.DELETE); } @@ -1639,7 +1574,7 @@ namespace Vocal { toolbar.set_play_pause_image (playpause_image); // If there is a video showing, return to the controller.library view - if (controller.current_episode.parent.content_type == MediaType.VIDEO) { + if (controller.get_episode ().parent.content_type == MediaType.VIDEO) { on_return_to_library (); } @@ -1648,18 +1583,13 @@ namespace Vocal { controller.playback_status_changed ("Stopped"); - controller.current_episode = controller.library.get_next_episode_in_queue (); - - if (controller.current_episode != null) { + controller.set_episode (controller.library.get_next_episode_in_queue ()); + if (controller.get_episode () != null) { controller.play (); - - // Set the shownotes, the media information, and update the last played media in the settings - controller.track_changed (controller.current_episode.title, controller.current_episode.parent.name, controller.current_episode.parent.coverart_uri, (uint64) controller.player.duration); - artwork_popover.set_notes_text (controller.current_episode.description); - controller.settings.last_played_media = "%s,%s".printf (controller.current_episode.title, controller.current_episode.parent.name); } else { controller.player.playing = false; + controller.settings.last_played_media = null; } // Regenerate the new episode list in case the ended episode was one of the new episodes @@ -1753,9 +1683,6 @@ namespace Vocal { // Show the store if (index == 0) { switch_visible_page (directory_scrolled); - - // Set the controller.library as the previous widget for return_to_library to work - previous_widget = all_scrolled; } // Add a new feed @@ -1810,7 +1737,7 @@ namespace Vocal { } // Update gpodder.net if necessary - controller.gpodder_client.update_episode (controller.current_episode, EpisodeAction.PLAY); + controller.gpodder_client.update_episode (controller.get_episode (), EpisodeAction.PLAY); // If an episode is currently playing and Vocal is set to keep playing in the background, hide the window if (controller.player.playing && controller.settings.keep_playing_in_background) { @@ -1818,7 +1745,7 @@ namespace Vocal { return true; } else if (downloads != null && downloads.downloads.size > 0) { //If there are downloads verify that the user wishes to exit and cancel the downloads - var downloads_active_dialog = new Gtk.MessageDialog (this, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, _ ("Vocal is currently downloading episodes. Exiting will cause the downloads to be canceled. Are you sure you want to exit?")); + var downloads_active_dialog = new Gtk.MessageDialog (this, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, _ ("Vocal is currently downloading episodes. Exiting will cause the downloads to be cancelled. Are you sure you want to exit?")); downloads_active_dialog.response.connect ((response_id) => { downloads_active_dialog.destroy (); if (response_id == Gtk.ResponseType.YES) { diff --git a/src/Objects/Episode.vala b/src/Objects/Episode.vala index efa95a0..0771536 100644 --- a/src/Objects/Episode.vala +++ b/src/Objects/Episode.vala @@ -124,9 +124,11 @@ namespace Vocal { // states that datetimes should be in RFC 822 format, // but date strings with different formats can cause a crash. string[] time_formats = { - "%a, %d %b %Y %H:%M:%S %Z", // Sunday, 01 Jan 1970 00:00:00 GMT - "%a, %b %d %Y %H:%M:%S %Z" // Sunday, Jan 01 1970 00:00:00 GMT + "%d %b %Y %H:%M:%S %Z", // 01 Jan 1970 00:00:00 GMT + "%b %d %Y %H:%M:%S %Z" // Jan 01 1970 00:00:00 GMT }; + // GLib.Time.strptime doesn't accept strings containing the weekday (just returns NULL instead) + date_released = date_released[5:date_released.length]; foreach (string format in time_formats) { var last_parsed_char = tm.strptime (date_released, format); if (last_parsed_char != null) { diff --git a/src/Utils/FeedParser.vala b/src/Utils/FeedParser.vala index c6eeeaa..513496a 100644 --- a/src/Utils/FeedParser.vala +++ b/src/Utils/FeedParser.vala @@ -132,6 +132,7 @@ namespace Vocal { Episode episode = new Episode (); string next_item_in_queue = null; bool found_summary = false; + bool found_media = false; while (next_item_in_queue != "item" && i < queue.size - 1) { i++; @@ -162,6 +163,11 @@ namespace Vocal { i++; string typestring = queue[i].slice (0, 5); + + if (typestring == "audio" || typestring == "video") { + found_media = true; + } + if (podcast.content_type == MediaType.UNKNOWN) { if (typestring == "audio") { podcast.content_type = MediaType.AUDIO; @@ -169,10 +175,6 @@ namespace Vocal { else if (typestring == "video") { podcast.content_type = MediaType.VIDEO; } - else { - podcast.content_type = MediaType.UNKNOWN; - } - } type_found = true; @@ -209,7 +211,9 @@ namespace Vocal { // Add the new episode to the podcast - podcast.add_episode (episode); + if (found_media) { + podcast.add_episode (episode); + } } @@ -349,6 +353,11 @@ namespace Vocal { podcast = create_podcast_from_queue (); } + if (podcast.content_type == MediaType.UNKNOWN) { + warning ("Feed doesn't contain any media files. Abort."); + return null; + } + if (podcast.name.length < 1) { warning ("Something went wrong during podcast parsing. Abort."); return null; @@ -552,6 +561,7 @@ namespace Vocal { Episode episode = new Episode (); string next_item_in_queue = null; bool found_summary = false; + bool found_media = false; while (next_item_in_queue != "item" && i < queue.size - 1) { @@ -582,6 +592,11 @@ namespace Vocal { i++; string typestring = queue[i].slice (0, 5); + + if (typestring == "audio" || typestring == "video") { + found_media = true; + } + if (podcast.content_type == MediaType.UNKNOWN) { if (typestring == "audio") { podcast.content_type = MediaType.AUDIO; @@ -589,10 +604,6 @@ namespace Vocal { else if (typestring == "video") { podcast.content_type = MediaType.VIDEO; } - else { - podcast.content_type = MediaType.UNKNOWN; - } - } type_found = true; @@ -628,17 +639,19 @@ namespace Vocal { } - episode.parent = podcast; - episode.podcast_uri = podcast.feed_uri; + if (found_media) { + episode.parent = podcast; + episode.podcast_uri = podcast.feed_uri; - if (previous_newest_episode != null) { - if (episode.title == previous_newest_episode.title.replace ("%27", "'")) { - previous_found = true; + if (previous_newest_episode != null) { + if (episode.title == previous_newest_episode.title.replace ("%27", "'")) { + previous_found = true; + } else { + new_episodes.add (episode); + } } else { new_episodes.add (episode); } - } else { - new_episodes.add (episode); } } @@ -687,6 +700,7 @@ namespace Vocal { /* Creating a Episode with values from tag. */ Episode entry = new Episode (); + bool found_media = false; for (Xml.Node* iterEntry = iter->children; iterEntry != null; iterEntry = iterEntry->next) { switch (iterEntry->name) { @@ -709,12 +723,14 @@ namespace Vocal { entry.uri=propEntry->children->content; entry.link = entry.uri; } else if (attr_name == "type" && podcast != null) { - podcast.content_type = MediaType.UNKNOWN; - if (propEntry->children->content.contains ("audio/")) { podcast.content_type = MediaType.AUDIO; + found_media = true; } else if (propEntry->children->content.contains ("video/")) { podcast.content_type = MediaType.VIDEO; + found_media = true; + } else { + podcast.content_type = MediaType.UNKNOWN; } } } @@ -727,10 +743,12 @@ namespace Vocal { } } - entry.parent=podcast; - entry.podcast_uri = podcast.feed_uri; + if (found_media) { + entry.parent=podcast; + entry.podcast_uri = podcast.feed_uri; - episodes.add (entry); + episodes.add (entry); + } } for (int i=episodes.size; i > 0 && !previous_found; i--) { diff --git a/src/Utils/MPRIS.vala b/src/Utils/MPRIS.vala index 8978433..2b10ce5 100644 --- a/src/Utils/MPRIS.vala +++ b/src/Utils/MPRIS.vala @@ -40,6 +40,8 @@ namespace Vocal { private unowned DBusConnection conn; private uint owner_id; + public signal void initialized (); + /* * Default constructor that simply sets the controller.window */ @@ -109,6 +111,7 @@ namespace Vocal { }); connection.register_object ("/org/mpris/MediaPlayer2", player); + initialized (); } @@ -146,7 +149,7 @@ namespace Vocal { private const string INTERFACE_NAME = "org.mpris.MediaPlayer2.Player"; - const string TRACK_ID = "/com/github/needleandthread/vocal/Track/%d"; + const string TRACK_ID = "/com/github/needleandthread/vocal/Track/%u"; public MprisPlayer (DBusConnection conn) { this.conn = conn; diff --git a/src/Utils/Player.vala b/src/Utils/Player.vala index 9bf9659..9fd70b7 100644 --- a/src/Utils/Player.vala +++ b/src/Utils/Player.vala @@ -40,22 +40,21 @@ namespace Vocal { private string tag_string; public Episode current_episode; + private bool restore_position; private Player (string[]? args) { - bool new_launch = true; - current_episode = null; // Check every half-second if the current media is playing, and if it is // send a signal that there is a new position available GLib.Timeout.add (500, () => { - - if (playing) + if (playing && duration > 0.0) { + if (restore_position) { + set_position (current_episode.last_played_position); + restore_position = false; + } new_position_available (); - if (new_launch && duration > 0.0) { - new_position_available (); - new_launch = false; } return true; }); @@ -107,6 +106,10 @@ namespace Vocal { // Set the URI this.uri = episode.playback_uri; info ("Setting playback URI: %s".printf (episode.playback_uri)); + + if (current_episode.last_played_position > 0) { + restore_position = true; + } /* // If it's a video podcast, get the width and height and configure that information @@ -145,11 +148,13 @@ namespace Vocal { /* * Sets the currently playing media position, in seconds */ - public void set_position (int seconds) { - double calculated_progress = (double)seconds / get_duration (); - set_progress (calculated_progress); - new_position_available (); - } + public void set_position (int seconds) { + if (duration > 0.0) { + double calculated_progress = (double)seconds / duration; + set_progress (calculated_progress); + new_position_available (); + } + } /* diff --git a/src/Utils/Utils.vala b/src/Utils/Utils.vala index 6ac202d..f45040c 100644 --- a/src/Utils/Utils.vala +++ b/src/Utils/Utils.vala @@ -95,6 +95,8 @@ public class Utils { markup = original; } + markup = markup.replace (" ", " "); + markup = markup.replace ("'", "'"); markup = markup.replace ("&", "&"); // Simplify (keep only href attribute) & preserve anchor tags. diff --git a/src/Utils/gpodderClient.vala b/src/Utils/gpodderClient.vala index 56cccf4..a6e7962 100644 --- a/src/Utils/gpodderClient.vala +++ b/src/Utils/gpodderClient.vala @@ -483,6 +483,10 @@ namespace Vocal { } public bool update_episode (Episode episode, EpisodeAction action) { + + if (controller.settings.gpodder_username == "") { + return false; + } var session = new Soup.Session (); session.user_agent = "vocal"; diff --git a/src/VocalSettings.vala b/src/VocalSettings.vala index 5578102..bfc9d6b 100644 --- a/src/VocalSettings.vala +++ b/src/VocalSettings.vala @@ -39,7 +39,7 @@ public class VocalSettings : Granite.Services.Settings { public int rewind_seconds { get; set;} public string library_location { get; set; } - public string last_played_media { get; set; } + public string[] last_played_media { get; set; } public string itunes_store_country { get; set; } public string archive_access_key { get; set; } public string archive_secret_key { get; set; } diff --git a/src/Widgets/DirectoryView.vala b/src/Widgets/DirectoryView.vala index 9f93ea3..8a45228 100644 --- a/src/Widgets/DirectoryView.vala +++ b/src/Widgets/DirectoryView.vala @@ -33,6 +33,8 @@ namespace Vocal { private Gtk.Button first_run_continue_button; private Gtk.Box loading_box; + private Gtk.Label itunes_title; + private Gtk.Label loading_label; private Gtk.ScrolledWindow scrolled_window; @@ -48,7 +50,7 @@ namespace Vocal { banner_box.get_style_context ().add_class ("toolbar"); banner_box.get_style_context ().add_class ("library-toolbar"); - var itunes_title = new Gtk.Label (_ ("iTunes Top 100 Podcasts")); + itunes_title = new Gtk.Label (_ ("iTunes Top 100 Podcasts")); itunes_title.margin_top = 15; itunes_title.margin_bottom = 5; itunes_title.justify = Gtk.Justification.CENTER; @@ -101,7 +103,7 @@ namespace Vocal { loading_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); var spinner = new Gtk.Spinner (); spinner.active = true; - var loading_label = new Gtk.Label (_ ("Loading iTunes Store")); + loading_label = new Gtk.Label (_("Loading iTunes Store (%d / 100)".printf (0))); loading_label.get_style_context ().add_class ("h2"); loading_box.add (loading_label); loading_box.add (spinner); @@ -131,6 +133,9 @@ namespace Vocal { int i = 1; if (entries == null) { info ("iterating over entries"); + itunes_title.set_text (_("Error when loading iTunes Top 100 Podcasts")); + loading_box.set_no_show_all (true); + loading_box.hide (); return null; } @@ -148,6 +153,7 @@ namespace Vocal { on_new_subscription (url); }); flowbox.add (directory_art); + loading_label.set_text (_("Loading iTunes Store (%d / 100)".printf (i))); i++; } diff --git a/src/Widgets/PodcastView.vala b/src/Widgets/PodcastView.vala index cc90ae9..76330ed 100644 --- a/src/Widgets/PodcastView.vala +++ b/src/Widgets/PodcastView.vala @@ -619,7 +619,7 @@ namespace Vocal { } name_label.set_text (podcast.name.replace ("%27", "'")); - description_label.set_text (podcast.description.replace ("""\n""", "")); + description_label.set_text (Utils.html_to_markup (podcast.description)); reset_episode_list (); populate_episodes (); @@ -663,7 +663,7 @@ namespace Vocal { private void on_row_selected () { GLib.List rows = listbox.get_selected_rows (); - if (rows.length () < 1) { + if (rows.length () != 1) { return; } @@ -721,7 +721,9 @@ namespace Vocal { private void reset_episode_list () { foreach (var item in listbox.get_children ()) { - listbox.remove (item); + ListBoxRow row = (ListBoxRow) item; + row.selectable = false; + listbox.remove (row); } boxes.clear (); diff --git a/src/Widgets/SearchResultsView.vala b/src/Widgets/SearchResultsView.vala index c08650a..e788fec 100644 --- a/src/Widgets/SearchResultsView.vala +++ b/src/Widgets/SearchResultsView.vala @@ -143,9 +143,7 @@ namespace Vocal { local_episodes_listbox.button_press_event.connect (on_episode_activated); local_podcasts_listbox.button_press_event.connect (on_podcast_activated); - local_episodes_listbox.expand = true; - local_podcasts_listbox.expand = true; - cloud_results_flowbox.expand = true; + content_box.expand = true; local_episodes_widgets = new Gee.ArrayList (); local_podcasts_widgets = new Gee.ArrayList (); diff --git a/src/Widgets/Toolbar.vala b/src/Widgets/Toolbar.vala index 59bc671..5ea084c 100644 --- a/src/Widgets/Toolbar.vala +++ b/src/Widgets/Toolbar.vala @@ -54,7 +54,6 @@ namespace Vocal { public Gtk.Button search_button; private Gtk.Button podcast_store_button; - public Gtk.Button playlist_button; public Gtk.Button new_episodes_button; public Gtk.MenuItem export_item; @@ -73,14 +72,6 @@ namespace Vocal { // Set the playback box in the middle of the HeaderBar playback_box.hexpand = true; - playlist_button = new Gtk.Button.from_icon_name ("media-playlist-consecutive-symbolic"); - playlist_button.tooltip_text = _ ("Coming up next"); - playlist_button.clicked.connect (() => { - playlist_selected (); - }); - playlist_button.relief = Gtk.ReliefStyle.NONE; - playlist_button.valign = Gtk.Align.CENTER; - if (on_elementary) { new_episodes_button = new Gtk.Button.from_icon_name ("help-about-symbolic", Gtk.IconSize.LARGE_TOOLBAR); new_episodes_button.relief = Gtk.ReliefStyle.NONE; @@ -375,8 +366,6 @@ namespace Vocal { right_button_box.pack_end (new_episodes_button); right_button_box.halign = Gtk.Align.END; - this.spacing = 0; - this.pack_start (left_button_box); this.set_custom_title (playback_box); this.pack_end (right_button_box); @@ -398,7 +387,6 @@ namespace Vocal { } } - public void show_playback_box () { if (playback_box != null) { this.playback_box.no_show_all = false; @@ -407,22 +395,6 @@ namespace Vocal { } } - - - public void show_playlist_button () { - if (playlist_button != null) { - playlist_button.set_no_show_all (false); - playlist_button.show (); - } - } - - public void hide_playlist_button () { - if (playlist_button != null) { - playlist_button.set_no_show_all (true); - playlist_button.hide (); - } - } - public void hide_download_button () { if (download != null) { this.download.set_no_show_all (true);