def _list_build_query(field, mpd_query): """Converts a ``list`` query to a Mopidy query.""" if mpd_query is None: return {} # shlex does not seem to be friends with unicode objects tokens = shlex.split(mpd_query.encode('utf-8')) tokens = [t.decode('utf-8') for t in tokens] if len(tokens) == 1: if field == u'album': return {'artist': [tokens[0]]} else: raise MpdArgError( u'should be "Album" for 3 arguments', command=u'list') elif len(tokens) % 2 == 0: query = {} while tokens: key = tokens[0].lower() key = str(key) # Needed for kwargs keys on OS X and Windows value = tokens[1] tokens = tokens[2:] if key not in (u'artist', u'album', u'date', u'genre'): raise MpdArgError(u'not able to parse args', command=u'list') if key in query: query[key].append(value) else: query[key] = [value] return query else: raise MpdArgError(u'not able to parse args', command=u'list')
def playpos(context, songpos): """ *musicpd.org, playback section:* ``play [SONGPOS]`` Begins playing the playlist at song number ``SONGPOS``. *Clarifications:* - ``play "-1"`` when playing is ignored. - ``play "-1"`` when paused resumes playback. - ``play "-1"`` when stopped with a current track starts playback at the current track. - ``play "-1"`` when stopped without a current track, e.g. after playlist replacement, starts playback at the first track. *BitMPC:* - issues ``play 6`` without quotes around the argument. """ songpos = int(songpos) if songpos == -1: return _play_minus_one(context) try: tl_track = context.core.tracklist.slice(songpos, songpos + 1).get()[0] return context.core.playback.play(tl_track).get() except IndexError: raise MpdArgError('Bad song index', command='play')
def addid(context, uri, songpos=None): """ *musicpd.org, current playlist section:* ``addid {URI} [POSITION]`` Adds a song to the playlist (non-recursive) and returns the song id. ``URI`` is always a single file or URL. For example:: addid "foo.mp3" Id: 999 OK *Clarifications:* - ``addid ""`` should return an error. """ if not uri: raise MpdNoExistError(u'No such song', command=u'addid') if songpos is not None: songpos = int(songpos) track = context.backend.library.lookup(uri).get() if track is None: raise MpdNoExistError(u'No such song', command=u'addid') if songpos and songpos > len( context.backend.current_playlist.tracks.get()): raise MpdArgError(u'Bad song index', command=u'addid') cp_track = context.backend.current_playlist.add(track, at_position=songpos).get() return ('Id', cp_track.cpid)
def addid(context, uri, songpos=None): """ *musicpd.org, current playlist section:* ``addid {URI} [POSITION]`` Adds a song to the playlist (non-recursive) and returns the song id. ``URI`` is always a single file or URL. For example:: addid "foo.mp3" Id: 999 OK *Clarifications:* - ``addid ""`` should return an error. """ if not uri: raise MpdNoExistError('No such song', command='addid') if songpos is not None: songpos = int(songpos) if songpos and songpos > context.core.tracklist.length.get(): raise MpdArgError('Bad song index', command='addid') tl_tracks = context.core.tracklist.add(uri=uri, at_position=songpos).get() if not tl_tracks: raise MpdNoExistError('No such song', command='addid') return ('Id', tl_tracks[0].tlid)
def playlistinfo(context, songpos=None, start=None, end=None): """ *musicpd.org, current playlist section:* ``playlistinfo [[SONGPOS] | [START:END]]`` Displays a list of all songs in the playlist, or if the optional argument is given, displays information only for the song ``SONGPOS`` or the range of songs ``START:END``. *ncmpc and mpc:* - uses negative indexes, like ``playlistinfo "-1"``, to request the entire playlist """ if songpos == '-1': songpos = None if songpos is not None: songpos = int(songpos) tl_track = context.core.tracklist.tl_tracks.get()[songpos] return translator.track_to_mpd_format(tl_track, position=songpos) else: if start is None: start = 0 start = int(start) if not (0 <= start <= context.core.tracklist.length.get()): raise MpdArgError('Bad song index', command='playlistinfo') if end is not None: end = int(end) if end > context.core.tracklist.length.get(): end = None tl_tracks = context.core.tracklist.tl_tracks.get() return translator.tracks_to_mpd_format(tl_tracks, start, end)
def delete_songpos(context, songpos): """See :meth:`delete_range`""" try: songpos = int(songpos) (cpid, _) = context.backend.current_playlist.cp_tracks.get()[songpos] context.backend.current_playlist.remove(cpid=cpid) except IndexError: raise MpdArgError(u'Bad song index', command=u'delete')
def delete_songpos(context, songpos): """See :meth:`delete_range`""" try: songpos = int(songpos) (tlid, _) = context.core.tracklist.slice(songpos, songpos + 1).get()[0] context.core.tracklist.remove(tlid=[tlid]) except IndexError: raise MpdArgError('Bad song index', command='delete')
def query_from_mpd_list_format(field, mpd_query): """ Converts an MPD ``list`` query to a Mopidy query. """ # NOTE kwargs dict keys must be bytestrings to work on Python < 2.6.5 # See https://github.com/mopidy/mopidy/issues/302 for details if mpd_query is None: return {} try: # shlex does not seem to be friends with unicode objects tokens = shlex.split(mpd_query.encode('utf-8')) except ValueError as error: if str(error) == 'No closing quotation': raise MpdArgError('Invalid unquoted character', command='list') else: raise tokens = [t.decode('utf-8') for t in tokens] if len(tokens) == 1: if field == 'album': if not tokens[0]: raise ValueError return {b'artist': [tokens[0]]} # See above NOTE else: raise MpdArgError('should be "Album" for 3 arguments', command='list') elif len(tokens) % 2 == 0: query = {} while tokens: key = str(tokens[0].lower()) # See above NOTE value = tokens[1] tokens = tokens[2:] if key not in ('artist', 'album', 'date', 'genre'): raise MpdArgError('not able to parse args', command='list') if not value: raise ValueError if key in query: query[key].append(value) else: query[key] = [value] return query else: raise MpdArgError('not able to parse args', command='list')
def query_from_mpd_list_format(field, mpd_query): """ Converts an MPD ``list`` query to a Mopidy query. """ if mpd_query is None: return {} try: # shlex does not seem to be friends with unicode objects tokens = shlex.split(mpd_query.encode('utf-8')) except ValueError as error: if str(error) == 'No closing quotation': raise MpdArgError('Invalid unquoted character', command='list') else: raise tokens = [t.decode('utf-8') for t in tokens] if len(tokens) == 1: if field == 'album': if not tokens[0]: raise ValueError return {'artist': [tokens[0]]} else: raise MpdArgError('should be "Album" for 3 arguments', command='list') elif len(tokens) % 2 == 0: query = {} while tokens: key = tokens[0].lower() value = tokens[1] tokens = tokens[2:] if key not in ('artist', 'album', 'albumartist', 'composer', 'date', 'genre', 'performer'): raise MpdArgError('not able to parse args', command='list') if not value: raise ValueError if key in query: query[key].append(value) else: query[key] = [value] return query else: raise MpdArgError('not able to parse args', command='list')
def playlistinfo(context, songpos=None, start=None, end=None): """ *musicpd.org, current playlist section:* ``playlistinfo [[SONGPOS] | [START:END]]`` Displays a list of all songs in the playlist, or if the optional argument is given, displays information only for the song ``SONGPOS`` or the range of songs ``START:END``. *ncmpc and mpc:* - uses negative indexes, like ``playlistinfo "-1"``, to request the entire playlist """ if songpos == "-1": songpos = None if songpos is not None: songpos = int(songpos) start = songpos end = songpos + 1 if start == -1: end = None cpids = [ ct[0] for ct in context.backend.current_playlist.cp_tracks.get() ] return tracks_to_mpd_format( context.backend.current_playlist.tracks.get(), start, end, cpids=cpids) else: if start is None: start = 0 start = int(start) if not (0 <= start <= len( context.backend.current_playlist.tracks.get())): raise MpdArgError(u'Bad song index', command=u'playlistinfo') if end is not None: end = int(end) if end > len(context.backend.current_playlist.tracks.get()): end = None cpids = [ ct[0] for ct in context.backend.current_playlist.cp_tracks.get() ] return tracks_to_mpd_format( context.backend.current_playlist.tracks.get(), start, end, cpids=cpids)
def delete_range(context, start, end=None): """ *musicpd.org, current playlist section:* ``delete [{POS} | {START:END}]`` Deletes a song from the playlist. """ start = int(start) if end is not None: end = int(end) else: end = len(context.backend.current_playlist.tracks.get()) cp_tracks = context.backend.current_playlist.cp_tracks.get()[start:end] if not cp_tracks: raise MpdArgError(u'Bad song index', command=u'delete') for (cpid, _) in cp_tracks: context.backend.current_playlist.remove(cpid=cpid)
def delete_range(context, start, end=None): """ *musicpd.org, current playlist section:* ``delete [{POS} | {START:END}]`` Deletes a song from the playlist. """ start = int(start) if end is not None: end = int(end) else: end = context.core.tracklist.length.get() tl_tracks = context.core.tracklist.slice(start, end).get() if not tl_tracks: raise MpdArgError('Bad song index', command='delete') for (tlid, _) in tl_tracks: context.core.tracklist.remove(tlid=[tlid])
def count(context, mpd_query): """ *musicpd.org, music database section:* ``count {TAG} {NEEDLE}`` Counts the number of songs and their total playtime in the db matching ``TAG`` exactly. *GMPC:* - does not add quotes around the tag argument. - use multiple tag-needle pairs to make more specific searches. """ try: query = translator.query_from_mpd_search_format(mpd_query) except ValueError: raise MpdArgError('incorrect arguments', command='count') results = context.core.library.find_exact(**query).get() result_tracks = _get_tracks(results) return [ ('songs', len(result_tracks)), ('playtime', sum(track.length for track in result_tracks) / 1000), ]