diff --git a/NEWS.txt b/NEWS.txt index 90b457883..3a03e2b33 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,3 +1,7 @@ +Version UNRELEASED - 2012-XX-XX + * Add %license% tag + * Made %writer% available to tagger scripts and plugins with contents of songwriter (PICARD-21) + Version 0.16 - 2011-10-23 * Added AcoustID support. * Fixed track metadata plugins. diff --git a/contrib/plugins/replaygain/__init__.py b/contrib/plugins/replaygain/__init__.py index fde44048c..e2b018b56 100644 --- a/contrib/plugins/replaygain/__init__.py +++ b/contrib/plugins/replaygain/__init__.py @@ -7,7 +7,7 @@ PLUGIN_NAME = u"ReplayGain" PLUGIN_AUTHOR = u"Philipp Wolfer" PLUGIN_DESCRIPTION = """Calculate ReplayGain for selected files and albums.""" PLUGIN_VERSION = "0.1" -PLUGIN_API_VERSIONS = ["0.10", "0.15"] +PLUGIN_API_VERSIONS = ["0.10", "0.15", "0.16"] from PyQt4 import QtCore @@ -17,7 +17,7 @@ from picard.track import Track from picard.file import File from picard.util import encode_filename, decode_filename, partial from picard.ui.options import register_options_page, OptionsPage -from picard.config import BoolOption, IntOption, TextOption +from picard.config import TextOption from picard.ui.itemviews import (BaseAction, register_file_action, register_album_action) from picard.plugins.replaygain.ui_options_replaygain import Ui_ReplayGainOptionsPage @@ -28,12 +28,13 @@ REPLAYGAIN_COMMANDS = { "Ogg Vorbis": ("replaygain_vorbisgain_command", "replaygain_vorbisgain_options"), "MPEG-1 Audio": ("replaygain_mp3gain_command", "replaygain_mp3gain_options"), "FLAC": ("replaygain_metaflac_command", "replaygain_metaflac_options"), + "WavPack": ("replaygain_wvgain_command", "replaygain_wvgain_options"), } def calculate_replay_gain_for_files(files, format, tagger): """Calculates the replay gain for a list of files in album mode.""" file_list = ['%s' % encode_filename(f.filename) for f in files] - + if REPLAYGAIN_COMMANDS.has_key(format) \ and tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]]: command = tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]] @@ -45,7 +46,7 @@ def calculate_replay_gain_for_files(files, format, tagger): class ReplayGain(BaseAction): NAME = N_("Calculate replay &gain...") - + def _add_file_to_queue(self, file): self.tagger.other_queue.put(( partial(self._calculate_replaygain, file), @@ -63,7 +64,7 @@ class ReplayGain(BaseAction): def _calculate_replaygain(self, file): self.tagger.window.set_statusbar_message(N_('Calculating replay gain for "%s"...'), file.filename) calculate_replay_gain_for_files([file], file.NAME, self.tagger) - + def _replaygain_callback(self, file, result=None, error=None): if not error: self.tagger.window.set_statusbar_message(N_('Replay gain for "%s" successfully calculated.'), file.filename) @@ -72,7 +73,7 @@ class ReplayGain(BaseAction): class AlbumGain(BaseAction): NAME = N_("Calculate album &gain...") - + def callback(self, objs): albums = [o for o in objs if isinstance(o, Album)] for album in albums: @@ -80,32 +81,32 @@ class AlbumGain(BaseAction): partial(self._calculate_albumgain, album), partial(self._albumgain_callback, album), QtCore.Qt.NormalEventPriority)) - + def split_files_by_type(self, files): """Split the given files by filetype into separate lists.""" files_by_format = {} - + for file in files: if not files_by_format.has_key(file.NAME): files_by_format[file.NAME] = [file] else: files_by_format[file.NAME].append(file) - + return files_by_format - + def _calculate_albumgain(self, album): self.tagger.window.set_statusbar_message(N_('Calculating album gain for "%s"...'), album.metadata["album"]) filelist = [t.linked_files[0] for t in album.tracks if t.is_linked()] - + for format, files in self.split_files_by_type(filelist).iteritems(): calculate_replay_gain_for_files(files, format, self.tagger) - + def _albumgain_callback(self, album, result=None, error=None): if not error: self.tagger.window.set_statusbar_message(N_('Album gain for "%s" successfully calculated.'), album.metadata["album"]) else: self.tagger.window.set_statusbar_message(N_('Could not calculate album gain for "%s".'), album.metadata["album"]) - + class ReplayGainOptionsPage(OptionsPage): NAME = "replaygain" @@ -119,6 +120,8 @@ class ReplayGainOptionsPage(OptionsPage): TextOption("setting", "replaygain_mp3gain_options", "-a"), TextOption("setting", "replaygain_metaflac_command", "metaflac"), TextOption("setting", "replaygain_metaflac_options", "--add-replay-gain"), + TextOption("setting", "replaygain_wvgain_command", "wvgain"), + TextOption("setting", "replaygain_wvgain_options", "-a") ] def __init__(self, parent=None): @@ -130,12 +133,14 @@ class ReplayGainOptionsPage(OptionsPage): self.ui.vorbisgain_command.setText(self.config.setting["replaygain_vorbisgain_command"]) self.ui.mp3gain_command.setText(self.config.setting["replaygain_mp3gain_command"]) self.ui.metaflac_command.setText(self.config.setting["replaygain_metaflac_command"]) - + self.ui.wvgain_command.setText(self.config.setting["replaygain_wvgain_command"]) + def save(self): self.config.setting["replaygain_vorbisgain_command"] = unicode(self.ui.vorbisgain_command.text()) self.config.setting["replaygain_mp3gain_command"] = unicode(self.ui.mp3gain_command.text()) self.config.setting["replaygain_metaflac_command"] = unicode(self.ui.metaflac_command.text()) - + self.config.setting["replaygain_wvgain_command"] = unicode(self.ui.wvgain_command.text()) + register_file_action(ReplayGain()) register_album_action(AlbumGain()) register_options_page(ReplayGainOptionsPage) diff --git a/contrib/plugins/replaygain/options_replaygain.ui b/contrib/plugins/replaygain/options_replaygain.ui index 94a6b7d36..32bfcd62f 100644 --- a/contrib/plugins/replaygain/options_replaygain.ui +++ b/contrib/plugins/replaygain/options_replaygain.ui @@ -1,7 +1,8 @@ - + + ReplayGainOptionsPage - - + + 0 0 @@ -9,82 +10,74 @@ 317 - - + + 6 - - 9 - - - 9 - - - 9 - - + 9 - - + + Replay Gain - - + + 2 - - 9 - - - 9 - - - 9 - - + 9 - - + + Path to VorbisGain: - + - - + + Path to MP3Gain: - + - - + + Path to metaflac: - + + + + + + Path to wvgain: + + + + + - + Qt::Vertical - + 263 21 diff --git a/contrib/plugins/replaygain/ui_options_replaygain.py b/contrib/plugins/replaygain/ui_options_replaygain.py index 7d8767a2a..95df97499 100644 --- a/contrib/plugins/replaygain/ui_options_replaygain.py +++ b/contrib/plugins/replaygain/ui_options_replaygain.py @@ -2,57 +2,58 @@ # Form implementation generated from reading ui file 'options_replaygain.ui' # -# Created: Thu Mar 13 23:07:48 2008 -# by: PyQt4 UI code generator 4.3 +# Created: Sun Jan 8 13:42:44 2012 +# by: PyQt4 UI code generator 4.9 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + _fromUtf8 = lambda s: s + class Ui_ReplayGainOptionsPage(object): def setupUi(self, ReplayGainOptionsPage): - ReplayGainOptionsPage.setObjectName("ReplayGainOptionsPage") - ReplayGainOptionsPage.resize(QtCore.QSize(QtCore.QRect(0,0,305,317).size()).expandedTo(ReplayGainOptionsPage.minimumSizeHint())) - + ReplayGainOptionsPage.setObjectName(_fromUtf8("ReplayGainOptionsPage")) + ReplayGainOptionsPage.resize(305, 317) self.vboxlayout = QtGui.QVBoxLayout(ReplayGainOptionsPage) self.vboxlayout.setSpacing(6) self.vboxlayout.setMargin(9) - self.vboxlayout.setObjectName("vboxlayout") - + self.vboxlayout.setObjectName(_fromUtf8("vboxlayout")) self.replay_gain = QtGui.QGroupBox(ReplayGainOptionsPage) - self.replay_gain.setObjectName("replay_gain") - + self.replay_gain.setObjectName(_fromUtf8("replay_gain")) self.vboxlayout1 = QtGui.QVBoxLayout(self.replay_gain) self.vboxlayout1.setSpacing(2) self.vboxlayout1.setMargin(9) - self.vboxlayout1.setObjectName("vboxlayout1") - + self.vboxlayout1.setObjectName(_fromUtf8("vboxlayout1")) self.label = QtGui.QLabel(self.replay_gain) - self.label.setObjectName("label") + self.label.setObjectName(_fromUtf8("label")) self.vboxlayout1.addWidget(self.label) - self.vorbisgain_command = QtGui.QLineEdit(self.replay_gain) - self.vorbisgain_command.setObjectName("vorbisgain_command") + self.vorbisgain_command.setObjectName(_fromUtf8("vorbisgain_command")) self.vboxlayout1.addWidget(self.vorbisgain_command) - self.label_2 = QtGui.QLabel(self.replay_gain) - self.label_2.setObjectName("label_2") + self.label_2.setObjectName(_fromUtf8("label_2")) self.vboxlayout1.addWidget(self.label_2) - self.mp3gain_command = QtGui.QLineEdit(self.replay_gain) - self.mp3gain_command.setObjectName("mp3gain_command") + self.mp3gain_command.setObjectName(_fromUtf8("mp3gain_command")) self.vboxlayout1.addWidget(self.mp3gain_command) - self.label_3 = QtGui.QLabel(self.replay_gain) - self.label_3.setObjectName("label_3") + self.label_3.setObjectName(_fromUtf8("label_3")) self.vboxlayout1.addWidget(self.label_3) - self.metaflac_command = QtGui.QLineEdit(self.replay_gain) - self.metaflac_command.setObjectName("metaflac_command") + self.metaflac_command.setObjectName(_fromUtf8("metaflac_command")) self.vboxlayout1.addWidget(self.metaflac_command) + self.label_4 = QtGui.QLabel(self.replay_gain) + self.label_4.setObjectName(_fromUtf8("label_4")) + self.vboxlayout1.addWidget(self.label_4) + self.wvgain_command = QtGui.QLineEdit(self.replay_gain) + self.wvgain_command.setObjectName(_fromUtf8("wvgain_command")) + self.vboxlayout1.addWidget(self.wvgain_command) self.vboxlayout.addWidget(self.replay_gain) - - spacerItem = QtGui.QSpacerItem(263,21,QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding) + spacerItem = QtGui.QSpacerItem(263, 21, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.vboxlayout.addItem(spacerItem) self.retranslateUi(ReplayGainOptionsPage) @@ -63,4 +64,5 @@ class Ui_ReplayGainOptionsPage(object): self.label.setText(QtGui.QApplication.translate("ReplayGainOptionsPage", "Path to VorbisGain:", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("ReplayGainOptionsPage", "Path to MP3Gain:", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("ReplayGainOptionsPage", "Path to metaflac:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("ReplayGainOptionsPage", "Path to wvgain:", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/picard/formats/id3.py b/picard/formats/id3.py index 30d2e85cd..06bbe4749 100644 --- a/picard/formats/id3.py +++ b/picard/formats/id3.py @@ -25,6 +25,7 @@ from picard.metadata import Metadata from picard.file import File from picard.formats.mutagenext import compatid3 from picard.util import encode_filename, sanitize_date +from urlparse import urlparse # Ugly, but... I need to save the text in ISO-8859-1 even if it contains @@ -57,6 +58,10 @@ id3.MultiSpec._write_orig = id3.MultiSpec.write id3.MultiSpec.write = patched_MultiSpec_write +id3.TCMP = compatid3.TCMP +id3.TSO2 = compatid3.TSO2 + + class ID3File(File): """Generic ID3-based file.""" _File = None @@ -64,7 +69,7 @@ class ID3File(File): __upgrade = { 'XSOP': 'TSOP', - 'XDOR': 'TDRC', + 'TXXX:ALBUMARTISTSORT': 'TSO2', } __translate = { @@ -88,10 +93,12 @@ class ID3File(File): 'TMED': 'media', 'TBPM': 'bpm', 'WOAR': 'website', + 'WCOP': 'license', 'TSRC': 'isrc', 'TENC': 'encodedby', 'TCOP': 'copyright', 'TSOA': 'albumsort', + 'TSO2': 'albumartistsort', 'TSOP': 'artistsort', 'TSOT': 'titlesort', 'TPUB': 'label', @@ -114,7 +121,7 @@ class ID3File(File): 'Acoustid Fingerprint': 'acoustid_fingerprint', 'Acoustid Id': 'acoustid_id', 'SCRIPT': 'script', - 'ALBUMARTISTSORT': 'albumartistsort', + 'LICENSE': 'license', 'CATALOGNUMBER': 'catalognumber', 'BARCODE': 'barcode', 'ASIN': 'asin', @@ -173,7 +180,7 @@ class ID3File(File): elif frameid == 'USLT': name = 'lyrics' if frame.desc: - name += frame.desc + name += ':%s' % frame.desc metadata.add(name, unicode(frame.text)) elif frameid == 'UFID' and frame.owner == 'http://musicbrainz.org': metadata['musicbrainz_trackid'] = unicode(frame.data) @@ -243,7 +250,6 @@ class ID3File(File): tmcl = mutagen.id3.TMCL(encoding=encoding, people=[]) tipl = mutagen.id3.TIPL(encoding=encoding, people=[]) - id3.TCMP = compatid3.TCMP tags.delall('TCMP') for name, values in metadata.rawitems(): if name.startswith('performer:'): @@ -284,9 +290,19 @@ class ID3File(File): elif name in self.__rtranslate: frameid = self.__rtranslate[name] if frameid.startswith('W'): - tags.add(getattr(id3, frameid)(url=values[0])) + # Only add WCOP if there is only one license URL, otherwise use TXXX:LICENSE + if frameid == 'WCOP' and len(values) == 1 and all(urlparse(values[0])[:2]): + tags.add(getattr(id3, frameid)(url=values[0])) + else: + tags.add(id3.TXXX(encoding=encoding, desc=self.__rtranslate_freetext[name], text=values)) elif frameid.startswith('T'): tags.add(getattr(id3, frameid)(encoding=encoding, text=values)) + if frameid == 'TSOA': + tags.delall('XSOA') + elif frameid == 'TSOP': + tags.delall('XSOP') + elif frameid == 'TSO2': + tags.delall('TXXX:ALBUMARTISTSORT') elif name in self.__rtranslate_freetext: tags.add(id3.TXXX(encoding=encoding, desc=self.__rtranslate_freetext[name], text=values)) elif name.startswith('~id3:'): @@ -307,9 +323,6 @@ class ID3File(File): tags.update_to_v23() tags.save(encode_filename(filename), v2=3, v1=v1) else: - # remove all custom 2.3 frames - for old in self.__upgrade.keys(): - tags.delall(old) tags.update_to_v24() tags.save(encode_filename(filename), v2=4, v1=v1) diff --git a/picard/formats/mp4.py b/picard/formats/mp4.py index 7063cc08f..e85a426cf 100644 --- a/picard/formats/mp4.py +++ b/picard/formats/mp4.py @@ -89,6 +89,7 @@ class MP4File(File): "----:com.apple.iTunes:ISRC": "isrc", "----:com.apple.iTunes:MEDIA": "media", "----:com.apple.iTunes:LABEL": "label", + "----:com.apple.iTunes:LICENSE": "license", "----:com.apple.iTunes:CATALOGNUMBER": "catalognumber", "----:com.apple.iTunes:SUBTITLE": "subtitle", "----:com.apple.iTunes:DISCSUBTITLE": "discsubtitle", diff --git a/picard/formats/mutagenext/compatid3.py b/picard/formats/mutagenext/compatid3.py index d2ee2cc31..8ca98a6bc 100644 --- a/picard/formats/mutagenext/compatid3.py +++ b/picard/formats/mutagenext/compatid3.py @@ -28,6 +28,9 @@ from mutagen.id3 import ID3, Frame, Frames, Frames_2_2, TextFrame, TORY, \ class TCMP(TextFrame): pass +class TSO2(TextFrame): + pass + class XDOR(TextFrame): pass @@ -49,6 +52,7 @@ class CompatID3(ID3): known_frames = dict(Frames) known_frames.update(dict(Frames_2_2)) known_frames["TCMP"] = TCMP + known_frames["TSO2"] = TSO2 known_frames["XDOR"] = XDOR known_frames["XSOP"] = XSOP kwargs["known_frames"] = known_frames @@ -212,13 +216,12 @@ class CompatID3(ID3): # ID3v2.2 LNK frames are just way too different to upgrade. self.delall("LINK") - if "TSOP" in self: - f = self.pop("TSOP") - self.add(XSOP(encoding=f.encoding, text=f.text)) + # leave TSOP, TSOA and TSOT even though they are officially defined + # only in ID3v2.4, because most applications use them also in ID3v2.3 # New frames added in v2.4. for key in ["ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG", - "TMOO", "TPRO", "TSOA", "TSOT", "TSST"]: + "TMOO", "TPRO"]: if key in self: del(self[key]) for frame in self.values(): diff --git a/picard/mbxml.py b/picard/mbxml.py index cc19fcb73..adf27c107 100644 --- a/picard/mbxml.py +++ b/picard/mbxml.py @@ -27,6 +27,7 @@ AMAZON_ASIN_URL_REGEX = re.compile(r'^http://(?:www.)?(.*?)(?:\:[0-9]+)?/.*/([0- _artist_rel_types = { "composer": "composer", + "writer": "writer", "conductor": "conductor", "chorus master": "conductor", "performing orchestra": "performer:orchestra", @@ -109,6 +110,9 @@ def _relations_to_metadata(relation_lists, m, config): match = AMAZON_ASIN_URL_REGEX.match(url) if match is not None and 'asin' not in m: m['asin'] = match.group(2) + if relation.type == 'license': + url = relation.target[0].text + m.add('license', url) def _translate_artist_node(node, config=None): diff --git a/picard/script.py b/picard/script.py index 4eaf36aac..af447bd33 100644 --- a/picard/script.py +++ b/picard/script.py @@ -21,8 +21,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import re -import unicodedata + from picard.plugin import ExtensionPoint +from inspect import getargspec class ScriptError(Exception): pass class ParseError(ScriptError): pass @@ -53,7 +54,23 @@ class ScriptVariable(object): class ScriptFunction(object): - def __init__(self, name, args): + def __init__(self, name, args, parser): + try: + expected_args = parser.functions[name][2] + if expected_args and (len(args) not in expected_args): + raise ScriptError( + "Wrong number of arguments for $%s: Expected %s, got %i at position %i, line %i" + % (name, + str(expected_args[0]) + if len(expected_args) == 1 + else + "%i - %i" % (min(expected_args), max(expected_args)), + len(args), + parser._x, + parser._y)) + except KeyError: + raise UnknownFunction("Unknown function '%s'" % name) + self.name = name self.args = args @@ -61,15 +78,12 @@ class ScriptFunction(object): return "" % (self.name, self.args) def eval(self, parser): - try: - function, eval_args = parser.functions[self.name] - if eval_args: - args = [arg.eval(parser) for arg in self.args] - else: - args = self.args - return function(parser, *args) - except KeyError: - raise UnknownFunction("Unknown function '%s'" % self.name) + function, eval_args, num_args = parser.functions[self.name] + if eval_args: + args = [arg.eval(parser) for arg in self.args] + else: + args = self.args + return function(parser, *args) class ScriptExpression(list): @@ -148,7 +162,7 @@ Grammar: name = self._text[start:self._pos-1] if name not in self.functions: raise UnknownFunction("Unknown function '%s'" % name) - return ScriptFunction(name, self.parse_arguments()) + return ScriptFunction(name, self.parse_arguments(), self) elif ch is None: self.__raise_eof() elif not isidentif(ch): @@ -212,8 +226,8 @@ Grammar: def load_functions(self): self.functions = {} - for name, function, eval_args in ScriptParser._function_registry: - self.functions[name] = (function, eval_args) + for name, function, eval_args, num_args in ScriptParser._function_registry: + self.functions[name] = (function, eval_args, num_args) def parse(self, script, functions=False): """Parse the script.""" @@ -237,22 +251,32 @@ Grammar: return ScriptParser._cache[key].eval(self) -def register_script_function(function, name=None, eval_args=True): +def register_script_function(function, name=None, eval_args=True, + check_argcount=True): + """Registers a script function. If ``name`` is ``None``, + ``function.__name__`` will be used. + If ``eval_args`` is ``False``, the arguments will not be evaluated before being + passed to ``function``. + If ``check_argcount`` is ``False`` the number of arguments passed to the + function will not be verified.""" + + argspec = getargspec(function) + argcount = (len(argspec.args) - 1,) # -1 for the parser + + if argspec.defaults is not None: + argcount = range(argcount[0] - len(argspec.defaults), argcount[0] + 1) + if name is None: name = function.__name__ - ScriptParser._function_registry.register(function.__module__, (name, function, eval_args)) + ScriptParser._function_registry.register(function.__module__, + (name, function, eval_args, + argcount if argcount and check_argcount else False) + ) - -def func_if(parser, *args): - """If ``if`` is not empty, it returns ``then``, otherwise it returns - ``else``.""" - nargs = len(args) - if nargs > 1: - if args[0].eval(parser): - return args[1].eval(parser) - if nargs == 3: - return args[2].eval(parser) - return '' +def func_if(parser, _if, _then, _else=None): + """If ``_if`` is not empty, it returns ``_then``, otherwise it returns + ``_else``.""" + return _then if _if else _else if _else else '' def func_if2(parser, *args): """Returns first non empty argument.""" @@ -510,9 +534,9 @@ def func_truncate(parser, text, length): length = None return text[:length].rstrip() -register_script_function(func_if, "if", eval_args=False) -register_script_function(func_if2, "if2", eval_args=False) -register_script_function(func_noop, "noop", eval_args=False) +register_script_function(func_if, "if") +register_script_function(func_if2, "if2", eval_args=False, check_argcount=False) +register_script_function(func_noop, "noop", eval_args=False, check_argcount=False) register_script_function(func_left, "left") register_script_function(func_right, "right") register_script_function(func_lower, "lower") diff --git a/picard/ui/options/renaming.py b/picard/ui/options/renaming.py index 2134b28f8..742cad128 100644 --- a/picard/ui/options/renaming.py +++ b/picard/ui/options/renaming.py @@ -87,6 +87,8 @@ class RenamingOptionsPage(OptionsPage): parser = ScriptParser() parser.eval(script, file.metadata) filename = file._make_filename(file.filename, file.metadata, settings) + if not settings["move_files"]: + return os.path.basename(filename) return filename except SyntaxError, e: return "" except TypeError, e: return "" diff --git a/picard/util/__init__.py b/picard/util/__init__.py index 289031cc2..f007b23e8 100644 --- a/picard/util/__init__.py +++ b/picard/util/__init__.py @@ -299,7 +299,7 @@ def translate_from_sortname(name, sortname): parts = [sortname] separator = "" return separator.join(map(_reverse_sortname, parts)) - return None + return name try: diff --git a/picard/util/tags.py b/picard/util/tags.py index 1ff0a2ef2..c7c76a2e1 100644 --- a/picard/util/tags.py +++ b/picard/util/tags.py @@ -39,7 +39,9 @@ TAG_NAMES = { 'mood': N_('Mood'), 'bpm': N_('BPM'), 'copyright': N_('Copyright'), + 'license': N_('License'), 'composer': N_('Composer'), + 'writer': N_('Writer'), 'conductor': N_('Conductor'), 'lyricist': N_('Lyricist'), 'arranger': N_('Arranger'), diff --git a/test/test_mbxml.py b/test/test_mbxml.py index 4602af9e0..8b5095ead 100644 --- a/test/test_mbxml.py +++ b/test/test_mbxml.py @@ -1,13 +1,13 @@ import unittest from picard.metadata import Metadata from picard.mbxml import track_to_metadata, release_to_metadata -from picard.webservice import XmlNode class config: setting = { "standardize_tracks": False, "standardize_artists": False, - "standardize_releases": False + "standardize_releases": False, + "translate_artist_names": False } class XmlNode(object): diff --git a/test/test_utils.py b/test/test_utils.py index 786db8d23..131cf5c1b 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -85,23 +85,23 @@ class ShortFilenameTest(unittest.TestCase): class TranslateArtistTest(unittest.TestCase): def test_latin(self): - self.failUnlessEqual(u"Jean Michel Jarre", util.translate_artist(u"Jean Michel Jarre", u"Jarre, Jean Michel")) - self.failIfEqual(u"Jarre, Jean Michel", util.translate_artist(u"Jean Michel Jarre", u"Jarre, Jean Michel")) + self.failUnlessEqual(u"Jean Michel Jarre", util.translate_from_sortname(u"Jean Michel Jarre", u"Jarre, Jean Michel")) + self.failIfEqual(u"Jarre, Jean Michel", util.translate_from_sortname(u"Jean Michel Jarre", u"Jarre, Jean Michel")) def test_kanji(self): - self.failUnlessEqual(u"Tetsuya Komuro", util.translate_artist(u"小室哲哉", u"Komuro, Tetsuya")) - self.failIfEqual(u"Komuro, Tetsuya", util.translate_artist(u"小室哲哉", u"Komuro, Tetsuya")) - self.failIfEqual(u"小室哲哉", util.translate_artist(u"小室哲哉", u"Komuro, Tetsuya")) + self.failUnlessEqual(u"Tetsuya Komuro", util.translate_from_sortname(u"小室哲哉", u"Komuro, Tetsuya")) + self.failIfEqual(u"Komuro, Tetsuya", util.translate_from_sortname(u"小室哲哉", u"Komuro, Tetsuya")) + self.failIfEqual(u"小室哲哉", util.translate_from_sortname(u"小室哲哉", u"Komuro, Tetsuya")) def test_kanji2(self): - self.failUnlessEqual(u"Ayumi Hamasaki & Keiko", util.translate_artist(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko")) - self.failIfEqual(u"浜崎あゆみ & KEIKO", util.translate_artist(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko")) - self.failIfEqual(u"Hamasaki, Ayumi & Keiko", util.translate_artist(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko")) + self.failUnlessEqual(u"Ayumi Hamasaki & Keiko", util.translate_from_sortname(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko")) + self.failIfEqual(u"浜崎あゆみ & KEIKO", util.translate_from_sortname(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko")) + self.failIfEqual(u"Hamasaki, Ayumi & Keiko", util.translate_from_sortname(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko")) def test_cyrillic(self): - self.failUnlessEqual(U"Pyotr Ilyich Tchaikovsky", util.translate_artist(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich")) - self.failIfEqual(u"Tchaikovsky, Pyotr Ilyich", util.translate_artist(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich")) - self.failIfEqual(u"Пётр Ильич Чайковский", util.translate_artist(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich")) + self.failUnlessEqual(U"Pyotr Ilyich Tchaikovsky", util.translate_from_sortname(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich")) + self.failIfEqual(u"Tchaikovsky, Pyotr Ilyich", util.translate_from_sortname(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich")) + self.failIfEqual(u"Пётр Ильич Чайковский", util.translate_from_sortname(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich")) class FormatTimeTest(unittest.TestCase):