MPD's load and listplaylistinfo lookup track metadata. Fixes #1511.

Includes tests and refactored all playlist lookups to use helper.
This commit is contained in:
nsteel 2017-01-17 23:10:13 +00:00 committed by Nick Steel
parent 53c8159bbc
commit 52a90a5a06
2 changed files with 67 additions and 44 deletions

View File

@ -3,7 +3,6 @@ from __future__ import absolute_import, division, unicode_literals
import datetime
import logging
import re
import warnings
from mopidy.compat import urllib
from mopidy.mpd import exceptions, protocol, translator
@ -16,6 +15,16 @@ def _check_playlist_name(name):
raise exceptions.MpdInvalidPlaylistName()
def _get_playlist(context, name, must_exist=True):
playlist = None
uri = context.lookup_playlist_uri_from_name(name)
if uri:
playlist = context.core.playlists.lookup(uri).get()
if must_exist and not playlist:
raise exceptions.MpdNoExistError('No such playlist')
return playlist
@protocol.commands.add('listplaylist')
def listplaylist(context, name):
"""
@ -31,10 +40,7 @@ def listplaylist(context, name):
file: relative/path/to/file2.ogg
file: relative/path/to/file3.mp3
"""
uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get()
if not playlist:
raise exceptions.MpdNoExistError('No such playlist')
playlist = _get_playlist(context, name)
return ['file: %s' % t.uri for t in playlist.tracks]
@ -52,10 +58,13 @@ def listplaylistinfo(context, name):
Standard track listing, with fields: file, Time, Title, Date,
Album, Artist, Track
"""
uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get()
if not playlist:
raise exceptions.MpdNoExistError('No such playlist')
playlist = _get_playlist(context, name)
track_uris = [track.uri for track in playlist.tracks]
tracks_map = context.core.library.lookup(uris=track_uris).get()
tracks = []
for uri in track_uris:
tracks.extend(tracks_map[uri])
playlist = playlist.replace(tracks=tracks)
return translator.playlist_to_mpd_format(playlist)
@ -134,14 +143,9 @@ def load(context, name, playlist_slice=slice(0, None)):
- MPD 0.17.1 does not fail if the specified range is outside the playlist,
in either or both ends.
"""
uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get()
if not playlist:
raise exceptions.MpdNoExistError('No such playlist')
with warnings.catch_warnings():
warnings.filterwarnings('ignore', 'tracklist.add.*"tracks".*')
context.core.tracklist.add(playlist.tracks[playlist_slice]).get()
playlist = _get_playlist(context, name)
track_uris = [track.uri for track in playlist.tracks[playlist_slice]]
context.core.tracklist.add(uris=track_uris).get()
@protocol.commands.add('playlistadd')
@ -156,8 +160,7 @@ def playlistadd(context, name, track_uri):
``NAME.m3u`` will be created if it does not exist.
"""
_check_playlist_name(name)
uri = context.lookup_playlist_uri_from_name(name)
old_playlist = uri is not None and context.core.playlists.lookup(uri).get()
old_playlist = _get_playlist(context, name, must_exist=False)
if not old_playlist:
# Create new playlist with this single track
lookup_res = context.core.library.lookup(uris=[track_uri]).get()
@ -249,10 +252,7 @@ def playlistdelete(context, name, songpos):
Deletes ``SONGPOS`` from the playlist ``NAME.m3u``.
"""
_check_playlist_name(name)
uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get()
if not playlist:
raise exceptions.MpdNoExistError('No such playlist')
playlist = _get_playlist(context, name)
try:
# Convert tracks to list and remove requested
@ -290,10 +290,7 @@ def playlistmove(context, name, from_pos, to_pos):
return
_check_playlist_name(name)
uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get()
if not playlist:
raise exceptions.MpdNoExistError('No such playlist')
playlist = _get_playlist(context, name)
if from_pos == to_pos:
return # Nothing to do
@ -325,21 +322,14 @@ def rename(context, old_name, new_name):
_check_playlist_name(old_name)
_check_playlist_name(new_name)
old_uri = context.lookup_playlist_uri_from_name(old_name)
if not old_uri:
raise exceptions.MpdNoExistError('No such playlist')
old_playlist = _get_playlist(context, old_name)
old_playlist = context.core.playlists.lookup(old_uri).get()
if not old_playlist:
raise exceptions.MpdNoExistError('No such playlist')
new_uri = context.lookup_playlist_uri_from_name(new_name)
if new_uri and context.core.playlists.lookup(new_uri).get():
if _get_playlist(context, new_name, must_exist=False):
raise exceptions.MpdExistError('Playlist already exists')
# TODO: should we purge the mapping in an else?
# Create copy of the playlist and remove original
uri_scheme = urllib.parse.urlparse(old_uri).scheme
uri_scheme = urllib.parse.urlparse(old_playlist.uri).scheme
new_playlist = context.core.playlists.create(new_name, uri_scheme).get()
new_playlist = new_playlist.replace(tracks=old_playlist.tracks)
saved_playlist = context.core.playlists.save(new_playlist).get()
@ -377,8 +367,7 @@ def save(context, name):
"""
_check_playlist_name(name)
tracks = context.core.tracklist.get_tracks().get()
uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get()
playlist = _get_playlist(context, name, must_exist=False)
if not playlist:
# Create new playlist
_create_playlist(context, name, tracks)
@ -388,4 +377,4 @@ def save(context, name):
saved_playlist = context.core.playlists.save(new_playlist).get()
if saved_playlist is None:
raise exceptions.MpdFailedToSavePlaylist(
urllib.parse.urlparse(uri).scheme)
urllib.parse.urlparse(playlist.uri).scheme)

View File

@ -46,6 +46,10 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK')
def test_listplaylistinfo(self):
tracks = [
Track(uri='dummy:a', name='Track A', length=5000),
]
self.backend.library.dummy_library = tracks
self.backend.playlists.set_dummy_playlists([
Playlist(
name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])])
@ -53,14 +57,20 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.send_request('listplaylistinfo "name"')
self.assertInResponse('file: dummy:a')
self.assertInResponse('Title: Track A')
self.assertInResponse('Time: 5')
self.assertNotInResponse('Track: 0')
self.assertNotInResponse('Pos: 0')
self.assertInResponse('OK')
def test_listplaylistinfo_without_quotes(self):
tracks = [
Track(uri='dummy:a'),
]
self.backend.library.dummy_library = tracks
self.backend.playlists.set_dummy_playlists([
Playlist(
name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])])
name='name', uri='dummy:name', tracks=tracks)])
self.send_request('listplaylistinfo name')
@ -76,13 +86,18 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
'ACK [50@0] {listplaylistinfo} No such playlist')
def test_listplaylistinfo_duplicate(self):
playlist1 = Playlist(name='a', uri='dummy:a1', tracks=[Track(uri='b')])
playlist2 = Playlist(name='a', uri='dummy:a2', tracks=[Track(uri='c')])
tracks = [
Track(uri='dummy:b'),
Track(uri='dummy:c'),
]
self.backend.library.dummy_library = tracks
playlist1 = Playlist(name='a', uri='dummy:a1', tracks=tracks[:1])
playlist2 = Playlist(name='a', uri='dummy:a2', tracks=tracks[1:])
self.backend.playlists.set_dummy_playlists([playlist1, playlist2])
self.send_request('listplaylistinfo "a [2]"')
self.assertInResponse('file: c')
self.assertInResponse('file: dummy:c')
self.assertNotInResponse('Track: 0')
self.assertNotInResponse('Pos: 0')
self.assertInResponse('OK')
@ -236,6 +251,25 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.send_request('load "unknown/playlist"')
self.assertEqualResponse('ACK [50@0] {load} No such playlist')
def test_load_full_track_metadata(self):
tracks = [
Track(uri='dummy:a', name='Track A', length=5000),
]
self.backend.library.dummy_library = tracks
self.backend.playlists.set_dummy_playlists([
Playlist(
name='A-list', uri='dummy:a1', tracks=[Track(uri='dummy:a')])])
self.send_request('load "A-list"')
tracks = self.core.tracklist.tracks.get()
self.assertEqual(1, len(tracks))
self.assertEqual('dummy:a', tracks[0].uri)
self.assertEqual('Track A', tracks[0].name)
self.assertEqual(5000, tracks[0].length)
self.assertInResponse('OK')
def test_playlistadd(self):
tracks = [
Track(uri='dummy:a'),