<NME> changelog.rst
<BEF> *********
Changelog
*********

v2.1.4 (2022-06-15)
===================

Maintenance release.


**Development milestones**

- 2013-12-16: Ensure we never call libspotify from two different threads at the
  same time. We can't assume that the CPython GIL will ensure this for us, as
  we target non-CPython interpreters like PyPy.
===================

Maintenance release.

- Document that the playlists API is broken. If it is used, emit a warning to notify
  the user of the playlist functionality.

- Update project links.


v2.1.2 (2019-12-10)
===================

Maintenance release.

- Silently abort libspotify ``sp_*_release()`` function calls that happen
  during process shutdown, after pyspotify's global lock is freed. (Fixes:
  :issue:`202`)


v2.1.1 (2019-11-17)
===================

Maintenance release.

- Add support for Python 3.8. No changes was required, but the test suite now
  runs on this version too.

- Switch from Travis CI to CircleCI.


v2.1.0 (2019-07-08)
===================

Maintenance release.

- Drop support for Python 3.3 and 3.4, as both has reached end of life.

- Add support for Python 3.6 and 3.7. No changes was required, but the test
  suite now runs on these versions too.

- On Python 3, import :class:`Iterable`, :class:`MutableSequence`, and
  :class:`Sequence` from :mod:`collections.abc` instead of :mod:`collections`.
  This fixes a deprecation warning on Python 3.7 and prepares for Python 3.8.

- Document that the search API is broken. If it is used, raise an exception
  instead of sending the search to Spotify, as that seems to disconnect your
  session. (Fixes: :issue:`183`)

- Format source code with Black.


v2.0.5 (2015-09-22)
===================

Bug fix release.

- To follow up on the previous release, the getters for the proxy configs now
  convert empty strings in the ``sp_session_config`` struct back to
  :class:`None`. Thus, the need to set these configs to empty strings in the
  struct to make sure the cached settings are cleared from disk are now an
  internal detail, hidden from the user of pyspotify.

- Make :attr:`~spotify.Config.tracefile` default to :class:`None` and set to
  ``NULL`` in the libspotify config struct. If it is set to an empty string by
  default, libspotify will try to use a file with an empty filename for cache
  and fail with "LibError: Unable to open trace file". Now empty strings are
  set as ``NULL`` in the ``sp_session_config`` struct. (Fixes: :ms-issue:`70`)

- libspotify segfaults if the ``device_id`` config is set to an empty string.
  We now avoid this segfault if :attr:`~spotify.Config.device_id` is set to an
  empty string by setting the ``device_id`` field in libspotify's
  ``sp_session_config`` struct to ``NULL`` instead.

- As some test tools (like coverage.py 4.0) no longer support Python 3.2, we no
  longer test pyspotify on Python 3.2. Though, we have not done anything to
  intentionally break support for Python 3.2 ourselves.


v2.0.4 (2015-09-15)
===================

Bug fix release.

- It has been observed that libspotify will reuse cached proxy settings from
  previous sessions if the proxy fields on the ``sp_session_config`` struct are
  set to ``NULL``. When the ``sp_session_config`` fields are set to an empty
  string, the cached settings are updated. When attributes on
  :class:`spotify.Config` are set to :class:`None`, we now set the fields on
  ``sp_session_config`` to empty strings instead of ``NULL``.


v2.0.3 (2015-09-05)
===================

Bug fix release.

- Make moving a playlist to its own location a no-op instead of causing an
  error like libspotify does. (Fixes: :issue:`175`)

- New better installation instructions. (Fixes: :issue:`174`)


v2.0.2 (2015-08-06)
===================

Bug fix release.

- Use ``sp_session_starred_for_user_create(session, username)`` instead of
  ``sp_playlist_create(session, link)`` to get starred playlists by URI. The
  previous approach caused segfaults under some circumstances. (Fixes:
  :ms-issue:`60`)


v2.0.1 (2015-07-20)
===================

Bug fix release.

- Make :meth:`spotify.Session.get_playlist` acquire the global lock before
  modifying the global playlist cache.

- Make :class:`~spotify.Playlist` and :class:`~spotify.PlaylistContainer`
  register callbacks with libspotify if and only if a Python event handler is
  added to the object. Previously, we always registered the callbacks with
  libspotify. Hopefully, this will remove the preconditions for the crashes in
  :issue:`122`, :issue:`153`, and :issue:`165`.


v2.0.0 (2015-06-01)
===================

pyspotify 2.x is a full rewrite of pyspotify. While pyspotify 1.x is a
CPython C extension, pyspotify 2.x uses `CFFI
<https://cffi.readthedocs.io/>`__ to wrap the libspotify C library. It works
on CPython 2.7 and 3.2+, as well as PyPy 2.6+. pyspotify 2.0 makes 100% of the
libspotify 12.1.51 API available from Python, going far beyond the API coverage
of pyspotify 1.x.

The following are the changes since pyspotify 2.0.0b5.

Dependency changes
------------------

- Require cffi >= 1.0. (Fixes: :issue:`133`, :issue:`160`)

- If you're using pyspotify with PyPy you need version 2.6 or newer as older
  versions of PyPy come with a too old cffi version. For PyPy3, you'll probably
  need the yet to be released PyPy3 2.5.

ALSA sink
---------

- Changed the :class:`spotify.AlsaSink` keyword argument ``card`` to ``device``
  to align with pyalsaaudio 0.8.

- Updated to work with pyalsaaudio 0.8 which changed the signature of
  :class:`alsaaudio.PCM`. :class:`spotify.AlsaSink` still works with
  pyalsaaudio 0.7, but 0.8 is recommended at least for Python 3 users, as it
  fixes a memory leak present on Python 3 (see :issue:`127`). (Fixes:
  :issue:`162`)


v2.0.0b5 (2015-05-09)
=====================

A fifth beta with a couple of bug fixes.

Minor changes
-------------

- Changed :meth:`spotify.Link.as_playlist()` to also support creating playlists
  from links with type :attr:`spotify.LinkType.STARRED`.

- Changed all ``load()`` methods to raise :exc:`spotify.Error` instead of
  :exc:`RuntimeError` if the session isn't logged in.

- Changed from nose to py.test as test runner.

Bug fixes
---------

- Work around segfault in libspotify when :attr:`spotify.Config.cache_location`
  is set to :class:`None` and then used to create a session. (Fixes:
  :issue:`151`)

- Return a :class:`spotify.PlaylistPlaceholder` object instead of raising an
  exception if the playlist container contains an element of type
  :attr:`~spotify.PlaylistType.PLACEHOLDER`. (Fixes: :issue:`159`)


v2.0.0b4 (2015-01-13)
=====================

The fourth beta includes a single API change, a couple of API additions, and
otherwise minor tweaks to logging.

pyspotify 2.x has been verified to work on PyPy3, and PyPy3 is now part of the
test matrix.

Minor changes
-------------

- Added :attr:`spotify.Link.url` which returns an
  ``https://open.spotify.com/...`` URL for the link object.

- Adjusted ``info``, ``warning``, and ``error`` level log messages to include
  the word "Spotify" or "pyspotify" for context in applications not including
  the logger name in the log. ``debug`` level messages have not been changed,
  as it is assumed that more details, including the logger name, is included in
  debug logs.

- Added :attr:`spotify.player.Player.state` which is maintained by calls to
  the various :class:`~spotify.player.Player` methods.

Bug fixes
---------

- Fix :meth:`spotify.Playlist.reorder_tracks`. It now accepts a list of
  track indexes instead of a list of tracks. This makes it possible to
  reorder any of multiple identical tracks in a playlist and is consistent with
  :meth:`spotify.Playlist.remove_tracks`. (Fixes: :issue:`134`)

- Fix pause/resume/stop in the ``examples/shell.py`` example. (PR:
  :issue:`140`)

- Errors passed to session callbacks are now logged with the full error type
  representation, instead of just the integer value. E.g. where previously
  only "8" was logged, we now log "<ErrorType.UNABLE_TO_CONTACT_SERVER: 8>".


v2.0.0b3 (2014-05-04)
=====================

The third beta includes a couple of changes to the API in the name of
consistency, as well as three minor improvements.

Also worth noticing is that with this release, pyspotify 2.x has been in
development for a year and a day. Happy birthday, pyspotify 2!

Refactoring: Connection cleanup
-------------------------------

Parts of :class:`spotify.Session` and :attr:`spotify.Session.offline` has been
moved to :attr:`spotify.Session.connection`:

- :meth:`~spotify.offline.Offline.set_connection_type` has been replaced by
  :attr:`session.connection.type <spotify.connection.Connection.type>`,
  which now also allows reading the current connection type.

- :meth:`~spotify.offline.Offline.set_connection_rules` has been replaced by:

  - :attr:`~spotify.connection.Connection.allow_network`
  - :attr:`~spotify.connection.Connection.allow_network_if_roaming`
  - :attr:`~spotify.connection.Connection.allow_sync_over_wifi`
  - :attr:`~spotify.connection.Connection.allow_sync_over_mobile`

  The new attributes allow reading the current connection rules, so your
  application don't have to keep track of what rules it has set.

- :attr:`session.connection_state <spotify.Session.connection_state>`
  has been replaced by :attr:`session.connection.state
  <spotify.connection.Connection.state>`

Refactoring: position vs index
------------------------------

Originally, pyspotify named everything identically with libspotify and have
thus ended up with a mix of the terms "position" and "index" for the same
concept. Now, we use "index" all over the place, as that's also the name used
in the Python world at large. This changes the signature of three methods,
which may affect you if you use keyword arguments to call the methods. There's
also a number of affected events, but these changes shouldn't stop your code
from working.

Affected functions include:

- :meth:`spotify.Playlist.add_tracks` now takes ``index`` instead of
  ``position``.
- :meth:`spotify.Playlist.remove_tracks` now takes ``indexes`` instead of
  ``positions``.
- :meth:`spotify.Playlist.reorder_tracks` now takes ``new_index`` instead of
  ``new_position``.

Affected events include:

- :attr:`spotify.PlaylistContainerEvent.PLAYLIST_ADDED`
- :attr:`spotify.PlaylistContainerEvent.PLAYLIST_REMOVED`
- :attr:`spotify.PlaylistContainerEvent.PLAYLIST_MOVED`
- :attr:`spotify.PlaylistEvent.TRACKS_ADDED`
- :attr:`spotify.PlaylistEvent.TRACKS_REMOVED`
- :attr:`spotify.PlaylistEvent.TRACKS_MOVED`
- :attr:`spotify.PlaylistEvent.TRACK_CREATED_CHANGED`
- :attr:`spotify.PlaylistEvent.TRACK_SEEN_CHANGED`
- :attr:`spotify.PlaylistEvent.TRACK_MESSAGE_CHANGED`

Minor changes
-------------

- ``load()`` methods now return the object if it is already loaded, even if
  :attr:`~spotify.connection.Connection.state` isn't
  :attr:`~spotify.ConnectionState.LOGGED_IN`. Previously, a
  :exc:`RuntimeError` was raised requiring the session to be logged in and
  online before loading already loaded objects.

- :attr:`spotify.Playlist.tracks` now implements the
  :attr:`collections.MutableSequence` contract, supporting deleting items with
  ``del playlist.tracks[i]``, adding items with ``playlist.tracks[i] =
  track``, etc.

- :meth:`spotify.Session.get_link` and all other methods accepting Spotify
  URIs now also understand open.spotify.com and play.spotify.com URLs.


v2.0.0b2 (2014-04-29)
=====================

The second beta is a minor bug fix release.

Bug fixes
---------

- Fix :class:`spotify.Playlist.remove_tracks`. It now accepts a list of
  track positions instead of a list of tracks. This makes it possible to
  remove any of multiple identical tracks in a playlist. (Fixes: :issue:`128`)

Minor changes
-------------

- Make all objects compare as equal and have the same hash if they wrap the
  same libspotify object. This makes it possible to find the index of a track
  in a playlist by doing ``playlist.tracks.index(track)``, where
  ``playlist.tracks`` is a custom collection always returning new
  :class:`~spotify.Track` instances. (Related to: :issue:`128`)

- :attr:`spotify.Config.ca_certs_filename` now works on systems where
  libspotify has this field. On systems where this field isn't present in
  libspotify, assigning to it will have no effect. Previously, assignment to
  this field was a noop on all platforms because the field is missing from
  libspotify on OS X.


v2.0.0b1 (2014-04-24)
=====================

pyspotify 2.x is a full rewrite of pyspotify. While pyspotify 1.x is a
CPython C extension, pyspotify 2.x uses `CFFI <http://cffi.readthedocs.io/>`__
to make 100% of the libspotify C library available from Python. It works on
CPython 2.7 and 3.2+, as well as PyPy 2.1+.

Since the previous release, pyspotify has become thread safe. That is,
pyspotify can safely be used from multiple threads. The added thread safety
made an integrated event loop possible, which greatly simplifies the usage of
pyspotify, as can be seen from the updated example in ``examples/shell.py``.
Audio sink helpers for ALSA and PortAudio have been added, together with
updated examples that can play music. A number of bugs have been fixed, and at
the time of the release, there are no known issues.

The pyspotify 2.0.0b1 release marks the completion of all planned features for
pyspotify 2.x. The plans for the next releases are focused on fixing bugs as
they surface, incrementally improving the documentation, and integrating
feedback from increased usage of the library in the wild.

Feature: Thread safety
----------------------

- Hold the global lock while we are working with pointers returned by
  libspotify. This ensures that we never call libspotify from another thread
  while we are still working on the data returned by the previous libspotify
  call, which could make the data garbage.

- Ensure we never edit shared data structures without holding the global lock.

Feature: Event loop
-------------------

- Add :class:`spotify.EventLoop` helper thread that reacts to
  :class:`~spotify.SessionEvent.NOTIFY_MAIN_THREAD` events and calls
  :meth:`~spotify.Session.process_events` for you when appropriate.

- Update ``examples/shell.py`` to be a lot simpler with the help of the new
  event loop.

Feature: Audio playback
-----------------------

- Add :class:`spotify.AlsaSink`, an audio sink for playback through ALSA on
  Linux systems.

- Add :class:`spotify.PortAudioSink`, an audio sink for playback through
  PortAudio on most platforms, including Linux, OS X, and Windows.

- Update ``examples/shell.py`` to use the ALSA sink to play music.

- Add ``examples/play_track.py`` as a simpler example of audio playback.

Refactoring: Remove global state
--------------------------------

To prepare for removing all global state, the use of the module attribute
:attr:`spotify.session_instance` has been replaced with explicit passing of the
session object to all objects that needs it. To allow for this, the following
new methods have been added, and should be used instead of their old
equivalents:

- :meth:`spotify.Session.get_link` replaces :class:`spotify.Link`.
- :meth:`spotify.Session.get_track` replaces :class:`spotify.Track`.
- :meth:`spotify.Session.get_local_track` replaces
  :class:`spotify.LocalTrack`.
- :meth:`spotify.Session.get_album` replaces :class:`spotify.Album`.
- :meth:`spotify.Session.get_artist` replaces :class:`spotify.Artist`.
- :meth:`spotify.Session.get_playlist` replaces :class:`spotify.Playlist`.
- :meth:`spotify.Session.get_user` replaces :class:`spotify.User`.
- :meth:`spotify.Session.get_image` replaces :class:`spotify.Image`.
- :meth:`spotify.Session.get_toplist` replaces :class:`spotify.Toplist`.

Refactoring: Consistent naming of ``Session`` members
-----------------------------------------------------

With all the above getters added to the :class:`spotify.Session` object, it
made sense to rename some existing methods of :class:`~spotify.Session` for
consistency:

- :meth:`spotify.Session.starred_for_user`
  is replaced by :meth:`~spotify.Session.get_starred`.

- :attr:`spotify.Session.starred` to get the currently logged in user's starred
  playlist is replaced by :meth:`~spotify.Session.get_starred` without any
  arguments.

- :meth:`spotify.Session.get_published_playlists` replaces
  :meth:`~spotify.Session.published_playlists_for_user`. As previously, it
  returns the published playlists for the currently logged in user if no
  username is provided.

Refactoring: Consistent naming of ``threading.Event`` objects
-------------------------------------------------------------

All :class:`threading.Event` objects have been renamed to be consistently
named across classes.

- :attr:`spotify.AlbumBrowser.loaded_event` replaces
  :attr:`spotify.AlbumBrowser.complete_event`.
- :attr:`spotify.ArtistBrowser.loaded_event` replaces
  :attr:`spotify.ArtistBrowser.complete_event`.
- :attr:`spotify.Image.loaded_event` replaces :attr:`spotify.Image.load_event`.
- :attr:`spotify.InboxPostResult.loaded_event` replaces
  :attr:`spotify.InboxPostResult.complete_event`.
- :attr:`spotify.Search.loaded_event` replaces
  :attr:`spotify.Search.complete_event`.
- :attr:`spotify.Toplist.loaded_event` replaces
  :attr:`spotify.Toplist.complete_event`.

Refactoring: Change how to register image load listeners
--------------------------------------------------------

pyspotify has two main schemes for registering listener functions:

- Objects that only emit an event when it is done loading, like
  :class:`~spotify.AlbumBrowser`, :class:`~spotify.ArtistBrowser`,
  :class:`~spotify.InboxPostResult`, :class:`~spotify.Search`, and
  :class:`~spotify.Toplist`, accept a single callback as a ``callback``
  argument to its constructor or constructor methods.

- Objects that have multiple callback events, like :class:`~spotify.Session`,
  :class:`~spotify.PlaylistContainer`, and :class:`~spotify.Playlist`, accept
  the registration and unregistration of one or more listener functions for
  each event it emits. This can happen any time during the object's life cycle.

Due to pyspotify's close mapping to libspotify's organization, :class:`Image`
objects used to use a third variant with two methods,
:meth:`~spotify.Image.add_load_callback` and
:meth:`~spotify.Image.remove_load_callback`, for adding and removing load
callbacks. These methods have now been removed, and :class:`~spotify.Image`
accepts a ``callback`` argument to its constructor and constructor methods:

- :meth:`spotify.Album.cover` accepts a ``callback`` argument.
- :meth:`spotify.Artist.portrait` accepts a ``callback`` argument.
- :meth:`spotify.ArtistBrowser.portraits` is now a method and accepts a
  ``callback`` argument.
- :meth:`spotify.Link.as_image` accepts a ``callback`` argument.
- :meth:`spotify.Playlist.image` is now a method and accepts a ``callback``
  argument.
- :meth:`spotify.Session.get_image` accepts a ``callback`` argument.

Bug fixes
---------

- Remove multiple extra ``sp_link_add_ref()`` calls, potentially causing
  memory leaks in libspotify.

- Add missing error check to :meth:`spotify.Playlist.add_tracks`.

- Keep album, artist, image, inbox, search, and toplist objects alive until
  their complete/load callbacks have been called, even if the library user
  doesn't keep any references to the objects. (Fixes: :issue:`121`)

- Fix flipped logic causing crash in :meth:`spotify.Album.cover_link`. (Fixes:
  :issue:`126`)

- Work around segfault in libspotify if
  :attr:`~spotify.social.Social.private_session` is set before the session is
  logged in and the first events are processed. This is a bug in libspotify
  which has been reported to Spotify through their IRC channel.

- Multiple attributes on :class:`~spotify.Track` raised an exception if
  accessed before the track was loaded. They now return :class:`None` or
  similar as documented.

- Fix segfault when creating local tracks without all arguments specified.
  ``NULL`` was used as the placeholder instead of the empty string.

- Support negative indexes on all custom sequence types. For example,
  ``collection[-1]`` returns the last element in the collection.

- We now cache playlists when created from URIs. Previously, only playlists
  created from ``sp_playlist`` objects were cached. This avoids a potentially
  large number of wrapper object recreations due to a flood of updates to the
  playlist when it is initially loaded. Combined with having registered a
  callback for the libspotify ``playlist_update_in_progress`` callback, this
  could cause deep call stacks reaching the maximum recursion depth. (Fixes:
  :issue:`122`)

Minor changes
-------------

- Add :func:`spotify.get_libspotify_api_version` and
  :func:`spotify.get_libspotify_build_id`.

- Running ``python setup.py test`` now runs the test suite.

- The tests are now compatible with CPython 3.4. No changes to the
  implementation was required.

- The test suite now runs on Mac OS X, using CPython 2.7, 3.2, 3.3, 3.4, and
  PyPy 2.2, on every push to GitHub.


v2.0.0a1 (2014-02-14)
=====================

pyspotify 2.x is a full rewrite of pyspotify. While pyspotify 1.x is a
CPython C extension, pyspotify 2.x uses `CFFI <http://cffi.readthedocs.io/>`__
to wrap the libspotify C library. It works on CPython 2.7 and 3.2+, as well as
PyPy 2.1+.

This first alpha release of pyspotify 2.0.0 makes 100% of the libspotify
12.1.51 API available from Python, going far beyond the API coverage of
pyspotify 1.x.

pyspotify 2.0.0a1 has an extensive test suite with 98% line coverage. All tests
pass on all combinations of CPython 2.7, 3.2, 3.3, PyPy 2.2 running on Linux on
i386, amd64, armel, and armhf. Mac OS X should work, but has not been tested
recently.

This release *does not* provide:

- thread safety,

- an event loop for regularly processing libspotify events, or

- audio playback drivers.

These features are planned for the upcoming prereleases.


Development milestones
----------------------

- 2014-02-13: Playlist callbacks complete. pyspotify 2.x now covers 100% of
  the libspotify 12 API. Docs reviewed, quickstart guide extended. Redundant
  getters/setters removed.

- 2014-02-08: Playlist container callbacks complete.

- 2014-01-31: Redesign session event listening to a model supporting multiple
  listeners per event, with a nicer API for registering listeners.

- 2013-12-16: Ensure we never call libspotify from two different threads at the
  same time. We can't assume that the CPython GIL will ensure this for us, as
  we target non-CPython interpreters like PyPy.

- 2013-12-13: Artist browsing complete.

- 2013-12-13: Album browsing complete.

- 2013-11-29: Toplist subsystem complete.

- 2013-11-27: Inbox subsystem complete.

- 2013-10-14: Playlist subsystem *almost* complete.

- 2013-06-21: Search subsystem complete.

- 2013-06-10: Album subsystem complete.

- 2013-06-09: Track and artist subsystem complete.

- 2013-06-02: Session subsystem complete, with all methods.

- 2013-06-01: Session callbacks complete.

- 2013-05-25: Session config complete.

- 2013-05-16: Link subsystem complete.

- 2013-05-09: User subsystem complete.

- 2013-05-08: Session configuration and creation, with login and logout works.

- 2013-05-03: The Python object ``spotify.lib`` is a working CFFI wrapper
  around the entire libspotify 12 API. This will be the foundation for more
  pythonic APIs. The library currently works on CPython 2.7, 3.3 and PyPy 2.


v1.x series
===========

See the `pyspotify 1.x changelog
<http://pyspotify.readthedocs.io/en/v1.x-develop/changes/>`__.

<MSG> docs: Update changelog and dev plans

<DFF> @@ -10,6 +10,9 @@ around libspotify instead of a CPython C extension.
 
 **Development milestones**
 
+- 2014-01-31: Redesign session event listening to a model supporting multiple
+  listeners per event, with a nicer API for registering listeners.
+
 - 2013-12-16: Ensure we never call libspotify from two different threads at the
   same time. We can't assume that the CPython GIL will ensure this for us, as
   we target non-CPython interpreters like PyPy.

{'commit': "diff --git a/docs/changelog.rst b/docs/changelog.rst\nindex 646e8f7..a09d691 100644\n--- a/docs/changelog.rst\n+++ b/docs/changelog.rst\n@@ -10,6 +10,9 @@ around libspotify instead of a CPython C extension.\n \n **Development milestones**\n \n+- 2014-01-31: Redesign session event listening to a model supporting multiple\n+  listeners per event, with a nicer API for registering listeners.\n+\n - 2013-12-16: Ensure we never call libspotify from two different threads at the\n   same time. We can't assume that the CPython GIL will ensure this for us, as\n   we target non-CPython interpreters like PyPy.\ndiff --git a/docs/plans.rst b/docs/plans.rst\nindex 05dd2b6..a78736d 100644\n--- a/docs/plans.rst\n+++ b/docs/plans.rst\n@@ -13,24 +13,17 @@ for audio playback.\n v2.0.0a1: 100% complete bindings\n ================================\n \n-- Complete the playlist subsystem. Mostly need to decide on a pretty way to\n-  register callbacks, if we should allow multiple callbacks per event, etc.\n+- Redo all callbacks to follow the same pattern as used for session events.\n \n-\n-v2.0.0a2: More consistent API\n-=============================\n-\n-- Redo all other callbacks to follow the same pattern as used for playlists.\n+- Add support for playlist events and playlist container events.\n \n - Get rid of a lot of properties or getter/setter pairs. The API doesn't feel\n   consistent enough as is.\n \n-- Update jukebox example to work again.\n-\n-- Write first draft of the tutorial.\n+- Complete first draft of the tutorial.\n \n \n-v2.0.0a3: Thread safety\n+v2.0.0a2: Thread safety\n =======================\n \n - Ensure we never call libspotify from another thread while a method is still\n", 'commit_subject': 'docs: Update changelog and dev plans', 'repo_name': 'jodal/pyspotify', 'committer': {'name': 'Stein Magnus Jodal', 'email': 'e14d2e665cf0bcfd7f54daa10a36c228abaf843a@jodal.no', 'time_sec': '1391210096', 'tz_offset': '60', 'date': {'seconds': '1391210096'}}, 'commit_tree': '5550994442b752bb766f2c9e8fad68d4292c2b2b', 'commit_message': 'docs: Update changelog and dev plans\n', 'language_name': 'Python', 'license': 'apache-2.0', 'hunks': "@@ -10,6 +10,9 @@ around libspotify instead of a CPython C extension.\n \n **Development milestones**\n \n+- 2014-01-31: Redesign session event listening to a model supporting multiple\n+  listeners per event, with a nicer API for registering listeners.\n+\n - 2013-12-16: Ensure we never call libspotify from two different threads at the\n   same time. We can't assume that the CPython GIL will ensure this for us, as\n   we target non-CPython interpreters like PyPy.\n", 'addition_count': 3, 'deletion_count': 0, 'src_file': 'a/docs/changelog.rst', 'tgt_file': 'b/docs/changelog.rst', 'file_extension': '.rst', 'before_file': '*********\nChangelog\n*********\n\nv2.1.4 (2022-06-15)\n===================\n\nMaintenance release.\n\n\n**Development milestones**\n\n- 2013-12-16: Ensure we never call libspotify from two different threads at the\n  same time. We can\'t assume that the CPython GIL will ensure this for us, as\n  we target non-CPython interpreters like PyPy.\n===================\n\nMaintenance release.\n\n- Document that the playlists API is broken. If it is used, emit a warning to notify\n  the user of the playlist functionality.\n\n- Update project links.\n\n\nv2.1.2 (2019-12-10)\n===================\n\nMaintenance release.\n\n- Silently abort libspotify ``sp_*_release()`` function calls that happen\n  during process shutdown, after pyspotify\'s global lock is freed. (Fixes:\n  :issue:`202`)\n\n\nv2.1.1 (2019-11-17)\n===================\n\nMaintenance release.\n\n- Add support for Python 3.8. No changes was required, but the test suite now\n  runs on this version too.\n\n- Switch from Travis CI to CircleCI.\n\n\nv2.1.0 (2019-07-08)\n===================\n\nMaintenance release.\n\n- Drop support for Python 3.3 and 3.4, as both has reached end of life.\n\n- Add support for Python 3.6 and 3.7. No changes was required, but the test\n  suite now runs on these versions too.\n\n- On Python 3, import :class:`Iterable`, :class:`MutableSequence`, and\n  :class:`Sequence` from :mod:`collections.abc` instead of :mod:`collections`.\n  This fixes a deprecation warning on Python 3.7 and prepares for Python 3.8.\n\n- Document that the search API is broken. If it is used, raise an exception\n  instead of sending the search to Spotify, as that seems to disconnect your\n  session. (Fixes: :issue:`183`)\n\n- Format source code with Black.\n\n\nv2.0.5 (2015-09-22)\n===================\n\nBug fix release.\n\n- To follow up on the previous release, the getters for the proxy configs now\n  convert empty strings in the ``sp_session_config`` struct back to\n  :class:`None`. Thus, the need to set these configs to empty strings in the\n  struct to make sure the cached settings are cleared from disk are now an\n  internal detail, hidden from the user of pyspotify.\n\n- Make :attr:`~spotify.Config.tracefile` default to :class:`None` and set to\n  ``NULL`` in the libspotify config struct. If it is set to an empty string by\n  default, libspotify will try to use a file with an empty filename for cache\n  and fail with "LibError: Unable to open trace file". Now empty strings are\n  set as ``NULL`` in the ``sp_session_config`` struct. (Fixes: :ms-issue:`70`)\n\n- libspotify segfaults if the ``device_id`` config is set to an empty string.\n  We now avoid this segfault if :attr:`~spotify.Config.device_id` is set to an\n  empty string by setting the ``device_id`` field in libspotify\'s\n  ``sp_session_config`` struct to ``NULL`` instead.\n\n- As some test tools (like coverage.py 4.0) no longer support Python 3.2, we no\n  longer test pyspotify on Python 3.2. Though, we have not done anything to\n  intentionally break support for Python 3.2 ourselves.\n\n\nv2.0.4 (2015-09-15)\n===================\n\nBug fix release.\n\n- It has been observed that libspotify will reuse cached proxy settings from\n  previous sessions if the proxy fields on the ``sp_session_config`` struct are\n  set to ``NULL``. When the ``sp_session_config`` fields are set to an empty\n  string, the cached settings are updated. When attributes on\n  :class:`spotify.Config` are set to :class:`None`, we now set the fields on\n  ``sp_session_config`` to empty strings instead of ``NULL``.\n\n\nv2.0.3 (2015-09-05)\n===================\n\nBug fix release.\n\n- Make moving a playlist to its own location a no-op instead of causing an\n  error like libspotify does. (Fixes: :issue:`175`)\n\n- New better installation instructions. (Fixes: :issue:`174`)\n\n\nv2.0.2 (2015-08-06)\n===================\n\nBug fix release.\n\n- Use ``sp_session_starred_for_user_create(session, username)`` instead of\n  ``sp_playlist_create(session, link)`` to get starred playlists by URI. The\n  previous approach caused segfaults under some circumstances. (Fixes:\n  :ms-issue:`60`)\n\n\nv2.0.1 (2015-07-20)\n===================\n\nBug fix release.\n\n- Make :meth:`spotify.Session.get_playlist` acquire the global lock before\n  modifying the global playlist cache.\n\n- Make :class:`~spotify.Playlist` and :class:`~spotify.PlaylistContainer`\n  register callbacks with libspotify if and only if a Python event handler is\n  added to the object. Previously, we always registered the callbacks with\n  libspotify. Hopefully, this will remove the preconditions for the crashes in\n  :issue:`122`, :issue:`153`, and :issue:`165`.\n\n\nv2.0.0 (2015-06-01)\n===================\n\npyspotify 2.x is a full rewrite of pyspotify. While pyspotify 1.x is a\nCPython C extension, pyspotify 2.x uses `CFFI\n<https://cffi.readthedocs.io/>`__ to wrap the libspotify C library. It works\non CPython 2.7 and 3.2+, as well as PyPy 2.6+. pyspotify 2.0 makes 100% of the\nlibspotify 12.1.51 API available from Python, going far beyond the API coverage\nof pyspotify 1.x.\n\nThe following are the changes since pyspotify 2.0.0b5.\n\nDependency changes\n------------------\n\n- Require cffi >= 1.0. (Fixes: :issue:`133`, :issue:`160`)\n\n- If you\'re using pyspotify with PyPy you need version 2.6 or newer as older\n  versions of PyPy come with a too old cffi version. For PyPy3, you\'ll probably\n  need the yet to be released PyPy3 2.5.\n\nALSA sink\n---------\n\n- Changed the :class:`spotify.AlsaSink` keyword argument ``card`` to ``device``\n  to align with pyalsaaudio 0.8.\n\n- Updated to work with pyalsaaudio 0.8 which changed the signature of\n  :class:`alsaaudio.PCM`. :class:`spotify.AlsaSink` still works with\n  pyalsaaudio 0.7, but 0.8 is recommended at least for Python 3 users, as it\n  fixes a memory leak present on Python 3 (see :issue:`127`). (Fixes:\n  :issue:`162`)\n\n\nv2.0.0b5 (2015-05-09)\n=====================\n\nA fifth beta with a couple of bug fixes.\n\nMinor changes\n-------------\n\n- Changed :meth:`spotify.Link.as_playlist()` to also support creating playlists\n  from links with type :attr:`spotify.LinkType.STARRED`.\n\n- Changed all ``load()`` methods to raise :exc:`spotify.Error` instead of\n  :exc:`RuntimeError` if the session isn\'t logged in.\n\n- Changed from nose to py.test as test runner.\n\nBug fixes\n---------\n\n- Work around segfault in libspotify when :attr:`spotify.Config.cache_location`\n  is set to :class:`None` and then used to create a session. (Fixes:\n  :issue:`151`)\n\n- Return a :class:`spotify.PlaylistPlaceholder` object instead of raising an\n  exception if the playlist container contains an element of type\n  :attr:`~spotify.PlaylistType.PLACEHOLDER`. (Fixes: :issue:`159`)\n\n\nv2.0.0b4 (2015-01-13)\n=====================\n\nThe fourth beta includes a single API change, a couple of API additions, and\notherwise minor tweaks to logging.\n\npyspotify 2.x has been verified to work on PyPy3, and PyPy3 is now part of the\ntest matrix.\n\nMinor changes\n-------------\n\n- Added :attr:`spotify.Link.url` which returns an\n  ``https://open.spotify.com/...`` URL for the link object.\n\n- Adjusted ``info``, ``warning``, and ``error`` level log messages to include\n  the word "Spotify" or "pyspotify" for context in applications not including\n  the logger name in the log. ``debug`` level messages have not been changed,\n  as it is assumed that more details, including the logger name, is included in\n  debug logs.\n\n- Added :attr:`spotify.player.Player.state` which is maintained by calls to\n  the various :class:`~spotify.player.Player` methods.\n\nBug fixes\n---------\n\n- Fix :meth:`spotify.Playlist.reorder_tracks`. It now accepts a list of\n  track indexes instead of a list of tracks. This makes it possible to\n  reorder any of multiple identical tracks in a playlist and is consistent with\n  :meth:`spotify.Playlist.remove_tracks`. (Fixes: :issue:`134`)\n\n- Fix pause/resume/stop in the ``examples/shell.py`` example. (PR:\n  :issue:`140`)\n\n- Errors passed to session callbacks are now logged with the full error type\n  representation, instead of just the integer value. E.g. where previously\n  only "8" was logged, we now log "<ErrorType.UNABLE_TO_CONTACT_SERVER: 8>".\n\n\nv2.0.0b3 (2014-05-04)\n=====================\n\nThe third beta includes a couple of changes to the API in the name of\nconsistency, as well as three minor improvements.\n\nAlso worth noticing is that with this release, pyspotify 2.x has been in\ndevelopment for a year and a day. Happy birthday, pyspotify 2!\n\nRefactoring: Connection cleanup\n-------------------------------\n\nParts of :class:`spotify.Session` and :attr:`spotify.Session.offline` has been\nmoved to :attr:`spotify.Session.connection`:\n\n- :meth:`~spotify.offline.Offline.set_connection_type` has been replaced by\n  :attr:`session.connection.type <spotify.connection.Connection.type>`,\n  which now also allows reading the current connection type.\n\n- :meth:`~spotify.offline.Offline.set_connection_rules` has been replaced by:\n\n  - :attr:`~spotify.connection.Connection.allow_network`\n  - :attr:`~spotify.connection.Connection.allow_network_if_roaming`\n  - :attr:`~spotify.connection.Connection.allow_sync_over_wifi`\n  - :attr:`~spotify.connection.Connection.allow_sync_over_mobile`\n\n  The new attributes allow reading the current connection rules, so your\n  application don\'t have to keep track of what rules it has set.\n\n- :attr:`session.connection_state <spotify.Session.connection_state>`\n  has been replaced by :attr:`session.connection.state\n  <spotify.connection.Connection.state>`\n\nRefactoring: position vs index\n------------------------------\n\nOriginally, pyspotify named everything identically with libspotify and have\nthus ended up with a mix of the terms "position" and "index" for the same\nconcept. Now, we use "index" all over the place, as that\'s also the name used\nin the Python world at large. This changes the signature of three methods,\nwhich may affect you if you use keyword arguments to call the methods. There\'s\nalso a number of affected events, but these changes shouldn\'t stop your code\nfrom working.\n\nAffected functions include:\n\n- :meth:`spotify.Playlist.add_tracks` now takes ``index`` instead of\n  ``position``.\n- :meth:`spotify.Playlist.remove_tracks` now takes ``indexes`` instead of\n  ``positions``.\n- :meth:`spotify.Playlist.reorder_tracks` now takes ``new_index`` instead of\n  ``new_position``.\n\nAffected events include:\n\n- :attr:`spotify.PlaylistContainerEvent.PLAYLIST_ADDED`\n- :attr:`spotify.PlaylistContainerEvent.PLAYLIST_REMOVED`\n- :attr:`spotify.PlaylistContainerEvent.PLAYLIST_MOVED`\n- :attr:`spotify.PlaylistEvent.TRACKS_ADDED`\n- :attr:`spotify.PlaylistEvent.TRACKS_REMOVED`\n- :attr:`spotify.PlaylistEvent.TRACKS_MOVED`\n- :attr:`spotify.PlaylistEvent.TRACK_CREATED_CHANGED`\n- :attr:`spotify.PlaylistEvent.TRACK_SEEN_CHANGED`\n- :attr:`spotify.PlaylistEvent.TRACK_MESSAGE_CHANGED`\n\nMinor changes\n-------------\n\n- ``load()`` methods now return the object if it is already loaded, even if\n  :attr:`~spotify.connection.Connection.state` isn\'t\n  :attr:`~spotify.ConnectionState.LOGGED_IN`. Previously, a\n  :exc:`RuntimeError` was raised requiring the session to be logged in and\n  online before loading already loaded objects.\n\n- :attr:`spotify.Playlist.tracks` now implements the\n  :attr:`collections.MutableSequence` contract, supporting deleting items with\n  ``del playlist.tracks[i]``, adding items with ``playlist.tracks[i] =\n  track``, etc.\n\n- :meth:`spotify.Session.get_link` and all other methods accepting Spotify\n  URIs now also understand open.spotify.com and play.spotify.com URLs.\n\n\nv2.0.0b2 (2014-04-29)\n=====================\n\nThe second beta is a minor bug fix release.\n\nBug fixes\n---------\n\n- Fix :class:`spotify.Playlist.remove_tracks`. It now accepts a list of\n  track positions instead of a list of tracks. This makes it possible to\n  remove any of multiple identical tracks in a playlist. (Fixes: :issue:`128`)\n\nMinor changes\n-------------\n\n- Make all objects compare as equal and have the same hash if they wrap the\n  same libspotify object. This makes it possible to find the index of a track\n  in a playlist by doing ``playlist.tracks.index(track)``, where\n  ``playlist.tracks`` is a custom collection always returning new\n  :class:`~spotify.Track` instances. (Related to: :issue:`128`)\n\n- :attr:`spotify.Config.ca_certs_filename` now works on systems where\n  libspotify has this field. On systems where this field isn\'t present in\n  libspotify, assigning to it will have no effect. Previously, assignment to\n  this field was a noop on all platforms because the field is missing from\n  libspotify on OS X.\n\n\nv2.0.0b1 (2014-04-24)\n=====================\n\npyspotify 2.x is a full rewrite of pyspotify. While pyspotify 1.x is a\nCPython C extension, pyspotify 2.x uses `CFFI <http://cffi.readthedocs.io/>`__\nto make 100% of the libspotify C library available from Python. It works on\nCPython 2.7 and 3.2+, as well as PyPy 2.1+.\n\nSince the previous release, pyspotify has become thread safe. That is,\npyspotify can safely be used from multiple threads. The added thread safety\nmade an integrated event loop possible, which greatly simplifies the usage of\npyspotify, as can be seen from the updated example in ``examples/shell.py``.\nAudio sink helpers for ALSA and PortAudio have been added, together with\nupdated examples that can play music. A number of bugs have been fixed, and at\nthe time of the release, there are no known issues.\n\nThe pyspotify 2.0.0b1 release marks the completion of all planned features for\npyspotify 2.x. The plans for the next releases are focused on fixing bugs as\nthey surface, incrementally improving the documentation, and integrating\nfeedback from increased usage of the library in the wild.\n\nFeature: Thread safety\n----------------------\n\n- Hold the global lock while we are working with pointers returned by\n  libspotify. This ensures that we never call libspotify from another thread\n  while we are still working on the data returned by the previous libspotify\n  call, which could make the data garbage.\n\n- Ensure we never edit shared data structures without holding the global lock.\n\nFeature: Event loop\n-------------------\n\n- Add :class:`spotify.EventLoop` helper thread that reacts to\n  :class:`~spotify.SessionEvent.NOTIFY_MAIN_THREAD` events and calls\n  :meth:`~spotify.Session.process_events` for you when appropriate.\n\n- Update ``examples/shell.py`` to be a lot simpler with the help of the new\n  event loop.\n\nFeature: Audio playback\n-----------------------\n\n- Add :class:`spotify.AlsaSink`, an audio sink for playback through ALSA on\n  Linux systems.\n\n- Add :class:`spotify.PortAudioSink`, an audio sink for playback through\n  PortAudio on most platforms, including Linux, OS X, and Windows.\n\n- Update ``examples/shell.py`` to use the ALSA sink to play music.\n\n- Add ``examples/play_track.py`` as a simpler example of audio playback.\n\nRefactoring: Remove global state\n--------------------------------\n\nTo prepare for removing all global state, the use of the module attribute\n:attr:`spotify.session_instance` has been replaced with explicit passing of the\nsession object to all objects that needs it. To allow for this, the following\nnew methods have been added, and should be used instead of their old\nequivalents:\n\n- :meth:`spotify.Session.get_link` replaces :class:`spotify.Link`.\n- :meth:`spotify.Session.get_track` replaces :class:`spotify.Track`.\n- :meth:`spotify.Session.get_local_track` replaces\n  :class:`spotify.LocalTrack`.\n- :meth:`spotify.Session.get_album` replaces :class:`spotify.Album`.\n- :meth:`spotify.Session.get_artist` replaces :class:`spotify.Artist`.\n- :meth:`spotify.Session.get_playlist` replaces :class:`spotify.Playlist`.\n- :meth:`spotify.Session.get_user` replaces :class:`spotify.User`.\n- :meth:`spotify.Session.get_image` replaces :class:`spotify.Image`.\n- :meth:`spotify.Session.get_toplist` replaces :class:`spotify.Toplist`.\n\nRefactoring: Consistent naming of ``Session`` members\n-----------------------------------------------------\n\nWith all the above getters added to the :class:`spotify.Session` object, it\nmade sense to rename some existing methods of :class:`~spotify.Session` for\nconsistency:\n\n- :meth:`spotify.Session.starred_for_user`\n  is replaced by :meth:`~spotify.Session.get_starred`.\n\n- :attr:`spotify.Session.starred` to get the currently logged in user\'s starred\n  playlist is replaced by :meth:`~spotify.Session.get_starred` without any\n  arguments.\n\n- :meth:`spotify.Session.get_published_playlists` replaces\n  :meth:`~spotify.Session.published_playlists_for_user`. As previously, it\n  returns the published playlists for the currently logged in user if no\n  username is provided.\n\nRefactoring: Consistent naming of ``threading.Event`` objects\n-------------------------------------------------------------\n\nAll :class:`threading.Event` objects have been renamed to be consistently\nnamed across classes.\n\n- :attr:`spotify.AlbumBrowser.loaded_event` replaces\n  :attr:`spotify.AlbumBrowser.complete_event`.\n- :attr:`spotify.ArtistBrowser.loaded_event` replaces\n  :attr:`spotify.ArtistBrowser.complete_event`.\n- :attr:`spotify.Image.loaded_event` replaces :attr:`spotify.Image.load_event`.\n- :attr:`spotify.InboxPostResult.loaded_event` replaces\n  :attr:`spotify.InboxPostResult.complete_event`.\n- :attr:`spotify.Search.loaded_event` replaces\n  :attr:`spotify.Search.complete_event`.\n- :attr:`spotify.Toplist.loaded_event` replaces\n  :attr:`spotify.Toplist.complete_event`.\n\nRefactoring: Change how to register image load listeners\n--------------------------------------------------------\n\npyspotify has two main schemes for registering listener functions:\n\n- Objects that only emit an event when it is done loading, like\n  :class:`~spotify.AlbumBrowser`, :class:`~spotify.ArtistBrowser`,\n  :class:`~spotify.InboxPostResult`, :class:`~spotify.Search`, and\n  :class:`~spotify.Toplist`, accept a single callback as a ``callback``\n  argument to its constructor or constructor methods.\n\n- Objects that have multiple callback events, like :class:`~spotify.Session`,\n  :class:`~spotify.PlaylistContainer`, and :class:`~spotify.Playlist`, accept\n  the registration and unregistration of one or more listener functions for\n  each event it emits. This can happen any time during the object\'s life cycle.\n\nDue to pyspotify\'s close mapping to libspotify\'s organization, :class:`Image`\nobjects used to use a third variant with two methods,\n:meth:`~spotify.Image.add_load_callback` and\n:meth:`~spotify.Image.remove_load_callback`, for adding and removing load\ncallbacks. These methods have now been removed, and :class:`~spotify.Image`\naccepts a ``callback`` argument to its constructor and constructor methods:\n\n- :meth:`spotify.Album.cover` accepts a ``callback`` argument.\n- :meth:`spotify.Artist.portrait` accepts a ``callback`` argument.\n- :meth:`spotify.ArtistBrowser.portraits` is now a method and accepts a\n  ``callback`` argument.\n- :meth:`spotify.Link.as_image` accepts a ``callback`` argument.\n- :meth:`spotify.Playlist.image` is now a method and accepts a ``callback``\n  argument.\n- :meth:`spotify.Session.get_image` accepts a ``callback`` argument.\n\nBug fixes\n---------\n\n- Remove multiple extra ``sp_link_add_ref()`` calls, potentially causing\n  memory leaks in libspotify.\n\n- Add missing error check to :meth:`spotify.Playlist.add_tracks`.\n\n- Keep album, artist, image, inbox, search, and toplist objects alive until\n  their complete/load callbacks have been called, even if the library user\n  doesn\'t keep any references to the objects. (Fixes: :issue:`121`)\n\n- Fix flipped logic causing crash in :meth:`spotify.Album.cover_link`. (Fixes:\n  :issue:`126`)\n\n- Work around segfault in libspotify if\n  :attr:`~spotify.social.Social.private_session` is set before the session is\n  logged in and the first events are processed. This is a bug in libspotify\n  which has been reported to Spotify through their IRC channel.\n\n- Multiple attributes on :class:`~spotify.Track` raised an exception if\n  accessed before the track was loaded. They now return :class:`None` or\n  similar as documented.\n\n- Fix segfault when creating local tracks without all arguments specified.\n  ``NULL`` was used as the placeholder instead of the empty string.\n\n- Support negative indexes on all custom sequence types. For example,\n  ``collection[-1]`` returns the last element in the collection.\n\n- We now cache playlists when created from URIs. Previously, only playlists\n  created from ``sp_playlist`` objects were cached. This avoids a potentially\n  large number of wrapper object recreations due to a flood of updates to the\n  playlist when it is initially loaded. Combined with having registered a\n  callback for the libspotify ``playlist_update_in_progress`` callback, this\n  could cause deep call stacks reaching the maximum recursion depth. (Fixes:\n  :issue:`122`)\n\nMinor changes\n-------------\n\n- Add :func:`spotify.get_libspotify_api_version` and\n  :func:`spotify.get_libspotify_build_id`.\n\n- Running ``python setup.py test`` now runs the test suite.\n\n- The tests are now compatible with CPython 3.4. No changes to the\n  implementation was required.\n\n- The test suite now runs on Mac OS X, using CPython 2.7, 3.2, 3.3, 3.4, and\n  PyPy 2.2, on every push to GitHub.\n\n\nv2.0.0a1 (2014-02-14)\n=====================\n\npyspotify 2.x is a full rewrite of pyspotify. While pyspotify 1.x is a\nCPython C extension, pyspotify 2.x uses `CFFI <http://cffi.readthedocs.io/>`__\nto wrap the libspotify C library. It works on CPython 2.7 and 3.2+, as well as\nPyPy 2.1+.\n\nThis first alpha release of pyspotify 2.0.0 makes 100% of the libspotify\n12.1.51 API available from Python, going far beyond the API coverage of\npyspotify 1.x.\n\npyspotify 2.0.0a1 has an extensive test suite with 98% line coverage. All tests\npass on all combinations of CPython 2.7, 3.2, 3.3, PyPy 2.2 running on Linux on\ni386, amd64, armel, and armhf. Mac OS X should work, but has not been tested\nrecently.\n\nThis release *does not* provide:\n\n- thread safety,\n\n- an event loop for regularly processing libspotify events, or\n\n- audio playback drivers.\n\nThese features are planned for the upcoming prereleases.\n\n\nDevelopment milestones\n----------------------\n\n- 2014-02-13: Playlist callbacks complete. pyspotify 2.x now covers 100% of\n  the libspotify 12 API. Docs reviewed, quickstart guide extended. Redundant\n  getters/setters removed.\n\n- 2014-02-08: Playlist container callbacks complete.\n\n- 2014-01-31: Redesign session event listening to a model supporting multiple\n  listeners per event, with a nicer API for registering listeners.\n\n- 2013-12-16: Ensure we never call libspotify from two different threads at the\n  same time. We can\'t assume that the CPython GIL will ensure this for us, as\n  we target non-CPython interpreters like PyPy.\n\n- 2013-12-13: Artist browsing complete.\n\n- 2013-12-13: Album browsing complete.\n\n- 2013-11-29: Toplist subsystem complete.\n\n- 2013-11-27: Inbox subsystem complete.\n\n- 2013-10-14: Playlist subsystem *almost* complete.\n\n- 2013-06-21: Search subsystem complete.\n\n- 2013-06-10: Album subsystem complete.\n\n- 2013-06-09: Track and artist subsystem complete.\n\n- 2013-06-02: Session subsystem complete, with all methods.\n\n- 2013-06-01: Session callbacks complete.\n\n- 2013-05-25: Session config complete.\n\n- 2013-05-16: Link subsystem complete.\n\n- 2013-05-09: User subsystem complete.\n\n- 2013-05-08: Session configuration and creation, with login and logout works.\n\n- 2013-05-03: The Python object ``spotify.lib`` is a working CFFI wrapper\n  around the entire libspotify 12 API. This will be the foundation for more\n  pythonic APIs. The library currently works on CPython 2.7, 3.3 and PyPy 2.\n\n\nv1.x series\n===========\n\nSee the `pyspotify 1.x changelog\n<http://pyspotify.readthedocs.io/en/v1.x-develop/changes/>`__.\n', 'message': 'docs: Update changelog and dev plans\n', 'author_name': 'Stein Magnus Jodal', 'lang': 'rst'}