From 779d92a419636b20858df949f66383e155c10b13 Mon Sep 17 00:00:00 2001 From: Benson Chu Date: Sat, 23 Aug 2025 09:51:59 -0500 Subject: [PATCH] New plugin? --- config.yaml | 19 +++++--- plugins/beetslabels.py | 106 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 plugins/beetslabels.py diff --git a/config.yaml b/config.yaml index d4dbd91..732ec81 100644 --- a/config.yaml +++ b/config.yaml @@ -1,17 +1,22 @@ plugins: - fetchart - lastgenre - edit - fromfilename - missing inline - playlist - smartplaylist + info + fetchart + edit duplicates mbsync + lastgenre + missing + playlist + smartplaylist + fromfilename + beetslabels # extrafiles # chromaprint +pluginpath: + - /home/benson/.config/beets/plugins + directory: /home/benson/Music library: /home/benson/Music/library.db diff --git a/plugins/beetslabels.py b/plugins/beetslabels.py new file mode 100644 index 0000000..ce11a48 --- /dev/null +++ b/plugins/beetslabels.py @@ -0,0 +1,106 @@ +from beets import ui +from beets.plugins import BeetsPlugin +from beets.ui import Subcommand, print_ +from beets.ui.commands import print_and_modify + +from beets.dbcore import types +from beets.dbcore import FieldQuery + +import optparse + +import json + +def do_modify_labels(lib, objs, label, action): + changed = [] + + for obj in objs: + labels = [] + if action != 2: + if "mylabels" in obj: + labels = json.loads(obj["mylabels"]) + + if action == 0: + labels.append(label) + labels = sorted(set(labels)) + elif label in labels: + labels.remove(label) + + obj_mods = { + "mylabels": json.dumps(labels) + } + if print_and_modify(obj, obj_mods, []) and obj not in changed: + changed.append(obj) + + # Still something to do? + if not changed: + print_("No changes to make.") + return + + # Confirm action. + changed = ui.input_select_objects( + "Really modify", + changed, + lambda o: print_and_modify(o, mods, dels), + ) + + # Apply changes to database and files + with lib.transaction(): + for obj in changed: + obj.try_sync(True, False, False) + +action_map = { + "add": 0, + "remove": 1, + "removeall": 2, + "show": 3 +} + +def modify_labels(lib, opts, args): + action = args[0] + + if action not in action_map: + print_("%s is not a valid action. " % action) + print_("Valid actions are: add, remove, removeall, show") + return + + actnum = action_map[action] + + if actnum >= 2: + label = None + query = args[1:] + else: + label = args[1] + query = args[2:] + + items = lib.items(query=query) + + if actnum == 3: + for obj in items: + print_(obj.title) + if "mylabels" in obj: + print_(" " + "; ".join(obj["mylabels"])) + else: + do_modify_labels(lib, items, label, actnum) + +labels_command = Subcommand('labels', help='Add or remove labels') +labels_command.func = modify_labels + +class JSONHas(FieldQuery): + @classmethod + def value_match(self, pattern, val): + if val is not None: + labels = json.loads(val) + return pattern in labels + return False + +class BeetsLabelsPlugin(BeetsPlugin): + # item_queries = { 'has_label': DelimitedHasExact } + # item_types = {'mylabels': types.SEMICOLON_SPACE_DSV} + + def queries(self): + return { + "JSON@" : JSONHas + } + + def commands(self): + return [labels_command]