from __future__ import absolute_import, unicode_literals import re from mopidy.models import TlTrack # TODO: special handling of local:// uri scheme normalize_path_re = re.compile(r'[^/]+') def normalize_path(path, relative=False): parts = normalize_path_re.findall(path or '') if not relative: parts.insert(0, '') return '/'.join(parts) def track_to_mpd_format(track, position=None): """ Format track for output to MPD client. :param track: the track :type track: :class:`mopidy.models.Track` or :class:`mopidy.models.TlTrack` :param position: track's position in playlist :type position: integer :param key: if we should set key :type key: boolean :param mtime: if we should set mtime :type mtime: boolean :rtype: list of two-tuples """ if isinstance(track, TlTrack): (tlid, track) = track else: (tlid, track) = (None, track) result = [ ('file', track.uri or ''), ('Time', track.length and (track.length // 1000) or 0), ('Artist', artists_to_mpd_format(track.artists)), ('Title', track.name or ''), ('Album', track.album and track.album.name or ''), ] if track.date: result.append(('Date', track.date)) if track.album is not None and track.album.num_tracks is not None: result.append(('Track', '%d/%d' % ( track.track_no or 0, track.album.num_tracks))) else: result.append(('Track', track.track_no or 0)) if position is not None and tlid is not None: result.append(('Pos', position)) result.append(('Id', tlid)) if track.album is not None and track.album.musicbrainz_id is not None: result.append(('MUSICBRAINZ_ALBUMID', track.album.musicbrainz_id)) # FIXME don't use first and best artist? # FIXME don't duplicate following code? if track.album is not None and track.album.artists: artists = artists_to_mpd_format(track.album.artists) result.append(('AlbumArtist', artists)) artists = filter( lambda a: a.musicbrainz_id is not None, track.album.artists) if artists: result.append( ('MUSICBRAINZ_ALBUMARTISTID', artists[0].musicbrainz_id)) if track.artists: artists = filter(lambda a: a.musicbrainz_id is not None, track.artists) if artists: result.append(('MUSICBRAINZ_ARTISTID', artists[0].musicbrainz_id)) if track.composers: result.append(('Composer', artists_to_mpd_format(track.composers))) if track.performers: result.append(('Performer', artists_to_mpd_format(track.performers))) if track.genre: result.append(('Genre', track.genre)) if track.disc_no: result.append(('Disc', track.disc_no)) if track.comment: result.append(('Comment', track.comment)) if track.musicbrainz_id is not None: result.append(('MUSICBRAINZ_TRACKID', track.musicbrainz_id)) return result def artists_to_mpd_format(artists): """ Format track artists for output to MPD client. :param artists: the artists :type track: array of :class:`mopidy.models.Artist` :rtype: string """ artists = list(artists) artists.sort(key=lambda a: a.name) return ', '.join([a.name for a in artists if a.name]) def tracks_to_mpd_format(tracks, start=0, end=None): """ Format list of tracks for output to MPD client. Optionally limit output to the slice ``[start:end]`` of the list. :param tracks: the tracks :type tracks: list of :class:`mopidy.models.Track` or :class:`mopidy.models.TlTrack` :param start: position of first track to include in output :type start: int (positive or negative) :param end: position after last track to include in output :type end: int (positive or negative) or :class:`None` for end of list :rtype: list of lists of two-tuples """ if end is None: end = len(tracks) tracks = tracks[start:end] positions = range(start, end) assert len(tracks) == len(positions) result = [] for track, position in zip(tracks, positions): result.append(track_to_mpd_format(track, position)) return result def playlist_to_mpd_format(playlist, *args, **kwargs): """ Format playlist for output to MPD client. Arguments as for :func:`tracks_to_mpd_format`, except the first one. """ return tracks_to_mpd_format(playlist.tracks, *args, **kwargs)