Skip to content

Commit

Permalink
Merge pull request #1779 from diego-plan9/prompthook
Browse files Browse the repository at this point in the history
mbsubmit: cleanup and completion
  • Loading branch information
diego-plan9 committed Jan 2, 2016
2 parents f582e04 + 75636a2 commit 7ff9990
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 13 deletions.
32 changes: 19 additions & 13 deletions beetsplug/mbsubmit.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@
"""Aid in submitting information to MusicBrainz.
This plugin allows the user to print track information in a format that is
parseable by the MusicBrainz track parser. Programmatic submitting is not
parseable by the MusicBrainz track parser [1]. Programmatic submitting is not
implemented by MusicBrainz yet.
[1] http://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings
"""

from __future__ import (division, absolute_import, print_function,
unicode_literals)


from beets.autotag import Recommendation
from beets.importer import action
from beets.plugins import BeetsPlugin
from beets.ui.commands import PromptChoice
from beetsplug.info import print_data
Expand All @@ -35,21 +36,26 @@ class MBSubmitPlugin(BeetsPlugin):
def __init__(self):
super(MBSubmitPlugin, self).__init__()

self.config.add({
'format': '$track. $title - $artist ($length)',
'threshold': 'medium',
})

# Validate and store threshold.
self.threshold = self.config['threshold'].as_choice({
'none': Recommendation.none,
'low': Recommendation.low,
'medium': Recommendation.medium,
'strong': Recommendation.strong
})

self.register_listener('before_choose_candidate',
self.before_choose_candidate_event)

def before_choose_candidate_event(self, session, task):
if not task.candidates or task.rec == Recommendation.none:
return [PromptChoice('p', 'Print tracks', self.print_tracks),
PromptChoice('k', 'print tracks and sKip',
self.print_tracks_and_skip)]
if task.rec <= self.threshold:
return [PromptChoice('p', 'Print tracks', self.print_tracks)]

# Callbacks for choices.
def print_tracks(self, session, task):
for i in task.items:
print_data(None, i, '$track. $artist - $title ($length)')

def print_tracks_and_skip(self, session, task):
for i in task.items:
print_data(None, i, '$track. $artist - $title ($length)')
return action.SKIP
print_data(None, i, self.config['format'].get())
3 changes: 3 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ New:
:bug:`1778`
* A new :doc:`/plugins/acousticbrainz` fetches acoustic-analysis information
from the `AcousticBrainz`_ project. Thanks to :user:`opatel99`. :bug:`1784`
* A new :doc:`/plugins/mbsubmit` lets you print the tracks of an album in a
format parseable by MusicBrainz track parser during an interactive import
session. :bug:`1779`

.. _AcousticBrainz: http://acousticbrainz.org/

Expand Down
2 changes: 2 additions & 0 deletions docs/plugins/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Each plugin has its own set of options that can be defined in a section bearing
lastimport
lyrics
mbcollection
mbsubmit
mbsync
metasync
missing
Expand Down Expand Up @@ -164,6 +165,7 @@ Miscellaneous
* :doc:`ihate`: Automatically skip albums and tracks during the import process.
* :doc:`info`: Print music files' tags to the console.
* :doc:`mbcollection`: Maintain your MusicBrainz collection list.
* :doc:`mbsubmit`: Print an album's tracks in a MusicBrainz-friendly format.
* :doc:`missing`: List missing tracks.
* :doc:`random`: Randomly choose albums and tracks from your library.
* :doc:`filefilter`: Automatically skip files during the import process based
Expand Down
54 changes: 54 additions & 0 deletions docs/plugins/mbsubmit.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
MusicBrainz Submit Plugin
=========================

The ``mbsubmit`` plugin provides an extra prompt choice during an import
session that prints the tracks of the current album in a format that is
parseable by MusicBrainz's `track parser`_.

.. _track parser: http://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings

Usage
-----

Enable the ``mbsubmit`` plugin in your configuration (see :ref:`using-plugins`)
and select the ``Print tracks`` choice which is by default displayed when no
strong recommendations are found for the album::

No matching release found for 3 tracks.
For help, see: http://beets.readthedocs.org/en/latest/faq.html#nomatch
[U]se as-is, as Tracks, Group albums, Skip, Enter search, enter Id, aBort,
Print tracks? p
01. An Obscure Track - An Obscure Artist (3:37)
02. Another Obscure Track - An Obscure Artist (2:05)
03. The Third Track - Another Obscure Artist (3:02)

No matching release found for 3 tracks.
For help, see: http://beets.readthedocs.org/en/latest/faq.html#nomatch
[U]se as-is, as Tracks, Group albums, Skip, Enter search, enter Id, aBort,
Print tracks?

As MusicBrainz currently does not support submitting albums programmatically,
the recommended workflow is to copy the output of the ``Print tracks`` choice
and paste it into the parser that can be found by clicking on the
"Track Parser" button on MusicBrainz "Tracklist" tab.

Configuration
-------------

To configure the plugin, make a ``mbsubmit:`` section in your configuration
file. The following options are available:

- **format**: The format used for printing the tracks, defined using the
same template syntax as beets’ :doc:`path formats </reference/pathformat>`.
Default: ``$track. $title - $artist ($length)``.
- **threshold**: The minimum strength of the autotagger recommendation that
will cause the ``Print tracks`` choice to be displayed on the prompt.
Default: ``medium`` (causing the choice to be displayed for all albums that
have a recommendation of medium strength or lower). Valid values: ``none``,
``low``, ``medium``, ``strong``.

Please note that some values of the ``threshold`` configuration option might
require other ``beets`` command line switches to be enabled in order to work as
intended. In particular, setting a threshold of ``strong`` will only display
the prompt if ``timid`` mode is enabled. You can find more information about
how the recommendation system works at :ref:`match-config`.
72 changes: 72 additions & 0 deletions test/test_mbsubmit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# This file is part of beets.
# Copyright 2016, Adrian Sampson and Diego Moreda.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

from __future__ import (division, absolute_import, print_function,
unicode_literals)

from test._common import unittest
from test.helper import capture_stdout, control_stdin, TestHelper
from test.test_importer import ImportHelper, AutotagStub
from test.test_ui_importer import TerminalImportSessionSetup


class MBSubmitPluginTest(TerminalImportSessionSetup, unittest.TestCase,
ImportHelper, TestHelper):
def setUp(self):
self.setup_beets()
self.load_plugins('mbsubmit')
self._create_import_dir(2)
self._setup_import_session()
self.matcher = AutotagStub().install()

def tearDown(self):
self.unload_plugins()
self.teardown_beets()

def test_print_tracks_output(self):
"""Test the output of the "print tracks" choice."""
self.matcher.matching = AutotagStub.BAD

with capture_stdout() as output:
with control_stdin('\n'.join(['p', 's'])):
# Print tracks; Skip
self.importer.run()

# Manually build the string for comparing the output.
tracklist = ('Print tracks? '
'01. Tag Title 1 - Tag Artist (0:01)\n'
'02. Tag Title 2 - Tag Artist (0:01)')
self.assertIn(tracklist, output.getvalue())

def test_print_tracks_output_as_tracks(self):
"""Test the output of the "print tracks" choice, as singletons."""
self.matcher.matching = AutotagStub.BAD

with capture_stdout() as output:
with control_stdin('\n'.join(['t', 's', 'p', 's'])):
# as Tracks; Skip; Print tracks; Skip
self.importer.run()

# Manually build the string for comparing the output.
tracklist = ('Print tracks? '
'02. Tag Title 2 - Tag Artist (0:01)')
self.assertIn(tracklist, output.getvalue())


def suite():
return unittest.TestLoader().loadTestsFromName(__name__)

if __name__ == b'__main__':
unittest.main(defaultTest='suite')

0 comments on commit 7ff9990

Please sign in to comment.