Merge branch 'develop' into feature/mpd-tokenized-requests
Conflicts: mopidy/mpd/protocol/stored_playlists.py
This commit is contained in:
commit
2e6f716b72
@ -8,6 +8,13 @@ This changelog is used to track all major changes to Mopidy.
|
|||||||
v0.19.0 (unreleased)
|
v0.19.0 (unreleased)
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
**Models**
|
||||||
|
|
||||||
|
- The type of :attr:`mopidy.models.Playlist.last_modified` has been redefined
|
||||||
|
from a :class:`datetime.datetime` instance to the number of milliseconds
|
||||||
|
since Unix epoch as an integer. This makes serialization of the time stamp
|
||||||
|
simpler.
|
||||||
|
|
||||||
**MPD**
|
**MPD**
|
||||||
|
|
||||||
- Minor refactor of context such that it stores password instead of config.
|
- Minor refactor of context such that it stores password instead of config.
|
||||||
|
|||||||
@ -418,8 +418,9 @@ class Playlist(ImmutableObject):
|
|||||||
:type name: string
|
:type name: string
|
||||||
:param tracks: playlist's tracks
|
:param tracks: playlist's tracks
|
||||||
:type tracks: list of :class:`Track` elements
|
:type tracks: list of :class:`Track` elements
|
||||||
:param last_modified: playlist's modification time in UTC
|
:param last_modified:
|
||||||
:type last_modified: :class:`datetime.datetime`
|
playlist's modification time in milliseconds since Unix epoch
|
||||||
|
:type last_modified: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#: The playlist URI. Read-only.
|
#: The playlist URI. Read-only.
|
||||||
@ -431,9 +432,10 @@ class Playlist(ImmutableObject):
|
|||||||
#: The playlist's tracks. Read-only.
|
#: The playlist's tracks. Read-only.
|
||||||
tracks = tuple()
|
tracks = tuple()
|
||||||
|
|
||||||
#: The playlist modification time in UTC. Read-only.
|
#: The playlist modification time in milliseconds since Unix epoch.
|
||||||
|
#: Read-only.
|
||||||
#:
|
#:
|
||||||
#: :class:`datetime.datetime`, or :class:`None` if unknown.
|
#: Integer, or :class:`None` if unknown.
|
||||||
last_modified = None
|
last_modified = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import division, unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@ -78,16 +78,27 @@ def listplaylists(context):
|
|||||||
continue
|
continue
|
||||||
name = context.lookup_playlist_name_from_uri(playlist.uri)
|
name = context.lookup_playlist_name_from_uri(playlist.uri)
|
||||||
result.append(('playlist', name))
|
result.append(('playlist', name))
|
||||||
last_modified = (
|
result.append(('Last-Modified', _get_last_modified(playlist)))
|
||||||
playlist.last_modified or datetime.datetime.utcnow()).isoformat()
|
|
||||||
# Remove microseconds
|
|
||||||
last_modified = last_modified.split('.')[0]
|
|
||||||
# Add time zone information
|
|
||||||
last_modified = last_modified + 'Z'
|
|
||||||
result.append(('Last-Modified', last_modified))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: move to translators?
|
||||||
|
def _get_last_modified(playlist):
|
||||||
|
"""Formats last modified timestamp of a playlist for MPD.
|
||||||
|
|
||||||
|
Time in UTC with second precision, formatted in the ISO 8601 format, with
|
||||||
|
the "Z" time zone marker for UTC. For example, "1970-01-01T00:00:00Z".
|
||||||
|
"""
|
||||||
|
if playlist.last_modified is None:
|
||||||
|
# If unknown, assume the playlist is modified
|
||||||
|
dt = datetime.datetime.utcnow()
|
||||||
|
else:
|
||||||
|
dt = datetime.datetime.utcfromtimestamp(
|
||||||
|
playlist.last_modified / 1000.0)
|
||||||
|
dt = dt.replace(microsecond=0)
|
||||||
|
return '%sZ' % dt.isoformat()
|
||||||
|
|
||||||
|
|
||||||
@protocol.commands.add('load', playlist_slice=protocol.RANGE)
|
@protocol.commands.add('load', playlist_slice=protocol.RANGE)
|
||||||
def load(context, name, playlist_slice=slice(0, None)):
|
def load(context, name, playlist_slice=slice(0, None)):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mopidy.mpd.protocol import music_db
|
from mopidy.mpd.protocol import music_db
|
||||||
@ -237,7 +236,7 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
|||||||
self.assertEqual(response1, response2)
|
self.assertEqual(response1, response2)
|
||||||
|
|
||||||
def test_lsinfo_without_path_returns_same_as_for_root(self):
|
def test_lsinfo_without_path_returns_same_as_for_root(self):
|
||||||
last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
last_modified = 1390942873222
|
||||||
self.backend.playlists.playlists = [
|
self.backend.playlists.playlists = [
|
||||||
Playlist(name='a', uri='dummy:/a', last_modified=last_modified)]
|
Playlist(name='a', uri='dummy:/a', last_modified=last_modified)]
|
||||||
|
|
||||||
@ -246,7 +245,7 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
|||||||
self.assertEqual(response1, response2)
|
self.assertEqual(response1, response2)
|
||||||
|
|
||||||
def test_lsinfo_with_empty_path_returns_same_as_for_root(self):
|
def test_lsinfo_with_empty_path_returns_same_as_for_root(self):
|
||||||
last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
last_modified = 1390942873222
|
||||||
self.backend.playlists.playlists = [
|
self.backend.playlists.playlists = [
|
||||||
Playlist(name='a', uri='dummy:/a', last_modified=last_modified)]
|
Playlist(name='a', uri='dummy:/a', last_modified=last_modified)]
|
||||||
|
|
||||||
@ -255,14 +254,14 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
|||||||
self.assertEqual(response1, response2)
|
self.assertEqual(response1, response2)
|
||||||
|
|
||||||
def test_lsinfo_for_root_includes_playlists(self):
|
def test_lsinfo_for_root_includes_playlists(self):
|
||||||
last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
last_modified = 1390942873222
|
||||||
self.backend.playlists.playlists = [
|
self.backend.playlists.playlists = [
|
||||||
Playlist(name='a', uri='dummy:/a', last_modified=last_modified)]
|
Playlist(name='a', uri='dummy:/a', last_modified=last_modified)]
|
||||||
|
|
||||||
self.sendRequest('lsinfo "/"')
|
self.sendRequest('lsinfo "/"')
|
||||||
self.assertInResponse('playlist: a')
|
self.assertInResponse('playlist: a')
|
||||||
# Date without microseconds and with time zone information
|
# Date without milliseconds and with time zone information
|
||||||
self.assertInResponse('Last-Modified: 2001-03-17T13:41:17Z')
|
self.assertInResponse('Last-Modified: 2014-01-28T21:01:13Z')
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_lsinfo_for_root_includes_dirs_for_each_lib_with_content(self):
|
def test_lsinfo_for_root_includes_dirs_for_each_lib_with_content(self):
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from mopidy.models import Track, Playlist
|
from mopidy.models import Track, Playlist
|
||||||
|
|
||||||
from tests.mpd import protocol
|
from tests.mpd import protocol
|
||||||
@ -78,14 +76,14 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
|
|||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_listplaylists(self):
|
def test_listplaylists(self):
|
||||||
last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
last_modified = 1390942873222
|
||||||
self.backend.playlists.playlists = [
|
self.backend.playlists.playlists = [
|
||||||
Playlist(name='a', uri='dummy:a', last_modified=last_modified)]
|
Playlist(name='a', uri='dummy:a', last_modified=last_modified)]
|
||||||
|
|
||||||
self.sendRequest('listplaylists')
|
self.sendRequest('listplaylists')
|
||||||
self.assertInResponse('playlist: a')
|
self.assertInResponse('playlist: a')
|
||||||
# Date without microseconds and with time zone information
|
# Date without milliseconds and with time zone information
|
||||||
self.assertInResponse('Last-Modified: 2001-03-17T13:41:17Z')
|
self.assertInResponse('Last-Modified: 2014-01-28T21:01:13Z')
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_listplaylists_duplicate(self):
|
def test_listplaylists_duplicate(self):
|
||||||
@ -99,7 +97,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
|
|||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_listplaylists_ignores_playlists_without_name(self):
|
def test_listplaylists_ignores_playlists_without_name(self):
|
||||||
last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
last_modified = 1390942873222
|
||||||
self.backend.playlists.playlists = [
|
self.backend.playlists.playlists = [
|
||||||
Playlist(name='', uri='dummy:', last_modified=last_modified)]
|
Playlist(name='', uri='dummy:', last_modified=last_modified)]
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
|
||||||
import json
|
import json
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@ -842,7 +841,7 @@ class PlaylistTest(unittest.TestCase):
|
|||||||
self.assertEqual(playlist.length, 3)
|
self.assertEqual(playlist.length, 3)
|
||||||
|
|
||||||
def test_last_modified(self):
|
def test_last_modified(self):
|
||||||
last_modified = datetime.datetime.utcnow()
|
last_modified = 1390942873000
|
||||||
playlist = Playlist(last_modified=last_modified)
|
playlist = Playlist(last_modified=last_modified)
|
||||||
self.assertEqual(playlist.last_modified, last_modified)
|
self.assertEqual(playlist.last_modified, last_modified)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
@ -850,7 +849,7 @@ class PlaylistTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_with_new_uri(self):
|
def test_with_new_uri(self):
|
||||||
tracks = [Track()]
|
tracks = [Track()]
|
||||||
last_modified = datetime.datetime.utcnow()
|
last_modified = 1390942873000
|
||||||
playlist = Playlist(
|
playlist = Playlist(
|
||||||
uri='an uri', name='a name', tracks=tracks,
|
uri='an uri', name='a name', tracks=tracks,
|
||||||
last_modified=last_modified)
|
last_modified=last_modified)
|
||||||
@ -862,7 +861,7 @@ class PlaylistTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_with_new_name(self):
|
def test_with_new_name(self):
|
||||||
tracks = [Track()]
|
tracks = [Track()]
|
||||||
last_modified = datetime.datetime.utcnow()
|
last_modified = 1390942873000
|
||||||
playlist = Playlist(
|
playlist = Playlist(
|
||||||
uri='an uri', name='a name', tracks=tracks,
|
uri='an uri', name='a name', tracks=tracks,
|
||||||
last_modified=last_modified)
|
last_modified=last_modified)
|
||||||
@ -874,7 +873,7 @@ class PlaylistTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_with_new_tracks(self):
|
def test_with_new_tracks(self):
|
||||||
tracks = [Track()]
|
tracks = [Track()]
|
||||||
last_modified = datetime.datetime.utcnow()
|
last_modified = 1390942873000
|
||||||
playlist = Playlist(
|
playlist = Playlist(
|
||||||
uri='an uri', name='a name', tracks=tracks,
|
uri='an uri', name='a name', tracks=tracks,
|
||||||
last_modified=last_modified)
|
last_modified=last_modified)
|
||||||
@ -887,8 +886,8 @@ class PlaylistTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_with_new_last_modified(self):
|
def test_with_new_last_modified(self):
|
||||||
tracks = [Track()]
|
tracks = [Track()]
|
||||||
last_modified = datetime.datetime.utcnow()
|
last_modified = 1390942873000
|
||||||
new_last_modified = last_modified + datetime.timedelta(1)
|
new_last_modified = last_modified + 1000
|
||||||
playlist = Playlist(
|
playlist = Playlist(
|
||||||
uri='an uri', name='a name', tracks=tracks,
|
uri='an uri', name='a name', tracks=tracks,
|
||||||
last_modified=last_modified)
|
last_modified=last_modified)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user