diff --git a/beets/__init__.py b/beets/__init__.py index ba2faec94..b8fe2a840 100644 --- a/beets/__init__.py +++ b/beets/__init__.py @@ -19,7 +19,7 @@ import os from beets.util import confit -__version__ = u'1.4.6' +__version__ = u'1.4.7' __author__ = u'Adrian Sampson ' diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 5c484d2b3..eb7c30808 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -36,6 +36,11 @@ if util.SNI_SUPPORTED: else: BASE_URL = 'http://musicbrainz.org/' +NON_AUDIO_FORMATS = ['Data CD', 'DVD', 'DVD-Video', 'Blu-ray', 'HD-DVD', 'VCD', + 'SVCD', 'UMD', 'VHS'] + +SKIPPED_TRACKS = ['[data track]'] + musicbrainzngs.set_useragent('beets', beets.__version__, 'http://beets.io/') @@ -275,11 +280,24 @@ def album_info(release): disctitle = medium.get('title') format = medium.get('format') + if format in NON_AUDIO_FORMATS: + continue + all_tracks = medium['track-list'] if 'pregap' in medium: all_tracks.insert(0, medium['pregap']) for track in all_tracks: + + if ('title' in track['recording'] and + track['recording']['title'] in SKIPPED_TRACKS): + continue + + if ('video' in track['recording'] and + track['recording']['video'] == 'true' and + config['match']['ignore_video_tracks']): + continue + # Basic information from the recording. index += 1 ti = track_info( diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 439a93f55..01c1e0f65 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -10,6 +10,7 @@ import: delete: no resume: ask incremental: no + from_scratch: no quiet_fallback: skip none_rec_action: ask timid: no @@ -125,5 +126,6 @@ match: original_year: no ignored: [] required: [] + ignore_video_tracks: yes track_length_grace: 10 track_length_max: 30 diff --git a/beets/importer.py b/beets/importer.py index e91b35656..186d824b6 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -534,6 +534,10 @@ class ImportTask(BaseImportTask): def apply_metadata(self): """Copy metadata from match info to the items. """ + if config['import']['from_scratch']: + for item in self.match.mapping: + item.clear() + autotag.apply_metadata(self.match.info, self.match.mapping) def duplicate_items(self, lib): diff --git a/beets/library.py b/beets/library.py index 597cfe625..64035e642 100644 --- a/beets/library.py +++ b/beets/library.py @@ -561,6 +561,11 @@ class Item(LibModel): if self.mtime == 0 and 'mtime' in values: self.mtime = values['mtime'] + def clear(self): + """Set all key/value pairs to None.""" + for key in self._media_fields: + setattr(self, key, None) + def get_album(self): """Get the Album object that this item belongs to, if any, or None if the item is a singleton or is not associated with a diff --git a/beets/ui/commands.py b/beets/ui/commands.py index c8beb11e2..3a1811cf3 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1004,6 +1004,10 @@ import_cmd.parser.add_option( u'-I', u'--noincremental', dest='incremental', action='store_false', help=u'do not skip already-imported directories' ) +import_cmd.parser.add_option( + u'--from-scratch', dest='from_scratch', action='store_true', + help=u'erase existing metadata before applying new metadata' +) import_cmd.parser.add_option( u'--flat', dest='flat', action='store_true', help=u'import an entire tree as a single album' diff --git a/beets/util/artresizer.py b/beets/util/artresizer.py index e84b775dc..c32f0f37d 100644 --- a/beets/util/artresizer.py +++ b/beets/util/artresizer.py @@ -88,14 +88,13 @@ def im_resize(maxwidth, path_in, path_out=None): log.debug(u'artresizer: ImageMagick resizing {0} to {1}', util.displayable_path(path_in), util.displayable_path(path_out)) - # "-resize widthxheight>" shrinks images with dimension(s) larger - # than the corresponding width and/or height dimension(s). The > - # "only shrink" flag is prefixed by ^ escape char for Windows - # compatibility. + # "-resize WIDTHx>" shrinks images with the width larger + # than the given width while maintaining the aspect ratio + # with regards to the height. try: util.command_output([ 'convert', util.syspath(path_in, prefix=False), - '-resize', '{0}x^>'.format(maxwidth), + '-resize', '{0}x>'.format(maxwidth), util.syspath(path_out, prefix=False), ]) except subprocess.CalledProcessError: diff --git a/beetsplug/acousticbrainz.py b/beetsplug/acousticbrainz.py index 4291d9117..f4960c301 100644 --- a/beetsplug/acousticbrainz.py +++ b/beetsplug/acousticbrainz.py @@ -110,6 +110,7 @@ class AcousticPlugin(plugins.BeetsPlugin): self.config.add({ 'auto': True, 'force': False, + 'tags': [] }) if self.config['auto']: @@ -164,6 +165,7 @@ class AcousticPlugin(plugins.BeetsPlugin): def _fetch_info(self, items, write, force): """Fetch additional information from AcousticBrainz for the `item`s. """ + tags = self.config['tags'].as_str_seq() for item in items: # If we're not forcing re-downloading for all tracks, check # whether the data is already present. We use one @@ -183,11 +185,18 @@ class AcousticPlugin(plugins.BeetsPlugin): data = self._get_data(item.mb_trackid) if data: for attr, val in self._map_data_to_scheme(data, ABSCHEME): - self._log.debug(u'attribute {} of {} set to {}', - attr, - item, - val) - setattr(item, attr, val) + if not tags or attr in tags: + self._log.debug(u'attribute {} of {} set to {}', + attr, + item, + val) + setattr(item, attr, val) + else: + self._log.debug(u'skipping attribute {} of {}' + u' (value {}) due to config', + attr, + item, + val) item.store() if write: item.try_write() diff --git a/beetsplug/duplicates.py b/beetsplug/duplicates.py index 2584e6628..b316cfda6 100644 --- a/beetsplug/duplicates.py +++ b/beetsplug/duplicates.py @@ -253,20 +253,19 @@ class DuplicatesPlugin(BeetsPlugin): "completeness" (objects with more non-null fields come first) and Albums are ordered by their track count. """ - if tiebreak: - kind = 'items' if all(isinstance(o, Item) - for o in objs) else 'albums' + kind = 'items' if all(isinstance(o, Item) for o in objs) else 'albums' + + if tiebreak and kind in tiebreak.keys(): key = lambda x: tuple(getattr(x, k) for k in tiebreak[kind]) else: - kind = Item if all(isinstance(o, Item) for o in objs) else Album - if kind is Item: + if kind == 'items': def truthy(v): # Avoid a Unicode warning by avoiding comparison # between a bytes object and the empty Unicode # string ''. return v is not None and \ (v != '' if isinstance(v, six.text_type) else True) - fields = kind.all_keys() + fields = Item.all_keys() key = lambda x: sum(1 for f in fields if truthy(getattr(x, f))) else: key = lambda x: len(x.items()) diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py index 5e1b822c2..56b68f756 100644 --- a/beetsplug/fromfilename.py +++ b/beetsplug/fromfilename.py @@ -27,30 +27,21 @@ import six # Filename field extraction patterns. PATTERNS = [ - # "01 - Track 01" and "01": do nothing - r'^(\d+)\s*-\s*track\s*\d$', - r'^\d+$', - - # Useful patterns. - r'^(?P.+)-(?P.+)-(?P<tag>.*)$', - r'^(?P<track>\d+)\s*-(?P<artist>.+)-(?P<title>.+)-(?P<tag>.*)$', - r'^(?P<track>\d+)\s(?P<artist>.+)-(?P<title>.+)-(?P<tag>.*)$', - r'^(?P<artist>.+)-(?P<title>.+)$', - r'^(?P<track>\d+)\.\s*(?P<artist>.+)-(?P<title>.+)$', - r'^(?P<track>\d+)\s*-\s*(?P<artist>.+)-(?P<title>.+)$', - r'^(?P<track>\d+)\s*-(?P<artist>.+)-(?P<title>.+)$', - r'^(?P<track>\d+)\s(?P<artist>.+)-(?P<title>.+)$', - r'^(?P<title>.+)$', - r'^(?P<track>\d+)\.\s*(?P<title>.+)$', - r'^(?P<track>\d+)\s*-\s*(?P<title>.+)$', - r'^(?P<track>\d+)\s(?P<title>.+)$', - r'^(?P<title>.+) by (?P<artist>.+)$', + # Useful patterns. + r'^(?P<artist>.+)[\-_](?P<title>.+)[\-_](?P<tag>.*)$', + r'^(?P<track>\d+)[\s.\-_]+(?P<artist>.+)[\-_](?P<title>.+)[\-_](?P<tag>.*)$', + r'^(?P<artist>.+)[\-_](?P<title>.+)$', + r'^(?P<track>\d+)[\s.\-_]+(?P<artist>.+)[\-_](?P<title>.+)$', + r'^(?P<title>.+)$', + r'^(?P<track>\d+)[\s.\-_]+(?P<title>.+)$', + r'^(?P<track>\d+)\s+(?P<title>.+)$', + r'^(?P<title>.+) by (?P<artist>.+)$', + r'^(?P<track>\d+).*$', ] # Titles considered "empty" and in need of replacement. BAD_TITLE_PATTERNS = [ r'^$', - r'\d+?\s?-?\s*track\s*\d+', ] diff --git a/docs/changelog.rst b/docs/changelog.rst index 9fba6392b..4ff54b222 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,66 +1,97 @@ Changelog ========= -1.4.6 (in development) +1.4.7 (in development) ---------------------- -New features: +Changelog goes here! -* When the importer finds duplicate albums, you can now merge all the tracks - together and try importing them as a single album. +Fixes: + +* Non-audio media (DVD-Video, etc.) are now skipped by the autotagger. :bug:`2688` +* Non-audio tracks (data tracks, video tracks, etc.) are now skipped by the + autotagger. :bug:`1210` + + +1.4.6 (December 21, 2017) +------------------------- + +The highlight of this release is "album merging," an oft-requested option in +the importer to add new tracks to an existing album you already have in your +library. This way, you no longer need to resort to removing the partial album +from your library, combining the files manually, and importing again. + +Here are the larger new features in this release: + +* When the importer finds duplicate albums, you can now merge all the + tracks---old and new---together and try importing them as a single, combined + album. Thanks to :user:`udiboy1209`. :bug:`112` :bug:`2725` * :doc:`/plugins/lyrics`: The plugin can now produce reStructuredText files for beautiful, readable books of lyrics. Thanks to :user:`anarcat`. :bug:`2628` -* :doc:`/plugins/convert`: Adds ``no_convert`` option which ignores transcoding - items matching provided query string. Thanks to :user:`Stunner`. +* A new :ref:`from_scratch` configuration option makes the importer remove old + metadata before applying new metadata. This new feature complements the + :doc:`zero </plugins/zero>` and :doc:`scrub </plugins/scrub>` plugins but is + slightly different: beets clears out all the old tags it knows about and + only keeps the new data it gets from the remote metadata source. + Thanks to :user:`tummychow`. + :bug:`934` :bug:`2755` + +There are also somewhat littler, but still great, new features: + +* :doc:`/plugins/convert`: A new ``no_convert`` option lets you skip + transcoding items matching a query. Instead, the files are just copied + as-is. Thanks to :user:`Stunner`. :bug:`2732` :bug:`2751` -* :doc:`/plugins/fetchart`: The plugin has now a quiet switch that will only - display missing album arts. Thanks to :user:`euri10`. +* :doc:`/plugins/fetchart`: A new quiet switch that only prints out messages + when album art is missing. + Thanks to :user:`euri10`. :bug:`2683` -* :doc:`/plugins/mbcollection`: The plugin now supports a custom MusicBrainz - collection via the ``collection`` configuration option. +* :doc:`/plugins/mbcollection`: You can configure a custom MusicBrainz + collection via the new ``collection`` configuration option. :bug:`2685` -* :doc:`/plugins/mbcollection`: The plugin now supports removing albums - from collections that are longer in the beets library. -* :doc:`/plugins/mpdstats`: The plugin now updates song stats when MPD switches - from a song to a stream and when it plays the same song consecutively. - :bug:`2707` +* :doc:`/plugins/mbcollection`: The collection update command can now remove + albums from collections that are longer in the beets library. * :doc:`/plugins/fetchart`: The ``clearart`` command now asks for confirmation before touching your files. Thanks to :user:`konman2`. :bug:`2708` :bug:`2427` -* :doc:`/plugins/lyrics`: The Genius backend should work again. - Thanks to :user:`lmagno`. - :bug:`2709` +* :doc:`/plugins/mpdstats`: The plugin now correctly updates song statistics + when MPD switches from a song to a stream and when it plays the same song + multiple times consecutively. + :bug:`2707` +* :doc:`/plugins/acousticbrainz`: The plugin can now be configured to write only + a specific list of tags. + Thanks to :user:`woparry`. -Fixes: +There are lots and lots of bug fixes: -* :doc:`/plugins/hook`: Fixed a problem whereby accessing non-string properties of - objects such as item or album (e.g. item.track) would cause a crash. +* :doc:`/plugins/hook`: Fixed a problem where accessing non-string properties + of ``item`` or ``album`` (e.g., ``item.track``) would cause a crash. Thanks to :user:`broddo`. :bug:`2740` -* :doc:`/plugins/play`: When ``relative_to`` is set, correctly emit relative paths - even when querying for albums rather than tracks. +* :doc:`/plugins/play`: When ``relative_to`` is set, the plugin correctly + emits relative paths even when querying for albums rather than tracks. Thanks to :user:`j000`. :bug:`2702` -* Prevent Python from warning about a ``BrokenPipeError`` being ignored even - though we do take it into account. This was an issue when using beets in - simple shell scripts. +* We suppress a spurious Python warning about a ``BrokenPipeError`` being + ignored. This was an issue when using beets in simple shell scripts. Thanks to :user:`Azphreal`. :bug:`2622` :bug:`2631` * :doc:`/plugins/replaygain`: Fix a regression in the previous release related to the new R128 tags. :bug:`2615` :bug:`2623` -* :doc:`/plugins/lyrics`: The MusixMatch backend now detect and warns - the user when blocked on the server. Thanks to - :user:`anarcat`. :bug:`2634` :bug:`2632` +* :doc:`/plugins/lyrics`: The MusixMatch backend now detects and warns + when the server has blocked the client. + Thanks to :user:`anarcat`. :bug:`2634` :bug:`2632` * :doc:`/plugins/importfeeds`: Fix an error on Python 3 in certain configurations. Thanks to :user:`djl`. :bug:`2467` :bug:`2658` -* :doc:`/plugins/edit`: Fix a bug when editing items during a ``-L`` - re-import. Previously, diffs against against unrelated items could be - shown or beets could crash with a traceback. :bug:`2659` -* :doc:`/plugins/kodiupdate`: Fix server URL and add better error reporting. +* :doc:`/plugins/edit`: Fix a bug when editing items during a re-import with + the ``-L`` flag. Previously, diffs against against unrelated items could be + shown or beets could crash. :bug:`2659` +* :doc:`/plugins/kodiupdate`: Fix the server URL and add better error + reporting. :bug:`2662` * Fixed a problem where "no-op" modifications would reset files' mtimes, resulting in unnecessary writes. This most prominently affected the @@ -70,26 +101,43 @@ Fixes: Python 3 on Windows with non-ASCII filenames. :bug:`2671` * :doc:`/plugins/absubmit`: Fix an occasional crash on Python 3 when the AB analysis tool produced non-ASCII metadata. :bug:`2673` -* :doc:`/plugins/duplicates`: Fix the `--key` command line option, which was +* :doc:`/plugins/duplicates`: Use the default tiebreak for items or albums + when the configuration only specifies a tiebreak for the other kind of + entity. + Thanks to :user:`cgevans`. + :bug:`2758` +* :doc:`/plugins/duplicates`: Fix the ``--key`` command line option, which was ignored. -* :doc:`/plugins/replaygain`: Fix album replaygain calculation with the - gstreamer backend. :bug:`2636` +* :doc:`/plugins/replaygain`: Fix album ReplayGain calculation with the + GStreamer backend. :bug:`2636` * :doc:`/plugins/scrub`: Handle errors when manipulating files using newer versions of Mutagen. :bug:`2716` -* :doc:`/plugins/fetchart`: Fix: don't skip running the fetchart plugin during import, when the - "Edit Candidates" option is used. :bug:`2734` +* :doc:`/plugins/fetchart`: The plugin no longer gets skipped during import + when the "Edit Candidates" option is used from the :doc:`/plugins/edit`. + :bug:`2734` * Fix a crash when numeric metadata fields contain just a minus or plus sign with no following numbers. Thanks to :user:`eigengrau`. :bug:`2741` +* :doc:`/plugins/fromfilename`: Recognize file names that contain *only* a + track number, such as `01.mp3`. Also, the plugin now allows underscores as a + separator between fields. + Thanks to :user:`Vrihub`. + :bug:`2738` :bug:`2759` +* Fixed an issue where images would be resized according to their longest + edge, instead of their width, when using the ``maxwidth`` config option in + the :doc:`/plugins/fetchart` and :doc:`/plugins/embedart`. Thanks to + :user:`sekjun9878`. :bug:`2729` -For developers: +There are some changes for developers: -* Fixed fields in Album and Item objects are now more strict about translating +* "Fixed fields" in Album and Item objects are now more strict about translating missing values into type-specific null-like values. This should help in cases where a string field is unexpectedly `None` sometimes instead of just showing up as an empty string. :bug:`2605` -* Refactored the move functions in library.py and the `manipulate_files` function - in importer.py to use a single parameter describing the file operation instead - of multiple boolean flags. :bug:`2682` +* Refactored the move functions the `beets.library` module and the + `manipulate_files` function in `beets.importer` to use a single parameter + describing the file operation instead of multiple Boolean flags. + There is a new numerated type describing how to move, copy, or link files. + :bug:`2682` 1.4.5 (June 20, 2017) diff --git a/docs/conf.py b/docs/conf.py index 9bfab940a..8f8e6f9ab 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ project = u'beets' copyright = u'2016, Adrian Sampson' version = '1.4' -release = '1.4.6' +release = '1.4.7' pygments_style = 'sphinx' diff --git a/docs/modd.conf b/docs/modd.conf new file mode 100644 index 000000000..9067700d3 --- /dev/null +++ b/docs/modd.conf @@ -0,0 +1,7 @@ +**/*.rst { + prep: make html +} + +_build/html/** { + daemon: devd -m _build/html +} diff --git a/docs/plugins/acousticbrainz.rst b/docs/plugins/acousticbrainz.rst index bf2102790..5bd514c64 100644 --- a/docs/plugins/acousticbrainz.rst +++ b/docs/plugins/acousticbrainz.rst @@ -54,10 +54,12 @@ Configuration ------------- To configure the plugin, make a ``acousticbrainz:`` section in your -configuration file. There is one option: +configuration file. There are three options: - **auto**: Enable AcousticBrainz during ``beet import``. Default: ``yes``. - **force**: Download AcousticBrainz data even for tracks that already have it. Default: ``no``. +- **tags**: Which tags from the list above to set on your files. + Default: [] (all) diff --git a/docs/plugins/thumbnails.rst b/docs/plugins/thumbnails.rst index 67f83ce98..5753a9f7e 100644 --- a/docs/plugins/thumbnails.rst +++ b/docs/plugins/thumbnails.rst @@ -1,7 +1,7 @@ Thumbnails Plugin ================== -The ``thumbnails`` plugin creates thumbnails your for album folders with the +The ``thumbnails`` plugin creates thumbnails for your album folders with the album cover. This works on freedesktop.org-compliant file managers such as Nautilus or Thunar, and is therefore POSIX-only. diff --git a/docs/reference/cli.rst b/docs/reference/cli.rst index 3e668f013..39a4b3f10 100644 --- a/docs/reference/cli.rst +++ b/docs/reference/cli.rst @@ -111,6 +111,12 @@ Optional command flags: time, when no subdirectories will be skipped. So consider enabling the ``incremental`` configuration option. +* When beets applies metadata to your music, it will retain the value of any + existing tags that weren't overwritten, and import them into the database. You + may prefer to only use existing metadata for finding matches, and to erase it + completely when new metadata is applied. You can enforce this behavior with + the ``--from-scratch`` option, or the ``from_scratch`` configuration option. + * By default, beets will proceed without asking if it finds a very close metadata match. To disable this and have the importer ask you every time, use the ``-t`` (for *timid*) option. diff --git a/docs/reference/config.rst b/docs/reference/config.rst index ce45a94ee..84f41a385 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -475,6 +475,15 @@ Either ``yes`` or ``no``, controlling whether imported directories are recorded and whether these recorded directories are skipped. This corresponds to the ``-i`` flag to ``beet import``. +.. _from_scratch: + +from_scratch +~~~~~~~~~~~~ + +Either ``yes`` or ``no`` (default), controlling whether existing metadata is +discarded when a match is applied. This corresponds to the ``--from_scratch`` +flag to ``beet import``. + quiet_fallback ~~~~~~~~~~~~~~ @@ -765,6 +774,17 @@ want to enforce to the ``required`` setting:: No tags are required by default. +.. _ignore_video_tracks: + +ignore_video_tracks +~~~~~~~~~~~~~~~~~~~ + +By default, video tracks within a release will be ignored. If you want them to +be included (for example if you would like to track the audio-only versions of +the video tracks), set it to ``no``. + +Default: ``yes``. + .. _path-format-config: Path Format Configuration diff --git a/docs/serve.py b/docs/serve.py deleted file mode 100644 index 2bbf6b623..000000000 --- a/docs/serve.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import division, absolute_import, print_function - -from livereload import Server, shell - -server = Server() -server.watch('*.rst', shell('make html')) -server.serve(root='_build/html') diff --git a/extra/release.py b/extra/release.py index ce3099a69..b0b7a96e0 100755 --- a/extra/release.py +++ b/extra/release.py @@ -166,7 +166,7 @@ def rst2md(text): """Use Pandoc to convert text from ReST to Markdown. """ pandoc = subprocess.Popen( - ['pandoc', '--from=rst', '--to=markdown', '--no-wrap'], + ['pandoc', '--from=rst', '--to=markdown', '--wrap=none'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, _ = pandoc.communicate(text.encode('utf-8')) diff --git a/setup.py b/setup.py index 24c571039..903f9d17f 100755 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ if 'sdist' in sys.argv: setup( name='beets', - version='1.4.6', + version='1.4.7', description='music tagger and library organizer', author='Adrian Sampson', author_email='adrian@radbox.org', diff --git a/test/test_importer.py b/test/test_importer.py index c6b021f33..e30f5609c 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -633,6 +633,17 @@ class ImportTest(_common.TestCase, ImportHelper): self.assert_file_in_lib( b'Applied Artist', b'Applied Album', b'Applied Title 1.mp3') + def test_apply_from_scratch_removes_other_metadata(self): + config['import']['from_scratch'] = True + + for mediafile in self.import_media: + mediafile.genre = u'Tag Genre' + mediafile.save() + + self.importer.add_choice(importer.action.APPLY) + self.importer.run() + self.assertEqual(self.lib.items().get().genre, u'') + def test_apply_with_move_deletes_import(self): config['import']['move'] = True diff --git a/test/test_mb.py b/test/test_mb.py index ca1bf2a1a..35f7c3aa2 100644 --- a/test/test_mb.py +++ b/test/test_mb.py @@ -27,7 +27,7 @@ import mock class MBAlbumInfoTest(_common.TestCase): def _make_release(self, date_str='2009', tracks=None, track_length=None, - track_artist=False): + track_artist=False, medium_format='FORMAT'): release = { 'title': 'ALBUM TITLE', 'id': 'ALBUM ID', @@ -90,12 +90,12 @@ class MBAlbumInfoTest(_common.TestCase): release['medium-list'].append({ 'position': '1', 'track-list': track_list, - 'format': 'FORMAT', + 'format': medium_format, 'title': 'MEDIUM TITLE', }) return release - def _make_track(self, title, tr_id, duration, artist=False): + def _make_track(self, title, tr_id, duration, artist=False, video=False): track = { 'title': title, 'id': tr_id, @@ -113,6 +113,8 @@ class MBAlbumInfoTest(_common.TestCase): 'name': 'RECORDING ARTIST CREDIT', } ] + if video: + track['video'] = 'true' return track def test_parse_release_with_year(self): @@ -324,6 +326,62 @@ class MBAlbumInfoTest(_common.TestCase): d = mb.album_info(release) self.assertEqual(d.data_source, 'MusicBrainz') + def test_skip_non_audio_dvd(self): + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + release = self._make_release(tracks=tracks, medium_format="DVD") + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 0) + + def test_skip_non_audio_dvd_video(self): + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + release = self._make_release(tracks=tracks, medium_format="DVD-Video") + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 0) + + def test_no_skip_dvd_audio(self): + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + release = self._make_release(tracks=tracks, medium_format="DVD-Audio") + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 2) + + def test_skip_data_track(self): + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('[data track]', 'ID DATA TRACK', + 100.0 * 1000.0), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + release = self._make_release(tracks=tracks) + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 2) + self.assertEqual(d.tracks[0].title, 'TITLE ONE') + self.assertEqual(d.tracks[1].title, 'TITLE TWO') + + def test_skip_video_tracks_by_default(self): + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE VIDEO', 'ID VIDEO', 100.0 * 1000.0, + False, True), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + release = self._make_release(tracks=tracks) + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 2) + self.assertEqual(d.tracks[0].title, 'TITLE ONE') + self.assertEqual(d.tracks[1].title, 'TITLE TWO') + + def test_no_skip_video_tracks_if_configured(self): + config['match']['ignore_video_tracks'] = False + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE VIDEO', 'ID VIDEO', 100.0 * 1000.0, + False, True), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + release = self._make_release(tracks=tracks) + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 3) + self.assertEqual(d.tracks[0].title, 'TITLE ONE') + self.assertEqual(d.tracks[1].title, 'TITLE VIDEO') + self.assertEqual(d.tracks[2].title, 'TITLE TWO') + class ParseIDTest(_common.TestCase): def test_parse_id_correct(self):