Repository: beetbox/beets

Issue:
mbsubmit: cleanup and completion
Glad to see a new release has been made!

I'm getting back to work on beets after a few days away from the computer, hopefully bringing issue #1689 to a close eventually. As hinted on the previous discussion, this pull request is intended to take care of the `mbsubmit` plugin cleanup, now that the underlying pieces are in place.

I have modified a bit the behaviour, making the decision of appending the `"Print tracks"` choice depend solely on `task.rec`. The default behaviour is to only append the choice to matches where the recommendation is equal or lower than `Recommendation.medium`, which hopefully covers the most obvious choices (albums with no matches, albums with weak-ish matches) and the original request by @awesomer, and also avoids polluting the prompt in the cases where the match is strong. A config option has been added that allows the user to modify this settings (extra-picky users might find it useful to always be able to print tracks for fixing spelling mistakes, other users might only want it on albums with no matches, etc).

Other than that, a configuration option for setting the format string has been added as well - I can't think of a case where this might come in handy currently, but maybe more creative users might find it useful.

A couple of notes:
- currently, the plugin makes no effort of nicely formatting items that might be lacking some of the required fields. Would it be useful to add some extra checks and fall back to printing the filename (or something more advanced with the help of `fromfilename`, etc) in those cases?
- there might be some problems on some combination on options: for example, if the user sets the threshold to `strong`, but launches the importer in non-timid mode, the prompt will not actually be displayed. Would a note on the (upcoming) documentation suffice, as handling this case probably requires some changes that seem to be a bit out of the scope of the plugin?

As usual, any comments and input are more than welcome!



Discussion:

Resolving patch:
diff --git a/beetsplug/mbsubmit.py b/beetsplug/mbsubmit.py
--- a/beetsplug/mbsubmit.py
+++ b/beetsplug/mbsubmit.py
@@ -16,8 +16,10 @@
 """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,
@@ -25,7 +27,6 @@
 
 
 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
@@ -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())


Test patch:
diff --git a/test/test_mbsubmit.py b/test/test_mbsubmit.py
new file mode 100644
--- /dev/null
+++ b/test/test_mbsubmit.py
@@ -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')