diff --git a/beetsplug/edit.py b/beetsplug/edit.py index 00de0f444..8fc57e087 100644 --- a/beetsplug/edit.py +++ b/beetsplug/edit.py @@ -275,8 +275,6 @@ class EditPlugin(plugins.BeetsPlugin): if self.diff_method: ostr = self.print_items[self.style](oldset) nwstr = self.print_items[self.style](newset) - pprint.pprint(self.diff_method) - pprint.pprint(type(self.diff_method)) self.diffresults[self.diff_method](ostr, nwstr) else: for obj in changedob: @@ -330,7 +328,6 @@ class EditPlugin(plugins.BeetsPlugin): newlines = newfilestr.splitlines() oldlines = oldfilestr.splitlines() diff = difflib.HtmlDiff() - pprint.pprint("here in html") df = diff.make_file(newlines, oldlines) ht = NamedTemporaryFile('w', suffix='.html', delete=False) ht.write(df) diff --git a/beetsplug/yamleditor.py b/beetsplug/yamleditor.py deleted file mode 100644 index 1124edd0e..000000000 --- a/beetsplug/yamleditor.py +++ /dev/null @@ -1,329 +0,0 @@ -# This file is part of beets. -# Copyright 2015, Jean-Marie Winters -# -# 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. - -"""open tags of items in texteditor,change them and save back to the items. -""" -from __future__ import (division, absolute_import, print_function, - unicode_literals) - -from beets import plugins -from beets.ui import Subcommand, decargs, library, print_ -import subprocess -import difflib -import yaml -import collections -import webbrowser -from sys import exit -from beets import config -from beets import ui -from tempfile import NamedTemporaryFile - - -class yamleditorPlugin(plugins.BeetsPlugin): - - def __init__(self): - super(yamleditorPlugin, self).__init__() - - self.config.add({ - 'style': 'yaml', - 'editor': '', - 'diff_method': '', - 'html_viewer': '', - 'editor_args': '', - 'html_args': '', - 'albumfields': 'album albumartist', - 'itemfields': 'track title artist album ', - 'not_fields': 'path', - 'separator': '-' - - }) - - def commands(self): - yamleditor_command = Subcommand( - 'yamleditor', - help='send items to yamleditor for editing tags') - yamleditor_command.parser.add_option( - '-e', '--extra', - action='store', - help='add additional fields to edit', - ) - yamleditor_command.parser.add_all_common_options() - yamleditor_command.func = self.editor_music - return[yamleditor_command] - - def editor_music(self, lib, opts, args): - """edit tags in a textfile in yaml-style - """ - self.style = self.config['style'].get() - """the editor field in the config lets you specify your editor. - Defaults to open with webrowser module""" - self.editor = self.config['editor'].get() - """the editor_args field in your config lets you specify - additional args for your editor""" - self.editor_args = self.config['editor_args'].get().split() - """the html_viewer field in your config lets you specify - your htmlviewer. Defaults to open with webrowser module""" - self.html_viewer = self.config['html_viewer'].get() - """the html_args field in your config lets you specify - additional args for your viewer""" - self.html_args = self.config['html_args'].get().split() - """the diff_method field in your config picks the way to see your - changes. Options are: - 'ndiff'(2 files with differences), - 'unified'(just the different lines and a few lines of context), - 'html'(view in html-format), - 'vimdiff'(view in VIM)""" - self.diff_method = self.config['diff_method'].get() - """the albumfields field in your config sets the tags that - you want to see/change for albums. - Defaults to album albumartist. - the ID tag will always be listed as it is used to identify the item""" - self.albumfields = self.config['albumfields'].get().split() - """the itemfields field in your config sets the tags that - you want to see/change or items. - Defaults to track title artist album. - the ID tag will always be listed as it is used to identify the item""" - self.itemfields = self.config['itemfields'].get().split() - '''the not_fields field in your config sets the tags that - will not be changed. - If you happen to change them, they will be restored to the original - value. The ID of an item will never be changed.''' - self.not_fields = self.config['not_fields'].get().split() - '''the separator in your config sets the separator that will be used - between fields in your terminal. Defaults to -''' - self.separator = self.config['separator'].get() - - query = decargs(args) - self.print_items = { - 'yaml': self.print_to_yaml} - self.diffresults = { - 'ndiff': self.ndiff, - 'unified': self.unified, - 'html': self.html, - 'vimdiff': self.vimdiff} - self.make_dict = { - 'all': self.get_all_fields, - "selected": self.get_selected_fields} - self.string_to_dict = { - 'yaml': self.yaml_to_dict} - - objs = self._get_objs(lib, opts, query) - if not objs: - print_('nothing found') - return - fmt = self.get_fields_from(objs, opts) - print_(fmt) - [print_(format(item, fmt)) for item in objs] - if not ui.input_yn(ui.colorize('action_default', "Edit?(n/y)"), True): - return - dict_from_objs = self.make_dict[self.pick](self.fields, objs, opts) - newyaml, oldyaml = self.change_objs(dict_from_objs) - changed_objs = self.check_diff(newyaml, oldyaml) - if not changed_objs: - print_("nothing to change") - return - self.save_items(changed_objs, lib, fmt, opts) - - '''from object to yaml''' - def print_to_yaml(self, arg): - return yaml.safe_dump_all( - arg, - allow_unicode=True, - default_flow_style=False) - - '''from yaml to object''' - def yaml_to_dict(self, yam): - return yaml.load_all(yam) - - def _get_objs(self, lib, opts, query): - if opts.album: - return list(lib.albums(query)) - else: - return list(lib.items(query)) - - def get_fields_from(self, objs, opts): - cl = ui.colorize('action', self.separator) - self.fields = self.albumfields if opts.album else self.itemfields - if opts.format: - self.fields = [] - self.fields.extend((opts.format).replace('$', "").split()) - if opts.extra: - fi = (opts.extra).replace('$', "").split() - self.fields.extend([f for f in fi if f not in self.fields]) - if 'id' not in self.fields: - self.fields.insert(0, 'id') - if "_all" in self.fields: - self.fields = None - self.pick = "all" - print_(ui.colorize('text_warning', "edit all fields from ...")) - if opts.album: - fmt = cl + cl.join(['$albumartist', '$album']) - else: - fmt = cl + cl.join(['$title', '$artist']) - else: - for it in self.fields: - if opts.album: - if it not in library.Album.all_keys(): - print_( - "{} not in albumfields.Removed it.".format( - ui.colorize( - 'text_warning', it))) - self.fields.remove(it) - else: - if it not in library.Item.all_keys(): - print_( - "{} not in itemfields.Removed it.".format( - ui.colorize( - 'text_warning', it))) - self.fields.remove(it) - self.pick = "selected" - fmtfields = ["$" + it for it in self.fields] - fmt = cl + cl.join(fmtfields[1:]) - - return fmt - - '''get the fields we want and make a dic from them''' - def get_selected_fields(self, myfields, objs, opts): - a = [] - for mod in objs: - a.append([{fi: mod[fi]}for fi in myfields]) - return a - - def get_all_fields(self, myfields, objs, opts): - a = [] - for mod in objs: - a.append([{fi: mod[fi]} for fi in sorted(mod._fields)]) - return a - - def change_objs(self, dict_items): - oldyaml = self.print_items[self.style](dict_items) - newyaml = self.print_items[self.style](dict_items) - new = NamedTemporaryFile(suffix='.yaml', delete=False) - new.write(newyaml) - new.close() - if not self.editor: - webbrowser.open(new.name, new=2, autoraise=True) - if self.editor and not self.editor_args: - subprocess.check_call([self.editor, new.name]) - elif self.editor and self.editor_args: - subprocess.check_call( - [self.editor, new.name, self.editor_args]) - - if ui.input_yn(ui.colorize('action_default', "done?(y)"), True): - with open(new.name) as f: - newyaml = f.read() - return newyaml, oldyaml - else: - exit() - - def save_items(self, oldnewlist, lib, fmt, opts): - oldset = [] - newset = [] - for old, new in oldnewlist: - oldset.append(old) - newset.append(new) - - no = [] - for newitem in range(0, len(newset)): - ordict = collections.OrderedDict() - for each in newset[newitem]: - ordict.update(each) - no.append(ordict) - - changedob = [] - for each in no: - if not opts.album: - ob = lib.get_item(each['id']) - else: - ob = lib.get_album(each['id']) - ob.update(each) - changedob.append(ob) - - if self.diff_method: - ostr = self.print_items[self.style](oldset) - nwstr = self.print_items[self.style](newset) - self.diffresults[self.diff_method](ostr, nwstr) - else: - for obj in changedob: - ui.show_model_changes(obj) - self.save_write(changedob) - - def save_write(self, changedob): - if not ui.input_yn('really modify? (y/n)'): - return - - for ob in changedob: - if config['import']['write'].get(bool): - ob.try_sync() - else: - ob.store() - print("changed: {0}".format(ob)) - - return - - def check_diff(self, newyaml, oldyaml): - nl = self.string_to_dict[self.style](newyaml) - ol = self.string_to_dict[self.style](oldyaml) - return filter(None, map(self.reduce_it, ol, nl)) - - '''if there is a forbidden field it gathers them here(check_ids)''' - def reduce_it(self, ol, nl): - if ol != nl: - for x in range(0, len(nl)): - if ol[x] != nl[x] and ol[x].keys()[0]in self.not_fields: - nl[x] = ol[x] - print_("reset forbidden field.") - if ol != nl: - return ol, nl - - def ndiff(self, newfilestr, oldfilestr): - newlines = newfilestr.splitlines() - oldlines = oldfilestr.splitlines() - diff = difflib.ndiff(newlines, oldlines) - print_('\n'.join(list(diff))) - return - - def unified(self, newfilestr, oldfilestr): - newlines = newfilestr.splitlines() - oldlines = oldfilestr.splitlines() - diff = difflib.unified_diff(newlines, oldlines, lineterm='') - print_('\n'.join(list(diff))) - return - - def html(self, newfilestr, oldfilestr): - newlines = newfilestr.splitlines() - oldlines = oldfilestr.splitlines() - diff = difflib.HtmlDiff() - df = diff.make_file(newlines, oldlines) - ht = NamedTemporaryFile('w', suffix='.html', delete=False) - ht.write(df) - hdn = ht.name - if not self.html_viewer: - webbrowser.open(hdn, new=2, autoraise=True) - else: - callmethod = [self.html_viewer] - callmethod.extend(self.html_args) - callmethod.append(hdn) - subprocess.call(callmethod) - return - - def vimdiff(self, newstringstr, oldstringstr): - - newdiff = NamedTemporaryFile(suffix='.old.yaml', delete=False) - newdiff.write(newstringstr) - newdiff.close() - olddiff = NamedTemporaryFile(suffix='.new.yaml', delete=False) - olddiff.write(oldstringstr) - olddiff.close() - subprocess.call(['vimdiff', newdiff.name, olddiff.name]) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0b0808372..0050c659b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,8 +3,8 @@ Changelog 1.3.16 (in development) ----------------------- -* A new plugin yamleditor helps you manually edit fields from items. - You search for items in the normal beets way.Then yamleditor opens a texteditor +* A new plugin edit helps you manually edit fields from items. + You search for items in the normal beets way.Then edit opens a texteditor with the items and the fields of the items you want to edit. Afterwards you can review your changes save them back into the items. diff --git a/docs/plugins/edit.rst b/docs/plugins/edit.rst index 68ec7a056..125f7ce0b 100644 --- a/docs/plugins/edit.rst +++ b/docs/plugins/edit.rst @@ -1,11 +1,11 @@ -editPlugin +Edit Plugin ============ The ``edit`` plugin lets you open the tags, fields from a group of items, edit them in a text-editor and save them back. Add the ``edit`` plugin to your ``plugins:`` in your ``config.yaml``. Then you simply put in a query like you normally do. :: - beet yamleditor beatles + beet yamleditor beatles beet yamleditor beatles -a beet yamleditor beatles -f '$title $lyrics' @@ -78,10 +78,12 @@ Make a ``edit:`` section in your config.yaml ``(beet config -e)`` Don't want to mess with them. * The default ``separator:`` prints like: + :: -02-The Night Before-The Beatles-Help! + but you can pick anything else. With "<>" it will look like: :: diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 0c95f366f..a4767cc22 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -40,6 +40,7 @@ Each plugin has its own set of options that can be defined in a section bearing discogs duplicates echonest + edit embedart fetchart fromfilename @@ -95,6 +96,7 @@ Metadata * :doc:`bpm`: Measure tempo using keystrokes. * :doc:`echonest`: Automatically fetch `acoustic attributes`_ from `the Echo Nest`_ (tempo, energy, danceability, ...). +* :doc:`edit`: Edit metadata from a texteditor. * :doc:`embedart`: Embed album art images into files' metadata. * :doc:`fetchart`: Fetch album cover art from various sources. * :doc:`ftintitle`: Move "featured" artists from the artist field to the title diff --git a/docs/plugins/yamleditor.rst b/docs/plugins/yamleditor.rst deleted file mode 100644 index d5f3641ba..000000000 --- a/docs/plugins/yamleditor.rst +++ /dev/null @@ -1,84 +0,0 @@ -Yamleditor Plugin -================= -The ``yamleditor`` plugin lets you open the tags, fields from a group of items, edit them in a text-editor and save them back. - -You simply put in a query like you normally do in beets. - - `beet yamleditor beatles` - - `beet yamleditor beatles -a` - - `beet yamleditor beatles -f'$title-$lyrics'` - - - -You get a list of hits and then you can edit them. The ``yamleditor`` opens your standard text-editor with a list of your hits and for each hit a bunch of fields. - -Without anything specified in your ``config.yaml`` for ``yamleditor:`` you will get - -for items - - `track-$title-$artist-$album` - -and for albums - - `$album-$albumartist` - -you can get fields from the cmdline by adding - - `-f '$genre $added'` - -or - - `-e '$year $comments'` - -If you use ``-f '$field ...'`` you get *only* what you specified. - -If you use ``-e '$field ...'`` you get what you specified *extra*. - -If you use ``-f or -e '$_all'`` you get all the fields. - -After you edit the values in your text-editor - *and you may only edit the values, no deleting fields or adding fields!* - you save the file, answer with y on ``Done`` and you get a summary of your changes. Check em, answer y or n and the changes are written to your library. - -Configuration -------------- - -Make a ``yamleditor:`` section in your config.yaml ``(beet config -e)`` - - yamleditor: - * editor: nano - * editor_args: - * diff_method: ndiff - * html_viewer:firefox - * html_args : - * albumfields: genre album - * itemfields: track artist - * not_fields: id path - * separator: "<>" - -* editor: you can pick your own texteditor. Defaults to systems default. -* editor_args: in case you need extra arguments for your text-editor. -* diff_method: 4 choices with no diff_method you get the beets way of showing differences. - * ndiff: you see original and the changed yaml files with the changes - * unified: you see the changes with a bit of context. Simple and compact. - * html: a html file that you can open in a browser. Looks nice. - * vimdiff: gives you VIM with the diffs - -* html_viewer: - If you pick ``html`` you can specify a viewer for it. If not the systems-default - will be picked. -* html_args: in case your html_viewer needs arguments -* The ``albumfields`` and ``itemfields`` let you put in a list of fields you want to change. - ``albumfields`` gets picked if you put -a in your search query else ``itemfields``. For a list of fields - do the ``beet fields``. - -* The ``not_fields`` always contain ``id`` and standard also the ``path``. - Don't want to mess with them. - -* The default ``separator`` prints like: - - ``-02-The Night Before-The Beatles-Help!`` - - but with ex "<>" it will look like: - - ``<>02<>The Night Before<>The Beatles<>Help!``