Hello community, here is the log from the commit of package pithos for openSUSE:Factory checked in at 2014-12-05 21:04:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/pithos (Old) and /work/SRC/openSUSE:Factory/.pithos.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "pithos" Changes: -------- --- /work/SRC/openSUSE:Factory/pithos/pithos.changes 2014-09-30 19:36:15.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.pithos.new/pithos.changes 2014-12-05 21:05:00.000000000 +0100 @@ -1,0 +2,17 @@ +Sun Nov 30 05:27:22 UTC 2014 - [email protected] + +- Update to version 1.0.1: + + Automatically install missing codecs if supported. + + Save window position between sessions. + + Fix saving last station on quit. + + Fix pacparser support. + + Improve pandora module docs. + + Add setup.py command to build docs. + + Add appdata file. + + Notification_icon: Make toggling visibility more reliable. + + mpris: Fix exception when querying positon. + + mpris: Implement setting volume. +- Update fixes Pandora stream and hangs at "loading songs" This + occurs randomly, sometimes very frequently (boo#907701). + +------------------------------------------------------------------- Old: ---- 1.0.0.tar.gz New: ---- 1.0.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ pithos.spec ++++++ --- /var/tmp/diff_new_pack.OC3zRt/_old 2014-12-05 21:05:01.000000000 +0100 +++ /var/tmp/diff_new_pack.OC3zRt/_new 2014-12-05 21:05:01.000000000 +0100 @@ -18,13 +18,13 @@ Name: pithos -Version: 1.0.0 +Version: 1.0.1 Release: 0 Summary: Native Pandora Radio client for Linux License: GPL-3.0 Group: Productivity/Multimedia/Other Url: http://pithos.github.io/ -Source0: https://github.com/pithos/pithos/archive/1.0.0.tar.gz +Source0: https://github.com/pithos/pithos/archive/1.0.1.tar.gz BuildRequires: fdupes # Needed for automatic typelib() Requires. BuildRequires: gobject-introspection @@ -78,5 +78,7 @@ %{python3_sitelib}/pithos-%{version}-py%{py3_ver}.egg-info %{_datadir}/applications/pithos.desktop %{_datadir}/icons/hicolor/*/apps/pithos* +%dir %{_datadir}/appdata/ +%{_datadir}/appdata/pithos.appdata.xml %changelog ++++++ 1.0.0.tar.gz -> 1.0.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/MANIFEST.in new/pithos-1.0.1/MANIFEST.in --- old/pithos-1.0.0/MANIFEST.in 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/MANIFEST.in 2014-09-21 12:30:03.000000000 +0200 @@ -1,4 +1,4 @@ include README.md -recursive-include data *.svg pithos.desktop +recursive-include data *.svg pithos.desktop *.xml recursive-include pithos/data/ui *.ui *.xml recursive-include pithos/data/media *.png *.svg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/data/pithos.appdata.xml new/pithos-1.0.1/data/pithos.appdata.xml --- old/pithos-1.0.0/data/pithos.appdata.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/pithos-1.0.1/data/pithos.appdata.xml 2014-09-21 12:30:03.000000000 +0200 @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<application> + <id type="desktop">pithos.desktop</id> + <metadata_license>CC0</metadata_license> + <project_license>GPL-3.0+</project_license> + <description> + <p>Pithos is a easy to use native Pandora Radio client that is more lightweight than the pandora.com web client and integrates with the desktop.</p> + <p>It supports most functionality of pandora.com such as rating songs, creating/managing stations, quickmix, etc. On top of that it has features such as last.fm scrobbling, media keys, notifications, proxies, and mpris support.</p> + </description> + <url type="homepage">https://pithos.github.io</url> + <screenshots> + <screenshot type="default">https://i.imgur.com/1vl3Dsk.png</screenshot> + </screenshots> + <updatecontact>tingping_at_fedoraproject.org</updatecontact> +</application> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/docs/conf.py new/pithos-1.0.1/docs/conf.py --- old/pithos-1.0.0/docs/conf.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pithos-1.0.1/docs/conf.py 2014-09-21 12:30:03.000000000 +0200 @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Pithos documentation build configuration file, created by +# sphinx-quickstart on Mon Sep 15 20:13:58 2014. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'Pithos' +copyright = '2014, Kevin Mehall' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Pithosdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Pithos.tex', 'Pithos Documentation', + 'Kevin Mehall', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pithos', 'Pithos Documentation', + ['Kevin Mehall'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Pithos', 'Pithos Documentation', + 'Kevin Mehall', 'Pithos', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/docs/index.rst new/pithos-1.0.1/docs/index.rst --- old/pithos-1.0.0/docs/index.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/pithos-1.0.1/docs/index.rst 2014-09-21 12:30:03.000000000 +0200 @@ -0,0 +1,38 @@ +.. Pithos documentation master file, created by + sphinx-quickstart on Mon Sep 15 20:13:58 2014. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Pithos's documentation! +================================== + +Pandora +------- + +.. autoclass:: pithos.pandora.Pandora + :members: + :undoc-members: + +.. autoclass:: pithos.pandora.Station + :members: + :undoc-members: + +.. autoclass:: pithos.pandora.Song + :members: + :undoc-members: + +.. autoclass:: pithos.pandora.SearchResult + :members: + :undoc-members: + +.. autoclass:: pithos.pandora.PandoraError + :members: + :undoc-members: + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/PreferencesPithosDialog.py new/pithos-1.0.1/pithos/PreferencesPithosDialog.py --- old/pithos-1.0.0/pithos/PreferencesPithosDialog.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/PreferencesPithosDialog.py 2014-09-21 12:30:03.000000000 +0200 @@ -87,6 +87,8 @@ self.__preferences = { "username":'', "password":'', + "x_pos": None, + "y_pos": None, "notify":True, "last_station_id":None, "proxy":'', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/__main__.py new/pithos-1.0.1/pithos/__main__.py --- old/pithos-1.0.0/pithos/__main__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pithos-1.0.1/pithos/__main__.py 2014-09-21 12:30:03.000000000 +0200 @@ -0,0 +1,3 @@ +from pithos.pithos import main + +main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/data/ui/PithosWindow.ui new/pithos-1.0.1/pithos/data/ui/PithosWindow.ui --- old/pithos-1.0.0/pithos/data/ui/PithosWindow.ui 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/data/ui/PithosWindow.ui 2014-09-21 12:30:03.000000000 +0200 @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.16.0 --> +<!-- Generated with glade 3.16.1 --> <interface> <requires lib="gtk+" version="3.6"/> - <requires lib="pithos_window" version="1.0"/> + <!-- interface-requires pithos_window 1.0 --> <object class="GtkAdjustment" id="adjustment1"> <property name="upper">100</property> <property name="step_increment">1</property> @@ -17,6 +17,7 @@ <property name="icon_name">pithos</property> <signal name="destroy" handler="on_destroy" swapped="no"/> <signal name="key-press-event" handler="on_kb_playpause" swapped="no"/> + <signal name="configure-event" handler="on_configure_event" swapped="no"/> <child> <object class="GtkBox" id="vbox1"> <property name="visible">True</property> @@ -243,7 +244,6 @@ <object class="GtkMessageDialog" id="api_update_dialog"> <property name="can_focus">False</property> <property name="border_width">5</property> - <property name="type">popup</property> <property name="modal">True</property> <property name="type_hint">dialog</property> <property name="transient_for">pithos_window</property> @@ -308,7 +308,6 @@ <object class="GtkMessageDialog" id="fatal_error_dialog"> <property name="can_focus">False</property> <property name="border_width">5</property> - <property name="type">popup</property> <property name="modal">True</property> <property name="type_hint">dialog</property> <property name="skip_taskbar_hint">True</property> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/mpris.py new/pithos-1.0.1/pithos/mpris.py --- old/pithos-1.0.0/pithos/mpris.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/mpris.py 2014-09-21 12:30:03.000000000 +0200 @@ -96,8 +96,11 @@ def _get_volume(self): return self.window.player.get_property("volume") + def _set_volume(self, new_volume): + self.window.player.set_property('volume', new_volume) + def _get_position(self): - return self.window.player.query_position(self.window.time_format, None)[0] / 1000 + return self.window.player.query_position(self.window.time_format)[0] / 1000 @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') def Get(self, interface_name, property_name): @@ -112,7 +115,8 @@ if interface_name == self.MEDIA_PLAYER2_IFACE: pass elif interface_name == self.MEDIA_PLAYER2_PLAYER_IFACE: - pass # TODO: volume + if property_name == 'Volume': + self._set_volume(new_value) else: raise dbus.exceptions.DBusException( 'org.mpris.MediaPlayer2.pithos', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/pandora/__init__.py new/pithos-1.0.1/pithos/pandora/__init__.py --- old/pithos-1.0.0/pithos/pandora/__init__.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/pandora/__init__.py 2014-09-21 12:30:03.000000000 +0200 @@ -14,7 +14,7 @@ #with this program. If not, see <http://www.gnu.org/licenses/>. ### END LICENSE -from pithos.pandora.pandora import * +from .pandora import * def make_pandora(testing=False): if testing: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/pandora/fake.py new/pithos-1.0.1/pithos/pandora/fake.py --- old/pithos-1.0.0/pithos/pandora/fake.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/pandora/fake.py 2014-09-21 12:30:03.000000000 +0200 @@ -14,7 +14,7 @@ #with this program. If not, see <http://www.gnu.org/licenses/>. ### END LICENSE -from pithos.pandora.pandora import * +from .pandora import * from gi.repository import Gtk import logging diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/pandora/pandora.py new/pithos-1.0.1/pithos/pandora/pandora.py --- old/pithos-1.0.0/pithos/pandora/pandora.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/pandora/pandora.py 2014-09-21 12:30:03.000000000 +0200 @@ -15,6 +15,11 @@ #with this program. If not, see <http://www.gnu.org/licenses/>. ### END LICENSE +"""Pandora JSON v5 API + +See http://6xq.net/playground/pandora-apidoc/json/ for API documentation. +""" + from .blowfish import Blowfish # from Crypto.Cipher import Blowfish from xml.dom import minidom @@ -25,10 +30,6 @@ import urllib.request, urllib.parse, urllib.error import codecs -# This is an implementation of the Pandora JSON API using Android partner -# credentials. -# See http://pan-do-ra-api.wikia.com/wiki/Json/5 for API documentation. - HTTP_TIMEOUT = 30 USER_AGENT = 'pithos' @@ -65,6 +66,17 @@ return s + b'\0' * (l - len(s)) class Pandora(object): + """Access the Pandora API + + To use the Pandora class, make sure to call :py:meth:`set_audio_quality` + and :py:meth:`connect` methods. + + Get information from Pandora using: + + - :py:meth:`get_stations` which populates the :py:attr:`stations` attribute + - :py:meth:`search` to find songs to add to stations or create a new station with + - :py:meth:`json_call` call into the JSON API directly + """ def __init__(self): self.opener = urllib.request.build_opener() pass @@ -131,7 +143,7 @@ raise PandoraAuthTokenInvalid(msg) elif code == API_ERROR_COUNTRY_NOT_SUPPORTED: raise PandoraError("Pandora not available", code, - submsg="Pandora is not available outside the United States.") + submsg="Pandora is not available in your country.") elif code == API_ERROR_API_VERSION_NOT_SUPPORTED: raise PandoraAPIVersionError(msg) elif code == API_ERROR_INSUFFICIENT_CONNECTIVITY: @@ -158,12 +170,24 @@ return tree['result'] def set_audio_quality(self, fmt): + """Set the desired audio quality + + Used by the :py:attr:`Song.audioUrl` property. + + :param fmt: An audio quality format from :py:data:`pithos.pandora.data.valid_audio_formats` + """ self.audio_quality = fmt def set_url_opener(self, opener): self.opener = opener def connect(self, client, user, password): + """Connect to the Pandora API and log the user in + + :param client: The client ID from :py:data:`pithos.pandora.data.client_keys` + :param user: The user's login email + :param password: The user's login password + """ self.partnerId = self.userId = self.partnerAuthToken = None self.userAuthToken = self.time_offset = None @@ -201,6 +225,8 @@ if i.id in self.quickMixStationIds: i.useQuickMix = True + return self.stations + def save_quick_mix(self): stationIds = [] for i in self.stations: @@ -281,6 +307,14 @@ logging.info("pandora: Deleting Station") self.pandora.json_call('station.deleteStation', {'stationToken': self.idToken}) + def __repr__(self): + return '<{}.{} {} "{}">'.format( + __name__, + __class__.__name__, + self.id, + self.name, + ) + class Song(object): def __init__(self, pandora, d): self.pandora = pandora @@ -378,6 +412,16 @@ def is_still_valid(self): return (time.time() - self.playlist_time) < PLAYLIST_VALIDITY_TIME + def __repr__(self): + return '<{}.{} {} "{}" by "{}" from "{}">'.format( + __name__, + __class__.__name__, + self.trackToken, + self.songName, + self.artist, + self.album, + ) + class SearchResult(object): def __init__(self, resultType, d): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/pithos.py new/pithos-1.0.1/pithos/pithos.py --- old/pithos-1.0.0/pithos/pithos.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/pithos.py 2014-09-21 12:30:03.000000000 +0200 @@ -23,7 +23,7 @@ import gi gi.require_version('Gst', '1.0') -from gi.repository import Gst, GObject, Gtk, Gdk, Pango, GdkPixbuf, Gio, GLib +from gi.repository import Gst, GstPbutils, GObject, Gtk, Gdk, Pango, GdkPixbuf, Gio, GLib import contextlib import html import math @@ -201,6 +201,7 @@ bus.connect("message::eos", self.on_gst_eos) bus.connect("message::buffering", self.on_gst_buffering) bus.connect("message::error", self.on_gst_error) + bus.connect("message::element", self.on_gst_element) bus.connect("message::tag", self.on_gst_tag) self.player.connect("notify::volume", self.on_gst_volume) self.player.connect("notify::source", self.on_gst_source) @@ -284,6 +285,8 @@ self.stations_combo.add_attribute(render_text, "text", 1) self.stations_combo.set_row_separator_func(lambda model, iter, data=None: model.get_value(iter, 0) is None, None) + self.set_initial_pos() + def worker_run(self, fn, args=(), callback=None, message=None, context='net'): if context and message: self.statusbar.push(self.statusbar.get_context_id(context), message) @@ -353,15 +356,24 @@ elif control_proxy_pac and pacparser_imported: pacparser.init() - pacparser.parse_pac_string(urllib.request.urlopen(control_proxy_pac).read()) - proxies = pacparser.find_proxy("http://pandora.com", "pandora.com").split(";") - for proxy in proxies: - match = re.search("PROXY (.*)", proxy) - if match: - control_proxy = match.group(1) - break + with urllib.request.urlopen(control_proxy_pac) as f: + pacstring = f.read().decode('utf-8') + try: + pacparser.parse_pac_string(pacstring) + except: + logging.warning('Failed to parse PAC.') + try: + proxies = pacparser.find_proxy("http://pandora.com", "pandora.com").split(";") + for proxy in proxies: + match = re.search("PROXY (.*)", proxy) + if match: + control_proxy = match.group(1) + break + except: + logging.warning('Failed to find proxy via PAC.') + pacparser.cleanup() elif control_proxy_pac and not pacparser_imported: - logging.warn("Disabled proxy auto-config support because python-pacparser module was not found.") + logging.warning("Disabled proxy auto-config support because python-pacparser module was not found.") self.worker_run('set_url_opener', (control_opener,)) @@ -572,6 +584,10 @@ dialog.props.secondary_text = submsg dialog.set_default_response(3) + if retry_cb is None: + btn = self.builder.get_object("button2") + btn.hide() + response = dialog.run() dialog.hide() @@ -622,19 +638,34 @@ logging.info("EOS") self.next_song() + def on_gst_plugin_installed(self, result, userdata): + if result == GstPbutils.InstallPluginsReturn.SUCCESS: + self.fatal_error_dialog("Codec installation successful", + submsg="The required codec was installed, please restart Pithos.") + else: + self.error_dialog("Codec installation failed", None, + submsg="The required codec failed to install. Either manually install it or try another quality setting.") + + def on_gst_element(self, bus, message): + if GstPbutils.is_missing_plugin_message(message): + if GstPbutils.install_plugins_supported(): + details = GstPbutils.missing_plugin_message_get_installer_detail(message) + GstPbutils.install_plugins_async([details,], None, self.on_gst_plugin_installed, None) + else: + self.error_dialog("Missing codec", None, + submsg="GStreamer is missing a plugin and it could not be automatically installed. Either manually install it or try another quality setting.") + def on_gst_error(self, bus, message): err, debug = message.parse_error() logging.error("Gstreamer error: %s, %s, %s" % (err, debug, err.code)) if self.current_song: self.current_song.message = "Error: "+str(err) - #if err.code is int(Gst.CORE_ERROR_MISSING_PLUGIN): - # self.fatal_error_dialog("Missing codec", submsg="GStreamer is missing a plugin") - # return - self.gstreamer_error = str(err) self.gstreamer_errorcount_1 += 1 - self.next_song() + + if not GstPbutils.install_plugins_installation_in_progress(): + self.next_song() def gst_tag_handler(self, tag_info): def handler(_x, tag, _y): @@ -951,10 +982,20 @@ def refresh_stations(self, *ignore): self.worker_run(self.pandora.get_stations, (), self.process_stations, "Refreshing stations...") + def set_initial_pos(self): + """ Moves window to position stored in preferences """ + x, y = self.preferences['x_pos'], self.preferences['y_pos'] + if not x is None and not y is None: + self.move(int(x), int(y)) + def bring_to_top(self, *ignore): + self.set_initial_pos() self.show() self.present() + def on_configure_event(self, widget, event): + self.preferences['x_pos'], self.preferences['y_pos'] = event.x, event.y + def on_kb_playpause(self, widget=None, data=None): if not isinstance(widget.get_focus(), Gtk.Button) and data.keyval == 32: self.playpause() @@ -1053,7 +1094,7 @@ def do_shutdown(self): Gtk.Application.do_shutdown(self) - self.quit() + self.window.destroy() def stations_cb(self, action, param): self.window.show_stations() @@ -1065,7 +1106,7 @@ self.window.show_about() def quit_cb(self, action, param): - self.quit() + self.window.destroy() def main(): app = PithosApplication() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/pithosconfig.py new/pithos-1.0.1/pithos/pithosconfig.py --- old/pithos-1.0.0/pithos/pithosconfig.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/pithosconfig.py 2014-09-21 12:30:03.000000000 +0200 @@ -19,7 +19,7 @@ __pithos_data_directory__ = 'data/' __license__ = 'GPL-3' -VERSION = '1.0.0' +VERSION = '1.0.1' import os diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/plugins/mediakeys.py new/pithos-1.0.1/pithos/plugins/mediakeys.py --- old/pithos-1.0.0/pithos/plugins/mediakeys.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/plugins/mediakeys.py 2014-09-21 12:30:03.000000000 +0200 @@ -22,7 +22,7 @@ class MediaKeyPlugin(PithosPlugin): preference = 'enable_mediakeys' - + def bind_dbus(self): try: import dbus @@ -86,12 +86,42 @@ self.hookman.KeyDown = self.kbevent self.hookman.HookKeyboard() return True + + def osx_playpause_handler(self): + self.window.playpause_notify() + return False # Don't let others get event + + def osx_skip_handler(self): + self.window.next_song() + return False + + def bind_osx(self): + try: + import osxmmkeys + except ImportError: + logging.warning('Please install osxmmkeys: https://github.com/pushrax/osxmmkeys') + return False + except RuntimeError as e: + logging.warning('osxmmkeys failed to import: {}'.format(e)) + return False + + tap = osxmmkeys.Tap() + tap.on('play_pause', self.osx_playpause_handler) + tap.on('next_track', self.osx_skip_handler) + tap.start() + + return True def on_enable(self): if sys.platform == 'win32': - self.bind_win32() or logging.error("Could not bind media keys") + loaded = self.bind_win32() + elif sys.platform == 'darwin': + loaded = self.bind_osx() else: - self.bind_dbus() or self.bind_keybinder() or logging.error("Could not bind media keys") + loaded = self.bind_dbus() or self.bind_keybinder() + + if not loaded: + logging.error("Could not bind media keys") def on_disable(self): logging.error("Not implemented: Can't disable media keys") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/plugins/notification_icon.py new/pithos-1.0.1/pithos/plugins/notification_icon.py --- old/pithos-1.0.0/pithos/plugins/notification_icon.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/plugins/notification_icon.py 2014-09-21 12:30:03.000000000 +0200 @@ -40,7 +40,6 @@ get_data_file('media')) def on_enable(self): - self.visible = True self.delete_callback_handle = self.window.connect("delete-event", self.toggle_visible) self.state_callback_handle = self.window.connect("play-state-changed", self.play_state_changed) self.song_callback_handle = self.window.connect("song-changed", self.song_changed) @@ -131,16 +130,14 @@ self.statusicon.set_tooltip_text("%s by %s"%(song.title, song.artist)) def _toggle_visible(self, *args): - if self.visible: - self.window.hide() - else: + self.window.set_visible(not self.window.get_visible()) + + if self.window.get_visible(): # Ensure it's on top self.window.bring_to_top() - self.visible = not self.visible - def toggle_visible(self, *args): if hasattr(self, 'visible_check'): - self.visible_check.set_active(not self.visible) + self.visible_check.set_active(not self.window.get_visible()) else: self._toggle_visible() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/plugins/notify.py new/pithos-1.0.1/pithos/plugins/notify.py --- old/pithos-1.0.0/pithos/plugins/notify.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/plugins/notify.py 2014-09-21 12:30:03.000000000 +0200 @@ -16,6 +16,7 @@ import logging import html +from sys import platform from pithos.plugin import PithosPlugin from pithos.pithosconfig import get_data_file from gi.repository import (GLib, Gtk) @@ -23,13 +24,29 @@ class NotifyPlugin(PithosPlugin): preference = 'notify' - has_notify = False + has_notifications = False supports_actions = False def on_prepare(self): + if platform == 'darwin': + self.prepare_osx() + else: + self.prepare_notify() + + def prepare_osx(self): + try: + from pync import Notifier + self.has_notifications = True + except ImportError: + logging.warning("pync not found.") + return + + self.notifier = Notifier + + def prepare_notify(self): try: from gi.repository import Notify - self.has_notify = True + self.has_notifications = True except ImportError: logging.warning ("libnotify not found.") return @@ -62,7 +79,7 @@ # self.notification.set_hint('resident', GLib.Variant.new_boolean(True)) def on_enable(self): - if self.has_notify: + if self.has_notifications: self.song_callback_handle = self.window.connect("song-changed", self.song_changed) self.state_changed_handle = self.window.connect("user-changed-play-state", self.playstate_changed) @@ -87,7 +104,7 @@ self.notification.add_action(skip_action, 'Skip', self.notification_skip_cb, None) - def set_notification(self, song, playing=True): + def set_notification_notify(self, song, playing): if self.supports_actions: self.set_actions(playing) @@ -100,6 +117,18 @@ self.notification.update(song.title, msg, 'audio-x-generic') self.notification.show() + def set_notification_osx(self, song, playing): + # TODO: Icons (buttons not possible?) + if playing: + self.notifier.notify('by {} from {}'.format(song.artist, song.album), + title=song.title) + + def set_notification(self, song, playing=True): + if platform == 'darwin': + self.set_notification_osx(song, playing) + else: + self.set_notification_notify(song, playing) + def notification_playpause_cb(self, notification, action, data, ignore=None): self.window.playpause_notify() @@ -115,6 +144,6 @@ GLib.idle_add(self.set_notification, window.current_song, state) def on_disable(self): - if self.has_notify: + if self.has_notifications: self.window.disconnect(self.song_callback_handle) self.window.disconnect(self.state_changed_handle) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/pithos/plugins/screensaver_pause.py new/pithos-1.0.1/pithos/plugins/screensaver_pause.py --- old/pithos-1.0.0/pithos/plugins/screensaver_pause.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/pithos/plugins/screensaver_pause.py 2014-09-21 12:30:03.000000000 +0200 @@ -1,29 +1,30 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- ### BEGIN LICENSE # Copyright (C) 2010-2012 Kevin Mehall <[email protected]> -#This program is free software: you can redistribute it and/or modify it -#under the terms of the GNU General Public License version 3, as published +#This program is free software: you can redistribute it and/or modify it +#under the terms of the GNU General Public License version 3, as published #by the Free Software Foundation. # -#This program is distributed in the hope that it will be useful, but -#WITHOUT ANY WARRANTY; without even the implied warranties of -#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +#This program is distributed in the hope that it will be useful, but +#WITHOUT ANY WARRANTY; without even the implied warranties of +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR #PURPOSE. See the GNU General Public License for more details. # -#You should have received a copy of the GNU General Public License along +#You should have received a copy of the GNU General Public License along #with this program. If not, see <http://www.gnu.org/licenses/>. ### END LICENSE from pithos.plugin import PithosPlugin import logging - +dbus = None class ScreenSaverPausePlugin(PithosPlugin): preference = 'enable_screensaverpause' session_bus = None - + def bind_session_bus(self): + global dbus try: import dbus except ImportError: @@ -34,13 +35,16 @@ return True except dbus.DBusException: return False - + def on_enable(self): if not self.bind_session_bus(): logging.error("Could not bind session bus") return self.connect_events() or logging.error("Could not connect events") + self.locked = 0 + self.wasplaying = False + def on_disable(self): if self.session_bus: self.disconnect_events() @@ -49,9 +53,16 @@ def connect_events(self): try: - self.session_bus.add_signal_receiver(self.playPause, 'ActiveChanged', 'org.gnome.ScreenSaver') - self.session_bus.add_signal_receiver(self.playPause, 'ActiveChanged', 'org.cinnamon.ScreenSaver') - self.session_bus.add_signal_receiver(self.playPause, 'ActiveChanged', 'org.freedesktop.ScreenSaver') + self.receivers = [ + self.session_bus.add_signal_receiver(*args) + for args in ((self.playPause, 'ActiveChanged', 'org.gnome.ScreenSaver'), + (self.playPause, 'ActiveChanged', 'org.cinnamon.ScreenSaver'), + (self.playPause, 'ActiveChanged', 'org.freedesktop.ScreenSaver'), + (self.pause, 'Locked', 'com.canonical.Unity.Session'), + (self.play, 'Unlocked', 'com.canonical.Unity.Session'), + ) + ] + return True except dbus.DBusException: logging.info("Enable failed") @@ -59,18 +70,27 @@ def disconnect_events(self): try: - self.session_bus.remove_signal_receiver(self.playPause, 'ActiveChanged', 'org.gnome.ScreenSaver') - self.session_bus.remove_signal_receiver(self.playPause, 'ActiveChanged', 'org.cinnamon.ScreenSaver') - self.session_bus.remove_signal_receiver(self.playPause, 'ActiveChanged', 'org.freedesktop.ScreenSaver') + for r in self.receivers: + r.remove() return True except dbus.DBusException: return False - - - def playPause(self,state): - if not state: - if self.wasplaying: - self.window.user_play() - else: + + def play(self): + self.locked -= 1 + if self.locked < 0: + self.locked = 0 + if not self.locked and self.wasplaying: + self.window.user_play() + + def pause(self): + if not self.locked: self.wasplaying = self.window.playing self.window.pause() + self.locked += 1 + + def playPause(self, screensaver_on): + if screensaver_on: + self.pause() + else: + self.play() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/requirements-osx.txt new/pithos-1.0.1/requirements-osx.txt --- old/pithos-1.0.0/requirements-osx.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/pithos-1.0.1/requirements-osx.txt 2014-09-21 12:30:03.000000000 +0200 @@ -0,0 +1,11 @@ +# scrobble +pylast + +# notify +pync + +# mediakeys +pyobjc-core +pyobjc-framework-Cocoa +pyobjc-framework-Quartz +osxmmkeys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pithos-1.0.0/setup.py new/pithos-1.0.1/setup.py --- old/pithos-1.0.0/setup.py 2014-05-02 00:33:16.000000000 +0200 +++ new/pithos-1.0.1/setup.py 2014-09-21 12:30:03.000000000 +0200 @@ -18,11 +18,27 @@ ###################### DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ###################### try: - from setuptools import setup, find_packages + from setuptools import setup, find_packages, Command except ImportError: import ez_setup ez_setup.use_setuptools() - from setuptools import setup, find_packages + from setuptools import setup, find_packages, Command + +try: + from sphinx.setup_command import BuildDoc +except ImportError: + class BuildDoc(Command): + description = "Build documentation with Sphinx." + user_options = [] + version = None + release = None + + def initialize_options(self): + pass + def finalize_options(self): + pass + def run(self): + print("Error: Sphinx not found!") import os import sys @@ -41,6 +57,7 @@ ('/usr/share/icons/hicolor/48x48/apps', ['data/icons/hicolor/pithos-tray-icon.png']), ('/usr/share/icons/ubuntu-mono-dark/apps/16', ['data/icons/ubuntu-mono-dark/pithos-tray-icon.svg']), ('/usr/share/icons/ubuntu-mono-light/apps/16', ['data/icons/ubuntu-mono-light/pithos-tray-icon.svg']), + ('/usr/share/appdata', ['data/pithos.appdata.xml']), ('/usr/share/applications', ['data/pithos.desktop']) ] else: @@ -64,6 +81,15 @@ 'Programming Language :: Python', 'Programming Language :: Python :: 3' ], + cmdclass={ + 'build_doc': BuildDoc + }, + command_options={ + 'build_doc': { + 'version': ('setup.py', VERSION), + 'release': ('setup.py', VERSION) + } + }, data_files=data_files, package_data={ 'pithos': [ -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
