diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index 193d825e..ca25f4dd 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -12,7 +12,7 @@ Gst.is_initialized() or Gst.init() import pykka from mopidy import exceptions -from mopidy.audio import utils +from mopidy.audio import tags as tags_lib, utils from mopidy.audio.constants import PlaybackState from mopidy.audio.listener import AudioListener from mopidy.internal import deprecation, process @@ -325,7 +325,7 @@ class _Handler(object): gst_logger.debug('Got ASYNC_DONE bus message.') def on_tag(self, taglist): - tags = utils.convert_taglist(taglist) + tags = tags_lib.convert_taglist(taglist) gst_logger.debug('Got TAG bus message: tags=%r', dict(tags)) self._audio._tags.update(tags) logger.debug('Audio event: tags_changed(tags=%r)', tags.keys()) diff --git a/mopidy/audio/scan.py b/mopidy/audio/scan.py index f7d8fd67..ed1c6424 100644 --- a/mopidy/audio/scan.py +++ b/mopidy/audio/scan.py @@ -10,7 +10,7 @@ from gi.repository import Gst, GstPbutils Gst.is_initialized() or Gst.init() from mopidy import exceptions -from mopidy.audio import utils +from mopidy.audio import tags as tags_lib, utils from mopidy.internal import encoding # GST_ELEMENT_FACTORY_LIST: @@ -214,7 +214,7 @@ def _process(pipeline, timeout_ms): elif message.type == Gst.MessageType.TAG: taglist = message.parse_tag() # Note that this will only keep the last tag. - tags.update(utils.convert_taglist(taglist)) + tags.update(tags_lib.convert_taglist(taglist)) now = int(time.time() * 1000) timeout -= now - previous diff --git a/mopidy/audio/tags.py b/mopidy/audio/tags.py new file mode 100644 index 00000000..ba2b021a --- /dev/null +++ b/mopidy/audio/tags.py @@ -0,0 +1,132 @@ +from __future__ import absolute_import, unicode_literals + +import collections +import logging +import numbers + +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst +Gst.is_initialized() or Gst.init() + +from mopidy import compat +from mopidy.models import Album, Artist, Track + + +logger = logging.getLogger(__name__) +TRACE = logging.getLevelName('TRACE') + + +def convert_taglist(taglist): + """Convert a :class:`Gst.TagList` to plain Python types. + + Knows how to convert: + + - Dates + - Buffers + - Numbers + - Strings + - Booleans + + Unknown types will be ignored and trace logged. Tag keys are all strings + defined as part GStreamer under GstTagList_. + + .. _GstTagList: https://developer.gnome.org/gstreamer/stable/\ +gstreamer-GstTagList.html + + :param taglist: A GStreamer taglist to be converted. + :type taglist: :class:`Gst.TagList` + :rtype: dictionary of tag keys with a list of values. + """ + result = collections.defaultdict(list) + + for n in range(taglist.n_tags()): + tag = taglist.nth_tag_name(n) + + for i in range(taglist.get_tag_size(tag)): + value = taglist.get_value_index(tag, i) + + if isinstance(value, Gst.DateTime): + result[tag].append(value.to_iso8601_string()) + if isinstance(value, (compat.string_types, bool, numbers.Number)): + result[tag].append(value) + else: + logger.log( + TRACE, 'Ignoring unknown tag data: %r = %r', tag, value) + + return result + + +# TODO: split based on "stream" and "track" based conversion? i.e. handle data +# from radios in it's own helper instead? +def convert_tags_to_track(tags): + """Convert our normalized tags to a track. + + :param tags: dictionary of tag keys with a list of values + :type tags: :class:`dict` + :rtype: :class:`mopidy.models.Track` + """ + album_kwargs = {} + track_kwargs = {} + + track_kwargs['composers'] = _artists(tags, Gst.TAG_COMPOSER) + track_kwargs['performers'] = _artists(tags, Gst.TAG_PERFORMER) + track_kwargs['artists'] = _artists(tags, Gst.TAG_ARTIST, + 'musicbrainz-artistid', + 'musicbrainz-sortname') + album_kwargs['artists'] = _artists( + tags, Gst.TAG_ALBUM_ARTIST, 'musicbrainz-albumartistid') + + track_kwargs['genre'] = '; '.join(tags.get(Gst.TAG_GENRE, [])) + track_kwargs['name'] = '; '.join(tags.get(Gst.TAG_TITLE, [])) + if not track_kwargs['name']: + track_kwargs['name'] = '; '.join(tags.get(Gst.TAG_ORGANIZATION, [])) + + track_kwargs['comment'] = '; '.join(tags.get('comment', [])) + if not track_kwargs['comment']: + track_kwargs['comment'] = '; '.join(tags.get(Gst.TAG_LOCATION, [])) + if not track_kwargs['comment']: + track_kwargs['comment'] = '; '.join(tags.get(Gst.TAG_COPYRIGHT, [])) + + track_kwargs['track_no'] = tags.get(Gst.TAG_TRACK_NUMBER, [None])[0] + track_kwargs['disc_no'] = tags.get(Gst.TAG_ALBUM_VOLUME_NUMBER, [None])[0] + track_kwargs['bitrate'] = tags.get(Gst.TAG_BITRATE, [None])[0] + track_kwargs['musicbrainz_id'] = tags.get('musicbrainz-trackid', [None])[0] + + album_kwargs['name'] = tags.get(Gst.TAG_ALBUM, [None])[0] + album_kwargs['num_tracks'] = tags.get(Gst.TAG_TRACK_COUNT, [None])[0] + album_kwargs['num_discs'] = tags.get(Gst.TAG_ALBUM_VOLUME_COUNT, [None])[0] + album_kwargs['musicbrainz_id'] = tags.get('musicbrainz-albumid', [None])[0] + + if tags.get(Gst.TAG_DATE) and tags.get(Gst.TAG_DATE)[0]: + track_kwargs['date'] = tags[Gst.TAG_DATE][0].isoformat() + + # Clear out any empty values we found + track_kwargs = {k: v for k, v in track_kwargs.items() if v} + album_kwargs = {k: v for k, v in album_kwargs.items() if v} + + # Only bother with album if we have a name to show. + if album_kwargs.get('name'): + track_kwargs['album'] = Album(**album_kwargs) + + return Track(**track_kwargs) + + +def _artists( + tags, artist_name, artist_id=None, artist_sortname=None): + + # Name missing, don't set artist + if not tags.get(artist_name): + return None + # One artist name and either id or sortname, include all available fields + if len(tags[artist_name]) == 1 and \ + (artist_id in tags or artist_sortname in tags): + attrs = {'name': tags[artist_name][0]} + if artist_id in tags: + attrs['musicbrainz_id'] = tags[artist_id][0] + if artist_sortname in tags: + attrs['sortname'] = tags[artist_sortname][0] + return [Artist(**attrs)] + + # Multiple artist, provide artists with name only to avoid ambiguity. + return [Artist(name=name) for name in tags[artist_name]] diff --git a/mopidy/audio/utils.py b/mopidy/audio/utils.py index 989fac4b..6a11c7a3 100644 --- a/mopidy/audio/utils.py +++ b/mopidy/audio/utils.py @@ -1,18 +1,10 @@ from __future__ import absolute_import, unicode_literals -import collections -import logging -import numbers - import gi gi.require_version('Gst', '1.0') from gi.repository import Gst -from mopidy import compat, httpclient -from mopidy.models import Album, Artist, Track - -logger = logging.getLogger(__name__) -TRACE = logging.getLevelName('TRACE') +from mopidy import httpclient def calculate_duration(num_samples, sample_rate): @@ -68,79 +60,6 @@ def supported_uri_schemes(uri_schemes): return supported_schemes -def _artists(tags, artist_name, artist_id=None, artist_sortname=None): - # Name missing, don't set artist - if not tags.get(artist_name): - return None - # One artist name and either id or sortname, include all available fields - if len(tags[artist_name]) == 1 and \ - (artist_id in tags or artist_sortname in tags): - attrs = {'name': tags[artist_name][0]} - if artist_id in tags: - attrs['musicbrainz_id'] = tags[artist_id][0] - if artist_sortname in tags: - attrs['sortname'] = tags[artist_sortname][0] - return [Artist(**attrs)] - - # Multiple artist, provide artists with name only to avoid ambiguity. - return [Artist(name=name) for name in tags[artist_name]] - - -# TODO: split based on "stream" and "track" based conversion? i.e. handle data -# from radios in it's own helper instead? -def convert_tags_to_track(tags): - """Convert our normalized tags to a track. - - :param tags: dictionary of tag keys with a list of values - :type tags: :class:`dict` - :rtype: :class:`mopidy.models.Track` - """ - album_kwargs = {} - track_kwargs = {} - - track_kwargs['composers'] = _artists(tags, Gst.TAG_COMPOSER) - track_kwargs['performers'] = _artists(tags, Gst.TAG_PERFORMER) - track_kwargs['artists'] = _artists(tags, Gst.TAG_ARTIST, - 'musicbrainz-artistid', - 'musicbrainz-sortname') - album_kwargs['artists'] = _artists( - tags, Gst.TAG_ALBUM_ARTIST, 'musicbrainz-albumartistid') - - track_kwargs['genre'] = '; '.join(tags.get(Gst.TAG_GENRE, [])) - track_kwargs['name'] = '; '.join(tags.get(Gst.TAG_TITLE, [])) - if not track_kwargs['name']: - track_kwargs['name'] = '; '.join(tags.get(Gst.TAG_ORGANIZATION, [])) - - track_kwargs['comment'] = '; '.join(tags.get('comment', [])) - if not track_kwargs['comment']: - track_kwargs['comment'] = '; '.join(tags.get(Gst.TAG_LOCATION, [])) - if not track_kwargs['comment']: - track_kwargs['comment'] = '; '.join(tags.get(Gst.TAG_COPYRIGHT, [])) - - track_kwargs['track_no'] = tags.get(Gst.TAG_TRACK_NUMBER, [None])[0] - track_kwargs['disc_no'] = tags.get(Gst.TAG_ALBUM_VOLUME_NUMBER, [None])[0] - track_kwargs['bitrate'] = tags.get(Gst.TAG_BITRATE, [None])[0] - track_kwargs['musicbrainz_id'] = tags.get('musicbrainz-trackid', [None])[0] - - album_kwargs['name'] = tags.get(Gst.TAG_ALBUM, [None])[0] - album_kwargs['num_tracks'] = tags.get(Gst.TAG_TRACK_COUNT, [None])[0] - album_kwargs['num_discs'] = tags.get(Gst.TAG_ALBUM_VOLUME_COUNT, [None])[0] - album_kwargs['musicbrainz_id'] = tags.get('musicbrainz-albumid', [None])[0] - - if tags.get(Gst.TAG_DATE) and tags.get(Gst.TAG_DATE)[0]: - track_kwargs['date'] = tags[Gst.TAG_DATE][0].isoformat() - - # Clear out any empty values we found - track_kwargs = {k: v for k, v in track_kwargs.items() if v} - album_kwargs = {k: v for k, v in album_kwargs.items() if v} - - # Only bother with album if we have a name to show. - if album_kwargs.get('name'): - track_kwargs['album'] = Album(**album_kwargs) - - return Track(**track_kwargs) - - def setup_proxy(element, config): """Configure a GStreamer element with proxy settings. @@ -157,46 +76,6 @@ def setup_proxy(element, config): element.set_property('proxy-pw', config.get('password')) -def convert_taglist(taglist): - """Convert a :class:`Gst.TagList` to plain Python types. - - Knows how to convert: - - - Dates - - Buffers - - Numbers - - Strings - - Booleans - - Unknown types will be ignored and debug logged. Tag keys are all strings - defined as part GStreamer under GstTagList_. - - .. _GstTagList: https://developer.gnome.org/gstreamer/stable/\ -gstreamer-GstTagList.html - - :param taglist: A GStreamer taglist to be converted. - :type taglist: :class:`Gst.TagList` - :rtype: dictionary of tag keys with a list of values. - """ - result = collections.defaultdict(list) - - for n in range(taglist.n_tags()): - tag = taglist.nth_tag_name(n) - - for i in range(taglist.get_tag_size(tag)): - value = taglist.get_value_index(tag, i) - - if isinstance(value, Gst.DateTime): - result[tag].append(value.to_iso8601_string()) - if isinstance(value, (compat.string_types, bool, numbers.Number)): - result[tag].append(value) - else: - logger.log( - TRACE, 'Ignoring unknown tag data: %r = %r', tag, value) - - return result - - class Signals(object): """Helper for tracking gobject signal registrations""" diff --git a/mopidy/file/library.py b/mopidy/file/library.py index 20ac0632..09fa2cf1 100644 --- a/mopidy/file/library.py +++ b/mopidy/file/library.py @@ -7,7 +7,7 @@ import sys import urllib2 from mopidy import backend, exceptions, models -from mopidy.audio import scan, utils +from mopidy.audio import scan, tags from mopidy.internal import path @@ -83,7 +83,7 @@ class FileLibraryProvider(backend.LibraryProvider): try: result = self._scanner.scan(uri) - track = utils.convert_tags_to_track(result.tags).copy( + track = tags.convert_tags_to_track(result.tags).copy( uri=uri, length=result.duration) except exceptions.ScannerError as e: logger.warning('Failed looking up %s: %s', uri, e) diff --git a/mopidy/local/commands.py b/mopidy/local/commands.py index d61cf441..ead874a0 100644 --- a/mopidy/local/commands.py +++ b/mopidy/local/commands.py @@ -6,7 +6,7 @@ import os import time from mopidy import commands, compat, exceptions -from mopidy.audio import scan, utils +from mopidy.audio import scan, tags from mopidy.internal import path from mopidy.local import translator @@ -140,18 +140,18 @@ class ScanCommand(commands.Command): relpath = translator.local_track_uri_to_path(uri, media_dir) file_uri = path.path_to_uri(os.path.join(media_dir, relpath)) result = scanner.scan(file_uri) - tags, duration = result.tags, result.duration if not result.playable: logger.warning('Failed %s: No audio found in file.', uri) - elif duration < MIN_DURATION_MS: + elif result.duration < MIN_DURATION_MS: logger.warning('Failed %s: Track shorter than %dms', uri, MIN_DURATION_MS) else: mtime = file_mtimes.get(os.path.join(media_dir, relpath)) - track = utils.convert_tags_to_track(tags).replace( - uri=uri, length=duration, last_modified=mtime) + track = tags.convert_tags_to_track(result.tags).replace( + uri=uri, length=result.duration, last_modified=mtime) if library.add_supports_tags_and_duration: - library.add(track, tags=tags, duration=duration) + library.add( + track, tags=result.tags, duration=result.duration) else: library.add(track) logger.debug('Added %s', track.uri) diff --git a/mopidy/stream/actor.py b/mopidy/stream/actor.py index 5f88b13b..c2e39652 100644 --- a/mopidy/stream/actor.py +++ b/mopidy/stream/actor.py @@ -8,7 +8,7 @@ import time import pykka from mopidy import audio as audio_lib, backend, exceptions, stream -from mopidy.audio import scan, utils +from mopidy.audio import scan, tags from mopidy.compat import urllib from mopidy.internal import http, playlists from mopidy.models import Track @@ -60,7 +60,7 @@ class StreamLibraryProvider(backend.LibraryProvider): try: result = self._scanner.scan(uri) - track = utils.convert_tags_to_track(result.tags).replace( + track = tags.convert_tags_to_track(result.tags).replace( uri=uri, length=result.duration) except exceptions.ScannerError as e: logger.warning('Problem looking up %s: %s', uri, e) diff --git a/tests/audio/test_tags.py b/tests/audio/test_tags.py new file mode 100644 index 00000000..355af68e --- /dev/null +++ b/tests/audio/test_tags.py @@ -0,0 +1,261 @@ +from __future__ import absolute_import, unicode_literals + +import datetime +import unittest + +from mopidy.audio import tags +from mopidy.models import Album, Artist, Track + + +# TODO: keep ids without name? +# TODO: current test is trying to test everything at once with a complete tags +# set, instead we might want to try with a minimal one making testing easier. +class TagsToTrackTest(unittest.TestCase): + + def setUp(self): # noqa: N802 + self.tags = { + 'album': ['album'], + 'track-number': [1], + 'artist': ['artist'], + 'composer': ['composer'], + 'performer': ['performer'], + 'album-artist': ['albumartist'], + 'title': ['track'], + 'track-count': [2], + 'album-disc-number': [2], + 'album-disc-count': [3], + 'date': [datetime.date(2006, 1, 1,)], + 'container-format': ['ID3 tag'], + 'genre': ['genre'], + 'comment': ['comment'], + 'musicbrainz-trackid': ['trackid'], + 'musicbrainz-albumid': ['albumid'], + 'musicbrainz-artistid': ['artistid'], + 'musicbrainz-sortname': ['sortname'], + 'musicbrainz-albumartistid': ['albumartistid'], + 'bitrate': [1000], + } + + artist = Artist(name='artist', musicbrainz_id='artistid', + sortname='sortname') + composer = Artist(name='composer') + performer = Artist(name='performer') + albumartist = Artist(name='albumartist', + musicbrainz_id='albumartistid') + + album = Album(name='album', num_tracks=2, num_discs=3, + musicbrainz_id='albumid', artists=[albumartist]) + + self.track = Track(name='track', date='2006-01-01', + genre='genre', track_no=1, disc_no=2, + comment='comment', musicbrainz_id='trackid', + album=album, bitrate=1000, artists=[artist], + composers=[composer], performers=[performer]) + + def check(self, expected): + actual = tags.convert_tags_to_track(self.tags) + self.assertEqual(expected, actual) + + def test_track(self): + self.check(self.track) + + def test_missing_track_no(self): + del self.tags['track-number'] + self.check(self.track.replace(track_no=None)) + + def test_multiple_track_no(self): + self.tags['track-number'].append(9) + self.check(self.track) + + def test_missing_track_disc_no(self): + del self.tags['album-disc-number'] + self.check(self.track.replace(disc_no=None)) + + def test_multiple_track_disc_no(self): + self.tags['album-disc-number'].append(9) + self.check(self.track) + + def test_missing_track_name(self): + del self.tags['title'] + self.check(self.track.replace(name=None)) + + def test_multiple_track_name(self): + self.tags['title'] = ['name1', 'name2'] + self.check(self.track.replace(name='name1; name2')) + + def test_missing_track_musicbrainz_id(self): + del self.tags['musicbrainz-trackid'] + self.check(self.track.replace(musicbrainz_id=None)) + + def test_multiple_track_musicbrainz_id(self): + self.tags['musicbrainz-trackid'].append('id') + self.check(self.track) + + def test_missing_track_bitrate(self): + del self.tags['bitrate'] + self.check(self.track.replace(bitrate=None)) + + def test_multiple_track_bitrate(self): + self.tags['bitrate'].append(1234) + self.check(self.track) + + def test_missing_track_genre(self): + del self.tags['genre'] + self.check(self.track.replace(genre=None)) + + def test_multiple_track_genre(self): + self.tags['genre'] = ['genre1', 'genre2'] + self.check(self.track.replace(genre='genre1; genre2')) + + def test_missing_track_date(self): + del self.tags['date'] + self.check(self.track.replace(date=None)) + + def test_multiple_track_date(self): + self.tags['date'].append(datetime.date(2030, 1, 1)) + self.check(self.track) + + def test_missing_track_comment(self): + del self.tags['comment'] + self.check(self.track.replace(comment=None)) + + def test_multiple_track_comment(self): + self.tags['comment'] = ['comment1', 'comment2'] + self.check(self.track.replace(comment='comment1; comment2')) + + def test_missing_track_artist_name(self): + del self.tags['artist'] + self.check(self.track.replace(artists=[])) + + def test_multiple_track_artist_name(self): + self.tags['artist'] = ['name1', 'name2'] + artists = [Artist(name='name1'), Artist(name='name2')] + self.check(self.track.replace(artists=artists)) + + def test_missing_track_artist_musicbrainz_id(self): + del self.tags['musicbrainz-artistid'] + artist = list(self.track.artists)[0].replace(musicbrainz_id=None) + self.check(self.track.replace(artists=[artist])) + + def test_multiple_track_artist_musicbrainz_id(self): + self.tags['musicbrainz-artistid'].append('id') + self.check(self.track) + + def test_missing_track_composer_name(self): + del self.tags['composer'] + self.check(self.track.replace(composers=[])) + + def test_multiple_track_composer_name(self): + self.tags['composer'] = ['composer1', 'composer2'] + composers = [Artist(name='composer1'), Artist(name='composer2')] + self.check(self.track.replace(composers=composers)) + + def test_missing_track_performer_name(self): + del self.tags['performer'] + self.check(self.track.replace(performers=[])) + + def test_multiple_track_performe_name(self): + self.tags['performer'] = ['performer1', 'performer2'] + performers = [Artist(name='performer1'), Artist(name='performer2')] + self.check(self.track.replace(performers=performers)) + + def test_missing_album_name(self): + del self.tags['album'] + self.check(self.track.replace(album=None)) + + def test_multiple_album_name(self): + self.tags['album'].append('album2') + self.check(self.track) + + def test_missing_album_musicbrainz_id(self): + del self.tags['musicbrainz-albumid'] + album = self.track.album.replace(musicbrainz_id=None, + images=[]) + self.check(self.track.replace(album=album)) + + def test_multiple_album_musicbrainz_id(self): + self.tags['musicbrainz-albumid'].append('id') + self.check(self.track) + + def test_missing_album_num_tracks(self): + del self.tags['track-count'] + album = self.track.album.replace(num_tracks=None) + self.check(self.track.replace(album=album)) + + def test_multiple_album_num_tracks(self): + self.tags['track-count'].append(9) + self.check(self.track) + + def test_missing_album_num_discs(self): + del self.tags['album-disc-count'] + album = self.track.album.replace(num_discs=None) + self.check(self.track.replace(album=album)) + + def test_multiple_album_num_discs(self): + self.tags['album-disc-count'].append(9) + self.check(self.track) + + def test_missing_album_artist_name(self): + del self.tags['album-artist'] + album = self.track.album.replace(artists=[]) + self.check(self.track.replace(album=album)) + + def test_multiple_album_artist_name(self): + self.tags['album-artist'] = ['name1', 'name2'] + artists = [Artist(name='name1'), Artist(name='name2')] + album = self.track.album.replace(artists=artists) + self.check(self.track.replace(album=album)) + + def test_missing_album_artist_musicbrainz_id(self): + del self.tags['musicbrainz-albumartistid'] + albumartist = list(self.track.album.artists)[0] + albumartist = albumartist.replace(musicbrainz_id=None) + album = self.track.album.replace(artists=[albumartist]) + self.check(self.track.replace(album=album)) + + def test_multiple_album_artist_musicbrainz_id(self): + self.tags['musicbrainz-albumartistid'].append('id') + self.check(self.track) + + def test_stream_organization_track_name(self): + del self.tags['title'] + self.tags['organization'] = ['organization'] + self.check(self.track.replace(name='organization')) + + def test_multiple_organization_track_name(self): + del self.tags['title'] + self.tags['organization'] = ['organization1', 'organization2'] + self.check(self.track.replace(name='organization1; organization2')) + + # TODO: combine all comment types? + def test_stream_location_track_comment(self): + del self.tags['comment'] + self.tags['location'] = ['location'] + self.check(self.track.replace(comment='location')) + + def test_multiple_location_track_comment(self): + del self.tags['comment'] + self.tags['location'] = ['location1', 'location2'] + self.check(self.track.replace(comment='location1; location2')) + + def test_stream_copyright_track_comment(self): + del self.tags['comment'] + self.tags['copyright'] = ['copyright'] + self.check(self.track.replace(comment='copyright')) + + def test_multiple_copyright_track_comment(self): + del self.tags['comment'] + self.tags['copyright'] = ['copyright1', 'copyright2'] + self.check(self.track.replace(comment='copyright1; copyright2')) + + def test_sortname(self): + self.tags['musicbrainz-sortname'] = ['another_sortname'] + artist = Artist(name='artist', sortname='another_sortname', + musicbrainz_id='artistid') + self.check(self.track.replace(artists=[artist])) + + def test_missing_sortname(self): + del self.tags['musicbrainz-sortname'] + artist = Artist(name='artist', sortname=None, + musicbrainz_id='artistid') + self.check(self.track.replace(artists=[artist])) diff --git a/tests/audio/test_utils.py b/tests/audio/test_utils.py index e10613d2..0ce15bcb 100644 --- a/tests/audio/test_utils.py +++ b/tests/audio/test_utils.py @@ -1,8 +1,5 @@ from __future__ import absolute_import, unicode_literals -import datetime -import unittest - import gi gi.require_version('Gst', '1.0') from gi.repository import Gst @@ -10,7 +7,6 @@ from gi.repository import Gst import pytest from mopidy.audio import utils -from mopidy.models import Album, Artist, Track class TestCreateBuffer(object): @@ -28,257 +24,3 @@ class TestCreateBuffer(object): utils.create_buffer(b'', timestamp=0, duration=1000000) assert 'Cannot create buffer without data' in str(excinfo.value) - - -# TODO: keep ids without name? -# TODO: current test is trying to test everything at once with a complete tags -# set, instead we might want to try with a minimal one making testing easier. -class TagsToTrackTest(unittest.TestCase): - - def setUp(self): # noqa: N802 - self.tags = { - 'album': ['album'], - 'track-number': [1], - 'artist': ['artist'], - 'composer': ['composer'], - 'performer': ['performer'], - 'album-artist': ['albumartist'], - 'title': ['track'], - 'track-count': [2], - 'album-disc-number': [2], - 'album-disc-count': [3], - 'date': [datetime.date(2006, 1, 1,)], - 'container-format': ['ID3 tag'], - 'genre': ['genre'], - 'comment': ['comment'], - 'musicbrainz-trackid': ['trackid'], - 'musicbrainz-albumid': ['albumid'], - 'musicbrainz-artistid': ['artistid'], - 'musicbrainz-sortname': ['sortname'], - 'musicbrainz-albumartistid': ['albumartistid'], - 'bitrate': [1000], - } - - artist = Artist(name='artist', musicbrainz_id='artistid', - sortname='sortname') - composer = Artist(name='composer') - performer = Artist(name='performer') - albumartist = Artist(name='albumartist', - musicbrainz_id='albumartistid') - - album = Album(name='album', num_tracks=2, num_discs=3, - musicbrainz_id='albumid', artists=[albumartist]) - - self.track = Track(name='track', date='2006-01-01', - genre='genre', track_no=1, disc_no=2, - comment='comment', musicbrainz_id='trackid', - album=album, bitrate=1000, artists=[artist], - composers=[composer], performers=[performer]) - - def check(self, expected): - actual = utils.convert_tags_to_track(self.tags) - self.assertEqual(expected, actual) - - def test_track(self): - self.check(self.track) - - def test_missing_track_no(self): - del self.tags['track-number'] - self.check(self.track.replace(track_no=None)) - - def test_multiple_track_no(self): - self.tags['track-number'].append(9) - self.check(self.track) - - def test_missing_track_disc_no(self): - del self.tags['album-disc-number'] - self.check(self.track.replace(disc_no=None)) - - def test_multiple_track_disc_no(self): - self.tags['album-disc-number'].append(9) - self.check(self.track) - - def test_missing_track_name(self): - del self.tags['title'] - self.check(self.track.replace(name=None)) - - def test_multiple_track_name(self): - self.tags['title'] = ['name1', 'name2'] - self.check(self.track.replace(name='name1; name2')) - - def test_missing_track_musicbrainz_id(self): - del self.tags['musicbrainz-trackid'] - self.check(self.track.replace(musicbrainz_id=None)) - - def test_multiple_track_musicbrainz_id(self): - self.tags['musicbrainz-trackid'].append('id') - self.check(self.track) - - def test_missing_track_bitrate(self): - del self.tags['bitrate'] - self.check(self.track.replace(bitrate=None)) - - def test_multiple_track_bitrate(self): - self.tags['bitrate'].append(1234) - self.check(self.track) - - def test_missing_track_genre(self): - del self.tags['genre'] - self.check(self.track.replace(genre=None)) - - def test_multiple_track_genre(self): - self.tags['genre'] = ['genre1', 'genre2'] - self.check(self.track.replace(genre='genre1; genre2')) - - def test_missing_track_date(self): - del self.tags['date'] - self.check(self.track.replace(date=None)) - - def test_multiple_track_date(self): - self.tags['date'].append(datetime.date(2030, 1, 1)) - self.check(self.track) - - def test_missing_track_comment(self): - del self.tags['comment'] - self.check(self.track.replace(comment=None)) - - def test_multiple_track_comment(self): - self.tags['comment'] = ['comment1', 'comment2'] - self.check(self.track.replace(comment='comment1; comment2')) - - def test_missing_track_artist_name(self): - del self.tags['artist'] - self.check(self.track.replace(artists=[])) - - def test_multiple_track_artist_name(self): - self.tags['artist'] = ['name1', 'name2'] - artists = [Artist(name='name1'), Artist(name='name2')] - self.check(self.track.replace(artists=artists)) - - def test_missing_track_artist_musicbrainz_id(self): - del self.tags['musicbrainz-artistid'] - artist = list(self.track.artists)[0].replace(musicbrainz_id=None) - self.check(self.track.replace(artists=[artist])) - - def test_multiple_track_artist_musicbrainz_id(self): - self.tags['musicbrainz-artistid'].append('id') - self.check(self.track) - - def test_missing_track_composer_name(self): - del self.tags['composer'] - self.check(self.track.replace(composers=[])) - - def test_multiple_track_composer_name(self): - self.tags['composer'] = ['composer1', 'composer2'] - composers = [Artist(name='composer1'), Artist(name='composer2')] - self.check(self.track.replace(composers=composers)) - - def test_missing_track_performer_name(self): - del self.tags['performer'] - self.check(self.track.replace(performers=[])) - - def test_multiple_track_performe_name(self): - self.tags['performer'] = ['performer1', 'performer2'] - performers = [Artist(name='performer1'), Artist(name='performer2')] - self.check(self.track.replace(performers=performers)) - - def test_missing_album_name(self): - del self.tags['album'] - self.check(self.track.replace(album=None)) - - def test_multiple_album_name(self): - self.tags['album'].append('album2') - self.check(self.track) - - def test_missing_album_musicbrainz_id(self): - del self.tags['musicbrainz-albumid'] - album = self.track.album.replace(musicbrainz_id=None, - images=[]) - self.check(self.track.replace(album=album)) - - def test_multiple_album_musicbrainz_id(self): - self.tags['musicbrainz-albumid'].append('id') - self.check(self.track) - - def test_missing_album_num_tracks(self): - del self.tags['track-count'] - album = self.track.album.replace(num_tracks=None) - self.check(self.track.replace(album=album)) - - def test_multiple_album_num_tracks(self): - self.tags['track-count'].append(9) - self.check(self.track) - - def test_missing_album_num_discs(self): - del self.tags['album-disc-count'] - album = self.track.album.replace(num_discs=None) - self.check(self.track.replace(album=album)) - - def test_multiple_album_num_discs(self): - self.tags['album-disc-count'].append(9) - self.check(self.track) - - def test_missing_album_artist_name(self): - del self.tags['album-artist'] - album = self.track.album.replace(artists=[]) - self.check(self.track.replace(album=album)) - - def test_multiple_album_artist_name(self): - self.tags['album-artist'] = ['name1', 'name2'] - artists = [Artist(name='name1'), Artist(name='name2')] - album = self.track.album.replace(artists=artists) - self.check(self.track.replace(album=album)) - - def test_missing_album_artist_musicbrainz_id(self): - del self.tags['musicbrainz-albumartistid'] - albumartist = list(self.track.album.artists)[0] - albumartist = albumartist.replace(musicbrainz_id=None) - album = self.track.album.replace(artists=[albumartist]) - self.check(self.track.replace(album=album)) - - def test_multiple_album_artist_musicbrainz_id(self): - self.tags['musicbrainz-albumartistid'].append('id') - self.check(self.track) - - def test_stream_organization_track_name(self): - del self.tags['title'] - self.tags['organization'] = ['organization'] - self.check(self.track.replace(name='organization')) - - def test_multiple_organization_track_name(self): - del self.tags['title'] - self.tags['organization'] = ['organization1', 'organization2'] - self.check(self.track.replace(name='organization1; organization2')) - - # TODO: combine all comment types? - def test_stream_location_track_comment(self): - del self.tags['comment'] - self.tags['location'] = ['location'] - self.check(self.track.replace(comment='location')) - - def test_multiple_location_track_comment(self): - del self.tags['comment'] - self.tags['location'] = ['location1', 'location2'] - self.check(self.track.replace(comment='location1; location2')) - - def test_stream_copyright_track_comment(self): - del self.tags['comment'] - self.tags['copyright'] = ['copyright'] - self.check(self.track.replace(comment='copyright')) - - def test_multiple_copyright_track_comment(self): - del self.tags['comment'] - self.tags['copyright'] = ['copyright1', 'copyright2'] - self.check(self.track.replace(comment='copyright1; copyright2')) - - def test_sortname(self): - self.tags['musicbrainz-sortname'] = ['another_sortname'] - artist = Artist(name='artist', sortname='another_sortname', - musicbrainz_id='artistid') - self.check(self.track.replace(artists=[artist])) - - def test_missing_sortname(self): - del self.tags['musicbrainz-sortname'] - artist = Artist(name='artist', sortname=None, - musicbrainz_id='artistid') - self.check(self.track.replace(artists=[artist]))