Repository: beetbox/beets

Issue:
plugins: add markunmatched
## Description

Add a plugin useful for bulk importers.

## To Do

- [x] Documentation. (If you've add a new command-line flag, for example, find the appropriate page under `docs/` to describe it.)
- [x] Changelog. (Add an entry to `docs/changelog.rst` near the top of the document.)
- [ ] Tests. (Encouraged but not strictly required.)



Discussion:
Hey and thanks for the review @ybnd!

Your points have made me think for a while about the purpose of this plugin, and I think I've reached good conclusions. In any case, I'd like to hear about use cases for the ideas you've suggested, in particular:

> - These commands don't have to be limited to notifications
> - The prompt option to create a "marker file" could also be a configurable command!

Although I am interested in hearing ideas, I tend to not want to add to many moving parts to this plugin. In fact, I was thinking even further, that perhaps what's implemented here could become an addition to the `mbsubmit` plugin, instead of a whole new plugin. Because all it tries to do is help making bulk imports easier. I'd be happy to get feedback for the following idea :):

Add another `PromptChoice` for `mbsubmit.py`, that will print the tracks _into a **F**ile_ in the same format as the regular printing. The file name indeed should be configurable, as pointed out by @ybnd. This brings us sort of 1 step closer to resolve #1866.

Then, a user will be able to run something like:

```
picard $(dirname **/beets-unmatched)
```

Which will open `picard` with all of those unmatched directories, making it easy to bulk submitting those items. And also, the `beets-unmatched` file will have useful contents, which perhaps other usages will be found by users.

Eventually though, copying the [`addrelease.py` logic from picard](https://github.com/metabrainz/picard-plugins/blob/2.0/plugins/addrelease/addrelease.py?rgh-link-date=2020-03-15T09%3A49%3A41Z) I mentioned in [#1866](https://github.com/beetbox/beets/issues/1866#issuecomment-599188611), into beets will be the ideal improvement to `mbsubmit` plugin.

What do you think @sampsyo? Perhaps I should open a new PR with at least that idea added to `mbsubmit.py`?

Resolving patch:
diff --git a/beetsplug/mbsubmit.py b/beetsplug/mbsubmit.py
--- a/beetsplug/mbsubmit.py
+++ b/beetsplug/mbsubmit.py
@@ -21,11 +21,13 @@
 [1] https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings
 """
 
+import subprocess
 
 from beets import ui
 from beets.autotag import Recommendation
 from beets.plugins import BeetsPlugin
 from beets.ui.commands import PromptChoice
+from beets.util import displayable_path
 from beetsplug.info import print_data
 
 
@@ -37,6 +39,7 @@ def __init__(self):
             {
                 "format": "$track. $title - $artist ($length)",
                 "threshold": "medium",
+                "picard_path": "picard",
             }
         )
 
@@ -56,7 +59,21 @@ def __init__(self):
 
     def before_choose_candidate_event(self, session, task):
         if task.rec <= self.threshold:
-            return [PromptChoice("p", "Print tracks", self.print_tracks)]
+            return [
+                PromptChoice("p", "Print tracks", self.print_tracks),
+                PromptChoice("o", "Open files with Picard", self.picard),
+            ]
+
+    def picard(self, session, task):
+        paths = []
+        for p in task.paths:
+            paths.append(displayable_path(p))
+        try:
+            picard_path = self.config["picard_path"].as_str()
+            subprocess.Popen([picard_path] + paths)
+            self._log.info("launched picard from\n{}", picard_path)
+        except OSError as exc:
+            self._log.error(f"Could not open picard, got error:\n{exc}")
 
     def print_tracks(self, session, task):
         for i in sorted(task.items, key=lambda i: i.track):


Test patch:
diff --git a/test/plugins/test_mbsubmit.py b/test/plugins/test_mbsubmit.py
--- a/test/plugins/test_mbsubmit.py
+++ b/test/plugins/test_mbsubmit.py
@@ -45,7 +45,7 @@ def test_print_tracks_output(self):
 
         # Manually build the string for comparing the output.
         tracklist = (
-            "Print tracks? "
+            "Open files with Picard? "
             "01. Tag Title 1 - Tag Artist (0:01)\n"
             "02. Tag Title 2 - Tag Artist (0:01)"
         )
@@ -61,7 +61,9 @@ def test_print_tracks_output_as_tracks(self):
                 self.importer.run()
 
         # Manually build the string for comparing the output.
-        tracklist = "Print tracks? " "02. Tag Title 2 - Tag Artist (0:01)"
+        tracklist = (
+            "Open files with Picard? " "02. Tag Title 2 - Tag Artist (0:01)"
+        )
         self.assertIn(tracklist, output.getvalue())