Hello community, here is the log from the commit of package vocal for openSUSE:Factory checked in at 2018-01-09 14:53:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/vocal (Old) and /work/SRC/openSUSE:Factory/.vocal.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "vocal" Tue Jan 9 14:53:01 2018 rev:2 rq:562200 version:2.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/vocal/vocal.changes 2017-07-21 22:46:00.457434235 +0200 +++ /work/SRC/openSUSE:Factory/.vocal.new/vocal.changes 2018-01-09 14:53:06.124058527 +0100 @@ -1,0 +2,25 @@ +Sun Dec 31 15:08:03 UTC 2017 - [email protected] + +- Update to version 2.1.0: + * Added HiDPI support for all images so everything looks nice + and sharp no matter how fancy your display is. + * Fixed an issue where Vocal would not show you the time elapsed + or remaining in an episode immediately after relaunching the + app. + * Reduced the animation time for switching between different + views so that it feels extra snappy. + * Gave the search results view a shave and a haircut. Two bits. + * Fixed a few issues where Vocal would be grouchy about showing + certain episode descriptions. + * When importing several podcasts the progress bar now stays one + size so you can actually judge the progress correctly. + * You can now drag and drop items in the play queue to rearrange + their order. + * Lots more checking to make sure that things exist so it won't + * crash nearly as much (hopefully never). +- Appdata moved to %{_datadir}/metainfo/; adapt file list + accordingly. +- Only run post scripts for openSUSE <= 1320; for newer versions + rpm triggers take care of running these scripts automatically. + +------------------------------------------------------------------- Old: ---- 2.0.20.tar.gz New: ---- 2.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ vocal.spec ++++++ --- /var/tmp/diff_new_pack.LTSTOd/_old 2018-01-09 14:53:06.944020087 +0100 +++ /var/tmp/diff_new_pack.LTSTOd/_new 2018-01-09 14:53:06.948019899 +0100 @@ -17,7 +17,7 @@ Name: vocal -Version: 2.0.20 +Version: 2.1.0 Release: 0 Summary: A podcast client for the desktop License: GPL-3.0 @@ -81,6 +81,7 @@ %suse_update_desktop_file com.github.needle-and-thread.vocal Player %fdupes %{buildroot}%{_datadir}/locale/ +%if 0%{?suse_version} <= 1320 %post %glib2_gsettings_schema_post %icon_theme_cache_post @@ -90,13 +91,14 @@ %glib2_gsettings_schema_postun %icon_theme_cache_postun %desktop_database_postun +%endif %files %defattr(-,root,root) %doc %attr(0644,root,root) AUTHORS COPYING README.md %{_bindir}/%{name} -%dir %{_datadir}/appdata -%{_datadir}/appdata/*.appdata.xml +%dir %{_datadir}/metainfo +%{_datadir}/metainfo/*.appdata.xml %{_datadir}/applications/com.github.needle-and-thread.vocal.desktop %{_datadir}/glib-2.0/schemas/*.xml %{_datadir}/icons/hicolor/*/apps/* ++++++ 2.0.20.tar.gz -> 2.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/CMakeLists.txt new/vocal-2.1.0/CMakeLists.txt --- old/vocal-2.0.20/CMakeLists.txt 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/CMakeLists.txt 2017-12-30 18:24:57.000000000 +0100 @@ -5,8 +5,8 @@ set (DATADIR "${CMAKE_INSTALL_PREFIX}/share") set (PKGDATADIR "${DATADIR}/vocal") set (GETTEXT_PACKAGE "vocal") -set (RELEASE_NAME "Landingham") -set (VERSION "2.0.1") +set (RELEASE_NAME "Gomer") +set (VERSION "2.1.0") set (VERSION_INFO "Release") set (EXEC_NAME "vocal") set (PREFIX ${CMAKE_INSTALL_PREFIX}) @@ -163,7 +163,7 @@ configure_file_translation (${CMAKE_CURRENT_SOURCE_DIR}/data/com.github.needle-and-thread.vocal.appdata.xml.in ${CMAKE_CURRENT_BINARY_DIR}/com.github.needle-and-thread.vocal.appdata.xml ${CMAKE_SOURCE_DIR}/po/) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/com.github.needle-and-thread.vocal.desktop DESTINATION share/applications) -install (FILES ${CMAKE_CURRENT_BINARY_DIR}/com.github.needle-and-thread.vocal.appdata.xml DESTINATION share/appdata) +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/com.github.needle-and-thread.vocal.appdata.xml DESTINATION share/metainfo) install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/icons/16/com.github.needle-and-thread.vocal.svg DESTINATION share/icons/hicolor/16x16/apps) install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/icons/22/com.github.needle-and-thread.vocal.svg DESTINATION share/icons/hicolor/22x22/apps) install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/icons/24/com.github.needle-and-thread.vocal.svg DESTINATION share/icons/hicolor/24x24/apps) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/CODE_OF_CONDUCT.md new/vocal-2.1.0/CODE_OF_CONDUCT.md --- old/vocal-2.0.20/CODE_OF_CONDUCT.md 1970-01-01 01:00:00.000000000 +0100 +++ new/vocal-2.1.0/CODE_OF_CONDUCT.md 2017-12-30 18:24:57.000000000 +0100 @@ -0,0 +1,45 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, no matter what. Period. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission (doxxing) +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [email protected]. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/README.md new/vocal-2.1.0/README.md --- old/vocal-2.0.20/README.md 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/README.md 2017-12-30 18:24:57.000000000 +0100 @@ -12,10 +12,9 @@ Vocal is available on elementary OS via download directly from AppCenter ### Flatpak -Vocal can be installed via Flatpak on most modern Linux systems via the following commands: +Vocal can be installed via Flatpak on most modern Linux systems via the following command: - flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - flatpak install flathub com.github.needleandthread.vocal + flatpak install --from https://flathub.org/repo/appstream/com.github.needleandthread.vocal.flatpakref ### Snap An official snap package is not yet available, although some community members are working on it. @@ -24,7 +23,10 @@ 64-bit .deb installers can be downloaded from our [releases](https://github.com/needle-and-thread/vocal/releases) page. Please note: this method is highly discouraged. With the benefits of sandboxing, improved security, and greater simplicity, we are moving away from older installation methods. **Also, please note that our stable and daily PPAs will be abandoned in the near future.** ### Official OS packages -We're working with a number of distributions to provide official packages as part of the OS. Stay tuned for more information. +We're working with a number of distributions to make sure it's as simple as possible to get Vocal out-of-the-box. Check below for the list of distributions where Vocal can be downloaded from the official repositories. + +* [Fedora](https://admin.fedoraproject.org/pkgdb/package/rpms/vocal/) +* [openSUSE](https://software.opensuse.org//download.html?project=multimedia%3Aapps&package=vocal) ## Get Involved diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/data/com.github.needle-and-thread.vocal.appdata.xml.in new/vocal-2.1.0/data/com.github.needle-and-thread.vocal.appdata.xml.in --- old/vocal-2.0.20/data/com.github.needle-and-thread.vocal.appdata.xml.in 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/data/com.github.needle-and-thread.vocal.appdata.xml.in 2017-12-30 18:24:57.000000000 +0100 @@ -11,7 +11,15 @@ <screenshots> <screenshot type="default"> <_caption>Library View</_caption> - <image width="1464" height="918">http://needleandthread.co/apps/vocal/screenshots/vocal_library.png</image> + <image width="2440" height="1628">http://vocalproject.net/screenshots/libraryview.png</image> + </screenshot> + <screenshot type="default"> + <_caption>Podcast View</_caption> + <image width="2440" height="1628">http://vocalproject.net/screenshots/podcastview.png</image> + </screenshot> + <screenshot type="default"> + <_caption>iTunes Top 100 Podcasts</_caption> + <image width="2440" height="1628">http://vocalproject.net/screenshots/itunes.png</image> </screenshot> </screenshots> @@ -49,6 +57,40 @@ </provides> <releases> + <release version="2.1" timestamp="1514653432"> + <description> + <_p>Grab some hot chocolate and curl up with a nice podcast this winter + season, it's time for a new Vocal release! This time around, enjoy + a ton of stability and performance enhancements, full HiDPI support, + and other bug fixes to make your experience as enjoyable as possible. + </_p> + <ul> + <_li>Added HiDPI support for all images so everything looks nice and + sharp no matter how fancy your display is. + </_li> + <_li>Fixed an issue where Vocal would not show you the time elapsed + or remaining in an episode immediately after relaunching the app. + </_li> + <_li>Reduced the animation time for switching between different + views so that it feels extra snappy. + </_li> + <_li>Gave the search results view a shave and a haircut. Two bits. + </_li> + <_li>Fixed a few issues where Vocal would be grouchy about + showing certain episode descriptions. + </_li> + <_li>When importing several podcasts the progress bar now stays + one size so you can actually judge the progress correctly. + </_li> + <_li>You can now drag and drop items in the play queue to rearrange + their order. + </_li> + <_li>Lots more checking to make sure that things exist so it won't + crash nearly as much (hopefully never). + </_li> + </ul> + </description> + </release> <release version="2.0" timestamp="1493395200"> <description> <_p>Vocal's most exciting release yet, featuring full iTunes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Library.vala new/vocal-2.1.0/src/Library.vala --- old/vocal-2.0.20/src/Library.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Library.vala 2017-12-30 18:24:57.000000000 +0100 @@ -27,7 +27,7 @@ public errordomain VocalLibraryError { - ADD_ERROR, IMPORT_ERROR; + ADD_ERROR, IMPORT_ERROR, MISSING_URI; } @@ -413,9 +413,13 @@ ThreadFunc<void*> run = () => { foreach(Podcast podcast in podcasts) { try { - int added = parser.update_feed(podcast); + + int added = -1; + if (podcast.feed_uri != null && podcast.feed_uri.length > 4) { + added = parser.update_feed(podcast); + } - while(added != 0) { + while(added > 0) { int index = podcast.episodes.size - added; // Add the new episode to the arraylist in case it needs to be downloaded later @@ -425,11 +429,15 @@ write_episode_to_database(podcast.episodes[index]); added--; } + + if (added == -1) { + critical ("Unable to update podcast due to missing feed URL: " + podcast.name); + } } catch(Error e) { throw e; } - + } Idle.add((owned) callback); @@ -508,16 +516,10 @@ // Set the path of the new file and create another object for the local file try { - - GLib.File test_cover = GLib.File.new_for_uri(episode.parent.coverart_uri); - - InputStream input_stream = test_cover.read(); - var pixbuf = new Gdk.Pixbuf.from_stream_at_scale(input_stream, 64, 64, true); - string path = library_location + "/%s/%s".printf(episode.parent.name.replace("%27", "'").replace("%", "_"), remote_file.get_basename()); GLib.File local_file = GLib.File.new_for_path(path); - detail_box = new DownloadDetailBox(episode, pixbuf); + detail_box = new DownloadDetailBox(episode); detail_box.download_has_completed_successfully.connect(on_successful_download); FileProgressCallback callback = detail_box.download_delegate; GLib.Cancellable cancellable = new GLib.Cancellable(); @@ -779,7 +781,7 @@ /* * Notifies the user that a download has completed successfully */ - public void on_successful_download(string episode_title, string parent_podcast_name, Gdk.Pixbuf notification_pixbuf) { + public void on_successful_download(string episode_title, string parent_podcast_name) { batch_download_count--; try { @@ -791,7 +793,6 @@ if(!batch_notification_needed) { string message = _("'%s' from '%s' has finished downloading.").printf(episode_title.replace("%27", "'"), parent_podcast_name.replace("%27","'")); var notification = new Notify.Notification(_("Episode Download Complete"), message, null); - notification.set_icon_from_pixbuf(notification_pixbuf); if(!main_window.focus_visible) notification.show(); } else { @@ -1012,8 +1013,9 @@ Sqlite.Statement stmt; - string prepared_query_str = "SELECT * FROM Podcast WHERE name LIKE '%" + term + "%' ORDER BY name"; + string prepared_query_str = "SELECT * FROM Podcast WHERE name LIKE ? ORDER BY name"; int ec = db.prepare_v2 (prepared_query_str, prepared_query_str.length, out stmt); + ec = stmt.bind_text(1, term, -1, null); if (ec != Sqlite.OK) { warning("%d: %s\n".printf(db.errcode (), db.errmsg ())); return matches; @@ -1074,8 +1076,9 @@ Sqlite.Statement stmt; - string prepared_query_str = "SELECT * FROM Episode WHERE title LIKE '%" + term + "%' ORDER BY title"; + string prepared_query_str = "SELECT * FROM Episode WHERE title LIKE ? ORDER BY title"; int ec = db.prepare_v2 (prepared_query_str, prepared_query_str.length, out stmt); + ec = stmt.bind_text(1, term, -1, null); if (ec != Sqlite.OK) { warning("%d: %s\n".printf(db.errcode (), db.errmsg ())); return matches; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/MainWindow.vala new/vocal-2.1.0/src/MainWindow.vala --- old/vocal-2.0.20/src/MainWindow.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/MainWindow.vala 2017-12-30 18:24:57.000000000 +0100 @@ -187,7 +187,7 @@ left top, left bottom, from (shade (@bg_color, 0.9)), to (@bg_color)); - border-bottom: 0.3px solid white; + border-bottom: 0.3px solid black; } .notebook-art { @@ -199,11 +199,6 @@ border-style: none; } -/* - .podcast-view-description { - //font: open sans 10px; - } -*/ .podcast-view-toolbar { } @@ -217,15 +212,6 @@ background-color: #fff; } - .video-back-button { - color: #af81d6; - } - - .video-toolbar * { - background-image: none; - background-color: #af81d6; - } - """; var css_provider = new Gtk.CssProvider(); @@ -384,7 +370,7 @@ int secs_remaining; int mins_elapsed; int secs_elapsed; - + // Progress is a percentage of completiong. Multiple by duration to get elapsed. double total_secs_elapsed = player.duration * player.progress; @@ -643,7 +629,7 @@ notebook = new Gtk.Stack(); notebook.transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT; - notebook.transition_duration = 300; + notebook.transition_duration = 200; details = new PodcastView (this, null, on_elementary); details.go_back.connect(() => { @@ -801,28 +787,17 @@ toolbar.playback_box.set_info_title(current_episode.title.replace("%27", "'"), current_episode.parent.name.replace("%27", "'")); track_changed(current_episode.title, current_episode.parent.name, current_episode.parent.coverart_uri, (uint64) player.duration); - string new_uri; - - // Determine how long the track is and set the progress bar to match - if(this.current_episode.local_uri != null) { - new_uri = """file://""" + this.current_episode.local_uri; - } else { - new_uri = this.current_episode.uri; - } - - bool episode_finished = false; try { - player.set_episode(this.current_episode); - - //toolbar.playback_box.set_progress(percentage, mins_remaining, secs_remaining, mins_elapsed, secs_elapsed); + player.set_episode(current_episode); + player.set_position(current_episode.last_played_position); shownotes.set_notes_text(episode.description); } catch(Error e) { warning(e.message); } - if(current_episode.last_played_position != 0 && !episode_finished) { + if(current_episode.last_played_position != 0) { toolbar.show_playback_box(); } else { @@ -1939,7 +1914,8 @@ private void on_remove_request() { if(highlighted_podcast != null) { Gtk.MessageDialog msg = new Gtk.MessageDialog (this, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.NONE, - _("Are you sure you want to remove '%s' from your library?".printf(highlighted_podcast.name.replace("%27", "'")))); + _("Are you sure you want to remove '%s' from your library?"), + highlighted_podcast.name.replace("%27", "'")); msg.add_button (_("No"), Gtk.ResponseType.NO); @@ -2154,7 +2130,7 @@ if(a.podcast == details.podcast) { GLib.File cover = GLib.File.new_for_path(path); InputStream input_stream = cover.read(); - var pixbuf = new Gdk.Pixbuf.from_stream_at_scale(input_stream, 275, 275, true); + var pixbuf = a.create_cover_image(input_stream); a.image.pixbuf = pixbuf; @@ -2220,8 +2196,10 @@ new_episodes = null; // Lastly, if there are new episodes, repopulate the views to obtain new counts - if(new_episode_count > 0) + if(new_episode_count > 0) { + info ("Repopulating views after the update process has finished."); this.populate_views(); + } } else { info("Vocal is already checking for updates."); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Objects/Episode.vala new/vocal-2.1.0/src/Objects/Episode.vala --- old/vocal-2.0.20/src/Objects/Episode.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Objects/Episode.vala 2017-12-30 18:24:57.000000000 +0100 @@ -21,19 +21,19 @@ namespace Vocal { - public class Episode { + public class Episode { - public string title; // the title of the episode - public string description; // the description/shownotes - public string uri; // the remote location for the media file - public string local_uri; // the local location for the media file, if any - public double last_played_position; // the latest position that has been played - public string date_released; // when the episode was released, in string form - public EpisodeStatus status; // whether the episode is played or unplayed - public DownloadStatus current_download_status;// whether the episode is downloaded or not downloaded + public string title = ""; // the title of the episode + public string description = ""; // the description/shownotes + public string uri = ""; // the remote location for the media file + public string local_uri = ""; // the local location for the media file, if any + public double last_played_position; // the latest position that has been played + public string date_released; // when the episode was released, in string form + public EpisodeStatus status; // whether the episode is played or unplayed + public DownloadStatus current_download_status; // whether the episode is downloaded or not downloaded - public Podcast parent; // the parent that the episode belongs to - public DateTime datetime_released; // the datetime corresponding the when the episode was released + public Podcast parent; // the parent that the episode belongs to + public DateTime datetime_released; // the datetime corresponding the when the episode was released /* * Gets the playback uri based on whether the file is local or remote diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Objects/Podcast.vala new/vocal-2.1.0/src/Objects/Podcast.vala --- old/vocal-2.0.20/src/Objects/Podcast.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Objects/Podcast.vala 2017-12-30 18:24:57.000000000 +0100 @@ -22,14 +22,14 @@ namespace Vocal { public class Podcast { - public ArrayList<Episode> episodes = null; // the episodes belonging to this podcast + public ArrayList<Episode> episodes = null; // the episodes belonging to this podcast - public string name; // podcast name - public string feed_uri; // the uri for the podcast - public string remote_art_uri; // the web link to the album art if local is unavailable - public string local_art_uri; // where the locally cached album art is located - public string description; // the episode's description - public MediaType content_type; // is the podcast an audio or video feed? + public string name = ""; // podcast name + public string feed_uri = ""; // the uri for the podcast + public string remote_art_uri = ""; // the web link to the album art if local is unavailable + public string local_art_uri = ""; // where the locally cached album art is located + public string description = ""; // the episode's description + public MediaType content_type; // is the podcast an audio or video feed? /* * Gets and sets the coverart, whether it's from a remote source diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Utils/FeedParser.vala new/vocal-2.1.0/src/Utils/FeedParser.vala --- old/vocal-2.0.20/src/Utils/FeedParser.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Utils/FeedParser.vala 2017-12-30 18:24:57.000000000 +0100 @@ -25,7 +25,7 @@ namespace Vocal { errordomain VocalUpdateError { - NETWORK_ERROR; + NETWORK_ERROR, EMPTY_ADDRESS_ERROR; } class FeedParser { @@ -127,6 +127,7 @@ while (next_item_in_queue != "item" && i < queue.size - 1) { i++; next_item_in_queue = queue[i]; + if(next_item_in_queue == "title") { i++; episode.title = queue[i]; @@ -177,14 +178,15 @@ episode.set_datetime_from_pubdate(); } - /* else if(next_item_in_queue == "summary") { - i++; - episode.description = queue[i]; - found_summary = true; - }*/ - //else if(next_item_in_queue == "description" && !found_summary) { - else if(next_item_in_queue == "description" && !found_summary) { + // Save the summary as description if we haven't found a description yet. + // Subsequent descriptions will overwrite this. + if (episode.description.char_count() == 0) { + i++; + episode.description = queue[i]; + } + } + else if(next_item_in_queue == "description") { i++; episode.description = queue[i]; } @@ -328,11 +330,11 @@ } - if(podcast.coverart_uri == null) { + if(podcast.coverart_uri == null || podcast.coverart_uri.length < 1) { podcast.coverart_uri = """//usr/share/vocal/vocal-missing.png"""; } - if(podcast.feed_uri == null) { + if(podcast.feed_uri == null || podcast.feed_uri.length < 1) { podcast.feed_uri = path; } @@ -453,7 +455,6 @@ } string path = podcast.feed_uri; - // Call the Xml.Parser to parse the file, which returns an unowned reference Xml.Doc* doc; @@ -556,13 +557,14 @@ episode.set_datetime_from_pubdate(); } - /* else if(next_item_in_queue == "summary") { - i++; - episode.description = queue[i]; - found_summary = true; - }*/ - //else if(next_item_in_queue == "description" && !found_summary) { + // Save the summary as description if we haven't found a description yet. + // Subsequent descriptions will overwrite this. + if (episode.description.char_count() == 0) { + i++; + episode.description = queue[i]; + } + } else if(next_item_in_queue == "description") { i++; episode.description = queue[i]; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Utils/Player.vala new/vocal-2.1.0/src/Utils/Player.vala --- old/vocal-2.0.20/src/Utils/Player.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Utils/Player.vala 2017-12-30 18:24:57.000000000 +0100 @@ -42,14 +42,21 @@ public Episode current_episode; 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) new_position_available(); + if (new_launch && duration > 0.0) { + new_position_available(); + new_launch = false; + } return true; }); } @@ -92,8 +99,6 @@ double percentage_of_total_seconds = num_seconds / total_seconds; set_position(progress - percentage_of_total_seconds); - - new_position_available(); } /* @@ -104,8 +109,6 @@ double percentage_of_total_seconds = num_seconds / total_seconds; set_position(progress + percentage_of_total_seconds); - - new_position_available(); } /* @@ -153,11 +156,13 @@ } */ + /* * Sets the currently playing media position */ public void set_position (double pos) { this.progress = pos; + new_position_available(); } /* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Utils/Utils.vala new/vocal-2.1.0/src/Utils/Utils.vala --- old/vocal-2.0.20/src/Utils/Utils.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Utils/Utils.vala 2017-12-30 18:24:57.000000000 +0100 @@ -146,14 +146,14 @@ // Simplify (keep only href attribute) & preserve anchor tags. Regex simpleLinks = new Regex("<a (.*?(href[\\s=]*?\".*?\").*?)>(.*?)<[\\s\\/]*?a[\\s>]*", - RegexCompileFlags.CASELESS); + RegexCompileFlags.CASELESS | RegexCompileFlags.DOTALL); markup = simpleLinks.replace(markup, -1, 0, "?a? \\2?a-end?\\3 ?/a?"); // Replace <br> tags with line breaks. Regex lineBreaks = new Regex("<br[\\s\\/]*?>", RegexCompileFlags.CASELESS); markup = lineBreaks.replace(markup, -1, 0, "\n"); - markup = markup.replace("<a", "?a"); + markup = markup.replace("<a", "?a?"); markup = markup.replace("</a>", "?/a?"); // Preserve bold tags diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/CoverArt.vala new/vocal-2.1.0/src/Widgets/CoverArt.vala --- old/vocal-2.0.20/src/Widgets/CoverArt.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/CoverArt.vala 2017-12-30 18:24:57.000000000 +0100 @@ -56,16 +56,11 @@ try { // Load the actual cover art - File cover_file = GLib.File.new_for_uri(path.replace("%27", "'")); - assert(cover_file != null); - bool exists = cover_file.query_exists(); - if(!exists) - { - info("Coverart at %s doesn't exist.".printf(path.replace("%27", "'"))); - } - InputStream input_stream = cover_file.read(); - var coverart_pixbuf = create_cover_image (input_stream); - image = new Gtk.Image.from_pixbuf(coverart_pixbuf); + var file = GLib.File.new_for_uri(path.replace("%27", "'")); + var icon = new GLib.FileIcon(file); + var image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG); + image.pixel_size = COVER_SIZE; + // Load the banner to be drawn on top of the cover art File triangle_file = GLib.File.new_for_path(GLib.Path.build_filename (Constants.PKGDATADIR, "banner.png")); @@ -109,45 +104,7 @@ count_overlay.add_overlay(count_label); triangle_overlay.add_overlay(count_overlay); -/* - * The code below shows the media type (audio/video) overlayed if the show_mimetype - * boolean value is set to true. I have since decided that it's too cluttered and - * doesn't provide any real additional value. - */ - -/* - if(show_mimetype) { - - Gtk.Overlay mimetype_overlay = new Gtk.Overlay(); - mimetype_overlay.add(triangle_overlay); - - - Gtk.Image mime_image; - if(podcast.content_type == MediaType.AUDIO) - mime_image = new Gtk.Image.from_icon_name ("media-audio-symbolic", IconSize.BUTTON); - else - mime_image = new Gtk.Image.from_icon_name ("media-video-symbolic", IconSize.BUTTON); - -string css = """ -* { - color: #e5e5e5; - icon-shadow: 2px 2px #2a2a2a; -} -"""; - Gtk.CssProvider provider = new Gtk.CssProvider(); - provider.load_from_data(css, css.length); - mime_image.get_style_context().add_provider(provider, 1); - - mime_image.set_alignment((float)0.05, (float)0.95); - - mimetype_overlay.add_overlay(mime_image); - - this.pack_start(mimetype_overlay, false, false, 0); - - } else { -*/ - this.pack_start(triangle_overlay, false, false, 0); -// } + this.pack_start(triangle_overlay, false, false, 0); this.valign = Align.START; image.set_no_show_all(false); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/DirectoryArt.vala new/vocal-2.1.0/src/Widgets/DirectoryArt.vala --- old/vocal-2.0.20/src/Widgets/DirectoryArt.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/DirectoryArt.vala 2017-12-30 18:24:57.000000000 +0100 @@ -116,26 +116,31 @@ // Load the album artwork Gtk.Image image = new Gtk.Image(); + + // By default we're only given the 170px version, but the 600px is available + var bigartwork = artworkUrl170.replace("170", "600"); try { - var missing_pixbuf = new Gdk.Pixbuf.from_file_at_scale(GLib.Path.build_filename (Constants.PKGDATADIR, "vocal-missing.png"), - 170, 170, true); - image = new Gtk.Image.from_pixbuf(missing_pixbuf); + var missing = GLib.File.new_for_path(GLib.Path.build_filename (Constants.PKGDATADIR, "vocal-missing.png")); + var icon = new GLib.FileIcon(missing); + image.gicon = icon; + image.pixel_size = 200; } catch (Error e) { warning ("Unable to open missing album art file."); } - image.margin = 2; image.expand = false; + image.margin_top = 5; + image.margin_bottom = 5; image.get_style_context().add_class("directory-art-image"); this.pack_start(image, false, false, 0); ImageCache image_cache = new ImageCache(); - image_cache.get_image.begin(artworkUrl170, (obj, res) => { + image_cache.get_image.begin(bigartwork, (obj, res) => { Gdk.Pixbuf pixbuf = image_cache.get_image.end(res); if (pixbuf != null) { image.clear(); - pixbuf = pixbuf.scale_simple(170, 170, Gdk.InterpType.BILINEAR); - image.set_from_pixbuf(pixbuf); + image.gicon = pixbuf; + image.pixel_size = 200; } }); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/DirectoryView.vala new/vocal-2.1.0/src/Widgets/DirectoryView.vala --- old/vocal-2.0.20/src/Widgets/DirectoryView.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/DirectoryView.vala 2017-12-30 18:24:57.000000000 +0100 @@ -47,7 +47,7 @@ banner_box.get_style_context().add_class("library-toolbar"); var itunes_title = new Gtk.Label(_("iTunes Top 100 Podcasts")); - itunes_title.margin_top = 5; + itunes_title.margin_top = 15; itunes_title.margin_bottom = 5; itunes_title.justify = Gtk.Justification.CENTER; itunes_title.expand = true; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/DownloadDetailBox.vala new/vocal-2.1.0/src/Widgets/DownloadDetailBox.vala --- old/vocal-2.0.20/src/Widgets/DownloadDetailBox.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/DownloadDetailBox.vala 2017-12-30 18:24:57.000000000 +0100 @@ -25,15 +25,13 @@ public signal void cancel_requested(Episode e); // Fired when the cancel button gets clicked // Fired upon successful download - public signal void download_has_completed_successfully(string title, string parent_name, Gdk.Pixbuf pixbuf); + public signal void download_has_completed_successfully(string title, string parent_name); // Fired when the box is ready for removal (usually when the download completes) public signal void ready_for_removal(DownloadDetailBox box); public signal void new_percentage_available(); // Fired when a new download percentage is available - public Gtk.Image image {private get; private set;} - public Gdk.Pixbuf image_pixbuf{ get; set; } public Gtk.Label title_label {private get; private set;} public Gtk.Label podcast_label {private get; private set;} public Gtk.ProgressBar progress_bar; @@ -55,13 +53,16 @@ * Constructor for the download details box, which shows a an episode title, podcast name, and image * along with a progress bar indicating progress of the download. */ - public DownloadDetailBox (Episode episode, Gdk.Pixbuf pixbuf) { + public DownloadDetailBox (Episode episode) { string title = episode.title; string parent_podcast_name = episode.parent.name; - image_pixbuf = pixbuf; - image = new Gtk.Image.from_pixbuf(pixbuf); + // Load the actual cover art + var file = GLib.File.new_for_uri(episode.parent.coverart_uri); + var icon = new GLib.FileIcon(file); + var image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG); + image.pixel_size = 64; this.episode_title = title; this.parent_podcast_name = parent_podcast_name; @@ -94,8 +95,6 @@ label_box.add(title_label); label_box.add(podcast_label); - this.image = image; - var details_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 12); details_box.add(image); details_box.add(label_box); @@ -163,7 +162,7 @@ // bar will see the completed value this.percentage = 1.0; new_percentage_available(); - download_has_completed_successfully(episode_title, parent_podcast_name, image_pixbuf); + download_has_completed_successfully(episode_title, parent_podcast_name); ready_for_removal(this); signal_has_been_sent = true; return; @@ -203,7 +202,7 @@ outdated_time_output = time_output; } - download_label.set_text(data_output + outdated_time_output); + download_label.set_markup(data_output + outdated_time_output); new_percentage_available(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/EpisodeDetailBox.vala new/vocal-2.1.0/src/Widgets/EpisodeDetailBox.vala --- old/vocal-2.0.20/src/Widgets/EpisodeDetailBox.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/EpisodeDetailBox.vala 2017-12-30 18:24:57.000000000 +0100 @@ -167,6 +167,10 @@ string text = Utils.html_to_markup(episode.description); + // Remove repeated whitespace from description before adding to label. + Regex condense_spaces = new Regex("\\s{2,}"); + text = condense_spaces.replace(text, -1, 0, " ").strip(); + description_label = new Gtk.Label(text != "(null)" ? text : _("No description available.")); description_label.justify = Gtk.Justification.LEFT; description_label.set_use_markup(true); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/PlaybackBox.vala new/vocal-2.1.0/src/Widgets/PlaybackBox.vala --- old/vocal-2.0.20/src/Widgets/PlaybackBox.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/PlaybackBox.vala 2017-12-30 18:24:57.000000000 +0100 @@ -72,6 +72,14 @@ this.add(info_label); this.add(scale_grid); } + + public override void get_preferred_width (out int minimum_width, out int natural_width) { + base.get_preferred_width (out minimum_width, out natural_width); + minimum_width = 300; + if (natural_width < 600) { + natural_width = 600; + } + } /* * Returns the percentage that the progress bar has been filled diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/PodcastView.vala new/vocal-2.1.0/src/Widgets/PodcastView.vala --- old/vocal-2.0.20/src/Widgets/PodcastView.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/PodcastView.vala 2017-12-30 18:24:57.000000000 +0100 @@ -517,12 +517,10 @@ } try { - GLib.File cover = GLib.File.new_for_uri(podcast.coverart_uri); - InputStream input_stream = cover.read(); - var pixbuf = new Gdk.Pixbuf.from_stream_at_scale(input_stream, 275, 275, true); - - - image = new Gtk.Image.from_pixbuf(pixbuf); + var cover = GLib.File.new_for_uri(podcast.coverart_uri); + var icon = new GLib.FileIcon(cover); + image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG); + image.pixel_size = 250; image.margin = 0; image.get_style_context().add_class("podcast-view-coverart"); @@ -662,7 +660,10 @@ remove(scrolled); } - paned.remove(scrolled); + if ( scrolled != null ) { + paned.remove(scrolled); + } + scrolled = new Gtk.ScrolledWindow (null, null); listbox = new Gtk.ListBox(); listbox.activate_on_single_click = false; @@ -817,10 +818,9 @@ //If the user selects a file, get the name and parse it if (decision == Gtk.ResponseType.ACCEPT) { GLib.File cover = GLib.File.new_for_path(file_name); - InputStream input_stream = cover.read(); - var pixbuf = new Gdk.Pixbuf.from_stream_at_scale(input_stream, 275, 275, true); - - image.pixbuf = pixbuf; + var icon = new GLib.FileIcon(cover); + image.gicon = icon; + image.pixel_size = 250; new_cover_art_set(file_name); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/QueuePopover.vala new/vocal-2.1.0/src/Widgets/QueuePopover.vala --- old/vocal-2.0.20/src/Widgets/QueuePopover.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/QueuePopover.vala 2017-12-30 18:24:57.000000000 +0100 @@ -53,7 +53,10 @@ * Sets the current queue */ public void set_queue(Gee.ArrayList<Episode> queue) { - scrolled_box.remove(episodes); + + if (episodes != null) { + scrolled_box.remove(episodes); + } if(queue.size > 0) { hide_label(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/QueueRow.vala new/vocal-2.1.0/src/Widgets/QueueRow.vala --- old/vocal-2.0.20/src/Widgets/QueueRow.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/QueueRow.vala 2017-12-30 18:24:57.000000000 +0100 @@ -51,10 +51,11 @@ handle.drag_data_get.connect(on_drag_data_get); try { - GLib.File cover = GLib.File.new_for_uri(episode.parent.coverart_uri); - InputStream input_stream = cover.read(); - var pixbuf = new Gdk.Pixbuf.from_stream_at_scale(input_stream, 64, 64, true); - var image = new Gtk.Image.from_pixbuf(pixbuf); + // Load the actual cover art + var file = GLib.File.new_for_uri(episode.parent.coverart_uri); + var icon = new GLib.FileIcon(file); + var image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG); + image.pixel_size = 64; image.margin = 0; image.expand = false; image.get_style_context().add_class("album-artwork"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/SearchResultsView.vala new/vocal-2.1.0/src/Widgets/SearchResultsView.vala --- old/vocal-2.0.20/src/Widgets/SearchResultsView.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/SearchResultsView.vala 2017-12-30 18:24:57.000000000 +0100 @@ -50,6 +50,10 @@ private Gtk.Label no_local_episodes_label; private Gtk.Label no_local_podcasts_label; + + private Gtk.Revealer local_podcasts_revealer; + private Gtk.Revealer local_episodes_revealer; + private Gtk.Revealer cloud_results_revealer; /* * Constructor for the full search results view. Shows all matches from the local library and across the iTunes ecosystem @@ -151,16 +155,37 @@ no_local_episodes_label.get_style_context ().add_class ("h3"); no_local_podcasts_label.get_style_context ().add_class ("h3"); - content_box.add(local_podcasts_label); - content_box.add(no_local_podcasts_label); - content_box.add(local_podcasts_listbox); - content_box.add(local_episodes_label); - content_box.add(no_local_episodes_label); - content_box.add(local_episodes_listbox); - content_box.add(iTunes_box); - content_box.add(cloud_results_flowbox); - - content_box.margin = 5; + + local_podcasts_revealer = new Gtk.Revealer(); + local_episodes_revealer = new Gtk.Revealer(); + cloud_results_revealer = new Gtk.Revealer(); + + var local_podcasts_container = new Gtk.Box(Gtk.Orientation.VERTICAL, 12); + local_podcasts_container.margin_left = 12; + local_podcasts_container.margin_right = 12; + local_podcasts_container.add(local_podcasts_label); + local_podcasts_container.add(no_local_podcasts_label); + local_podcasts_container.add(local_podcasts_listbox); + local_podcasts_revealer.add(local_podcasts_container); + + var local_episodes_container = new Gtk.Box(Gtk.Orientation.VERTICAL, 12); + local_episodes_container.margin_left = 12; + local_episodes_container.margin_right = 12; + local_episodes_container.add(local_episodes_label); + local_episodes_container.add(no_local_episodes_label); + local_episodes_container.add(local_episodes_listbox); + local_episodes_revealer.add(local_episodes_container); + + var cloud_results_container = new Gtk.Box(Gtk.Orientation.VERTICAL, 12); + cloud_results_container.margin_left = 12; + cloud_results_container.margin_right = 12; + cloud_results_container.add(iTunes_box); + cloud_results_container.add(cloud_results_flowbox); + cloud_results_revealer.add(cloud_results_container); + + content_box.add(local_podcasts_revealer); + content_box.add(local_episodes_revealer); + content_box.add(cloud_results_revealer); hide_spinner (); hide_no_local_podcasts (); @@ -181,6 +206,9 @@ foreach (Gtk.Widget c in cloud_results_flowbox.get_children ()) { cloud_results_flowbox.remove (c); } + local_podcasts_revealer.reveal_child = false; + local_episodes_revealer.reveal_child = false; + cloud_results_revealer.reveal_child = false; show_all (); } @@ -214,8 +242,14 @@ foreach(Widget w in cloud_results_widgets) { cloud_results_flowbox.add(w); } + hide_spinner (); show_all(); + if (cloud_results_widgets.size < 1) { + cloud_results_revealer.reveal_child = false; + } else { + cloud_results_revealer.reveal_child = true; + } } @@ -286,6 +320,8 @@ show_local_episodes_listbox (); } show_all (); + local_podcasts_revealer.reveal_child = true; + local_episodes_revealer.reveal_child = true; } /* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/Widgets/SettingsDialog.vala new/vocal-2.1.0/src/Widgets/SettingsDialog.vala --- old/vocal-2.0.20/src/Widgets/SettingsDialog.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/Widgets/SettingsDialog.vala 2017-12-30 18:24:57.000000000 +0100 @@ -128,7 +128,7 @@ Gtk.Separator check_spacer = new Gtk.Separator(Gtk.Orientation.HORIZONTAL); - check_spacer.expand = true; + check_spacer.expand = false; check_spacer.margin = 5; check_spacer.margin_top = 10; check_spacer.margin_bottom = 10; @@ -228,7 +228,7 @@ }); Gtk.Separator store_spacer = new Gtk.Separator(Gtk.Orientation.HORIZONTAL); - store_spacer.expand = true; + store_spacer.expand = false; store_spacer.margin = 5; store_spacer.margin_top = 10; store_spacer.margin_bottom = 10; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vocal-2.0.20/src/config.vala new/vocal-2.1.0/src/config.vala --- old/vocal-2.0.20/src/config.vala 2017-06-05 23:20:39.000000000 +0200 +++ new/vocal-2.1.0/src/config.vala 2017-12-30 18:24:57.000000000 +0100 @@ -3,8 +3,8 @@ public const string PKGDATADIR = "/usr/share/vocal"; public const string GETTEXT_PACKAGE = "vocal"; public const string RELEASE_NAME = "Landingham"; - public const string VERSION = "2.0.1"; + public const string VERSION = "2.1.0"; public const string VERSION_INFO = "Release"; public const string CACHE_DIR = "~/.cache/vocal"; - public const string USER_AGENT = "vocal 2.0.1"; + public const string USER_AGENT = "vocal 2.1.0"; }
