From a0598bdc6fb85dd3ffd38f828ac059d6d90fc129 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 25 Dec 2013 02:46:05 +0100 Subject: [PATCH 01/59] Ignore hidden files and directories based on `show_hidden_files` option. http://tickets.musicbrainz.org/browse/PICARD-528 --- picard/tagger.py | 7 ++++++- picard/util/__init__.py | 6 ++++++ test/test_utils.py | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/picard/tagger.py b/picard/tagger.py index b2d2208af..ac993ffae 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -70,7 +70,8 @@ from picard.util import ( thread, mbid_validate, check_io_encoding, - uniqify + uniqify, + is_hidden_path, ) from picard.webservice import XmlWebService @@ -329,9 +330,13 @@ class Tagger(QtGui.QApplication): def add_files(self, filenames, target=None): """Add files to the tagger.""" log.debug("Adding files %r", filenames) + ignore_hidden = not config.persist["show_hidden_files"] new_files = [] for filename in filenames: filename = os.path.normpath(os.path.realpath(filename)) + if ignore_hidden and is_hidden_path(filename): + log.debug("File ignored (hidden): %s" % (filename)) + continue if filename not in self.files: file = open_file(filename) if file: diff --git a/picard/util/__init__.py b/picard/util/__init__.py index f4797fc79..03ea2dc5a 100644 --- a/picard/util/__init__.py +++ b/picard/util/__init__.py @@ -365,3 +365,9 @@ if sys.platform == 'win32': return ap1 == ap2 else: os_path_samefile = os.path.samefile + + +def is_hidden_path(path): + """Returns true if at least one element of the path starts with a dot""" + path = os.path.normpath(path) # we need to ignore /./ and /a/../ cases + return any(s.startswith('.') for s in path.split(os.sep)) diff --git a/test/test_utils.py b/test/test_utils.py index 12f863fe2..13462a7a0 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import os.path import unittest from picard import util @@ -119,3 +120,18 @@ class SaveReleaseTypeScoresTest(unittest.TestCase): self.assertTrue("Single 0.50" in saved_scores) self.assertTrue("Other 0.00" in saved_scores) self.assertEqual(6, len(saved_scores.split())) + + +class HiddenPathTest(unittest.TestCase): + + def test(self): + self.assertEqual(util.is_hidden_path('/a/.b/c.mp3'), True) + self.assertEqual(util.is_hidden_path('/a/b/c.mp3'), False) + self.assertEqual(util.is_hidden_path('/a/.b/.c.mp3'), True) + self.assertEqual(util.is_hidden_path('/a/b/.c.mp3'), True) + self.assertEqual(util.is_hidden_path('c.mp3'), False) + self.assertEqual(util.is_hidden_path('.c.mp3'), True) + self.assertEqual(util.is_hidden_path('/a/./c.mp3'), False) + self.assertEqual(util.is_hidden_path('/a/./.c.mp3'), True) + self.assertEqual(util.is_hidden_path('/a/../c.mp3'), False) + self.assertEqual(util.is_hidden_path('/a/../.c.mp3'), True) From 8cc0a5780305bb91d5e50fc54267fb09cb07ba47 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Fri, 27 Dec 2013 11:10:30 +0100 Subject: [PATCH 02/59] Add "Ignore files according to show_hidden_files" to NEWS.txt --- NEWS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.txt b/NEWS.txt index 7bd9ba336..f7c1c77b4 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -16,6 +16,7 @@ * Main window is now emitting a "selection_updated" signal, plugin api version bumps to 1.3.0 * Append system information to user-agent string * Compilation tag/variable aligned with iTunes, set only for Various Artists type compilations. + * Ignore directories and files while indexing when show_hidden_files option is set to False (PICARD-528) Version 1.2 - 2013-03-30 * Picard now requires at least Python 2.6 From 100cc348d4af93b30c6459dedb236ab959d961a5 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 25 Dec 2013 02:47:57 +0100 Subject: [PATCH 03/59] Move STYLESHEET_ERROR to parent class as it may be used in any page --- picard/ui/options/__init__.py | 1 + picard/ui/options/renaming.py | 2 -- picard/ui/options/scripting.py | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/picard/ui/options/__init__.py b/picard/ui/options/__init__.py index da17d3b4b..b8cf15731 100644 --- a/picard/ui/options/__init__.py +++ b/picard/ui/options/__init__.py @@ -33,6 +33,7 @@ class OptionsPage(QtGui.QWidget): PARENT = None SORT_ORDER = 1000 ACTIVE = True + STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold }" def info(self): raise NotImplementedError diff --git a/picard/ui/options/renaming.py b/picard/ui/options/renaming.py index 67ec77001..a487376b7 100644 --- a/picard/ui/options/renaming.py +++ b/picard/ui/options/renaming.py @@ -270,8 +270,6 @@ class RenamingOptionsPage(OptionsPage): file.metadata['musicbrainz_releasetrackid'] = 'eac99807-93d4-3668-9714-fa0c1b487ccf' return file - STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold }" - def move_files_to_browse(self): path = QtGui.QFileDialog.getExistingDirectory(self, "", self.ui.move_files_to.text()) if path: diff --git a/picard/ui/options/scripting.py b/picard/ui/options/scripting.py index 6ec9d0e89..9df9c6e66 100644 --- a/picard/ui/options/scripting.py +++ b/picard/ui/options/scripting.py @@ -70,8 +70,6 @@ class ScriptingOptionsPage(OptionsPage): config.TextOption("setting", "tagger_script", ""), ] - STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold }" - def __init__(self, parent=None): super(ScriptingOptionsPage, self).__init__(parent) self.ui = Ui_ScriptingOptionsPage() From 8102061860e04588ef6f01ba8d22891199544efe Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 25 Dec 2013 02:51:04 +0100 Subject: [PATCH 04/59] Add ignore_regex option to Advanced options Filepaths matching this regex will be ignored. --- picard/tagger.py | 7 ++++ picard/ui/options/advanced.py | 41 ++++++++++++++++++++- picard/ui/ui_options_advanced.py | 48 ++++++++++++++++++++++++ ui/options_advanced.ui | 63 ++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 picard/ui/ui_options_advanced.py create mode 100644 ui/options_advanced.ui diff --git a/picard/tagger.py b/picard/tagger.py index ac993ffae..7a543e7a7 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -330,6 +330,10 @@ class Tagger(QtGui.QApplication): def add_files(self, filenames, target=None): """Add files to the tagger.""" log.debug("Adding files %r", filenames) + ignoreregex = None + pattern = config.setting['ignore_regex'] + if pattern: + ignoreregex = re.compile(pattern) ignore_hidden = not config.persist["show_hidden_files"] new_files = [] for filename in filenames: @@ -337,6 +341,9 @@ class Tagger(QtGui.QApplication): if ignore_hidden and is_hidden_path(filename): log.debug("File ignored (hidden): %s" % (filename)) continue + if ignoreregex is not None and ignoreregex.search(filename): + log.info("File ignored (matching %s): %s" % (pattern, filename)) + continue if filename not in self.files: file = open_file(filename) if file: diff --git a/picard/ui/options/advanced.py b/picard/ui/options/advanced.py index 881312fa4..3f3593ed7 100644 --- a/picard/ui/options/advanced.py +++ b/picard/ui/options/advanced.py @@ -17,7 +17,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from picard.ui.options import OptionsPage, register_options_page +from PyQt4.QtGui import QPalette, QColor +import re + +from picard import config +from picard.ui.options import OptionsPage, OptionsCheckError, register_options_page +from picard.ui.ui_options_advanced import Ui_AdvancedOptionsPage class AdvancedOptionsPage(OptionsPage): @@ -26,7 +31,39 @@ class AdvancedOptionsPage(OptionsPage): TITLE = N_("Advanced") PARENT = None SORT_ORDER = 90 - ACTIVE = False + ACTIVE = True + + options = [ + config.TextOption("setting", "ignore_regex", ""), + ] + + def __init__(self, parent=None): + super(AdvancedOptionsPage, self).__init__(parent) + self.ui = Ui_AdvancedOptionsPage() + self.ui.setupUi(self) + self.ui.ignore_regex.textChanged.connect(self.live_checker) + + def load(self): + self.ui.ignore_regex.setText(config.setting["ignore_regex"]) + + def save(self): + config.setting["ignore_regex"] = unicode(self.ui.ignore_regex.text()) + + def live_checker(self, text): + self.ui.regex_error.setStyleSheet("") + self.ui.regex_error.setText("") + try: + self.check() + except OptionsCheckError as e: + self.ui.regex_error.setStyleSheet(self.STYLESHEET_ERROR) + self.ui.regex_error.setText(e.info) + return + + def check(self): + try: + re.compile(unicode(self.ui.ignore_regex.text())) + except re.error as e: + raise OptionsCheckError(_("Regex Error"), str(e)) register_options_page(AdvancedOptionsPage) diff --git a/picard/ui/ui_options_advanced.py b/picard/ui/ui_options_advanced.py new file mode 100644 index 000000000..8d3fb9c82 --- /dev/null +++ b/picard/ui/ui_options_advanced.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ui/options_advanced.ui' +# +# Created: Wed Dec 25 02:35:20 2013 +# by: PyQt4 UI code generator 4.9.3 +# +# 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_AdvancedOptionsPage(object): + def setupUi(self, AdvancedOptionsPage): + AdvancedOptionsPage.setObjectName(_fromUtf8("AdvancedOptionsPage")) + AdvancedOptionsPage.resize(338, 435) + self.vboxlayout = QtGui.QVBoxLayout(AdvancedOptionsPage) + self.vboxlayout.setObjectName(_fromUtf8("vboxlayout")) + self.groupBox = QtGui.QGroupBox(AdvancedOptionsPage) + self.groupBox.setObjectName(_fromUtf8("groupBox")) + self.gridlayout = QtGui.QGridLayout(self.groupBox) + self.gridlayout.setSpacing(2) + self.gridlayout.setObjectName(_fromUtf8("gridlayout")) + self.label_ignore_regex = QtGui.QLabel(self.groupBox) + self.label_ignore_regex.setObjectName(_fromUtf8("label_ignore_regex")) + self.gridlayout.addWidget(self.label_ignore_regex, 1, 0, 1, 1) + self.ignore_regex = QtGui.QLineEdit(self.groupBox) + self.ignore_regex.setObjectName(_fromUtf8("ignore_regex")) + self.gridlayout.addWidget(self.ignore_regex, 2, 0, 1, 1) + self.regex_error = QtGui.QLabel(self.groupBox) + self.regex_error.setText(_fromUtf8("")) + self.regex_error.setObjectName(_fromUtf8("regex_error")) + self.gridlayout.addWidget(self.regex_error, 3, 0, 1, 1) + self.vboxlayout.addWidget(self.groupBox) + spacerItem = QtGui.QSpacerItem(181, 21, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.vboxlayout.addItem(spacerItem) + + self.retranslateUi(AdvancedOptionsPage) + QtCore.QMetaObject.connectSlotsByName(AdvancedOptionsPage) + + def retranslateUi(self, AdvancedOptionsPage): + self.groupBox.setTitle(_("Advanced options")) + self.label_ignore_regex.setText(_("Ignore file paths matching the following regular expression:")) + diff --git a/ui/options_advanced.ui b/ui/options_advanced.ui new file mode 100644 index 000000000..0d8a399c5 --- /dev/null +++ b/ui/options_advanced.ui @@ -0,0 +1,63 @@ + + + AdvancedOptionsPage + + + + 0 + 0 + 338 + 435 + + + + + + + Advanced options + + + + 2 + + + + + Ignore file paths matching the following regular expression: + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 181 + 21 + + + + + + + + ignore_regex + + + + From faffa6db01bd3730d9ed7768de139c5c5fe4a8e6 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Fri, 27 Dec 2013 11:10:55 +0100 Subject: [PATCH 05/59] Add "ignore_regex ..." to NEWS.txt --- NEWS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.txt b/NEWS.txt index f7c1c77b4..8babfed17 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -17,6 +17,7 @@ * Append system information to user-agent string * Compilation tag/variable aligned with iTunes, set only for Various Artists type compilations. * Ignore directories and files while indexing when show_hidden_files option is set to False (PICARD-528) + * Add ignore_regex option which allows one to ignore matching paths, can be set in Options > Advanced (PICARD-528) Version 1.2 - 2013-03-30 * Picard now requires at least Python 2.6 From 30b166fba11870fff4eaa75651586e8dc79e7a90 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Fri, 27 Dec 2013 21:57:29 +0100 Subject: [PATCH 06/59] Fix missing QtCore import --- picard/ui/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/ui/util.py b/picard/ui/util.py index d7b36d80c..d3056f8a5 100644 --- a/picard/ui/util.py +++ b/picard/ui/util.py @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import sys -from PyQt4 import QtGui +from PyQt4 import QtCore, QtGui from picard import config from picard.util import find_existing_path From be75583546d9a941e99f84d1f8221403ab02b7c9 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Fri, 27 Dec 2013 23:56:25 +0100 Subject: [PATCH 07/59] Remove unused imports --- picard/acoustid.py | 2 +- picard/cluster.py | 3 +-- picard/config.py | 2 +- picard/const.py | 1 - picard/coverart.py | 3 +-- picard/file.py | 2 -- picard/formats/mutagenext/compatid3.py | 2 +- picard/releasegroup.py | 3 +-- picard/tagger.py | 2 -- picard/ui/infostatus.py | 1 - picard/ui/mainwindow.py | 1 - picard/ui/options/dialog.py | 1 - picard/ui/options/fingerprinting.py | 2 +- picard/ui/options/metadata.py | 1 - picard/ui/passworddialog.py | 2 +- picard/util/thread.py | 2 +- 16 files changed, 9 insertions(+), 21 deletions(-) diff --git a/picard/acoustid.py b/picard/acoustid.py index 2d7b526f1..c50c7e532 100644 --- a/picard/acoustid.py +++ b/picard/acoustid.py @@ -21,7 +21,7 @@ from collections import deque from functools import partial from PyQt4 import QtCore from picard import config, log -from picard.const import ACOUSTID_KEY, FPCALC_NAMES +from picard.const import FPCALC_NAMES from picard.util import find_executable from picard.webservice import XmlNode diff --git a/picard/cluster.py b/picard/cluster.py index c14e47818..0977b6672 100644 --- a/picard/cluster.py +++ b/picard/cluster.py @@ -24,10 +24,9 @@ from heapq import heappush, heappop from PyQt4 import QtCore from picard import config from picard.metadata import Metadata -from picard.similarity import similarity2, similarity +from picard.similarity import similarity from picard.ui.item import Item from picard.util import format_time -from picard.mbxml import artist_credit_from_node class Cluster(QtCore.QObject, Item): diff --git a/picard/config.py b/picard/config.py index 5a4f8c176..983091980 100644 --- a/picard/config.py +++ b/picard/config.py @@ -20,7 +20,7 @@ from operator import itemgetter from PyQt4 import QtCore from picard import (PICARD_APP_NAME, PICARD_ORG_NAME, PICARD_VERSION, - version_to_string, version_from_string, log) + version_to_string, version_from_string) from picard.util import LockableObject, rot13 diff --git a/picard/const.py b/picard/const.py index 31152db57..68af4f98b 100644 --- a/picard/const.py +++ b/picard/const.py @@ -19,7 +19,6 @@ import os import sys -import re # Install gettext "noop" function in case const.py gets imported directly. import __builtin__ diff --git a/picard/coverart.py b/picard/coverart.py index df5cac28d..a71264f0f 100644 --- a/picard/coverart.py +++ b/picard/coverart.py @@ -24,10 +24,9 @@ import json import re import traceback -import picard.webservice from functools import partial from picard import config, log -from picard.metadata import Metadata, is_front_image +from picard.metadata import is_front_image from picard.util import mimetype, parse_amazon_url from picard.const import CAA_HOST, CAA_PORT from PyQt4.QtCore import QUrl, QObject diff --git a/picard/file.py b/picard/file.py index 3536ddce6..a9c7f8cf3 100644 --- a/picard/file.py +++ b/picard/file.py @@ -29,11 +29,9 @@ from operator import itemgetter from collections import defaultdict from PyQt4 import QtCore from picard import config, log -from picard.track import Track from picard.metadata import Metadata from picard.ui.item import Item from picard.script import ScriptParser -from picard.similarity import similarity2 from picard.util import ( decode_filename, encode_filename, diff --git a/picard/formats/mutagenext/compatid3.py b/picard/formats/mutagenext/compatid3.py index aa3612645..a3098c5eb 100644 --- a/picard/formats/mutagenext/compatid3.py +++ b/picard/formats/mutagenext/compatid3.py @@ -22,7 +22,7 @@ import struct from struct import pack, unpack import mutagen from mutagen._util import insert_bytes -from mutagen.id3 import ID3, Frame, Frames, Frames_2_2, TextFrame, TORY, \ +from mutagen.id3 import ID3, Frames, Frames_2_2, TextFrame, TORY, \ TYER, TIME, APIC, IPLS, TDAT, BitPaddedInt, MakeID3v1 diff --git a/picard/releasegroup.py b/picard/releasegroup.py index 70d4bc031..274ecba28 100644 --- a/picard/releasegroup.py +++ b/picard/releasegroup.py @@ -21,8 +21,7 @@ import traceback from collections import defaultdict from functools import partial from itertools import combinations -from PyQt4 import QtCore -from picard import config, log +from picard import log from picard.metadata import Metadata from picard.dataobj import DataObject from picard.mbxml import media_formats_from_node, label_info_from_node diff --git a/picard/tagger.py b/picard/tagger.py index b2d2208af..f5904fa51 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -26,7 +26,6 @@ import re import shutil import signal import sys -from collections import deque from functools import partial from itertools import chain @@ -61,7 +60,6 @@ from picard.track import Track, NonAlbumTrack from picard.releasegroup import ReleaseGroup from picard.collection import load_user_collections from picard.ui.mainwindow import MainWindow -from picard.ui.itemviews import BaseTreeView from picard.plugin import PluginManager from picard.acoustidmanager import AcoustIDManager from picard.util import ( diff --git a/picard/ui/infostatus.py b/picard/ui/infostatus.py index 769a5b9db..bfb1c8dce 100644 --- a/picard/ui/infostatus.py +++ b/picard/ui/infostatus.py @@ -18,7 +18,6 @@ from PyQt4 import QtCore, QtGui from PyQt4.QtGui import QIcon -from picard import config from picard.util import icontheme from picard.ui.ui_infostatus import Ui_InfoStatus diff --git a/picard/ui/mainwindow.py b/picard/ui/mainwindow.py index 6d4ad1c2d..3e27eafc7 100644 --- a/picard/ui/mainwindow.py +++ b/picard/ui/mainwindow.py @@ -22,7 +22,6 @@ from PyQt4 import QtCore, QtGui import sys import os.path -from functools import partial from picard import config, log from picard.file import File from picard.track import Track diff --git a/picard/ui/options/dialog.py b/picard/ui/options/dialog.py index 6a27b107b..9c49cfeb7 100644 --- a/picard/ui/options/dialog.py +++ b/picard/ui/options/dialog.py @@ -19,7 +19,6 @@ from PyQt4 import QtCore, QtGui from picard import config -from picard.plugin import ExtensionPoint from picard.util import webbrowser2 from picard.ui.util import StandardButton from picard.ui.options import ( diff --git a/picard/ui/options/fingerprinting.py b/picard/ui/options/fingerprinting.py index 52f0bc355..3a3e434ce 100644 --- a/picard/ui/options/fingerprinting.py +++ b/picard/ui/options/fingerprinting.py @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import os -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from picard import config from picard.util import webbrowser2, find_executable from picard.const import FPCALC_NAMES diff --git a/picard/ui/options/metadata.py b/picard/ui/options/metadata.py index 6ee2eba21..410535844 100644 --- a/picard/ui/options/metadata.py +++ b/picard/ui/options/metadata.py @@ -17,7 +17,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from PyQt4 import QtCore from picard import config from picard.ui.options import OptionsPage, register_options_page from picard.ui.ui_options_metadata import Ui_MetadataOptionsPage diff --git a/picard/ui/passworddialog.py b/picard/ui/passworddialog.py index 804e7e062..32335f519 100644 --- a/picard/ui/passworddialog.py +++ b/picard/ui/passworddialog.py @@ -17,7 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from picard import config from picard.ui.ui_passworddialog import Ui_PasswordDialog from picard.util import rot13 diff --git a/picard/util/thread.py b/picard/util/thread.py index 8c9b36fbf..095f8bbab 100644 --- a/picard/util/thread.py +++ b/picard/util/thread.py @@ -19,7 +19,7 @@ import sys import traceback -from PyQt4.QtCore import QThreadPool, QRunnable, QCoreApplication, QEvent +from PyQt4.QtCore import QRunnable, QCoreApplication, QEvent class ProxyToMainEvent(QEvent): From 1b84102265937425c5a9476f1092d8b14de3a9cc Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Fri, 27 Dec 2013 23:15:22 +0100 Subject: [PATCH 08/59] Remove unused code --- picard/util/bytes2human.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/picard/util/bytes2human.py b/picard/util/bytes2human.py index bfc7ceca7..34dc33459 100644 --- a/picard/util/bytes2human.py +++ b/picard/util/bytes2human.py @@ -18,8 +18,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import locale -import picard.i18n - """ Helper class to convert bytes to human-readable form @@ -118,8 +116,3 @@ def calc_unit(number, multiple=1000): return (sign * n, suffix) else: n /= multiple - - -if __name__ == "__main__": - import doctest - doctest.testmod() From 7c6d3e9c3bfee7caf51c83e6acb28465ac0fc94f Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Fri, 27 Dec 2013 23:16:31 +0100 Subject: [PATCH 09/59] decimal(): fix missing parameter `prec` and update tests --- picard/util/bytes2human.py | 2 +- test/test_bytes2human.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/picard/util/bytes2human.py b/picard/util/bytes2human.py index 34dc33459..9b31c23b3 100644 --- a/picard/util/bytes2human.py +++ b/picard/util/bytes2human.py @@ -51,7 +51,7 @@ def decimal(number, prec=1): >>> [decimal(n) for n in [1000, 1024, 15500]] ['1 kB', '1 kB', '15.5 kB'] """ - return short_string(int(number), 1000) + return short_string(int(number), 1000, prec) def binary(number, prec=1): diff --git a/test/test_bytes2human.py b/test/test_bytes2human.py index 7a37737c2..9f8d53305 100644 --- a/test/test_bytes2human.py +++ b/test/test_bytes2human.py @@ -30,8 +30,11 @@ class Testbytes2human(unittest.TestCase): self.assertEqual(bytes2human.binary(45682), '44.6 KiB') self.assertEqual(bytes2human.binary(-45682), '-44.6 KiB') + self.assertEqual(bytes2human.binary(-45682, 2), '-44.61 KiB') self.assertEqual(bytes2human.decimal(45682), '45.7 kB') + self.assertEqual(bytes2human.decimal(45682, 2), '45.68 kB') self.assertEqual(bytes2human.decimal(9223372036854775807), '9223.4 PB') + self.assertEqual(bytes2human.decimal(9223372036854775807, 3), '9223.372 PB') self.assertEqual(bytes2human.decimal(123.6), '123 B') self.assertRaises(ValueError, bytes2human.decimal, 'xxx') self.assertRaises(ValueError, bytes2human.decimal, '123.6') From 5a02f9d719c1a0588d5eff675371265682782e29 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 02:16:05 +0100 Subject: [PATCH 10/59] `prec` -> `scale` --- picard/util/bytes2human.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/picard/util/bytes2human.py b/picard/util/bytes2human.py index 9b31c23b3..ea8fb4eb2 100644 --- a/picard/util/bytes2human.py +++ b/picard/util/bytes2human.py @@ -44,26 +44,26 @@ _BYTES_STRINGS_I18N = ( ) -def decimal(number, prec=1): +def decimal(number, scale=1): """ Convert bytes to short human-readable string, decimal mode >>> [decimal(n) for n in [1000, 1024, 15500]] ['1 kB', '1 kB', '15.5 kB'] """ - return short_string(int(number), 1000, prec) + return short_string(int(number), 1000, scale) -def binary(number, prec=1): +def binary(number, scale=1): """ Convert bytes to short human-readable string, binary mode >>> [binary(n) for n in [1000, 1024, 15500]] ['1000 B', '1 KiB', '15.1 KiB'] """ - return short_string(int(number), 1024, prec) + return short_string(int(number), 1024, scale) -def short_string(number, multiple, prec=1): +def short_string(number, multiple, scale=1): """ Returns short human-readable string for `number` bytes >>> [short_string(n, 1024, 2) for n in [1000, 1100, 15500]] @@ -73,12 +73,12 @@ def short_string(number, multiple, prec=1): """ num, unit = calc_unit(number, multiple) n = int(num) - nr = round(num, prec) + nr = round(num, scale) if n == nr or unit == 'B': fmt = '%d' num = n else: - fmt = '%%0.%df' % prec + fmt = '%%0.%df' % scale num = nr fmtnum = locale.format(fmt, num) return _("%s " + unit) % fmtnum From ff0de4e38250487939789c538738f81ab3ec4778 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 10:52:34 +0100 Subject: [PATCH 11/59] ClusterEngine.cluster(): remove unused return value --- picard/cluster.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/picard/cluster.py b/picard/cluster.py index c14e47818..9ce6afac7 100644 --- a/picard/cluster.py +++ b/picard/cluster.py @@ -190,10 +190,10 @@ class Cluster(QtCore.QObject, Item): albumDict.add(album))) artist_cluster_engine = ClusterEngine(artistDict) - artist_cluster = artist_cluster_engine.cluster(threshold) + artist_cluster_engine.cluster(threshold) album_cluster_engine = ClusterEngine(albumDict) - album_cluster = album_cluster_engine.cluster(threshold) + album_cluster_engine.cluster(threshold) # Arrange tracks into albums albums = {} @@ -459,7 +459,5 @@ class ClusterEngine(object): self.idClusterIndex[match] = match0 del self.clusterBins[match1] - return self.clusterBins - def can_refresh(self): return False From 4b0239fc0c0e626a179fd93952538af9d074e36f Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 11:56:34 +0100 Subject: [PATCH 12/59] get_cdrom_drives(): code cleanup --- picard/util/cdrom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/picard/util/cdrom.py b/picard/util/cdrom.py index 64ae157d3..4b0cef41b 100644 --- a/picard/util/cdrom.py +++ b/picard/util/cdrom.py @@ -79,10 +79,11 @@ elif sys.platform == 'linux2' and QFile.exists(LINUX_CDROM_INFO): elif key == 'Can play audio': drive_audio_caps = [v == '1' for v in QString(values).trimmed().split(QRegExp("\\s+"), QString.SkipEmptyParts)] + break # no need to continue passed this line line = cdinfo.readLine() # Show only drives that are capable of playing audio - for drive in drive_names: - if drive_audio_caps[drive_names.indexOf(drive)]: + for index, drive in enumerate(drive_names): + if drive_audio_caps[index]: device = u'/dev/%s' % drive symlink_target = QFile.symLinkTarget(device) if symlink_target != '': From e4852cf0d4d5bf73ed99d0d6cc2d57f7c8039f4e Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 12:02:06 +0100 Subject: [PATCH 13/59] File._load(): makes proto matches subclass definitions, add filename parameter --- picard/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/file.py b/picard/file.py index 3536ddce6..620bcfa59 100644 --- a/picard/file.py +++ b/picard/file.py @@ -144,7 +144,7 @@ class File(QtCore.QObject, Item): def has_error(self): return self.state == File.ERROR - def _load(self): + def _load(self, filename): """Load metadata from the file.""" raise NotImplementedError From 17bb6df137dcef73cdaaaf834ddbb4eee4a9b82d Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 12:36:41 +0100 Subject: [PATCH 14/59] Indentation fix --- picard/file.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/picard/file.py b/picard/file.py index 620bcfa59..041bc7621 100644 --- a/picard/file.py +++ b/picard/file.py @@ -359,8 +359,8 @@ class File(QtCore.QObject, Item): # image multiple times if (os.path.exists(new_filename) and os.path.getsize(new_filename) == len(data)): - log.debug("Identical file size, not saving %r", image_filename) - continue + log.debug("Identical file size, not saving %r", image_filename) + continue log.debug("Saving cover images to %r", image_filename) new_dirname = os.path.dirname(image_filename) if not os.path.isdir(new_dirname): From cac1e3dd895858535872a288391db4bf78ea7d24 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 12:37:07 +0100 Subject: [PATCH 15/59] Unused variable `e` removed --- picard/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/log.py b/picard/log.py index d4faef298..1f4317f37 100644 --- a/picard/log.py +++ b/picard/log.py @@ -62,7 +62,7 @@ class Logger(object): for func in self._receivers: try: thread.to_main(func, level, time, message) - except Exception as e: + except: import traceback traceback.print_exc() From b84380fd70ecc7a299509a7eaf1fb2fe396aa416 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 12:37:59 +0100 Subject: [PATCH 16/59] Rewrite loop as expression value is unused --- picard/ui/cdlookup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/picard/ui/cdlookup.py b/picard/ui/cdlookup.py index cc5bd4810..cd9355590 100644 --- a/picard/ui/cdlookup.py +++ b/picard/ui/cdlookup.py @@ -50,7 +50,8 @@ class CDLookupDialog(QtGui.QDialog): item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(release.id)) self.ui.release_list.setCurrentItem(self.ui.release_list.topLevelItem(0)) self.ui.ok_button.setEnabled(True) - [self.ui.release_list.resizeColumnToContents(i) for i in range(self.ui.release_list.columnCount() - 1)] + for i in range(self.ui.release_list.columnCount() - 1): + self.ui.release_list.resizeColumnToContents(i) # Sort by descending date, then ascending country self.ui.release_list.sortByColumn(3, QtCore.Qt.AscendingOrder) self.ui.release_list.sortByColumn(2, QtCore.Qt.DescendingOrder) From 60b6cbddab7725ba4cdbf4bfaf6977d9ac2c1141 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 12:38:28 +0100 Subject: [PATCH 17/59] Rewrite test as expression value is unused --- picard/ui/itemviews.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/picard/ui/itemviews.py b/picard/ui/itemviews.py index 1a6d759d7..9b5d52ec8 100644 --- a/picard/ui/itemviews.py +++ b/picard/ui/itemviews.py @@ -308,7 +308,9 @@ class BaseTreeView(QtGui.QTreeWidget): action.setChecked(True) action.triggered.connect(partial(obj.switch_release_version, version["id"])) - _add_other_versions() if obj.release_group.loaded else \ + if obj.release_group.loaded: + _add_other_versions() + else: obj.release_group.load_versions(_add_other_versions) releases_menu.setEnabled(True) else: From 88ae83de7891fc4349ff599a295e76b1809a1b23 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 12:38:34 +0100 Subject: [PATCH 18/59] Rewrite loop as expression value is unused --- picard/ui/metadatabox.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/picard/ui/metadatabox.py b/picard/ui/metadatabox.py index bec999ff0..94b7d08ba 100644 --- a/picard/ui/metadatabox.py +++ b/picard/ui/metadatabox.py @@ -290,7 +290,9 @@ class MetadataBox(QtGui.QTableWidget): self.set_tag_values(tag, [""]) def remove_selected_tags(self): - (self.remove_tag(tag) for tag in self.selected_tags() if self.tag_is_removable(tag)) + for tag in self.selected_tags(): + if self.tag_is_removable(tag): + self.remove_tag(tag) def tag_is_removable(self, tag): return self.tag_diff.status[tag] & TagStatus.NotRemovable == 0 From aed7091c3a762e867d00a2f04e5164143189cb6c Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 13:22:49 +0100 Subject: [PATCH 19/59] Add missing header --- picard/i18n.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/picard/i18n.py b/picard/i18n.py index 8520ee252..03b61850e 100644 --- a/picard/i18n.py +++ b/picard/i18n.py @@ -1,3 +1,22 @@ +# -*- coding: utf-8 -*- +# +# Picard, the next-generation MusicBrainz tagger +# Copyright (C) 2013 Laurent Monin +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + import gettext import locale import os.path From 220710b046039b24cdf553bb287f4eb55bd35a0b Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 28 Dec 2013 13:23:47 +0100 Subject: [PATCH 20/59] coding: UTF-8 -> coding: utf-8, as all other files --- picard/ui/filebrowser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/ui/filebrowser.py b/picard/ui/filebrowser.py index c469aaa67..310a2f567 100644 --- a/picard/ui/filebrowser.py +++ b/picard/ui/filebrowser.py @@ -1,4 +1,4 @@ -# -*- coding: UTF-8 -*- +# -*- coding: utf-8 -*- # # Picard, the next-generation MusicBrainz tagger # Copyright (C) 2006-2007 Lukáš Lalinský From f79f981ac4d862e6897f73d8dfcda6920b235312 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sun, 29 Dec 2013 01:27:27 +0100 Subject: [PATCH 21/59] Fix encoding of limit parameter value in webservice query (limit=%19) This is a very old bug it seems, it was indirectly introduced by commit 9092a7abcae5254e581497b346b2167a5e37cb26 The missing str() was causing integer value 25 to be encoded as ascii char 25, then, after percent encoding, limit=%19 in the search url. --- picard/webservice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/webservice.py b/picard/webservice.py index d9b17b8b8..721d34617 100644 --- a/picard/webservice.py +++ b/picard/webservice.py @@ -392,7 +392,7 @@ class XmlWebService(QtCore.QObject): query = [] for name, value in kwargs.items(): if name == 'limit': - filters.append((name, value)) + filters.append((name, str(value))) else: value = _escape_lucene_query(value).strip().lower() if value: From fa11a96b410c64c7c096fee37a3da2f80d483ad1 Mon Sep 17 00:00:00 2001 From: Johannes Dewender Date: Sun, 29 Dec 2013 19:46:11 +0100 Subject: [PATCH 22/59] PICARD-544: include socket module in exe This is needed by urllib, which is imported since 26f20228eb95d4b55968e56796228bca7ebcffa7. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 306869719..4873da869 100755 --- a/setup.py +++ b/setup.py @@ -405,7 +405,7 @@ try: args['options'] = { 'bdist_nsis': { 'includes': ['json', 'sip'] + [e.name for e in ext_modules], - 'excludes': ['ssl', 'socket', 'bz2'], + 'excludes': ['ssl', 'bz2'], 'optimize': 2, }, } From e633bcde115e4afccb1a98e322905054617eed9d Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sun, 29 Dec 2013 21:01:14 +0100 Subject: [PATCH 23/59] Drop urllib dependency, use QUrl.toPercentEncoding instead of urllib.quote --- picard/webservice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/picard/webservice.py b/picard/webservice.py index 721d34617..55442e2b9 100644 --- a/picard/webservice.py +++ b/picard/webservice.py @@ -28,7 +28,6 @@ import re import time import os.path import platform -import urllib from collections import deque, defaultdict from functools import partial from PyQt4 import QtCore, QtNetwork @@ -55,8 +54,9 @@ USER_AGENT_STRING = '%s-%s/%s (%s;%s-%s)' % (PICARD_ORG_NAME, PICARD_APP_NAME, platform.platform(), platform.python_implementation(), platform.python_version()) -CLIENT_STRING = urllib.quote('%s %s-%s' % (PICARD_ORG_NAME, PICARD_APP_NAME, - PICARD_VERSION_STR)) +CLIENT_STRING = str(QUrl.toPercentEncoding('%s %s-%s' % (PICARD_ORG_NAME, + PICARD_APP_NAME, + PICARD_VERSION_STR))) def _escape_lucene_query(text): From 13423436590dc6d598b92d8f896e54dcce11546a Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sun, 29 Dec 2013 21:01:43 +0100 Subject: [PATCH 24/59] Exclude socket module again, since it was only needed by urllib --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4873da869..306869719 100755 --- a/setup.py +++ b/setup.py @@ -405,7 +405,7 @@ try: args['options'] = { 'bdist_nsis': { 'includes': ['json', 'sip'] + [e.name for e in ext_modules], - 'excludes': ['ssl', 'bz2'], + 'excludes': ['ssl', 'socket', 'bz2'], 'optimize': 2, }, } From 5289f7b461014f3f16e139aca3bc5cabf4adad35 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Mon, 30 Dec 2013 12:08:57 +0100 Subject: [PATCH 25/59] transifex.net is deprecated, use transifex.com instead http://support.transifex.com/customer/portal/questions/1160404-improve-ssl-security-for-transifex-net tx client is giving a message about it too Also add translation type 'PO'. --- .tx/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.tx/config b/.tx/config index ea944391c..9ada80424 100644 --- a/.tx/config +++ b/.tx/config @@ -1,8 +1,8 @@ [main] -host = https://www.transifex.net +host = https://www.transifex.com [musicbrainz.picard] file_filter = po/.po source_file = po/picard.pot source_lang = en - +type = PO From e63bc3dab57f2595f362c247289268f8fabcfc07 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 14:07:19 +0100 Subject: [PATCH 26/59] Name upgrade functions after full version --- picard/tagger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/picard/tagger.py b/picard/tagger.py index f5904fa51..b4064ca2e 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -165,7 +165,7 @@ class Tagger(QtGui.QApplication): # In version 1.0, the file naming formats for single and various # artist releases were merged. - def upgrade_to_v1_0(): + def upgrade_to_v1_0_0_final_0(): def remove_va_file_naming_format(merge=True): if merge: config.setting["file_naming_format"] = ( @@ -197,7 +197,7 @@ class Tagger(QtGui.QApplication): # default format, disabled remove_va_file_naming_format(merge=False) - def upgrade_to_v1_3(): + def upgrade_to_v1_3_0_dev_2(): _s = config.setting # the setting `windows_compatible_filenames` has been renamed # to `windows_compatibility` @@ -213,8 +213,8 @@ class Tagger(QtGui.QApplication): log.debug("Config upgrade: convert preserved_tags separator " "from spaces to comma") - cfg.register_upgrade_hook("1.0.0final0", upgrade_to_v1_0) - cfg.register_upgrade_hook("1.3.0dev2", upgrade_to_v1_3) + cfg.register_upgrade_hook("1.0.0final0", upgrade_to_v1_0_0_final_0) + cfg.register_upgrade_hook("1.3.0dev2", upgrade_to_v1_3_0_dev_2) cfg.run_upgrade_hooks() From 61900e1b80444e08efee75d8e2baafadc88e0f04 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 15:29:16 +0100 Subject: [PATCH 27/59] Ensure each upgrade function is run only once --- picard/tagger.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/picard/tagger.py b/picard/tagger.py index b4064ca2e..55c503aa6 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -197,7 +197,7 @@ class Tagger(QtGui.QApplication): # default format, disabled remove_va_file_naming_format(merge=False) - def upgrade_to_v1_3_0_dev_2(): + def upgrade_to_v1_3_0_dev_1(): _s = config.setting # the setting `windows_compatible_filenames` has been renamed # to `windows_compatibility` @@ -207,6 +207,8 @@ class Tagger(QtGui.QApplication): log.debug("Config upgrade: windows_compatible_filenames " "renamed windows_compatibility") + def upgrade_to_v1_3_0_dev_2(): + _s = config.setting # preserved_tags spaces to comma separator, PICARD-536 if "preserved_tags" in _s: _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) @@ -214,6 +216,7 @@ class Tagger(QtGui.QApplication): "from spaces to comma") cfg.register_upgrade_hook("1.0.0final0", upgrade_to_v1_0_0_final_0) + cfg.register_upgrade_hook("1.3.0dev1", upgrade_to_v1_3_0_dev_1) cfg.register_upgrade_hook("1.3.0dev2", upgrade_to_v1_3_0_dev_2) cfg.run_upgrade_hooks() From f8e85c3957231ef4e22dd16bb8496caadbe3e3f9 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 14:09:34 +0100 Subject: [PATCH 28/59] Move variable near where it is used --- picard/tagger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/tagger.py b/picard/tagger.py index 55c503aa6..c76d2fe6a 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -161,7 +161,6 @@ class Tagger(QtGui.QApplication): self.window = MainWindow() def _upgrade_config(self): - cfg = config._config # In version 1.0, the file naming formats for single and various # artist releases were merged. @@ -215,6 +214,7 @@ class Tagger(QtGui.QApplication): log.debug("Config upgrade: convert preserved_tags separator " "from spaces to comma") + cfg = config._config cfg.register_upgrade_hook("1.0.0final0", upgrade_to_v1_0_0_final_0) cfg.register_upgrade_hook("1.3.0dev1", upgrade_to_v1_3_0_dev_1) cfg.register_upgrade_hook("1.3.0dev2", upgrade_to_v1_3_0_dev_2) From 672f47f72c76c78acfad82660fe18545511448f2 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 15:31:07 +0100 Subject: [PATCH 29/59] Define _s once in parent function --- picard/tagger.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/picard/tagger.py b/picard/tagger.py index c76d2fe6a..b94956213 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -161,6 +161,7 @@ class Tagger(QtGui.QApplication): self.window = MainWindow() def _upgrade_config(self): + _s = config.setting # In version 1.0, the file naming formats for single and various # artist releases were merged. @@ -197,7 +198,6 @@ class Tagger(QtGui.QApplication): remove_va_file_naming_format(merge=False) def upgrade_to_v1_3_0_dev_1(): - _s = config.setting # the setting `windows_compatible_filenames` has been renamed # to `windows_compatibility` if "windows_compatible_filenames" in _s: @@ -207,7 +207,6 @@ class Tagger(QtGui.QApplication): "renamed windows_compatibility") def upgrade_to_v1_3_0_dev_2(): - _s = config.setting # preserved_tags spaces to comma separator, PICARD-536 if "preserved_tags" in _s: _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) From 6932ac0d1a008dfbb8c40a2b09214367a3ecd18a Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 14:11:09 +0100 Subject: [PATCH 30/59] config.setting -> _s in upgrade_to_v1_0_0_final_0() too --- picard/tagger.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/picard/tagger.py b/picard/tagger.py index b94956213..b0e005b49 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -168,23 +168,23 @@ class Tagger(QtGui.QApplication): def upgrade_to_v1_0_0_final_0(): def remove_va_file_naming_format(merge=True): if merge: - config.setting["file_naming_format"] = ( + _s["file_naming_format"] = ( "$if($eq(%compilation%,1),\n$noop(Various Artist " "albums)\n%s,\n$noop(Single Artist Albums)\n%s)" % ( - config.setting["va_file_naming_format"].toString(), - config.setting["file_naming_format"] + _s["va_file_naming_format"].toString(), + _s["file_naming_format"] )) - config.setting.remove("va_file_naming_format") - config.setting.remove("use_va_format") + _s.remove("va_file_naming_format") + _s.remove("use_va_format") - if ("va_file_naming_format" in config.setting and - "use_va_format" in config.setting): + if ("va_file_naming_format" in _s and + "use_va_format" in _s): - if config.setting["use_va_format"].toBool(): + if _s["use_va_format"].toBool(): remove_va_file_naming_format() self.window.show_va_removal_notice() - elif (config.setting["va_file_naming_format"].toString() != + elif (_s["va_file_naming_format"].toString() != r"$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldis" "cs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - " "%title%"): From b93c4e37861816db1d251f418a03e2e7475c59f2 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 14:12:22 +0100 Subject: [PATCH 31/59] Add FIXME about broken upgrade hook --- picard/tagger.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/picard/tagger.py b/picard/tagger.py index b0e005b49..563a1c260 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -165,6 +165,8 @@ class Tagger(QtGui.QApplication): # In version 1.0, the file naming formats for single and various # artist releases were merged. + # FIXME: this hook will not work as it is called before self.window + # instanciation def upgrade_to_v1_0_0_final_0(): def remove_va_file_naming_format(merge=True): if merge: From 042b47db6a08806b7374ee9592da9008ed21e015 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 14:14:14 +0100 Subject: [PATCH 32/59] Make ConfigUpgradeError message more useful including traceback --- picard/config.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/picard/config.py b/picard/config.py index 983091980..2ea475da1 100644 --- a/picard/config.py +++ b/picard/config.py @@ -124,13 +124,15 @@ class Config(QtCore.QSettings): if self._version < hook['to']: try: hook['func'](*hook['args']) - except Exception as e: + except: + import traceback raise ConfigUpgradeError( "Error during config upgrade from version %s to %s " - "using %s(): %s" % ( + "using %s():\n%s" % ( version_to_string(self._version), version_to_string(hook['to']), - hook['func'].__name__, e + hook['func'].__name__, + traceback.format_exc() )) else: hook['done'] = True From 4554898e4288c4941d3a50e8fe3cbdc413058e2a Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 14:58:55 +0100 Subject: [PATCH 33/59] version_to_string(): be smarter and accept list with strings --- picard/__init__.py | 25 +++++++++++++++++-------- test/test_versions.py | 9 ++++++--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/picard/__init__.py b/picard/__init__.py index 22abb9e92..d0e324c66 100644 --- a/picard/__init__.py +++ b/picard/__init__.py @@ -25,16 +25,25 @@ PICARD_ORG_NAME = "MusicBrainz" PICARD_VERSION = (1, 3, 0, 'dev', 2) -def version_to_string(version_tuple, short=False): - assert len(version_tuple) == 5 - assert version_tuple[3] in ('final', 'dev') - if short and version_tuple[3] == 'final': - if version_tuple[2] == 0: - version_str = '%d.%d' % version_tuple[:2] +def version_to_string(version, short=False): + assert len(version) == 5 + assert version[3] in ('final', 'dev') + _version = [] + for p in version: + try: + n = int(p) + except ValueError: + n = p + pass + _version.append(n) + version = tuple(_version) + if short and version[3] == 'final': + if version[2] == 0: + version_str = '%d.%d' % version[:2] else: - version_str = '%d.%d.%d' % version_tuple[:3] + version_str = '%d.%d.%d' % version[:3] else: - version_str = '%d.%d.%d%s%d' % version_tuple + version_str = '%d.%d.%d%s%d' % version return version_str diff --git a/test/test_versions.py b/test/test_versions.py index 69d5a813a..d30832d3f 100644 --- a/test/test_versions.py +++ b/test/test_versions.py @@ -32,9 +32,8 @@ class VersionsTest(unittest.TestCase): self.assertEqual(l, version_from_string(s)) def test_version_conv_6(self): - self.assertRaises(TypeError, version_to_string, ('1', 0, 2, 'final', 0)) - self.assertRaises(AssertionError, version_to_string, (1, 0)) - self.assertRaises(TypeError, version_from_string, 1) + l = (1, 0, 2, 'xx', 0) + self.assertRaises(AssertionError, version_to_string, (l)) def test_version_conv_7(self): l, s = (1, 1, 0, 'final', 0), '1.1' @@ -51,3 +50,7 @@ class VersionsTest(unittest.TestCase): def test_version_conv_10(self): l, s = (1, 1, 0, 'dev', 0), '1.1.0dev0' self.assertEqual(version_to_string(l, short=True), s) + + def test_version_conv_11(self): + l, s = ('1', '1', '0', 'dev', '0'), '1.1.0dev0' + self.assertEqual(version_to_string(l), s) From 5fc4129b32a252cd0fea9e2d49f2159dfbc87c14 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 15:26:22 +0100 Subject: [PATCH 34/59] Simplify hook registration by detecting upgrade hook functions --- picard/config.py | 21 ++++++++++++++++++++- picard/tagger.py | 5 +---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/picard/config.py b/picard/config.py index 2ea475da1..f0cf18a3f 100644 --- a/picard/config.py +++ b/picard/config.py @@ -17,6 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +import re from operator import itemgetter from PyQt4 import QtCore from picard import (PICARD_APP_NAME, PICARD_ORG_NAME, PICARD_VERSION, @@ -94,7 +95,25 @@ class Config(QtCore.QSettings): else: raise KeyError("Unknown profile '%s'" % (profilename,)) - def register_upgrade_hook(self, to_version_str, func, *args): + def _detect_upgrade_hooks(self, symbol_table): + """Detect upgrade functions based on their names""" + hooks = dict() + pattern = re.compile("^upgrade_to_v(\d+)_(\d+)_(\d+)_(dev|final)_(\d+)$") + for symbol in symbol_table: + match = re.search(pattern, symbol) + if not match: + continue + version = match.groups() + version_string = version_to_string(version) + hooks[version_string] = symbol_table[symbol] + return hooks + + def register_upgrade_hooks(self, symbol_table): + hooks = self._detect_upgrade_hooks(symbol_table) + for version in sorted(hooks): + self._register_upgrade_hook(version, hooks[version]) + + def _register_upgrade_hook(self, to_version_str, func, *args): """Register a function to upgrade from one config version to another""" to_version = version_from_string(to_version_str) assert to_version <= PICARD_VERSION, "%r > %r !!!" % (to_version, PICARD_VERSION) diff --git a/picard/tagger.py b/picard/tagger.py index 563a1c260..e60aedf17 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -216,10 +216,7 @@ class Tagger(QtGui.QApplication): "from spaces to comma") cfg = config._config - cfg.register_upgrade_hook("1.0.0final0", upgrade_to_v1_0_0_final_0) - cfg.register_upgrade_hook("1.3.0dev1", upgrade_to_v1_3_0_dev_1) - cfg.register_upgrade_hook("1.3.0dev2", upgrade_to_v1_3_0_dev_2) - + cfg.register_upgrade_hooks(locals()) cfg.run_upgrade_hooks() def move_files_to_album(self, files, albumid=None, album=None): From 0b90379c71d15a2024a3882efcf12f0a4516d511 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 31 Dec 2013 15:26:41 +0100 Subject: [PATCH 35/59] Add a comment about adding an upgrade hook --- picard/tagger.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/picard/tagger.py b/picard/tagger.py index e60aedf17..c8216d99f 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -163,6 +163,13 @@ class Tagger(QtGui.QApplication): def _upgrade_config(self): _s = config.setting + # TO ADD AN UPGRADE HOOK: + # ---------------------- + # add a function here, named after the version you want upgrade to + # ie. upgrade_to_v1_0_0_dev_1() for 1.0.0dev1 + # and modify PICARD_VERSION to match it + # + # In version 1.0, the file naming formats for single and various # artist releases were merged. # FIXME: this hook will not work as it is called before self.window From 895fadf3c95aa6975dd6fef3ad722aa71a7bb623 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 1 Jan 2014 12:07:40 +0100 Subject: [PATCH 36/59] Add missing % character escaping --- picard/tagger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/tagger.py b/picard/tagger.py index c8216d99f..f413d545c 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -178,7 +178,7 @@ class Tagger(QtGui.QApplication): def remove_va_file_naming_format(merge=True): if merge: _s["file_naming_format"] = ( - "$if($eq(%compilation%,1),\n$noop(Various Artist " + "$if($eq(%%compilation%%,1),\n$noop(Various Artist " "albums)\n%s,\n$noop(Single Artist Albums)\n%s)" % ( _s["va_file_naming_format"].toString(), _s["file_naming_format"] From c66c728c7913d042e39c5cd56578434facbb94a1 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 1 Jan 2014 12:53:15 +0100 Subject: [PATCH 37/59] Setup gettext before _upgrade_config() call --- picard/tagger.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/picard/tagger.py b/picard/tagger.py index f413d545c..06220b114 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -125,10 +125,12 @@ class Tagger(QtGui.QApplication): check_io_encoding() - self._upgrade_config() - + # Must be before _upgrade_config() because upgrade dialogs need to be + # translated setup_gettext(localedir, config.setting["ui_language"], log.debug) + self._upgrade_config() + self.xmlws = XmlWebService() load_user_collections() From ab1a827282f13e1574bf21efd5004e6730c4e174 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 1 Jan 2014 12:54:51 +0100 Subject: [PATCH 38/59] Move upgrade message boxes to _upgrade_config() instead of MainWindow Re-format messages so they wrap nicely. Remove related FIXME --- picard/tagger.py | 23 +++++++++++++++++++---- picard/ui/mainwindow.py | 17 ----------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/picard/tagger.py b/picard/tagger.py index 06220b114..e640e8a6f 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -174,8 +174,6 @@ class Tagger(QtGui.QApplication): # In version 1.0, the file naming formats for single and various # artist releases were merged. - # FIXME: this hook will not work as it is called before self.window - # instanciation def upgrade_to_v1_0_0_final_0(): def remove_va_file_naming_format(merge=True): if merge: @@ -191,16 +189,33 @@ class Tagger(QtGui.QApplication): if ("va_file_naming_format" in _s and "use_va_format" in _s): + msgbox = QtGui.QMessageBox() if _s["use_va_format"].toBool(): remove_va_file_naming_format() - self.window.show_va_removal_notice() + msgbox.information(msgbox, + _("Various Artists file naming scheme removal"), + _("The separate file naming scheme for various artists " + "albums has been removed in this version of Picard.\n" + "Your file naming scheme has automatically been " + "merged with that of single artist albums."), + QtGui.QMessageBox.Ok) elif (_s["va_file_naming_format"].toString() != r"$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldis" "cs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - " "%title%"): - if self.window.confirm_va_removal(): + answer = msgbox.question(msgbox, + _("Various Artists file naming scheme removal"), + _("The separate file naming scheme for various artists " + "albums has been removed in this version of Picard.\n" + "You currently do not use this option, but have a " + "separate file naming scheme defined.\n" + "Do you want to remove it or merge it with your file " + "naming scheme for single artist albums?"), + _("Merge"), _("Remove")) + + if answer: remove_va_file_naming_format(merge=False) else: remove_va_file_naming_format() diff --git a/picard/ui/mainwindow.py b/picard/ui/mainwindow.py index 3e27eafc7..d8e004d5f 100644 --- a/picard/ui/mainwindow.py +++ b/picard/ui/mainwindow.py @@ -668,23 +668,6 @@ class MainWindow(QtGui.QMainWindow): from picard.ui.logview import HistoryView HistoryView(self).show() - def confirm_va_removal(self): - return QtGui.QMessageBox.question(self, - _("Various Artists file naming scheme removal"), -_("""The separate file naming scheme for various artists albums has been -removed in this version of Picard. You currently do not use the this option, -but have a separate file naming scheme defined. Do you want to remove it or -merge it with your file naming scheme for single artist albums?"""), - _("Merge"), _("Remove")) - - def show_va_removal_notice(self): - QtGui.QMessageBox.information(self, - _("Various Artists file naming scheme removal"), -_("""The separate file naming scheme for various artists albums has been -removed in this version of Picard. Your file naming scheme has automatically -been merged with that of single artist albums."""), - QtGui.QMessageBox.Ok) - def open_bug_report(self): webbrowser2.goto('troubleshooting') From ef07fc2e900c2be5fce1aa383563f9d83977a99c Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 1 Jan 2014 16:25:19 +0100 Subject: [PATCH 39/59] Move upgrade code to its own file, no need to be a Tagger method --- picard/config_upgrade.py | 106 +++++++++++++++++++++++++++++++++++++++ picard/tagger.py | 87 ++------------------------------ 2 files changed, 109 insertions(+), 84 deletions(-) create mode 100644 picard/config_upgrade.py diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py new file mode 100644 index 000000000..4f4ab6021 --- /dev/null +++ b/picard/config_upgrade.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# +# Picard, the next-generation MusicBrainz tagger +# Copyright (C) 2013-2014 Laurent Monin +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from PyQt4 import QtGui + +import re +from picard import (log, config) + + +def upgrade_config(): + _s = config.setting + + # TO ADD AN UPGRADE HOOK: + # ---------------------- + # add a function here, named after the version you want upgrade to + # ie. upgrade_to_v1_0_0_dev_1() for 1.0.0dev1 + # and modify PICARD_VERSION to match it + # + + # In version 1.0, the file naming formats for single and various + # artist releases were merged. + def upgrade_to_v1_0_0_final_0(): + def remove_va_file_naming_format(merge=True): + if merge: + _s["file_naming_format"] = ( + "$if($eq(%%compilation%%,1),\n$noop(Various Artist " + "albums)\n%s,\n$noop(Single Artist Albums)\n%s)" % ( + _s["va_file_naming_format"].toString(), + _s["file_naming_format"] + )) + _s.remove("va_file_naming_format") + _s.remove("use_va_format") + + if ("va_file_naming_format" in _s and + "use_va_format" in _s): + + msgbox = QtGui.QMessageBox() + if _s["use_va_format"].toBool(): + remove_va_file_naming_format() + msgbox.information(msgbox, + _("Various Artists file naming scheme removal"), + _("The separate file naming scheme for various artists " + "albums has been removed in this version of Picard.\n" + "Your file naming scheme has automatically been " + "merged with that of single artist albums."), + QtGui.QMessageBox.Ok) + + elif (_s["va_file_naming_format"].toString() != + r"$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldis" + "cs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - " + "%title%"): + + answer = msgbox.question(msgbox, + _("Various Artists file naming scheme removal"), + _("The separate file naming scheme for various artists " + "albums has been removed in this version of Picard.\n" + "You currently do not use this option, but have a " + "separate file naming scheme defined.\n" + "Do you want to remove it or merge it with your file " + "naming scheme for single artist albums?"), + _("Merge"), _("Remove")) + + if answer: + remove_va_file_naming_format(merge=False) + else: + remove_va_file_naming_format() + else: + # default format, disabled + remove_va_file_naming_format(merge=False) + + def upgrade_to_v1_3_0_dev_1(): + # the setting `windows_compatible_filenames` has been renamed + # to `windows_compatibility` + if "windows_compatible_filenames" in _s: + _s["windows_compatibility"] = _s["windows_compatible_filenames"] + _s.remove("windows_compatible_filenames") + log.debug("Config upgrade: windows_compatible_filenames " + "renamed windows_compatibility") + + def upgrade_to_v1_3_0_dev_2(): + # preserved_tags spaces to comma separator, PICARD-536 + if "preserved_tags" in _s: + _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) + log.debug("Config upgrade: convert preserved_tags separator " + "from spaces to comma") + + cfg = config._config + cfg.register_upgrade_hooks(locals()) + cfg.run_upgrade_hooks() diff --git a/picard/tagger.py b/picard/tagger.py index e640e8a6f..52e5c42cf 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -22,7 +22,6 @@ from PyQt4 import QtGui, QtCore import getopt import os.path -import re import shutil import signal import sys @@ -62,6 +61,7 @@ from picard.collection import load_user_collections from picard.ui.mainwindow import MainWindow from picard.plugin import PluginManager from picard.acoustidmanager import AcoustIDManager +from picard.config_upgrade import upgrade_config from picard.util import ( decode_filename, encode_filename, @@ -125,11 +125,11 @@ class Tagger(QtGui.QApplication): check_io_encoding() - # Must be before _upgrade_config() because upgrade dialogs need to be + # Must be before config upgrade because upgrade dialogs need to be # translated setup_gettext(localedir, config.setting["ui_language"], log.debug) - self._upgrade_config() + upgrade_config() self.xmlws = XmlWebService() @@ -162,87 +162,6 @@ class Tagger(QtGui.QApplication): self.nats = None self.window = MainWindow() - def _upgrade_config(self): - _s = config.setting - - # TO ADD AN UPGRADE HOOK: - # ---------------------- - # add a function here, named after the version you want upgrade to - # ie. upgrade_to_v1_0_0_dev_1() for 1.0.0dev1 - # and modify PICARD_VERSION to match it - # - - # In version 1.0, the file naming formats for single and various - # artist releases were merged. - def upgrade_to_v1_0_0_final_0(): - def remove_va_file_naming_format(merge=True): - if merge: - _s["file_naming_format"] = ( - "$if($eq(%%compilation%%,1),\n$noop(Various Artist " - "albums)\n%s,\n$noop(Single Artist Albums)\n%s)" % ( - _s["va_file_naming_format"].toString(), - _s["file_naming_format"] - )) - _s.remove("va_file_naming_format") - _s.remove("use_va_format") - - if ("va_file_naming_format" in _s and - "use_va_format" in _s): - - msgbox = QtGui.QMessageBox() - if _s["use_va_format"].toBool(): - remove_va_file_naming_format() - msgbox.information(msgbox, - _("Various Artists file naming scheme removal"), - _("The separate file naming scheme for various artists " - "albums has been removed in this version of Picard.\n" - "Your file naming scheme has automatically been " - "merged with that of single artist albums."), - QtGui.QMessageBox.Ok) - - elif (_s["va_file_naming_format"].toString() != - r"$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldis" - "cs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - " - "%title%"): - - answer = msgbox.question(msgbox, - _("Various Artists file naming scheme removal"), - _("The separate file naming scheme for various artists " - "albums has been removed in this version of Picard.\n" - "You currently do not use this option, but have a " - "separate file naming scheme defined.\n" - "Do you want to remove it or merge it with your file " - "naming scheme for single artist albums?"), - _("Merge"), _("Remove")) - - if answer: - remove_va_file_naming_format(merge=False) - else: - remove_va_file_naming_format() - else: - # default format, disabled - remove_va_file_naming_format(merge=False) - - def upgrade_to_v1_3_0_dev_1(): - # the setting `windows_compatible_filenames` has been renamed - # to `windows_compatibility` - if "windows_compatible_filenames" in _s: - _s["windows_compatibility"] = _s["windows_compatible_filenames"] - _s.remove("windows_compatible_filenames") - log.debug("Config upgrade: windows_compatible_filenames " - "renamed windows_compatibility") - - def upgrade_to_v1_3_0_dev_2(): - # preserved_tags spaces to comma separator, PICARD-536 - if "preserved_tags" in _s: - _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) - log.debug("Config upgrade: convert preserved_tags separator " - "from spaces to comma") - - cfg = config._config - cfg.register_upgrade_hooks(locals()) - cfg.run_upgrade_hooks() - def move_files_to_album(self, files, albumid=None, album=None): """Move `files` to tracks on album `albumid`.""" if album is None: From 432ef5b996065db800201f2f0cd8c616c76f0c11 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Wed, 1 Jan 2014 18:17:02 +0100 Subject: [PATCH 40/59] Log explicit messages for config upgrades and drop now useless comments Use log.info() instead of log.debug() as it is useful for any user to know config options were modified. --- picard/config_upgrade.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py index 4f4ab6021..695d30026 100644 --- a/picard/config_upgrade.py +++ b/picard/config_upgrade.py @@ -86,20 +86,17 @@ def upgrade_config(): remove_va_file_naming_format(merge=False) def upgrade_to_v1_3_0_dev_1(): - # the setting `windows_compatible_filenames` has been renamed - # to `windows_compatibility` if "windows_compatible_filenames" in _s: _s["windows_compatibility"] = _s["windows_compatible_filenames"] _s.remove("windows_compatible_filenames") - log.debug("Config upgrade: windows_compatible_filenames " - "renamed windows_compatibility") + log.info('Config upgrade: option "windows_compatible_filenames" ' + ' was renamed "windows_compatibility" (PICARD-110).') def upgrade_to_v1_3_0_dev_2(): - # preserved_tags spaces to comma separator, PICARD-536 if "preserved_tags" in _s: _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) - log.debug("Config upgrade: convert preserved_tags separator " - "from spaces to comma") + log.info('Config upgrade: option "preserved_tags" is now using ' + 'comma instead of spaces as tag separator (PICARD-536).') cfg = config._config cfg.register_upgrade_hooks(locals()) From 69f59348c1d7b09129e682afa88b5449127ceae7 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 03:38:59 +0100 Subject: [PATCH 41/59] Add a README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..f74a37151 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +MusicBrainz Picard +================== + +[MusicBrainz Picard](http://musicbrainz.org/doc/MusicBrainz_Picard) is a cross-platform (Linux/Mac OS X/Windows) application written in Python and is the official [MusicBrainz](http://musicbrainz.org) tagger. + +Picard supports the majority of audio file formats, is capable of using audio fingerprints ([AcoustIDs](http://musicbrainz.org/doc/AcoustID)), performing CD lookups and [disc ID](http://musicbrainz.org/doc/Disc_ID) submissions, and it has excellent Unicode support. Additionally, there are several plugins available that extend Picard's features. + +When tagging files, Picard uses an album-oriented approach. This approach allows it to utilize the MusicBrainz data as effectively as possible and correctly tag your music. For more information, [see the illustrated quick start guide to tagging](http://musicbrainz.org/doc/How_To_Tag_Files_With_Picard). + +Picard is named after Captain Jean-Luc Picard from the TV series Star Trek: The Next Generation. + +Binary downloads are available [here](http://musicbrainz.org/doc/MusicBrainz_Picard). + +To submit bugs or improvements, please use [Picard bug-tracker](http://tickets.musicbrainz.org/browse/PICARD). From 81ec25afcdea27158cd4cb5601aad0f61f1257d5 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 11:50:51 +0100 Subject: [PATCH 42/59] Make version_from_string() more tolerant --- picard/__init__.py | 2 +- test/test_versions.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/picard/__init__.py b/picard/__init__.py index d0e324c66..eb5d67bba 100644 --- a/picard/__init__.py +++ b/picard/__init__.py @@ -48,7 +48,7 @@ def version_to_string(version, short=False): def version_from_string(version_str): - g = re.match(r"^(\d+).(\d+).(\d+)(dev|final)(\d+)$", version_str).groups() + g = re.match(r"^(\d+)[._](\d+)[._](\d+)[._]?(dev|final)[._]?(\d+)$", version_str).groups() return (int(g[0]), int(g[1]), int(g[2]), g[3], int(g[4])) diff --git a/test/test_versions.py b/test/test_versions.py index d30832d3f..c99dca7e5 100644 --- a/test/test_versions.py +++ b/test/test_versions.py @@ -54,3 +54,7 @@ class VersionsTest(unittest.TestCase): def test_version_conv_11(self): l, s = ('1', '1', '0', 'dev', '0'), '1.1.0dev0' self.assertEqual(version_to_string(l), s) + + def test_version_conv_12(self): + l, s = (1, 1, 0, 'dev', 0), '1_1_0_dev_0' + self.assertEqual(l, version_from_string(s)) From d0005ccac93158b816a8dc3b285d7d02a7bc79cc Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 11:52:15 +0100 Subject: [PATCH 43/59] Drop autodetection of hooks, but still extract version from hook name --- picard/config.py | 26 ++++++-------------------- picard/config_upgrade.py | 4 +++- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/picard/config.py b/picard/config.py index f0cf18a3f..5604ae582 100644 --- a/picard/config.py +++ b/picard/config.py @@ -95,27 +95,13 @@ class Config(QtCore.QSettings): else: raise KeyError("Unknown profile '%s'" % (profilename,)) - def _detect_upgrade_hooks(self, symbol_table): - """Detect upgrade functions based on their names""" - hooks = dict() - pattern = re.compile("^upgrade_to_v(\d+)_(\d+)_(\d+)_(dev|final)_(\d+)$") - for symbol in symbol_table: - match = re.search(pattern, symbol) - if not match: - continue - version = match.groups() - version_string = version_to_string(version) - hooks[version_string] = symbol_table[symbol] - return hooks - - def register_upgrade_hooks(self, symbol_table): - hooks = self._detect_upgrade_hooks(symbol_table) - for version in sorted(hooks): - self._register_upgrade_hook(version, hooks[version]) - - def _register_upgrade_hook(self, to_version_str, func, *args): + def register_upgrade_hook(self, func, *args): """Register a function to upgrade from one config version to another""" - to_version = version_from_string(to_version_str) + pattern = re.compile("(\d+_\d+_\d+_(?:dev|final)_\d+)$") + match = re.search(pattern, func.__name__) + assert match, "config upgrade function name '%s' " \ + "have to match '%s' regex" % (func.__name__, pattern.pattern) + to_version = version_from_string(match.group(1)) assert to_version <= PICARD_VERSION, "%r > %r !!!" % (to_version, PICARD_VERSION) hook = { 'to': to_version, diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py index 695d30026..fb8955d94 100644 --- a/picard/config_upgrade.py +++ b/picard/config_upgrade.py @@ -99,5 +99,7 @@ def upgrade_config(): 'comma instead of spaces as tag separator (PICARD-536).') cfg = config._config - cfg.register_upgrade_hooks(locals()) + cfg.register_upgrade_hook(upgrade_to_v1_0_0_final_0) + cfg.register_upgrade_hook(upgrade_to_v1_3_0_dev_1) + cfg.register_upgrade_hook(upgrade_to_v1_3_0_dev_2) cfg.run_upgrade_hooks() From 57732ce17beff29e030eefd1b6c15e6eca352909 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 13:00:40 +0100 Subject: [PATCH 44/59] Un-indent upgrade functions --- picard/config_upgrade.py | 130 ++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py index fb8955d94..fa66ec20f 100644 --- a/picard/config_upgrade.py +++ b/picard/config_upgrade.py @@ -24,80 +24,84 @@ import re from picard import (log, config) -def upgrade_config(): - _s = config.setting +# TO ADD AN UPGRADE HOOK: +# ---------------------- +# add a function here, named after the version you want upgrade to +# ie. upgrade_to_v1_0_0_dev_1() for 1.0.0dev1 +# and modify PICARD_VERSION to match it +# - # TO ADD AN UPGRADE HOOK: - # ---------------------- - # add a function here, named after the version you want upgrade to - # ie. upgrade_to_v1_0_0_dev_1() for 1.0.0dev1 - # and modify PICARD_VERSION to match it - # +_s = config.setting - # In version 1.0, the file naming formats for single and various - # artist releases were merged. - def upgrade_to_v1_0_0_final_0(): - def remove_va_file_naming_format(merge=True): - if merge: - _s["file_naming_format"] = ( - "$if($eq(%%compilation%%,1),\n$noop(Various Artist " - "albums)\n%s,\n$noop(Single Artist Albums)\n%s)" % ( - _s["va_file_naming_format"].toString(), - _s["file_naming_format"] - )) - _s.remove("va_file_naming_format") - _s.remove("use_va_format") - if ("va_file_naming_format" in _s and - "use_va_format" in _s): +# In version 1.0, the file naming formats for single and various +# artist releases were merged. +def upgrade_to_v1_0_0_final_0(): + def remove_va_file_naming_format(merge=True): + if merge: + _s["file_naming_format"] = ( + "$if($eq(%%compilation%%,1),\n$noop(Various Artist " + "albums)\n%s,\n$noop(Single Artist Albums)\n%s)" % ( + _s["va_file_naming_format"].toString(), + _s["file_naming_format"] + )) + _s.remove("va_file_naming_format") + _s.remove("use_va_format") - msgbox = QtGui.QMessageBox() - if _s["use_va_format"].toBool(): - remove_va_file_naming_format() - msgbox.information(msgbox, - _("Various Artists file naming scheme removal"), - _("The separate file naming scheme for various artists " - "albums has been removed in this version of Picard.\n" - "Your file naming scheme has automatically been " - "merged with that of single artist albums."), - QtGui.QMessageBox.Ok) + if ("va_file_naming_format" in _s and + "use_va_format" in _s): - elif (_s["va_file_naming_format"].toString() != - r"$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldis" - "cs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - " - "%title%"): + msgbox = QtGui.QMessageBox() + if _s["use_va_format"].toBool(): + remove_va_file_naming_format() + msgbox.information(msgbox, + _("Various Artists file naming scheme removal"), + _("The separate file naming scheme for various artists " + "albums has been removed in this version of Picard.\n" + "Your file naming scheme has automatically been " + "merged with that of single artist albums."), + QtGui.QMessageBox.Ok) - answer = msgbox.question(msgbox, - _("Various Artists file naming scheme removal"), - _("The separate file naming scheme for various artists " - "albums has been removed in this version of Picard.\n" - "You currently do not use this option, but have a " - "separate file naming scheme defined.\n" - "Do you want to remove it or merge it with your file " - "naming scheme for single artist albums?"), - _("Merge"), _("Remove")) + elif (_s["va_file_naming_format"].toString() != + r"$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldis" + "cs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - " + "%title%"): - if answer: - remove_va_file_naming_format(merge=False) - else: - remove_va_file_naming_format() - else: - # default format, disabled + answer = msgbox.question(msgbox, + _("Various Artists file naming scheme removal"), + _("The separate file naming scheme for various artists " + "albums has been removed in this version of Picard.\n" + "You currently do not use this option, but have a " + "separate file naming scheme defined.\n" + "Do you want to remove it or merge it with your file " + "naming scheme for single artist albums?"), + _("Merge"), _("Remove")) + + if answer: remove_va_file_naming_format(merge=False) + else: + remove_va_file_naming_format() + else: + # default format, disabled + remove_va_file_naming_format(merge=False) - def upgrade_to_v1_3_0_dev_1(): - if "windows_compatible_filenames" in _s: - _s["windows_compatibility"] = _s["windows_compatible_filenames"] - _s.remove("windows_compatible_filenames") - log.info('Config upgrade: option "windows_compatible_filenames" ' - ' was renamed "windows_compatibility" (PICARD-110).') - def upgrade_to_v1_3_0_dev_2(): - if "preserved_tags" in _s: - _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) - log.info('Config upgrade: option "preserved_tags" is now using ' - 'comma instead of spaces as tag separator (PICARD-536).') +def upgrade_to_v1_3_0_dev_1(): + if "windows_compatible_filenames" in _s: + _s["windows_compatibility"] = _s["windows_compatible_filenames"] + _s.remove("windows_compatible_filenames") + log.info('Config upgrade: option "windows_compatible_filenames" ' + ' was renamed "windows_compatibility" (PICARD-110).') + +def upgrade_to_v1_3_0_dev_2(): + if "preserved_tags" in _s: + _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) + log.info('Config upgrade: option "preserved_tags" is now using ' + 'comma instead of spaces as tag separator (PICARD-536).') + + +def upgrade_config(): cfg = config._config cfg.register_upgrade_hook(upgrade_to_v1_0_0_final_0) cfg.register_upgrade_hook(upgrade_to_v1_3_0_dev_1) From 708faa9ba48b0e0fd4436fcc0e44d746911e4cd2 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 14:23:34 +0100 Subject: [PATCH 45/59] _upgrade_hooks is now a dict, with version as key --- picard/config.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/picard/config.py b/picard/config.py index 5604ae582..7fce0bba5 100644 --- a/picard/config.py +++ b/picard/config.py @@ -85,7 +85,7 @@ class Config(QtCore.QSettings): TextOption("application", "version", '0.0.0dev0') self._version = version_from_string(self.application["version"]) - self._upgrade_hooks = [] + self._upgrade_hooks = dict() def switchProfile(self, profilename): """Sets the current profile.""" @@ -104,12 +104,11 @@ class Config(QtCore.QSettings): to_version = version_from_string(match.group(1)) assert to_version <= PICARD_VERSION, "%r > %r !!!" % (to_version, PICARD_VERSION) hook = { - 'to': to_version, 'func': func, 'args': args, 'done': False } - self._upgrade_hooks.append(hook) + self._upgrade_hooks[to_version] = hook def run_upgrade_hooks(self): """Executes registered functions to upgrade config version to the latest""" @@ -123,10 +122,9 @@ class Config(QtCore.QSettings): version_to_string(PICARD_VERSION) )) return - # sort upgrade hooks by version - self._upgrade_hooks.sort(key=itemgetter("to")) - for hook in self._upgrade_hooks: - if self._version < hook['to']: + for version in sorted(self._upgrade_hooks): + hook = self._upgrade_hooks[version] + if self._version < version: try: hook['func'](*hook['args']) except: @@ -135,19 +133,19 @@ class Config(QtCore.QSettings): "Error during config upgrade from version %s to %s " "using %s():\n%s" % ( version_to_string(self._version), - version_to_string(hook['to']), + version_to_string(version), hook['func'].__name__, traceback.format_exc() )) else: hook['done'] = True - self._version = hook['to'] + self._version = version self._write_version() else: # hook is not applicable, mark as done hook['done'] = True - if all(map(itemgetter("done"), self._upgrade_hooks)): + if all(map(itemgetter("done"), self._upgrade_hooks.values())): # all hooks were executed, ensure config is marked with latest version self._version = PICARD_VERSION self._write_version() From 22271d880cf4d8d6e38bb8dbb063115213a304d3 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 17:09:28 +0100 Subject: [PATCH 46/59] Remove useless intermediate variable --- picard/config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/picard/config.py b/picard/config.py index 7fce0bba5..efd31fcf4 100644 --- a/picard/config.py +++ b/picard/config.py @@ -103,12 +103,11 @@ class Config(QtCore.QSettings): "have to match '%s' regex" % (func.__name__, pattern.pattern) to_version = version_from_string(match.group(1)) assert to_version <= PICARD_VERSION, "%r > %r !!!" % (to_version, PICARD_VERSION) - hook = { + self._upgrade_hooks[to_version] = { 'func': func, 'args': args, 'done': False } - self._upgrade_hooks[to_version] = hook def run_upgrade_hooks(self): """Executes registered functions to upgrade config version to the latest""" From 6e889ca0a06f6e1cca1284dcdcc5b6b15ea11a02 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 17:28:41 +0100 Subject: [PATCH 47/59] Use exceptions in place of assertions -> VersionError --- picard/__init__.py | 19 +++++++++++++++---- test/test_versions.py | 10 ++++++---- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/picard/__init__.py b/picard/__init__.py index eb5d67bba..954830c5f 100644 --- a/picard/__init__.py +++ b/picard/__init__.py @@ -25,9 +25,15 @@ PICARD_ORG_NAME = "MusicBrainz" PICARD_VERSION = (1, 3, 0, 'dev', 2) +class VersionError(Exception): + pass + + def version_to_string(version, short=False): - assert len(version) == 5 - assert version[3] in ('final', 'dev') + if len(version) != 5: + raise VersionError("Length != 5") + if version[3] not in ('final', 'dev'): + raise VersionError("Should be either 'final' or 'dev'") _version = [] for p in version: try: @@ -47,9 +53,14 @@ def version_to_string(version, short=False): return version_str +_version_re = re.compile("^(\d+)[._](\d+)[._](\d+)[._]?(dev|final)[._]?(\d+)$") def version_from_string(version_str): - g = re.match(r"^(\d+)[._](\d+)[._](\d+)[._]?(dev|final)[._]?(\d+)$", version_str).groups() - return (int(g[0]), int(g[1]), int(g[2]), g[3], int(g[4])) + m = _version_re.search(version_str) + if m: + g = m.groups() + return (int(g[0]), int(g[1]), int(g[2]), g[3], int(g[4])) + raise VersionError("String '%s' do not match regex '%s'" % (version_str, + _version_re.pattern)) __version__ = PICARD_VERSION_STR = version_to_string(PICARD_VERSION) diff --git a/test/test_versions.py b/test/test_versions.py index c99dca7e5..aad5b8e6f 100644 --- a/test/test_versions.py +++ b/test/test_versions.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- import unittest -from picard import version_to_string, version_from_string +from picard import (version_to_string, + version_from_string, + VersionError) class VersionsTest(unittest.TestCase): @@ -23,8 +25,8 @@ class VersionsTest(unittest.TestCase): def test_version_conv_4(self): l, s = (1, 0, 2, '', 0), '1.0.2' - self.assertRaises(AssertionError, version_to_string, (l)) - self.assertRaises(AttributeError, version_from_string, (s)) + self.assertRaises(VersionError, version_to_string, (l)) + self.assertRaises(VersionError, version_from_string, (s)) def test_version_conv_5(self): l, s = (999, 999, 999, 'dev', 999), '999.999.999dev999' @@ -33,7 +35,7 @@ class VersionsTest(unittest.TestCase): def test_version_conv_6(self): l = (1, 0, 2, 'xx', 0) - self.assertRaises(AssertionError, version_to_string, (l)) + self.assertRaises(VersionError, version_to_string, (l)) def test_version_conv_7(self): l, s = (1, 1, 0, 'final', 0), '1.1' From c2d53a4d4d1ea00138347b00bfa696d1ef32bc42 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 17:32:34 +0100 Subject: [PATCH 48/59] version_from_string(): match end of the string instead full string --- picard/__init__.py | 2 +- test/test_versions.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/picard/__init__.py b/picard/__init__.py index 954830c5f..d1d6cb897 100644 --- a/picard/__init__.py +++ b/picard/__init__.py @@ -53,7 +53,7 @@ def version_to_string(version, short=False): return version_str -_version_re = re.compile("^(\d+)[._](\d+)[._](\d+)[._]?(dev|final)[._]?(\d+)$") +_version_re = re.compile("(\d+)[._](\d+)[._](\d+)[._]?(dev|final)[._]?(\d+)$") def version_from_string(version_str): m = _version_re.search(version_str) if m: diff --git a/test/test_versions.py b/test/test_versions.py index aad5b8e6f..a473fb771 100644 --- a/test/test_versions.py +++ b/test/test_versions.py @@ -60,3 +60,11 @@ class VersionsTest(unittest.TestCase): def test_version_conv_12(self): l, s = (1, 1, 0, 'dev', 0), '1_1_0_dev_0' self.assertEqual(l, version_from_string(s)) + + def test_version_conv_13(self): + l, s = (1, 1, 0, 'dev', 0), 'anything_28_1_1_0_dev_0' + self.assertEqual(l, version_from_string(s)) + + def test_version_conv_14(self): + l = 'anything_28x_1_0_dev_0' + self.assertRaises(VersionError, version_to_string, (l)) From d713d1cf005924d85cbe9356cd6f5512cea16b6a Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 17:36:29 +0100 Subject: [PATCH 49/59] Let version_from_string() raises an exception if hook name is incorrect --- picard/config.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/picard/config.py b/picard/config.py index efd31fcf4..ed0923199 100644 --- a/picard/config.py +++ b/picard/config.py @@ -97,11 +97,7 @@ class Config(QtCore.QSettings): def register_upgrade_hook(self, func, *args): """Register a function to upgrade from one config version to another""" - pattern = re.compile("(\d+_\d+_\d+_(?:dev|final)_\d+)$") - match = re.search(pattern, func.__name__) - assert match, "config upgrade function name '%s' " \ - "have to match '%s' regex" % (func.__name__, pattern.pattern) - to_version = version_from_string(match.group(1)) + to_version = version_from_string(func.__name__) assert to_version <= PICARD_VERSION, "%r > %r !!!" % (to_version, PICARD_VERSION) self._upgrade_hooks[to_version] = { 'func': func, From 861ed91800f948867b77704f58b1bde69c89e589 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 17:47:02 +0100 Subject: [PATCH 50/59] Mark messages for translation. --- picard/config_upgrade.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py index fa66ec20f..3aff6a9c6 100644 --- a/picard/config_upgrade.py +++ b/picard/config_upgrade.py @@ -90,15 +90,15 @@ def upgrade_to_v1_3_0_dev_1(): if "windows_compatible_filenames" in _s: _s["windows_compatibility"] = _s["windows_compatible_filenames"] _s.remove("windows_compatible_filenames") - log.info('Config upgrade: option "windows_compatible_filenames" ' - ' was renamed "windows_compatibility" (PICARD-110).') + log.info(_('Config upgrade: option "windows_compatible_filenames" ' + ' was renamed "windows_compatibility" (PICARD-110).')) def upgrade_to_v1_3_0_dev_2(): if "preserved_tags" in _s: _s["preserved_tags"] = re.sub(r"\s+", ",", _s["preserved_tags"].strip()) - log.info('Config upgrade: option "preserved_tags" is now using ' - 'comma instead of spaces as tag separator (PICARD-536).') + log.info(_('Config upgrade: option "preserved_tags" is now using ' + 'comma instead of spaces as tag separator (PICARD-536).')) def upgrade_config(): From 1de7f3fa550d7a73f33507a1eb85bedd35639fdd Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 17:50:31 +0100 Subject: [PATCH 51/59] Update comment --- picard/config_upgrade.py | 1 + 1 file changed, 1 insertion(+) diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py index 3aff6a9c6..702936e16 100644 --- a/picard/config_upgrade.py +++ b/picard/config_upgrade.py @@ -28,6 +28,7 @@ from picard import (log, config) # ---------------------- # add a function here, named after the version you want upgrade to # ie. upgrade_to_v1_0_0_dev_1() for 1.0.0dev1 +# register it in upgrade_config() # and modify PICARD_VERSION to match it # From 45b9619b4c9b60df8a0d71d1f29daca69af9b9d8 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 2 Jan 2014 18:11:52 +0100 Subject: [PATCH 52/59] Regenerate picard.pot --- po/picard.pot | 981 ++++++++++++++++++++++++++------------------------ 1 file changed, 502 insertions(+), 479 deletions(-) diff --git a/po/picard.pot b/po/picard.pot index 211e2ee3f..e5ea78f78 100644 --- a/po/picard.pot +++ b/po/picard.pot @@ -1,14 +1,14 @@ # Translations template for PROJECT. -# Copyright (C) 2013 ORGANIZATION +# Copyright (C) 2014 ORGANIZATION # This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2013. +# FIRST AUTHOR , 2014. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2013-11-26 22:35+0100\n" +"POT-Creation-Date: 2014-01-02 18:11+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -150,42 +150,42 @@ msgstr "" msgid "AcoustIDs successfully submitted!" msgstr "" -#: picard/album.py:64 picard/cluster.py:235 +#: picard/album.py:64 picard/cluster.py:234 msgid "Unmatched Files" msgstr "" -#: picard/album.py:181 +#: picard/album.py:187 #, python-format msgid "[could not load album %s]" msgstr "" -#: picard/album.py:267 +#: picard/album.py:272 #, python-format msgid "Album %s loaded" msgstr "" -#: picard/album.py:283 +#: picard/album.py:288 msgid "[loading album information]" msgstr "" -#: picard/album.py:455 +#: picard/album.py:460 #, python-format msgid "; %i image" msgid_plural "; %i images" msgstr[0] "" msgstr[1] "" -#: picard/cluster.py:151 picard/cluster.py:160 +#: picard/cluster.py:150 picard/cluster.py:159 #, python-format msgid "No matching releases for cluster %s" msgstr "" -#: picard/cluster.py:162 +#: picard/cluster.py:161 #, python-format msgid "Cluster %s identified!" msgstr "" -#: picard/cluster.py:169 +#: picard/cluster.py:168 #, python-format msgid "Looking up the metadata for cluster %s..." msgstr "" @@ -209,1193 +209,1235 @@ msgstr[1] "" msgid "Error loading collections: %s" msgstr "" -#: picard/const.py:55 -msgid "CD" +#: picard/config_upgrade.py:59 picard/config_upgrade.py:72 +msgid "Various Artists file naming scheme removal" msgstr "" -#: picard/const.py:56 -msgid "CD-R" +#: picard/config_upgrade.py:60 +msgid "" +"The separate file naming scheme for various artists albums has been " +"removed in this version of Picard.\n" +"Your file naming scheme has automatically been merged with that of single" +" artist albums." msgstr "" -#: picard/const.py:57 -msgid "HDCD" +#: picard/config_upgrade.py:73 +msgid "" +"The separate file naming scheme for various artists albums has been " +"removed in this version of Picard.\n" +"You currently do not use this option, but have a separate file naming " +"scheme defined.\n" +"Do you want to remove it or merge it with your file naming scheme for " +"single artist albums?" msgstr "" -#: picard/const.py:58 -msgid "8cm CD" +#: picard/config_upgrade.py:79 +msgid "Merge" msgstr "" -#: picard/const.py:59 -msgid "Vinyl" +#: picard/config_upgrade.py:79 picard/ui/metadatabox.py:254 +msgid "Remove" msgstr "" -#: picard/const.py:60 -msgid "7\" Vinyl" +#: picard/config_upgrade.py:94 +msgid "" +"Config upgrade: option \"windows_compatible_filenames\" was renamed " +"\"windows_compatibility\" (PICARD-110)." msgstr "" -#: picard/const.py:61 -msgid "10\" Vinyl" -msgstr "" - -#: picard/const.py:62 -msgid "12\" Vinyl" -msgstr "" - -#: picard/const.py:63 -msgid "Digital Media" -msgstr "" - -#: picard/const.py:64 -msgid "USB Flash Drive" -msgstr "" - -#: picard/const.py:65 -msgid "slotMusic" -msgstr "" - -#: picard/const.py:66 -msgid "Cassette" +#: picard/config_upgrade.py:101 +msgid "" +"Config upgrade: option \"preserved_tags\" is now using comma instead of " +"spaces as tag separator (PICARD-536)." msgstr "" #: picard/const.py:67 -msgid "DVD" +msgid "CD" msgstr "" #: picard/const.py:68 -msgid "DVD-Audio" +msgid "CD-R" msgstr "" #: picard/const.py:69 -msgid "DVD-Video" +msgid "HDCD" msgstr "" #: picard/const.py:70 -msgid "SACD" +msgid "8cm CD" msgstr "" #: picard/const.py:71 -msgid "DualDisc" +msgid "Vinyl" msgstr "" #: picard/const.py:72 -msgid "MiniDisc" +msgid "7\" Vinyl" msgstr "" #: picard/const.py:73 -msgid "Blu-ray" +msgid "10\" Vinyl" msgstr "" #: picard/const.py:74 -msgid "HD-DVD" +msgid "12\" Vinyl" msgstr "" #: picard/const.py:75 -msgid "Videotape" +msgid "Digital Media" msgstr "" #: picard/const.py:76 -msgid "VHS" +msgid "USB Flash Drive" msgstr "" #: picard/const.py:77 -msgid "Betamax" +msgid "slotMusic" msgstr "" #: picard/const.py:78 -msgid "VCD" +msgid "Cassette" msgstr "" #: picard/const.py:79 -msgid "SVCD" +msgid "DVD" msgstr "" #: picard/const.py:80 +msgid "DVD-Audio" +msgstr "" + +#: picard/const.py:81 +msgid "DVD-Video" +msgstr "" + +#: picard/const.py:82 +msgid "SACD" +msgstr "" + +#: picard/const.py:83 +msgid "DualDisc" +msgstr "" + +#: picard/const.py:84 +msgid "MiniDisc" +msgstr "" + +#: picard/const.py:85 +msgid "Blu-ray" +msgstr "" + +#: picard/const.py:86 +msgid "HD-DVD" +msgstr "" + +#: picard/const.py:87 +msgid "Videotape" +msgstr "" + +#: picard/const.py:88 +msgid "VHS" +msgstr "" + +#: picard/const.py:89 +msgid "Betamax" +msgstr "" + +#: picard/const.py:90 +msgid "VCD" +msgstr "" + +#: picard/const.py:91 +msgid "SVCD" +msgstr "" + +#: picard/const.py:92 msgid "UMD" msgstr "" -#: picard/const.py:81 picard/coverartarchive.py:33 +#: picard/const.py:93 picard/coverartarchive.py:33 #: picard/ui/ui_options_releases.py:226 msgid "Other" msgstr "" -#: picard/const.py:82 +#: picard/const.py:94 msgid "LaserDisc" msgstr "" -#: picard/const.py:83 +#: picard/const.py:95 msgid "Cartridge" msgstr "" -#: picard/const.py:84 +#: picard/const.py:96 msgid "Reel-to-reel" msgstr "" -#: picard/const.py:85 +#: picard/const.py:97 msgid "DAT" msgstr "" -#: picard/const.py:86 +#: picard/const.py:98 msgid "Wax Cylinder" msgstr "" -#: picard/const.py:87 +#: picard/const.py:99 msgid "Piano Roll" msgstr "" -#: picard/const.py:88 +#: picard/const.py:100 msgid "DCC" msgstr "" -#: picard/const.py:93 +#: picard/const.py:105 msgid "Bangladesh" msgstr "" -#: picard/const.py:94 +#: picard/const.py:106 msgid "Belgium" msgstr "" -#: picard/const.py:95 +#: picard/const.py:107 msgid "Burkina Faso" msgstr "" -#: picard/const.py:96 +#: picard/const.py:108 msgid "Bulgaria" msgstr "" -#: picard/const.py:97 +#: picard/const.py:109 msgid "Barbados" msgstr "" -#: picard/const.py:98 +#: picard/const.py:110 msgid "Wallis and Futuna Islands" msgstr "" -#: picard/const.py:99 +#: picard/const.py:111 msgid "Bermuda" msgstr "" -#: picard/const.py:100 +#: picard/const.py:112 msgid "Brunei Darussalam" msgstr "" -#: picard/const.py:101 +#: picard/const.py:113 msgid "Bolivia" msgstr "" -#: picard/const.py:102 +#: picard/const.py:114 msgid "Bahrain" msgstr "" -#: picard/const.py:103 +#: picard/const.py:115 msgid "Burundi" msgstr "" -#: picard/const.py:104 +#: picard/const.py:116 msgid "Benin" msgstr "" -#: picard/const.py:105 +#: picard/const.py:117 msgid "Bhutan" msgstr "" -#: picard/const.py:106 +#: picard/const.py:118 msgid "Jamaica" msgstr "" -#: picard/const.py:107 +#: picard/const.py:119 msgid "Bouvet Island" msgstr "" -#: picard/const.py:108 +#: picard/const.py:120 msgid "Botswana" msgstr "" -#: picard/const.py:109 +#: picard/const.py:121 msgid "Samoa" msgstr "" -#: picard/const.py:110 +#: picard/const.py:122 msgid "Brazil" msgstr "" -#: picard/const.py:111 +#: picard/const.py:123 msgid "Bahamas" msgstr "" -#: picard/const.py:112 +#: picard/const.py:124 msgid "Belarus" msgstr "" -#: picard/const.py:113 +#: picard/const.py:125 msgid "Belize" msgstr "" -#: picard/const.py:114 +#: picard/const.py:126 msgid "Russian Federation" msgstr "" -#: picard/const.py:115 +#: picard/const.py:127 msgid "Rwanda" msgstr "" -#: picard/const.py:116 +#: picard/const.py:128 msgid "Reunion" msgstr "" -#: picard/const.py:117 +#: picard/const.py:129 msgid "Turkmenistan" msgstr "" -#: picard/const.py:118 +#: picard/const.py:130 msgid "Tajikistan" msgstr "" -#: picard/const.py:119 +#: picard/const.py:131 msgid "Romania" msgstr "" -#: picard/const.py:120 +#: picard/const.py:132 msgid "Tokelau" msgstr "" -#: picard/const.py:121 +#: picard/const.py:133 msgid "Guinea-Bissa" msgstr "" -#: picard/const.py:122 +#: picard/const.py:134 msgid "Guam" msgstr "" -#: picard/const.py:123 +#: picard/const.py:135 msgid "Guatemala" msgstr "" -#: picard/const.py:124 +#: picard/const.py:136 msgid "Greece" msgstr "" -#: picard/const.py:125 +#: picard/const.py:137 msgid "Equatorial Guinea" msgstr "" -#: picard/const.py:126 +#: picard/const.py:138 msgid "Guadeloupe" msgstr "" -#: picard/const.py:127 +#: picard/const.py:139 msgid "Japan" msgstr "" -#: picard/const.py:128 +#: picard/const.py:140 msgid "Guyana" msgstr "" -#: picard/const.py:129 +#: picard/const.py:141 msgid "French Guiana" msgstr "" -#: picard/const.py:130 +#: picard/const.py:142 msgid "Georgia" msgstr "" -#: picard/const.py:131 +#: picard/const.py:143 msgid "Grenada" msgstr "" -#: picard/const.py:132 +#: picard/const.py:144 msgid "United Kingdom" msgstr "" -#: picard/const.py:133 +#: picard/const.py:145 msgid "Gabon" msgstr "" -#: picard/const.py:134 +#: picard/const.py:146 msgid "El Salvador" msgstr "" -#: picard/const.py:135 +#: picard/const.py:147 msgid "Guinea" msgstr "" -#: picard/const.py:136 +#: picard/const.py:148 msgid "Gambia" msgstr "" -#: picard/const.py:137 +#: picard/const.py:149 msgid "Greenland" msgstr "" -#: picard/const.py:138 +#: picard/const.py:150 msgid "Gibraltar" msgstr "" -#: picard/const.py:139 +#: picard/const.py:151 msgid "Ghana" msgstr "" -#: picard/const.py:140 +#: picard/const.py:152 msgid "Oman" msgstr "" -#: picard/const.py:141 +#: picard/const.py:153 msgid "Tunisia" msgstr "" -#: picard/const.py:142 +#: picard/const.py:154 msgid "Jordan" msgstr "" -#: picard/const.py:143 +#: picard/const.py:155 msgid "Haiti" msgstr "" -#: picard/const.py:144 +#: picard/const.py:156 msgid "Hungary" msgstr "" -#: picard/const.py:145 +#: picard/const.py:157 msgid "Hong Kong" msgstr "" -#: picard/const.py:146 +#: picard/const.py:158 msgid "Honduras" msgstr "" -#: picard/const.py:147 +#: picard/const.py:159 msgid "Heard and Mc Donald Islands" msgstr "" -#: picard/const.py:148 +#: picard/const.py:160 msgid "Venezuela" msgstr "" -#: picard/const.py:149 +#: picard/const.py:161 msgid "Puerto Rico" msgstr "" -#: picard/const.py:150 +#: picard/const.py:162 msgid "Palau" msgstr "" -#: picard/const.py:151 +#: picard/const.py:163 msgid "Portugal" msgstr "" -#: picard/const.py:152 +#: picard/const.py:164 msgid "Svalbard and Jan Mayen Islands" msgstr "" -#: picard/const.py:153 +#: picard/const.py:165 msgid "Paraguay" msgstr "" -#: picard/const.py:154 +#: picard/const.py:166 msgid "Iraq" msgstr "" -#: picard/const.py:155 +#: picard/const.py:167 msgid "Panama" msgstr "" -#: picard/const.py:156 +#: picard/const.py:168 msgid "French Polynesia" msgstr "" -#: picard/const.py:157 +#: picard/const.py:169 msgid "Papua New Guinea" msgstr "" -#: picard/const.py:158 +#: picard/const.py:170 msgid "Peru" msgstr "" -#: picard/const.py:159 +#: picard/const.py:171 msgid "Pakistan" msgstr "" -#: picard/const.py:160 +#: picard/const.py:172 msgid "Philippines" msgstr "" -#: picard/const.py:161 +#: picard/const.py:173 msgid "Pitcairn" msgstr "" -#: picard/const.py:162 +#: picard/const.py:174 msgid "Poland" msgstr "" -#: picard/const.py:163 +#: picard/const.py:175 msgid "St. Pierre and Miquelon" msgstr "" -#: picard/const.py:164 +#: picard/const.py:176 msgid "Zambia" msgstr "" -#: picard/const.py:165 +#: picard/const.py:177 msgid "Western Sahara" msgstr "" -#: picard/const.py:166 +#: picard/const.py:178 msgid "Estonia" msgstr "" -#: picard/const.py:167 +#: picard/const.py:179 msgid "Egypt" msgstr "" -#: picard/const.py:168 +#: picard/const.py:180 msgid "South Africa" msgstr "" -#: picard/const.py:169 +#: picard/const.py:181 msgid "Ecuador" msgstr "" -#: picard/const.py:170 +#: picard/const.py:182 msgid "Italy" msgstr "" -#: picard/const.py:171 +#: picard/const.py:183 msgid "Viet Nam" msgstr "" -#: picard/const.py:172 +#: picard/const.py:184 msgid "Solomon Islands" msgstr "" -#: picard/const.py:173 +#: picard/const.py:185 msgid "Ethiopia" msgstr "" -#: picard/const.py:174 +#: picard/const.py:186 msgid "Somalia" msgstr "" -#: picard/const.py:175 +#: picard/const.py:187 msgid "Zimbabwe" msgstr "" -#: picard/const.py:176 +#: picard/const.py:188 msgid "Saudi Arabia" msgstr "" -#: picard/const.py:177 +#: picard/const.py:189 msgid "Spain" msgstr "" -#: picard/const.py:178 +#: picard/const.py:190 msgid "Eritrea" msgstr "" -#: picard/const.py:179 +#: picard/const.py:191 msgid "Moldova, Republic of" msgstr "" -#: picard/const.py:180 +#: picard/const.py:192 msgid "Madagascar" msgstr "" -#: picard/const.py:181 +#: picard/const.py:193 msgid "Morocco" msgstr "" -#: picard/const.py:182 +#: picard/const.py:194 msgid "Monaco" msgstr "" -#: picard/const.py:183 +#: picard/const.py:195 msgid "Uzbekistan" msgstr "" -#: picard/const.py:184 +#: picard/const.py:196 msgid "Myanmar" msgstr "" -#: picard/const.py:185 +#: picard/const.py:197 msgid "Mali" msgstr "" -#: picard/const.py:186 +#: picard/const.py:198 msgid "Macau" msgstr "" -#: picard/const.py:187 +#: picard/const.py:199 msgid "Mongolia" msgstr "" -#: picard/const.py:188 +#: picard/const.py:200 msgid "Marshall Islands" msgstr "" -#: picard/const.py:189 +#: picard/const.py:201 msgid "Macedonia, The Former Yugoslav Republic of" msgstr "" -#: picard/const.py:190 +#: picard/const.py:202 msgid "Mauritius" msgstr "" -#: picard/const.py:191 +#: picard/const.py:203 msgid "Malta" msgstr "" -#: picard/const.py:192 +#: picard/const.py:204 msgid "Malawi" msgstr "" -#: picard/const.py:193 +#: picard/const.py:205 msgid "Maldives" msgstr "" -#: picard/const.py:194 +#: picard/const.py:206 msgid "Martinique" msgstr "" -#: picard/const.py:195 +#: picard/const.py:207 msgid "Northern Mariana Islands" msgstr "" -#: picard/const.py:196 +#: picard/const.py:208 msgid "Montserrat" msgstr "" -#: picard/const.py:197 +#: picard/const.py:209 msgid "Mauritania" msgstr "" -#: picard/const.py:198 +#: picard/const.py:210 msgid "Uganda" msgstr "" -#: picard/const.py:199 +#: picard/const.py:211 msgid "Malaysia" msgstr "" -#: picard/const.py:200 +#: picard/const.py:212 msgid "Mexico" msgstr "" -#: picard/const.py:201 +#: picard/const.py:213 msgid "Israel" msgstr "" -#: picard/const.py:202 +#: picard/const.py:214 msgid "France" msgstr "" -#: picard/const.py:203 +#: picard/const.py:215 msgid "British Indian Ocean Territory" msgstr "" -#: picard/const.py:204 +#: picard/const.py:216 msgid "St. Helena" msgstr "" -#: picard/const.py:205 +#: picard/const.py:217 msgid "Finland" msgstr "" -#: picard/const.py:206 +#: picard/const.py:218 msgid "Fiji" msgstr "" -#: picard/const.py:207 +#: picard/const.py:219 msgid "Falkland Islands (Malvinas)" msgstr "" -#: picard/const.py:208 +#: picard/const.py:220 msgid "Micronesia, Federated States of" msgstr "" -#: picard/const.py:209 +#: picard/const.py:221 msgid "Faroe Islands" msgstr "" -#: picard/const.py:210 +#: picard/const.py:222 msgid "Nicaragua" msgstr "" -#: picard/const.py:211 +#: picard/const.py:223 msgid "Netherlands" msgstr "" -#: picard/const.py:212 +#: picard/const.py:224 msgid "Norway" msgstr "" -#: picard/const.py:213 +#: picard/const.py:225 msgid "Namibia" msgstr "" -#: picard/const.py:214 +#: picard/const.py:226 msgid "Vanuatu" msgstr "" -#: picard/const.py:215 +#: picard/const.py:227 msgid "New Caledonia" msgstr "" -#: picard/const.py:216 +#: picard/const.py:228 msgid "Niger" msgstr "" -#: picard/const.py:217 +#: picard/const.py:229 msgid "Norfolk Island" msgstr "" -#: picard/const.py:218 +#: picard/const.py:230 msgid "Nigeria" msgstr "" -#: picard/const.py:219 +#: picard/const.py:231 msgid "New Zealand" msgstr "" -#: picard/const.py:220 +#: picard/const.py:232 msgid "Zaire" msgstr "" -#: picard/const.py:221 +#: picard/const.py:233 msgid "Nepal" msgstr "" -#: picard/const.py:222 +#: picard/const.py:234 msgid "Nauru" msgstr "" -#: picard/const.py:223 +#: picard/const.py:235 msgid "Niue" msgstr "" -#: picard/const.py:224 +#: picard/const.py:236 msgid "Cook Islands" msgstr "" -#: picard/const.py:225 +#: picard/const.py:237 msgid "Cote d'Ivoire" msgstr "" -#: picard/const.py:226 +#: picard/const.py:238 msgid "Switzerland" msgstr "" -#: picard/const.py:227 +#: picard/const.py:239 msgid "Colombia" msgstr "" -#: picard/const.py:228 +#: picard/const.py:240 msgid "China" msgstr "" -#: picard/const.py:229 +#: picard/const.py:241 msgid "Cameroon" msgstr "" -#: picard/const.py:230 +#: picard/const.py:242 msgid "Chile" msgstr "" -#: picard/const.py:231 +#: picard/const.py:243 msgid "Cocos (Keeling) Islands" msgstr "" -#: picard/const.py:232 +#: picard/const.py:244 msgid "Canada" msgstr "" -#: picard/const.py:233 +#: picard/const.py:245 msgid "Congo" msgstr "" -#: picard/const.py:234 +#: picard/const.py:246 msgid "Central African Republic" msgstr "" -#: picard/const.py:235 +#: picard/const.py:247 msgid "Czech Republic" msgstr "" -#: picard/const.py:236 +#: picard/const.py:248 msgid "Cyprus" msgstr "" -#: picard/const.py:237 +#: picard/const.py:249 msgid "Christmas Island" msgstr "" -#: picard/const.py:238 +#: picard/const.py:250 msgid "Costa Rica" msgstr "" -#: picard/const.py:239 +#: picard/const.py:251 msgid "Cape Verde" msgstr "" -#: picard/const.py:240 +#: picard/const.py:252 msgid "Cuba" msgstr "" -#: picard/const.py:241 +#: picard/const.py:253 msgid "Swaziland" msgstr "" -#: picard/const.py:242 +#: picard/const.py:254 msgid "Syrian Arab Republic" msgstr "" -#: picard/const.py:243 +#: picard/const.py:255 msgid "Kyrgyzstan" msgstr "" -#: picard/const.py:244 +#: picard/const.py:256 msgid "Kenya" msgstr "" -#: picard/const.py:245 +#: picard/const.py:257 msgid "Suriname" msgstr "" -#: picard/const.py:246 +#: picard/const.py:258 msgid "Kiribati" msgstr "" -#: picard/const.py:247 +#: picard/const.py:259 msgid "Cambodia" msgstr "" -#: picard/const.py:248 +#: picard/const.py:260 msgid "Saint Kitts and Nevis" msgstr "" -#: picard/const.py:249 +#: picard/const.py:261 msgid "Comoros" msgstr "" -#: picard/const.py:250 +#: picard/const.py:262 msgid "Sao Tome and Principe" msgstr "" -#: picard/const.py:251 +#: picard/const.py:263 msgid "Slovenia" msgstr "" -#: picard/const.py:252 +#: picard/const.py:264 msgid "Kuwait" msgstr "" -#: picard/const.py:253 +#: picard/const.py:265 msgid "Senegal" msgstr "" -#: picard/const.py:254 +#: picard/const.py:266 msgid "San Marino" msgstr "" -#: picard/const.py:255 +#: picard/const.py:267 msgid "Sierra Leone" msgstr "" -#: picard/const.py:256 +#: picard/const.py:268 msgid "Seychelles" msgstr "" -#: picard/const.py:257 +#: picard/const.py:269 msgid "Kazakhstan" msgstr "" -#: picard/const.py:258 +#: picard/const.py:270 msgid "Cayman Islands" msgstr "" -#: picard/const.py:259 +#: picard/const.py:271 msgid "Singapore" msgstr "" -#: picard/const.py:260 +#: picard/const.py:272 msgid "Sweden" msgstr "" -#: picard/const.py:261 +#: picard/const.py:273 msgid "Sudan" msgstr "" -#: picard/const.py:262 +#: picard/const.py:274 msgid "Dominican Republic" msgstr "" -#: picard/const.py:263 +#: picard/const.py:275 msgid "Dominica" msgstr "" -#: picard/const.py:264 +#: picard/const.py:276 msgid "Djibouti" msgstr "" -#: picard/const.py:265 +#: picard/const.py:277 msgid "Denmark" msgstr "" -#: picard/const.py:266 +#: picard/const.py:278 msgid "Virgin Islands (British)" msgstr "" -#: picard/const.py:267 +#: picard/const.py:279 msgid "Germany" msgstr "" -#: picard/const.py:268 +#: picard/const.py:280 msgid "Yemen" msgstr "" -#: picard/const.py:269 +#: picard/const.py:281 msgid "Algeria" msgstr "" -#: picard/const.py:270 +#: picard/const.py:282 msgid "United States" msgstr "" -#: picard/const.py:271 +#: picard/const.py:283 msgid "Uruguay" msgstr "" -#: picard/const.py:272 +#: picard/const.py:284 msgid "Mayotte" msgstr "" -#: picard/const.py:273 +#: picard/const.py:285 msgid "United States Minor Outlying Islands" msgstr "" -#: picard/const.py:274 +#: picard/const.py:286 msgid "Lebanon" msgstr "" -#: picard/const.py:275 +#: picard/const.py:287 msgid "Saint Lucia" msgstr "" -#: picard/const.py:276 +#: picard/const.py:288 msgid "Lao People's Democratic Republic" msgstr "" -#: picard/const.py:277 +#: picard/const.py:289 msgid "Tuvalu" msgstr "" -#: picard/const.py:278 +#: picard/const.py:290 msgid "Taiwan" msgstr "" -#: picard/const.py:279 +#: picard/const.py:291 msgid "Trinidad and Tobago" msgstr "" -#: picard/const.py:280 +#: picard/const.py:292 msgid "Turkey" msgstr "" -#: picard/const.py:281 +#: picard/const.py:293 msgid "Sri Lanka" msgstr "" -#: picard/const.py:282 +#: picard/const.py:294 msgid "Liechtenstein" msgstr "" -#: picard/const.py:283 +#: picard/const.py:295 msgid "Latvia" msgstr "" -#: picard/const.py:284 +#: picard/const.py:296 msgid "Tonga" msgstr "" -#: picard/const.py:285 +#: picard/const.py:297 msgid "Lithuania" msgstr "" -#: picard/const.py:286 +#: picard/const.py:298 msgid "Luxembourg" msgstr "" -#: picard/const.py:287 +#: picard/const.py:299 msgid "Liberia" msgstr "" -#: picard/const.py:288 +#: picard/const.py:300 msgid "Lesotho" msgstr "" -#: picard/const.py:289 +#: picard/const.py:301 msgid "Thailand" msgstr "" -#: picard/const.py:290 +#: picard/const.py:302 msgid "French Southern Territories" msgstr "" -#: picard/const.py:291 +#: picard/const.py:303 msgid "Togo" msgstr "" -#: picard/const.py:292 +#: picard/const.py:304 msgid "Chad" msgstr "" -#: picard/const.py:293 +#: picard/const.py:305 msgid "Turks and Caicos Islands" msgstr "" -#: picard/const.py:294 +#: picard/const.py:306 msgid "Libyan Arab Jamahiriya" msgstr "" -#: picard/const.py:295 +#: picard/const.py:307 msgid "Vatican City State (Holy See)" msgstr "" -#: picard/const.py:296 +#: picard/const.py:308 msgid "Saint Vincent and The Grenadines" msgstr "" -#: picard/const.py:297 +#: picard/const.py:309 msgid "United Arab Emirates" msgstr "" -#: picard/const.py:298 +#: picard/const.py:310 msgid "Andorra" msgstr "" -#: picard/const.py:299 +#: picard/const.py:311 msgid "Antigua and Barbuda" msgstr "" -#: picard/const.py:300 +#: picard/const.py:312 msgid "Afghanistan" msgstr "" -#: picard/const.py:301 +#: picard/const.py:313 msgid "Anguilla" msgstr "" -#: picard/const.py:302 +#: picard/const.py:314 msgid "Virgin Islands (U.S.)" msgstr "" -#: picard/const.py:303 +#: picard/const.py:315 msgid "Iceland" msgstr "" -#: picard/const.py:304 +#: picard/const.py:316 msgid "Iran (Islamic Republic of)" msgstr "" -#: picard/const.py:305 +#: picard/const.py:317 msgid "Armenia" msgstr "" -#: picard/const.py:306 +#: picard/const.py:318 msgid "Albania" msgstr "" -#: picard/const.py:307 +#: picard/const.py:319 msgid "Angola" msgstr "" -#: picard/const.py:308 +#: picard/const.py:320 msgid "Netherlands Antilles" msgstr "" -#: picard/const.py:309 +#: picard/const.py:321 msgid "Antarctica" msgstr "" -#: picard/const.py:310 +#: picard/const.py:322 msgid "American Samoa" msgstr "" -#: picard/const.py:311 +#: picard/const.py:323 msgid "Argentina" msgstr "" -#: picard/const.py:312 +#: picard/const.py:324 msgid "Australia" msgstr "" -#: picard/const.py:313 +#: picard/const.py:325 msgid "Austria" msgstr "" -#: picard/const.py:314 +#: picard/const.py:326 msgid "Aruba" msgstr "" -#: picard/const.py:315 +#: picard/const.py:327 msgid "India" msgstr "" -#: picard/const.py:316 +#: picard/const.py:328 msgid "Tanzania, United Republic of" msgstr "" -#: picard/const.py:317 +#: picard/const.py:329 msgid "Azerbaijan" msgstr "" -#: picard/const.py:318 +#: picard/const.py:330 msgid "Ireland" msgstr "" -#: picard/const.py:319 +#: picard/const.py:331 msgid "Indonesia" msgstr "" -#: picard/const.py:320 +#: picard/const.py:332 msgid "Ukraine" msgstr "" -#: picard/const.py:321 +#: picard/const.py:333 msgid "Qatar" msgstr "" -#: picard/const.py:322 +#: picard/const.py:334 msgid "Mozambique" msgstr "" -#: picard/const.py:323 +#: picard/const.py:335 msgid "Bosnia and Herzegovina" msgstr "" -#: picard/const.py:324 +#: picard/const.py:336 msgid "Congo, The Democratic Republic of the" msgstr "" -#: picard/const.py:325 +#: picard/const.py:337 msgid "Serbia and Montenegro (historical, 2003-2006)" msgstr "" -#: picard/const.py:326 +#: picard/const.py:338 msgid "Serbia" msgstr "" -#: picard/const.py:327 +#: picard/const.py:339 msgid "Montenegro" msgstr "" -#: picard/const.py:328 +#: picard/const.py:340 msgid "Croatia" msgstr "" -#: picard/const.py:329 +#: picard/const.py:341 msgid "Korea (North), Democratic People's Republic of" msgstr "" -#: picard/const.py:330 +#: picard/const.py:342 msgid "Korea (South), Republic of" msgstr "" -#: picard/const.py:331 +#: picard/const.py:343 msgid "Slovakia" msgstr "" -#: picard/const.py:332 +#: picard/const.py:344 msgid "Soviet Union (historical, 1922-1991)" msgstr "" -#: picard/const.py:333 +#: picard/const.py:345 msgid "East Timor" msgstr "" -#: picard/const.py:334 +#: picard/const.py:346 msgid "Czechoslovakia (historical, 1918-1992)" msgstr "" -#: picard/const.py:335 +#: picard/const.py:347 msgid "Europe" msgstr "" -#: picard/const.py:336 +#: picard/const.py:348 msgid "East Germany (historical, 1949-1990)" msgstr "" -#: picard/const.py:337 +#: picard/const.py:349 msgid "[Unknown Country]" msgstr "" -#: picard/const.py:338 +#: picard/const.py:350 msgid "[Worldwide]" msgstr "" -#: picard/const.py:339 +#: picard/const.py:351 msgid "Yugoslavia (historical, 1918-1992)" msgstr "" -#: picard/const.py:351 +#: picard/const.py:363 msgid "Danish" msgstr "" -#: picard/const.py:352 +#: picard/const.py:364 msgid "German" msgstr "" -#: picard/const.py:354 +#: picard/const.py:366 msgid "English" msgstr "" -#: picard/const.py:355 +#: picard/const.py:367 msgid "English (Canada)" msgstr "" -#: picard/const.py:356 +#: picard/const.py:368 msgid "English (UK)" msgstr "" -#: picard/const.py:358 +#: picard/const.py:370 msgid "Spanish" msgstr "" -#: picard/const.py:359 +#: picard/const.py:371 msgid "Estonian" msgstr "" -#: picard/const.py:361 +#: picard/const.py:373 msgid "Finnish" msgstr "" -#: picard/const.py:363 +#: picard/const.py:375 msgid "French" msgstr "" -#: picard/const.py:372 +#: picard/const.py:384 msgid "Italian" msgstr "" -#: picard/const.py:379 +#: picard/const.py:391 msgid "Dutch" msgstr "" -#: picard/const.py:381 +#: picard/const.py:393 msgid "Polish" msgstr "" -#: picard/const.py:383 +#: picard/const.py:395 msgid "Brazilian Portuguese" msgstr "" -#: picard/const.py:390 +#: picard/const.py:402 msgid "Swedish" msgstr "" -#: picard/coverart.py:99 +#: picard/coverart.py:98 #, python-format msgid "Coverart %s downloaded" msgstr "" -#: picard/coverart.py:260 +#: picard/coverart.py:259 #, python-format msgid "Downloading http://%s:%i%s" msgstr "" @@ -1440,40 +1482,44 @@ msgstr "" msgid "Unknown" msgstr "" -#: picard/file.py:536 +#: picard/file.py:534 #, python-format msgid "No matching tracks for file %s" msgstr "" -#: picard/file.py:548 +#: picard/file.py:546 #, python-format msgid "No matching tracks above the threshold for file %s" msgstr "" -#: picard/file.py:551 +#: picard/file.py:549 #, python-format msgid "File %s identified!" msgstr "" -#: picard/file.py:567 +#: picard/file.py:565 #, python-format msgid "Looking up the metadata for file %s..." msgstr "" #: picard/releasegroup.py:64 +msgid "[no barcode]" +msgstr "" + +#: picard/releasegroup.py:84 msgid "[no release info]" msgstr "" -#: picard/tagger.py:348 +#: picard/tagger.py:298 #, python-format msgid "Loading directory %s" msgstr "" -#: picard/tagger.py:484 +#: picard/tagger.py:434 msgid "CD Lookup Error" msgstr "" -#: picard/tagger.py:485 +#: picard/tagger.py:435 #, python-format msgid "" "Error while reading CD:\n" @@ -1507,7 +1553,7 @@ msgstr "" msgid "Catalog #s" msgstr "" -#: picard/ui/cdlookup.py:35 picard/util/tags.py:77 +#: picard/ui/cdlookup.py:35 picard/util/tags.py:78 msgid "Barcode" msgstr "" @@ -1594,19 +1640,19 @@ msgstr "" msgid "&Info" msgstr "" -#: picard/ui/infostatus.py:52 +#: picard/ui/infostatus.py:51 msgid "Files" msgstr "" -#: picard/ui/infostatus.py:53 +#: picard/ui/infostatus.py:52 msgid "Albums" msgstr "" -#: picard/ui/infostatus.py:54 +#: picard/ui/infostatus.py:53 msgid "Pending files" msgstr "" -#: picard/ui/infostatus.py:55 +#: picard/ui/infostatus.py:54 msgid "Pending requests" msgstr "" @@ -1614,7 +1660,7 @@ msgstr "" msgid "Title" msgstr "" -#: picard/ui/itemviews.py:95 picard/util/tags.py:86 +#: picard/ui/itemviews.py:95 picard/util/tags.py:87 msgid "Length" msgstr "" @@ -1634,31 +1680,31 @@ msgstr "" msgid "Loading..." msgstr "" -#: picard/ui/itemviews.py:331 +#: picard/ui/itemviews.py:333 msgid "Collections" msgstr "" -#: picard/ui/itemviews.py:334 +#: picard/ui/itemviews.py:336 msgid "&Plugins" msgstr "" -#: picard/ui/itemviews.py:510 +#: picard/ui/itemviews.py:512 msgid "file view" msgstr "" -#: picard/ui/itemviews.py:511 +#: picard/ui/itemviews.py:513 msgid "Contains unmatched files and clusters" msgstr "" -#: picard/ui/itemviews.py:531 +#: picard/ui/itemviews.py:533 msgid "Clusters" msgstr "" -#: picard/ui/itemviews.py:540 +#: picard/ui/itemviews.py:542 msgid "album view" msgstr "" -#: picard/ui/itemviews.py:541 +#: picard/ui/itemviews.py:543 msgid "Contains albums and matched files" msgstr "" @@ -1670,34 +1716,34 @@ msgstr "" msgid "Status History" msgstr "" -#: picard/ui/mainwindow.py:77 +#: picard/ui/mainwindow.py:78 msgid "MusicBrainz Picard" msgstr "" -#: picard/ui/mainwindow.py:158 +#: picard/ui/mainwindow.py:159 msgid "Unsaved Changes" msgstr "" -#: picard/ui/mainwindow.py:159 +#: picard/ui/mainwindow.py:160 msgid "Are you sure you want to quit Picard?" msgstr "" -#: picard/ui/mainwindow.py:160 +#: picard/ui/mainwindow.py:161 #, python-format msgid "There is %d unsaved file. Closing Picard will lose all unsaved changes." msgid_plural "There are %d unsaved files. Closing Picard will lose all unsaved changes." msgstr[0] "" msgstr[1] "" -#: picard/ui/mainwindow.py:167 +#: picard/ui/mainwindow.py:168 msgid "&Quit Picard" msgstr "" -#: picard/ui/mainwindow.py:219 +#: picard/ui/mainwindow.py:220 msgid "Ready" msgstr "" -#: picard/ui/mainwindow.py:223 +#: picard/ui/mainwindow.py:224 msgid "" "Picard listens on a port to integrate with your browser and downloads " "release information when you click the \"Tagger\" buttons on the " @@ -1955,47 +2001,17 @@ msgstr "" msgid "All Supported Formats" msgstr "" -#: picard/ui/mainwindow.py:673 picard/ui/mainwindow.py:682 -msgid "Various Artists file naming scheme removal" -msgstr "" - -#: picard/ui/mainwindow.py:674 -msgid "" -"The separate file naming scheme for various artists albums has been\n" -"removed in this version of Picard. You currently do not use the this " -"option,\n" -"but have a separate file naming scheme defined. Do you want to remove it " -"or\n" -"merge it with your file naming scheme for single artist albums?" -msgstr "" - -#: picard/ui/mainwindow.py:678 -msgid "Merge" -msgstr "" - -#: picard/ui/mainwindow.py:678 picard/ui/metadatabox.py:254 -msgid "Remove" -msgstr "" - -#: picard/ui/mainwindow.py:683 -msgid "" -"The separate file naming scheme for various artists albums has been\n" -"removed in this version of Picard. Your file naming scheme has " -"automatically\n" -"been merged with that of single artist albums." -msgstr "" - -#: picard/ui/mainwindow.py:727 +#: picard/ui/mainwindow.py:710 msgid "Configuration Required" msgstr "" -#: picard/ui/mainwindow.py:728 +#: picard/ui/mainwindow.py:711 msgid "" "Audio fingerprinting is not yet configured. Would you like to configure " "it now?" msgstr "" -#: picard/ui/mainwindow.py:806 picard/ui/mainwindow.py:813 +#: picard/ui/mainwindow.py:789 picard/ui/mainwindow.py:796 #, python-format msgid " (Error: %s)" msgstr "" @@ -2276,7 +2292,7 @@ msgstr "" msgid "Username:" msgstr "" -#: picard/ui/ui_options_general.py:98 picard/ui/options/general.py:29 +#: picard/ui/ui_options_general.py:98 picard/ui/options/general.py:30 msgid "General" msgstr "" @@ -2332,7 +2348,7 @@ msgstr "" msgid "Minimal similarity for cluster lookups:" msgstr "" -#: picard/ui/ui_options_metadata.py:114 picard/ui/options/metadata.py:30 +#: picard/ui/ui_options_metadata.py:114 picard/ui/options/metadata.py:29 msgid "Metadata" msgstr "" @@ -2401,7 +2417,7 @@ msgstr "" msgid "Name" msgstr "" -#: picard/ui/ui_options_plugins.py:133 picard/util/tags.py:37 +#: picard/ui/ui_options_plugins.py:133 picard/util/tags.py:38 msgid "Version" msgstr "" @@ -2457,7 +2473,7 @@ msgstr "" msgid "EP" msgstr "" -#: picard/ui/ui_options_releases.py:219 picard/util/tags.py:68 +#: picard/ui/ui_options_releases.py:219 msgid "Compilation" msgstr "" @@ -2572,7 +2588,7 @@ msgid "" msgstr "" #: picard/ui/ui_options_tags.py:172 -msgid "Tags are separated by spaces, and are case-sensitive." +msgid "Tags are separated by commas, and are case-sensitive." msgstr "" #: picard/ui/ui_options_tags.py:173 @@ -2650,24 +2666,24 @@ msgstr "" msgid "&Cancel" msgstr "" -#: picard/ui/options/about.py:32 +#: picard/ui/options/about.py:33 msgid "About" msgstr "" -#: picard/ui/options/about.py:47 +#: picard/ui/options/about.py:48 msgid "is not installed" msgstr "" -#: picard/ui/options/about.py:56 +#: picard/ui/options/about.py:59 msgid "translator-credits" msgstr "" -#: picard/ui/options/about.py:59 +#: picard/ui/options/about.py:62 #, python-format msgid "
Translated to LANG by %s" msgstr "" -#: picard/ui/options/about.py:63 +#: picard/ui/options/about.py:66 #, python-format msgid "" "

\n" -"

Donate " -"now!

\n" +"

Donate now!

" +"\n" "

Credits
\n" "Copyright © 2004-2011 Robert Kaye, Lukáš Lalinský and others" "%(translator-credits)s

\n" -"

http://musicbrainz.org/doc/MusicBrainz_Picard

" -"\n" +"

%(picard-doc-" +"url)s

\n" msgstr "" #: picard/ui/options/advanced.py:26 @@ -2715,11 +2730,11 @@ msgstr "" msgid "System default" msgstr "" -#: picard/ui/options/interface.py:92 +#: picard/ui/options/interface.py:96 msgid "Language changed" msgstr "" -#: picard/ui/options/interface.py:92 +#: picard/ui/options/interface.py:96 msgid "" "You have changed the interface language. You have to restart Picard in " "order for the change to take effect." @@ -2749,15 +2764,15 @@ msgstr "" msgid "File naming" msgstr "" -#: picard/ui/options/renaming.py:166 +#: picard/ui/options/renaming.py:184 msgid "Error" msgstr "" -#: picard/ui/options/renaming.py:166 +#: picard/ui/options/renaming.py:184 msgid "The location to move files to must not be empty." msgstr "" -#: picard/ui/options/renaming.py:176 +#: picard/ui/options/renaming.py:194 msgid "The file naming format must not be empty." msgstr "" @@ -2769,62 +2784,62 @@ msgstr "" msgid "Script Error" msgstr "" -#: picard/util/bytes2human.py:35 +#: picard/util/bytes2human.py:33 #, python-format msgid "%s B" msgstr "" -#: picard/util/bytes2human.py:36 +#: picard/util/bytes2human.py:34 #, python-format msgid "%s kB" msgstr "" -#: picard/util/bytes2human.py:37 +#: picard/util/bytes2human.py:35 #, python-format msgid "%s KiB" msgstr "" -#: picard/util/bytes2human.py:38 +#: picard/util/bytes2human.py:36 #, python-format msgid "%s MB" msgstr "" -#: picard/util/bytes2human.py:39 +#: picard/util/bytes2human.py:37 #, python-format msgid "%s MiB" msgstr "" -#: picard/util/bytes2human.py:40 +#: picard/util/bytes2human.py:38 #, python-format msgid "%s GB" msgstr "" -#: picard/util/bytes2human.py:41 +#: picard/util/bytes2human.py:39 #, python-format msgid "%s GiB" msgstr "" -#: picard/util/bytes2human.py:42 +#: picard/util/bytes2human.py:40 #, python-format msgid "%s TB" msgstr "" -#: picard/util/bytes2human.py:43 +#: picard/util/bytes2human.py:41 #, python-format msgid "%s TiB" msgstr "" -#: picard/util/bytes2human.py:44 +#: picard/util/bytes2human.py:42 #, python-format msgid "%s PB" msgstr "" -#: picard/util/bytes2human.py:45 +#: picard/util/bytes2human.py:43 #, python-format msgid "%s PiB" msgstr "" -#: picard/util/bytes2human.py:86 +#: picard/util/bytes2human.py:84 #, python-format msgid "%s " msgstr "" @@ -2834,242 +2849,250 @@ msgid "Original Release Date" msgstr "" #: picard/util/tags.py:26 -msgid "Album Artist" +msgid "Original Year" msgstr "" #: picard/util/tags.py:27 -msgid "Track Number" +msgid "Album Artist" msgstr "" #: picard/util/tags.py:28 -msgid "Total Tracks" +msgid "Track Number" msgstr "" #: picard/util/tags.py:29 -msgid "Disc Number" +msgid "Total Tracks" msgstr "" #: picard/util/tags.py:30 -msgid "Total Discs" +msgid "Disc Number" msgstr "" #: picard/util/tags.py:31 -msgid "Album Artist Sort Order" +msgid "Total Discs" msgstr "" #: picard/util/tags.py:32 -msgid "Artist Sort Order" +msgid "Album Artist Sort Order" msgstr "" #: picard/util/tags.py:33 -msgid "Title Sort Order" +msgid "Artist Sort Order" msgstr "" #: picard/util/tags.py:34 -msgid "Album Sort Order" +msgid "Title Sort Order" msgstr "" #: picard/util/tags.py:35 -msgid "ASIN" +msgid "Album Sort Order" msgstr "" #: picard/util/tags.py:36 +msgid "ASIN" +msgstr "" + +#: picard/util/tags.py:37 msgid "Grouping" msgstr "" -#: picard/util/tags.py:38 +#: picard/util/tags.py:39 msgid "ISRC" msgstr "" -#: picard/util/tags.py:39 +#: picard/util/tags.py:40 msgid "Mood" msgstr "" -#: picard/util/tags.py:40 +#: picard/util/tags.py:41 msgid "BPM" msgstr "" -#: picard/util/tags.py:41 +#: picard/util/tags.py:42 msgid "Copyright" msgstr "" -#: picard/util/tags.py:42 +#: picard/util/tags.py:43 msgid "License" msgstr "" -#: picard/util/tags.py:43 +#: picard/util/tags.py:44 msgid "Composer" msgstr "" -#: picard/util/tags.py:44 +#: picard/util/tags.py:45 msgid "Writer" msgstr "" -#: picard/util/tags.py:45 +#: picard/util/tags.py:46 msgid "Conductor" msgstr "" -#: picard/util/tags.py:46 +#: picard/util/tags.py:47 msgid "Lyricist" msgstr "" -#: picard/util/tags.py:47 +#: picard/util/tags.py:48 msgid "Arranger" msgstr "" -#: picard/util/tags.py:48 +#: picard/util/tags.py:49 msgid "Producer" msgstr "" -#: picard/util/tags.py:49 +#: picard/util/tags.py:50 msgid "Engineer" msgstr "" -#: picard/util/tags.py:50 +#: picard/util/tags.py:51 msgid "Subtitle" msgstr "" -#: picard/util/tags.py:51 +#: picard/util/tags.py:52 msgid "Disc Subtitle" msgstr "" -#: picard/util/tags.py:52 +#: picard/util/tags.py:53 msgid "Remixer" msgstr "" -#: picard/util/tags.py:53 +#: picard/util/tags.py:54 msgid "MusicBrainz Recording Id" msgstr "" -#: picard/util/tags.py:54 +#: picard/util/tags.py:55 msgid "MusicBrainz Track Id" msgstr "" -#: picard/util/tags.py:55 +#: picard/util/tags.py:56 msgid "MusicBrainz Release Id" msgstr "" -#: picard/util/tags.py:56 +#: picard/util/tags.py:57 msgid "MusicBrainz Artist Id" msgstr "" -#: picard/util/tags.py:57 +#: picard/util/tags.py:58 msgid "MusicBrainz Release Artist Id" msgstr "" -#: picard/util/tags.py:58 +#: picard/util/tags.py:59 msgid "MusicBrainz Work Id" msgstr "" -#: picard/util/tags.py:59 +#: picard/util/tags.py:60 msgid "MusicBrainz Release Group Id" msgstr "" -#: picard/util/tags.py:60 +#: picard/util/tags.py:61 msgid "MusicBrainz Disc Id" msgstr "" -#: picard/util/tags.py:61 +#: picard/util/tags.py:62 msgid "MusicBrainz Sort Name" msgstr "" -#: picard/util/tags.py:62 +#: picard/util/tags.py:63 msgid "MusicIP PUID" msgstr "" -#: picard/util/tags.py:63 +#: picard/util/tags.py:64 msgid "MusicIP Fingerprint" msgstr "" -#: picard/util/tags.py:64 +#: picard/util/tags.py:65 msgid "AcoustID" msgstr "" -#: picard/util/tags.py:65 +#: picard/util/tags.py:66 msgid "AcoustID Fingerprint" msgstr "" -#: picard/util/tags.py:66 +#: picard/util/tags.py:67 msgid "Disc Id" msgstr "" -#: picard/util/tags.py:67 +#: picard/util/tags.py:68 msgid "Website" msgstr "" #: picard/util/tags.py:69 -msgid "Comment" +msgid "Compilation (iTunes)" msgstr "" #: picard/util/tags.py:70 -msgid "Genre" +msgid "Comment" msgstr "" #: picard/util/tags.py:71 -msgid "Encoded By" +msgid "Genre" msgstr "" #: picard/util/tags.py:72 -msgid "Performer" +msgid "Encoded By" msgstr "" #: picard/util/tags.py:73 -msgid "Release Type" +msgid "Performer" msgstr "" #: picard/util/tags.py:74 -msgid "Release Status" +msgid "Release Type" msgstr "" #: picard/util/tags.py:75 -msgid "Release Country" +msgid "Release Status" msgstr "" #: picard/util/tags.py:76 +msgid "Release Country" +msgstr "" + +#: picard/util/tags.py:77 msgid "Record Label" msgstr "" -#: picard/util/tags.py:78 +#: picard/util/tags.py:79 msgid "Catalog Number" msgstr "" -#: picard/util/tags.py:79 +#: picard/util/tags.py:80 msgid "Format" msgstr "" -#: picard/util/tags.py:80 +#: picard/util/tags.py:81 msgid "DJ-Mixer" msgstr "" -#: picard/util/tags.py:81 +#: picard/util/tags.py:82 msgid "Media" msgstr "" -#: picard/util/tags.py:82 +#: picard/util/tags.py:83 msgid "Lyrics" msgstr "" -#: picard/util/tags.py:83 +#: picard/util/tags.py:84 msgid "Mixer" msgstr "" -#: picard/util/tags.py:84 +#: picard/util/tags.py:85 msgid "Language" msgstr "" -#: picard/util/tags.py:85 +#: picard/util/tags.py:86 msgid "Script" msgstr "" -#: picard/util/tags.py:87 +#: picard/util/tags.py:88 msgid "Rating" msgstr "" -#: picard/util/webbrowser2.py:88 +#: picard/util/webbrowser2.py:90 msgid "Web Browser Error" msgstr "" -#: picard/util/webbrowser2.py:88 +#: picard/util/webbrowser2.py:90 #, python-format msgid "" "Error while launching a web browser:\n" From 0a1b97732bbbb5c82e265edd8b13980a3b40d353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionu=C8=9B=20Cioc=C3=AErlan?= Date: Sat, 4 Jan 2014 21:43:39 +0200 Subject: [PATCH 53/59] PICARD-540: return relative paths for shortened filenames --- picard/file.py | 2 ++ picard/util/filenaming.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/picard/file.py b/picard/file.py index 9a4b77c45..fb225b612 100644 --- a/picard/file.py +++ b/picard/file.py @@ -279,6 +279,8 @@ class File(QtCore.QObject, Item): new_filename = os.path.basename(new_filename) new_filename = make_short_filename(new_dirname, new_filename, config.setting['windows_compatibility'], config.setting['windows_compatibility_drive_root']) + # TODO: move following logic under util.filenaming + # (and reconsider its necessity) # win32 compatibility fixes if settings['windows_compatibility'] or sys.platform == 'win32': new_filename = new_filename.replace('./', '_/').replace('.\\', '_\\') diff --git a/picard/util/filenaming.py b/picard/util/filenaming.py index 1e166124d..9981bb57d 100644 --- a/picard/util/filenaming.py +++ b/picard/util/filenaming.py @@ -311,7 +311,7 @@ def make_short_filename(basedir, relpath, win_compat=False, relative_to=""): reserved = len(basedir) if not basedir.endswith(os.path.sep): reserved += 1 - return os.path.join(basedir, _make_win_short_filename(relpath, reserved)) + return _make_win_short_filename(relpath, reserved) # if we're being windows compatible, figure out how much # needs to be reserved for the basedir part if win_compat: @@ -337,4 +337,4 @@ def make_short_filename(basedir, relpath, win_compat=False, relative_to=""): # and filesystem-dependent limit = _get_filename_limit(basedir) relpath = shorten_path(relpath, limit, mode=SHORTEN_BYTES) - return os.path.join(basedir, relpath) + return relpath From a8fa19588dccb43e4f74d44cc29ffce9843fa299 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sun, 5 Jan 2014 13:38:03 +0100 Subject: [PATCH 54/59] Drop useless spaces in button label It is problematic when it comes to i18n and is useless here. --- picard/ui/ui_cdlookup.py | 2 +- ui/cdlookup.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/picard/ui/ui_cdlookup.py b/picard/ui/ui_cdlookup.py index 4d12eed1a..31eca9cdd 100644 --- a/picard/ui/ui_cdlookup.py +++ b/picard/ui/ui_cdlookup.py @@ -66,6 +66,6 @@ class Ui_Dialog(object): Dialog.setWindowTitle(_("CD Lookup")) self.label.setText(_("The following releases on MusicBrainz match the CD:")) self.ok_button.setText(_("OK")) - self.lookup_button.setText(_(" Lookup manually ")) + self.lookup_button.setText(_("Lookup manually")) self.cancel_button.setText(_("Cancel")) diff --git a/ui/cdlookup.ui b/ui/cdlookup.ui index b1738ae42..36c6af31b 100644 --- a/ui/cdlookup.ui +++ b/ui/cdlookup.ui @@ -68,7 +68,7 @@ - Lookup manually + Lookup manually From 3e833083572d23a1fd7f7b9dda99f6f2e9500f74 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sun, 5 Jan 2014 14:01:09 +0100 Subject: [PATCH 55/59] Regenerate picard.pot --- po/picard.pot | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/po/picard.pot b/po/picard.pot index e5ea78f78..b63f5dcd7 100644 --- a/po/picard.pot +++ b/po/picard.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-01-02 18:11+0100\n" +"POT-Creation-Date: 2014-01-05 14:00+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1482,22 +1482,22 @@ msgstr "" msgid "Unknown" msgstr "" -#: picard/file.py:534 +#: picard/file.py:536 #, python-format msgid "No matching tracks for file %s" msgstr "" -#: picard/file.py:546 +#: picard/file.py:548 #, python-format msgid "No matching tracks above the threshold for file %s" msgstr "" -#: picard/file.py:549 +#: picard/file.py:551 #, python-format msgid "File %s identified!" msgstr "" -#: picard/file.py:565 +#: picard/file.py:567 #, python-format msgid "Looking up the metadata for file %s..." msgstr "" @@ -2100,7 +2100,7 @@ msgid "OK" msgstr "" #: picard/ui/ui_cdlookup.py:69 -msgid " Lookup manually " +msgid "Lookup manually" msgstr "" #: picard/ui/ui_cdlookup.py:70 From cbcbc734081e949846b5c02b90b74f32c51a1c40 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Mon, 6 Jan 2014 11:25:27 +0100 Subject: [PATCH 56/59] Update README.md Typo fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f74a37151..006cb9c79 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ Picard is named after Captain Jean-Luc Picard from the TV series Star Trek: The Binary downloads are available [here](http://musicbrainz.org/doc/MusicBrainz_Picard). -To submit bugs or improvements, please use [Picard bug-tracker](http://tickets.musicbrainz.org/browse/PICARD). +To submit bugs or improvements, please use [Picard bug tracker](http://tickets.musicbrainz.org/browse/PICARD). From 0d04cbdf8ad52b7408763e99f78255f1861c4867 Mon Sep 17 00:00:00 2001 From: Ismael Olea Date: Mon, 6 Jan 2014 17:11:27 +0100 Subject: [PATCH 57/59] Update about.py updated copyright dates --- picard/ui/options/about.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/picard/ui/options/about.py b/picard/ui/options/about.py index bb7a3c3c3..84d55d272 100644 --- a/picard/ui/options/about.py +++ b/picard/ui/options/about.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # Picard, the next-generation MusicBrainz tagger -# Copyright (C) 2006 Lukáš Lalinský +# Copyright (C) 2006-2014 Lukáš Lalinský # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -75,7 +75,7 @@ Discid %(discid-version)s Thank you for using Picard. Picard relies on the MusicBrainz database, which is operated by the MetaBrainz Foundation with the help of thousands of volunteers. If you like this application please consider donating to the MetaBrainz Foundation to keep the service running.

Donate now!

Credits
-Copyright © 2004-2011 Robert Kaye, Lukáš Lalinský and others%(translator-credits)s

+Copyright © 2004-2014 Robert Kaye, Lukáš Lalinský and others%(translator-credits)s

%(picard-doc-url)s

""") % args self.ui.label.setOpenExternalLinks(True) From 156aeda8a0c45009ddf9fe869e6b3bd9f485489f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionu=C8=9B=20Cioc=C3=AErlan?= Date: Tue, 7 Jan 2014 17:59:50 +0200 Subject: [PATCH 58/59] updated tests for filename shortening (which now returns relative paths) --- test/test_util_filenaming.py | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/test/test_util_filenaming.py b/test/test_util_filenaming.py index b7b2056fa..e04441ca5 100644 --- a/test/test_util_filenaming.py +++ b/test/test_util_filenaming.py @@ -17,7 +17,7 @@ class ShortFilenameTest(unittest.TestCase): def test_bmp_unicode_on_unicode_fs(self): char = u"\N{LATIN SMALL LETTER SHARP S}" fn = make_short_filename(self.root, os.path.join(*[char * 120] * 2)) - self.assertEqual(fn, os.path.join(self.root, *[char * 120] * 2)) + self.assertEqual(fn, os.path.join(*[char * 120] * 2)) @unittest.skipUnless(sys.platform not in ("win32", "darwin"), "non-windows, non-osx test") def test_bmp_unicode_on_nix(self): @@ -25,28 +25,28 @@ class ShortFilenameTest(unittest.TestCase): max_len = 255 divisor = len(char.encode(sys.getfilesystemencoding())) fn = make_short_filename(self.root, os.path.join(*[char * 200] * 2)) - self.assertEqual(fn, os.path.join(self.root, *[char * (max_len // divisor)] * 2)) + self.assertEqual(fn, os.path.join(*[char * (max_len // divisor)] * 2)) @unittest.skipUnless(sys.platform == "darwin", "os x test") def test_precomposed_unicode_on_osx(self): char = u"\N{LATIN SMALL LETTER A WITH BREVE}" max_len = 255 fn = make_short_filename(self.root, os.path.join(*[char * 200] * 2)) - self.assertEqual(fn, os.path.join(self.root, *[char * (max_len // 2)] * 2)) + self.assertEqual(fn, os.path.join(*[char * (max_len // 2)] * 2)) @unittest.skipUnless(sys.platform == "win32", "windows test") def test_nonbmp_unicode_on_windows(self): char = u"\N{MUSICAL SYMBOL G CLEF}" remaining = 259 - (3 + 10 + 1 + 200 + 1) fn = make_short_filename(self.root, os.path.join(*[char * 100] * 2)) - self.assertEqual(fn, os.path.join(self.root, char * 100, char * (remaining // 2))) + self.assertEqual(fn, os.path.join(char * 100, char * (remaining // 2))) @unittest.skipUnless(sys.platform == "darwin", "os x test") def test_nonbmp_unicode_on_osx(self): char = u"\N{MUSICAL SYMBOL G CLEF}" max_len = 255 fn = make_short_filename(self.root, os.path.join(*[char * 200] * 2)) - self.assertEqual(fn, os.path.join(self.root, *[char * (max_len // 2)] * 2)) + self.assertEqual(fn, os.path.join(*[char * (max_len // 2)] * 2)) @unittest.skipUnless(sys.platform not in ("win32", "darwin"), "non-windows, non-osx test") def test_nonbmp_unicode_on_nix(self): @@ -54,7 +54,7 @@ class ShortFilenameTest(unittest.TestCase): max_len = 255 divisor = len(char.encode(sys.getfilesystemencoding())) fn = make_short_filename(self.root, os.path.join(*[char * 100] * 2)) - self.assertEqual(fn, os.path.join(self.root, *[char * (max_len // divisor)] * 2)) + self.assertEqual(fn, os.path.join(*[char * (max_len // divisor)] * 2)) @unittest.skipUnless(sys.platform not in ("win32", "darwin"), "non-windows, non-osx test") def test_nonbmp_unicode_on_nix_with_windows_compat(self): @@ -63,45 +63,49 @@ class ShortFilenameTest(unittest.TestCase): remaining = 259 - (3 + 10 + 1 + 200 + 1) divisor = len(char.encode(sys.getfilesystemencoding())) fn = make_short_filename(self.root, os.path.join(*[char * 100] * 2), win_compat=True) - self.assertEqual(fn, os.path.join(self.root, char * (max_len // divisor), char * (remaining // 2))) + self.assertEqual(fn, os.path.join(char * (max_len // divisor), char * (remaining // 2))) def test_windows_shortening(self): fn = make_short_filename(self.root, os.path.join("a" * 200, "b" * 200, "c" * 200 + ".ext"), win_compat=True) - self.assertEqual(fn, os.path.join(self.root, "a" * 116, "b" * 116, "c" * 7 + ".ext")) + self.assertEqual(fn, os.path.join("a" * 116, "b" * 116, "c" * 7 + ".ext")) @unittest.skipUnless(sys.platform != "win32", "non-windows test") def test_windows_shortening_with_ancestor_on_nix(self): + root = os.path.join(self.root, "w" * 10, "x" * 10, "y" * 9, "z" * 9) fn = make_short_filename( - os.path.join(self.root, "w" * 10, "x" * 10, "y" * 9, "z" * 9), os.path.join("b" * 200, "c" * 200, "d" * 200 + ".ext"), + root, os.path.join("b" * 200, "c" * 200, "d" * 200 + ".ext"), win_compat=True, relative_to = self.root) - self.assertEqual(fn, os.path.join(self.root, "w" * 10, "x" * 10, "y" * 9, "z" * 9, "b" * 100, "c" * 100, "d" * 7 + ".ext")) + self.assertEqual(fn, os.path.join("b" * 100, "c" * 100, "d" * 7 + ".ext")) def test_windows_node_maxlength_shortening(self): max_len = 226 remaining = 259 - (3 + 10 + 1 + max_len + 1) fn = make_short_filename(self.root, os.path.join("a" * 300, "b" * 100 + ".ext"), win_compat=True) - self.assertEqual(fn, os.path.join(self.root, "a" * max_len, "b" * (remaining - 4) + ".ext")) + self.assertEqual(fn, os.path.join("a" * max_len, "b" * (remaining - 4) + ".ext")) def test_windows_selective_shortening(self): root = self.root + "x" * (44 - 10 - 3) fn = make_short_filename(root, os.path.join( os.path.join(*["a" * 9] * 10 + ["b" * 15] * 10), "c" * 10), win_compat=True) - self.assertEqual(fn, os.path.join(root, os.path.join(*["a" * 9] * 10 + ["b" * 9] * 10), "c" * 10)) + self.assertEqual(fn, os.path.join(os.path.join(*["a" * 9] * 10 + ["b" * 9] * 10), "c" * 10)) def test_windows_shortening_not_needed(self): - fn = make_short_filename(self.root + "x" * 33, os.path.join( + root = self.root + "x" * 33 + fn = make_short_filename(root, os.path.join( os.path.join(*["a" * 9] * 20), "b" * 10), win_compat=True) - self.assertEqual(fn, os.path.join(self.root + "x" * 33, os.path.join(*["a" * 9] * 20), "b" * 10)) + self.assertEqual(fn, os.path.join(os.path.join(*["a" * 9] * 20), "b" * 10)) def test_windows_path_too_long(self): + root = self.root + "x" * 230 self.assertRaises(IOError, make_short_filename, - self.root + "x" * 230, os.path.join("a", "b", "c", "d"), win_compat=True) + root, os.path.join("a", "b", "c", "d"), win_compat=True) def test_windows_path_not_too_long(self): - fn = make_short_filename(self.root + "x" * 230, os.path.join("a", "b", "c"), win_compat=True) - self.assertEqual(fn, os.path.join(self.root + "x" * 230, "a", "b", "c")) + root = self.root + "x" * 230 + fn = make_short_filename(root, os.path.join("a", "b", "c"), win_compat=True) + self.assertEqual(fn, os.path.join("a", "b", "c")) def test_whitespace(self): fn = make_short_filename(self.root, os.path.join("a1234567890 ", " b1234567890 ")) - self.assertEqual(fn, os.path.join(self.root, "a1234567890", "b1234567890")) + self.assertEqual(fn, os.path.join("a1234567890", "b1234567890")) From 9747a46780aa08e3a2a97df167a5f83a7ec5ff8b Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Thu, 9 Jan 2014 15:54:03 +0100 Subject: [PATCH 59/59] Remove extra imports --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 306869719..134d6f916 100755 --- a/setup.py +++ b/setup.py @@ -89,8 +89,6 @@ class picard_test(Command): self.verbosity = int(self.verbosity) def run(self): - import os.path - import glob import unittest names = []