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
diff --git a/NEWS.txt b/NEWS.txt
index 449f841f7..db44c9f11 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -16,6 +16,8 @@
* 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)
+ * Add ignore_regex option which allows one to ignore matching paths, can be set in Options > Advanced (PICARD-528)
* Added an "artists" tag to track metadata, based on the one in Jaikoz, which
contains the individual artist names from the artist credit.
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..006cb9c79
--- /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).
diff --git a/picard/__init__.py b/picard/__init__.py
index 22abb9e92..d1d6cb897 100644
--- a/picard/__init__.py
+++ b/picard/__init__.py
@@ -25,22 +25,42 @@ 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]
+class VersionError(Exception):
+ pass
+
+
+def version_to_string(version, short=False):
+ 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:
+ 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
+_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/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 2bdc2cce1..ad718dc47 100644
--- a/picard/cluster.py
+++ b/picard/cluster.py
@@ -24,7 +24,7 @@ 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
@@ -189,10 +189,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 = {}
@@ -458,7 +458,5 @@ class ClusterEngine(object):
self.idClusterIndex[match] = match0
del self.clusterBins[match1]
- return self.clusterBins
-
def can_refresh(self):
return False
diff --git a/picard/config.py b/picard/config.py
index 5a4f8c176..ed0923199 100644
--- a/picard/config.py
+++ b/picard/config.py
@@ -17,10 +17,11 @@
# 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,
- version_to_string, version_from_string, log)
+ version_to_string, version_from_string)
from picard.util import LockableObject, rot13
@@ -84,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."""
@@ -94,17 +95,15 @@ class Config(QtCore.QSettings):
else:
raise KeyError("Unknown profile '%s'" % (profilename,))
- 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)
+ to_version = version_from_string(func.__name__)
assert to_version <= PICARD_VERSION, "%r > %r !!!" % (to_version, PICARD_VERSION)
- hook = {
- 'to': to_version,
+ self._upgrade_hooks[to_version] = {
'func': func,
'args': args,
'done': False
}
- self._upgrade_hooks.append(hook)
def run_upgrade_hooks(self):
"""Executes registered functions to upgrade config version to the latest"""
@@ -118,29 +117,30 @@ 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 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
+ 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()
diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py
new file mode 100644
index 000000000..702936e16
--- /dev/null
+++ b/picard/config_upgrade.py
@@ -0,0 +1,110 @@
+# -*- 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)
+
+
+# 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
+# register it in upgrade_config()
+# 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):
+
+ 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():
+ 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)
+ cfg.register_upgrade_hook(upgrade_to_v1_3_0_dev_2)
+ cfg.run_upgrade_hooks()
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..fb225b612 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,
@@ -144,7 +142,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
@@ -281,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('.\\', '_\\')
@@ -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):
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/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
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()
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..0158272ed 100644
--- a/picard/tagger.py
+++ b/picard/tagger.py
@@ -22,11 +22,9 @@ from PyQt4 import QtGui, QtCore
import getopt
import os.path
-import re
import shutil
import signal
import sys
-from collections import deque
from functools import partial
from itertools import chain
@@ -61,16 +59,17 @@ 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.config_upgrade import upgrade_config
from picard.util import (
decode_filename,
encode_filename,
thread,
mbid_validate,
check_io_encoding,
- uniqify
+ uniqify,
+ is_hidden_path,
)
from picard.webservice import XmlWebService
@@ -127,10 +126,12 @@ class Tagger(QtGui.QApplication):
check_io_encoding()
- self._upgrade_config()
-
+ # Must be before config upgrade because upgrade dialogs need to be
+ # translated
setup_gettext(localedir, config.setting["ui_language"], log.debug)
+ upgrade_config()
+
self.xmlws = XmlWebService()
load_user_collections()
@@ -162,64 +163,6 @@ class Tagger(QtGui.QApplication):
self.nats = None
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.
- def upgrade_to_v1_0():
- def remove_va_file_naming_format(merge=True):
- if merge:
- config.setting["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"]
- ))
- config.setting.remove("va_file_naming_format")
- config.setting.remove("use_va_format")
-
- if ("va_file_naming_format" in config.setting and
- "use_va_format" in config.setting):
-
- if config.setting["use_va_format"].toBool():
- remove_va_file_naming_format()
- self.window.show_va_removal_notice()
-
- elif (config.setting["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():
- 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():
- _s = config.setting
- # 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")
-
- # 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.register_upgrade_hook("1.0.0final0", upgrade_to_v1_0)
- cfg.register_upgrade_hook("1.3.0dev2", upgrade_to_v1_3)
-
- 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:
@@ -329,9 +272,20 @@ 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:
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 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/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)
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ý
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/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:
diff --git a/picard/ui/mainwindow.py b/picard/ui/mainwindow.py
index 6d4ad1c2d..d8e004d5f 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
@@ -669,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')
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
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/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)
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/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/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()
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/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/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/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
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/picard/util/bytes2human.py b/picard/util/bytes2human.py
index bfc7ceca7..ea8fb4eb2 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
@@ -46,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)
+ 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]]
@@ -75,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
@@ -118,8 +116,3 @@ def calc_unit(number, multiple=1000):
return (sign * n, suffix)
else:
n /= multiple
-
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
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 != '':
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
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):
diff --git a/picard/webservice.py b/picard/webservice.py
index d9b17b8b8..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):
@@ -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:
diff --git a/po/picard.pot b/po/picard.pot
index 211e2ee3f..b63f5dcd7 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-05 14:00+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 ""
@@ -1461,19 +1503,23 @@ 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 ""
@@ -2084,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
@@ -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"
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 = []
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')
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"))
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)
diff --git a/test/test_versions.py b/test/test_versions.py
index 69d5a813a..a473fb771 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'
@@ -32,9 +34,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(VersionError, version_to_string, (l))
def test_version_conv_7(self):
l, s = (1, 1, 0, 'final', 0), '1.1'
@@ -51,3 +52,19 @@ 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)
+
+ 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))
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
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
+
+
+
+