Merge pull request #254 from zas/cover_art_types

Use attributes.pot from musicbrainz server to generate up-to-date cover art types and medium formats lists
This commit is contained in:
Laurent Monin
2014-04-13 10:16:32 +02:00
21 changed files with 311 additions and 494 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ locale
*.exe
.DS_Store
po/countries/countries.pot
po/attributes/attributes.pot

View File

@@ -12,3 +12,9 @@ file_filter = po/countries/<lang>.po
source_file = po/countries/countries.pot
source_lang = en
type = PO
[musicbrainz.attributes]
file_filter = po/attributes/<lang>.po
source_file = po/attributes/attributes.pot
source_lang = en
type = PO

View File

@@ -24,7 +24,6 @@
* Added "_artists_sort", "_albumartists", "_albumartists_sort" variables for scripts and plugins.
* Made Picard use the country names also used on the MusicBrainz website (PICARD-205)
* New setup.py command `get_po_files` (Retrieve po files from transifex)
* New setup.py command `update_countries` (Regenerate countries.py)
* New setup.py command `regen_pot_file` (Regenerate po/picard.pot)
* New Work tag (which for Classical music is often different from the track title) saved as ID3 TOAL tag.
* New Composer Sort Order tag (variable %composersort%).
@@ -38,6 +37,9 @@
* Show the ID3 version of the file in the Info... dialog (Ctrl-I) (PICARD-218)
* Fixed a bug where Picard crashed if a MP3 file had malformed TRCK or TPOS tags (PICARD-112)
* Add --files option to setup.py build_ui, used to force .ui to .py regeneration (PICARD-566)
* New setup.py command `update_constants` (Regenerate countries.py and attributes.py)
* Made Picard use release groups, medium formats and cover art types also used on the MusicBrainz website
* Use MusicBrainz Server translations for release groups, medium formats and cover art types

View File

@@ -22,7 +22,7 @@ import re
PICARD_APP_NAME = "Picard"
PICARD_ORG_NAME = "MusicBrainz"
PICARD_VERSION = (1, 3, 0, 'dev', 3)
PICARD_VERSION = (1, 3, 0, 'dev', 4)
class VersionError(Exception):

73
picard/attributes.py Normal file
View File

@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
# Automatically generated - don't edit.
# Use `python setup.py update_constants` to update it.
MB_ATTRIBUTES = {
u'DB:cover_art_archive.art_type/name:001': u'Front',
u'DB:cover_art_archive.art_type/name:002': u'Back',
u'DB:cover_art_archive.art_type/name:003': u'Booklet',
u'DB:cover_art_archive.art_type/name:004': u'Medium',
u'DB:cover_art_archive.art_type/name:005': u'Obi',
u'DB:cover_art_archive.art_type/name:006': u'Spine',
u'DB:cover_art_archive.art_type/name:007': u'Track',
u'DB:cover_art_archive.art_type/name:008': u'Other',
u'DB:cover_art_archive.art_type/name:009': u'Tray',
u'DB:cover_art_archive.art_type/name:010': u'Sticker',
u'DB:cover_art_archive.art_type/name:011': u'Poster',
u'DB:cover_art_archive.art_type/name:012': u'Liner',
u'DB:cover_art_archive.art_type/name:013': u'Watermark',
u'DB:medium_format/name:001': u'CD',
u'DB:medium_format/name:002': u'DVD',
u'DB:medium_format/name:003': u'SACD',
u'DB:medium_format/name:004': u'DualDisc',
u'DB:medium_format/name:005': u'LaserDisc',
u'DB:medium_format/name:006': u'MiniDisc',
u'DB:medium_format/name:007': u'Vinyl',
u'DB:medium_format/name:008': u'Cassette',
u'DB:medium_format/name:009': u'Cartridge',
u'DB:medium_format/name:010': u'Reel-to-reel',
u'DB:medium_format/name:011': u'DAT',
u'DB:medium_format/name:012': u'Digital Media',
u'DB:medium_format/name:013': u'Other',
u'DB:medium_format/name:014': u'Wax Cylinder',
u'DB:medium_format/name:015': u'Piano Roll',
u'DB:medium_format/name:016': u'DCC',
u'DB:medium_format/name:017': u'HD-DVD',
u'DB:medium_format/name:018': u'DVD-Audio',
u'DB:medium_format/name:019': u'DVD-Video',
u'DB:medium_format/name:020': u'Blu-ray',
u'DB:medium_format/name:021': u'VHS',
u'DB:medium_format/name:022': u'VCD',
u'DB:medium_format/name:023': u'SVCD',
u'DB:medium_format/name:024': u'Betamax',
u'DB:medium_format/name:025': u'HDCD',
u'DB:medium_format/name:026': u'USB Flash Drive',
u'DB:medium_format/name:027': u'slotMusic',
u'DB:medium_format/name:028': u'UMD',
u'DB:medium_format/name:029': u'7" Vinyl',
u'DB:medium_format/name:030': u'10" Vinyl',
u'DB:medium_format/name:031': u'12" Vinyl',
u'DB:medium_format/name:032': u'Videotape',
u'DB:medium_format/name:033': u'CD-R',
u'DB:medium_format/name:034': u'8cm CD',
u'DB:medium_format/name:035': u'Blu-spec CD',
u'DB:medium_format/name:036': u'SHM-CD',
u'DB:medium_format/name:037': u'HQCD',
u'DB:medium_format/name:038': u'Hybrid SACD',
u'DB:medium_format/name:039': u'CD+G',
u'DB:medium_format/name:040': u'8cm CD+G',
u'DB:release_group_primary_type/name:001': u'Album',
u'DB:release_group_primary_type/name:002': u'Single',
u'DB:release_group_primary_type/name:003': u'EP',
u'DB:release_group_primary_type/name:011': u'Other',
u'DB:release_group_primary_type/name:012': u'Broadcast',
u'DB:release_group_secondary_type/name:001': u'Compilation',
u'DB:release_group_secondary_type/name:002': u'Soundtrack',
u'DB:release_group_secondary_type/name:003': u'Spokenword',
u'DB:release_group_secondary_type/name:004': u'Interview',
u'DB:release_group_secondary_type/name:005': u'Audiobook',
u'DB:release_group_secondary_type/name:006': u'Live',
u'DB:release_group_secondary_type/name:007': u'Remix',
u'DB:release_group_secondary_type/name:008': u'DJ-mix',
u'DB:release_group_secondary_type/name:009': u'Mixtape/Street',
}

View File

@@ -118,10 +118,29 @@ def upgrade_to_v1_3_0_dev_3():
_s[opt] = _s.raw_value(opt).split(sep)
def upgrade_to_v1_3_0_dev_4():
"""Option "release_type_scores" is now a list of tuples
"""
def load_release_type_scores(setting):
scores = []
values = setting.split()
for i in range(0, len(values), 2):
try:
score = float(values[i + 1])
except IndexError:
score = 0.0
scores.append((values[i], score))
return scores
opt = "release_type_scores"
_s[opt] = load_release_type_scores(_s.raw_value(opt))
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.register_upgrade_hook(upgrade_to_v1_3_0_dev_3)
cfg.register_upgrade_hook(upgrade_to_v1_3_0_dev_4)
cfg.run_upgrade_hooks(log.debug)

View File

@@ -63,42 +63,17 @@ PICARD_URLS = {
VARIOUS_ARTISTS_ID = '89ad4ac3-39f7-470e-963a-56509c546377'
# Release formats
RELEASE_FORMATS = {
u'CD': N_('CD'),
u'CD-R': N_('CD-R'),
u'HDCD': N_('HDCD'),
u'8cm CD': N_('8cm CD'),
u'Vinyl': N_('Vinyl'),
u'7" Vinyl': N_('7" Vinyl'),
u'10" Vinyl': N_('10" Vinyl'),
u'12" Vinyl': N_('12" Vinyl'),
u'Digital Media': N_('Digital Media'),
u'USB Flash Drive': N_('USB Flash Drive'),
u'slotMusic': N_('slotMusic'),
u'Cassette': N_('Cassette'),
u'DVD': N_('DVD'),
u'DVD-Audio': N_('DVD-Audio'),
u'DVD-Video': N_('DVD-Video'),
u'SACD': N_('SACD'),
u'DualDisc': N_('DualDisc'),
u'MiniDisc': N_('MiniDisc'),
u'Blu-ray': N_('Blu-ray'),
u'HD-DVD': N_('HD-DVD'),
u'Videotape': N_('Videotape'),
u'VHS': N_('VHS'),
u'Betamax': N_('Betamax'),
u'VCD': N_('VCD'),
u'SVCD': N_('SVCD'),
u'UMD': N_('UMD'),
u'Other': N_('Other'),
u'LaserDisc': N_('LaserDisc'),
u'Cartridge': N_('Cartridge'),
u'Reel-to-reel': N_('Reel-to-reel'),
u'DAT': N_('DAT'),
u'Wax Cylinder': N_('Wax Cylinder'),
u'Piano Roll': N_('Piano Roll'),
u'DCC': N_('DCC')
}
from picard.attributes import MB_ATTRIBUTES
RELEASE_FORMATS = {}
RELEASE_PRIMARY_GROUPS = {}
RELEASE_SECONDARY_GROUPS = {}
for k, v in MB_ATTRIBUTES.iteritems():
if k.startswith(u'DB:medium_format/name:'):
RELEASE_FORMATS[v] = v
elif k.startswith(u'DB:release_group_primary_type/name:'):
RELEASE_PRIMARY_GROUPS[v] = v
elif k.startswith(u'DB:release_group_secondary_type/name:'):
RELEASE_SECONDARY_GROUPS[v] = v
# Release countries
from picard.countries import RELEASE_COUNTRIES

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Automatically generated - don't edit.
# Use `python setup.py update_countries` to update it.
# Use `python setup.py update_constants` to update it.
RELEASE_COUNTRIES = {
u'AD': u'Andorra',

View File

@@ -17,19 +17,14 @@
# 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.attributes import MB_ATTRIBUTES
# list of types from http://musicbrainz.org/doc/Cover_Art/Types
# order of declaration is preserved in selection box
CAA_TYPES = [
{'name': "front", 'title': N_("Front")},
{'name': "back", 'title': N_("Back")},
{'name': "booklet", 'title': N_("Booklet")},
{'name': "medium", 'title': N_("Medium")},
{'name': "tray", 'title': N_("Tray")},
{'name': "obi", 'title': N_("Obi")},
{'name': "spine", 'title': N_("Spine")},
{'name': "track", 'title': N_("Track")},
{'name': "sticker", 'title': N_("Sticker")},
{'name': "other", 'title': N_("Other")},
{'name': "unknown", 'title': N_("Unknown")}, # pseudo type, used for the no type case
]
CAA_TYPES = []
for k, v in sorted(MB_ATTRIBUTES.items(), key=lambda (k,v): k):
if k.startswith(u'DB:cover_art_archive.art_type/name:'):
CAA_TYPES.append({'name': v.lower(), 'title': v})
# pseudo type, used for the no type case
CAA_TYPES.append({'name': "unknown", 'title': N_(u"Unknown")})

View File

@@ -72,6 +72,9 @@ def setup_gettext(localedir, ui_language=None, logger=None):
logger("Loading gettext translation (picard-countries), localedir=%r", localedir)
trans_countries = gettext.translation("picard-countries", localedir)
_ugettext_countries = trans_countries.ugettext
logger("Loading gettext translation (picard-attributes), localedir=%r", localedir)
trans_attributes = gettext.translation("picard-attributes", localedir)
_ugettext_attributes = trans_attributes.ugettext
except IOError as e:
logger(e)
__builtin__.__dict__['_'] = lambda a: a
@@ -85,10 +88,38 @@ def setup_gettext(localedir, ui_language=None, logger=None):
def _ugettext_countries(msg):
return msg
def _ugettext_attributes(msg):
return msg
__builtin__.__dict__['ungettext'] = _ungettext
__builtin__.__dict__['ugettext_countries'] = _ugettext_countries
__builtin__.__dict__['ugettext_attributes'] = _ugettext_attributes
logger("_ = %r", _)
logger("N_ = %r", N_)
logger("ungettext = %r", ungettext)
logger("ugettext_countries = %r", ugettext_countries)
logger("ugettext_attributes = %r", ugettext_attributes)
# Workaround for po files with msgctxt which isn't supported by current python
# gettext
# msgctxt are used within attributes.po, and ugettext is failing to translate
# strings due to that
# This workaround is a hack until we get proper msgctxt support
_CONTEXT_SEPARATOR = "\x04"
def ugettext_ctxt(ugettext_, message, context=None):
if context is None:
return ugettext_(message)
msg_with_ctxt = u"%s%s%s" % (context, _CONTEXT_SEPARATOR, message)
translated = ugettext_(msg_with_ctxt)
if _CONTEXT_SEPARATOR in translated:
# no translation found, return original message
return message
return translated
def ugettext_attr(message, context=None):
"""Translate MB attributes, depending on context"""
return ugettext_ctxt(ugettext_attributes, message, context)

View File

@@ -32,7 +32,6 @@ from picard.plugin import ExtensionPoint
from picard.similarity import similarity2
from picard.util import (
encode_filename,
load_release_type_scores,
mimetype as mime,
replace_non_ascii,
replace_win32_incompat,
@@ -288,7 +287,7 @@ class Metadata(dict):
parts.append((score, weights["format"]))
if "releasetype" in weights:
type_scores = load_release_type_scores(config.setting["release_type_scores"])
type_scores = dict(config.setting["release_type_scores"])
if 'release_group' in release.children and 'type' in release.release_group[0].attribs:
release_type = release.release_group[0].type
score = type_scores.get(release_type, type_scores.get('Other', 0.5))

View File

@@ -22,6 +22,7 @@ from picard import config
from picard.ui.options import OptionsPage, register_options_page
from picard.ui.ui_options_cover import Ui_CoverOptionsPage
from picard.coverartarchive import CAA_TYPES
from picard.i18n import ugettext_attr
class CAATypesSelector(object):
@@ -39,7 +40,11 @@ class CAATypesSelector(object):
def _add_item(self, typ, enabled=False):
item = QtGui.QListWidgetItem(self.widget)
item.setText(typ['title'])
if typ['name'] == 'unknown':
title = _(typ['title'])
else:
title = ugettext_attr(typ['title'], u"cover_art_type")
item.setText(title)
tooltip = u"CAA: %(name)s" % typ
item.setToolTip(tooltip)
if enabled:

View File

@@ -21,10 +21,64 @@ from operator import itemgetter
from locale import strcoll
from PyQt4 import QtCore, QtGui
from picard import config
from picard.util import load_release_type_scores, save_release_type_scores
from picard.ui.options import OptionsPage, register_options_page
from picard.ui.ui_options_releases import Ui_ReleasesOptionsPage
from picard.const import RELEASE_COUNTRIES, RELEASE_FORMATS
from picard.const import (RELEASE_COUNTRIES,
RELEASE_FORMATS,
RELEASE_PRIMARY_GROUPS,
RELEASE_SECONDARY_GROUPS)
from picard.i18n import ugettext_attr
_DEFAULT_SCORE = 0.5
_release_type_scores = [(g, _DEFAULT_SCORE) for g in RELEASE_PRIMARY_GROUPS.keys() + RELEASE_SECONDARY_GROUPS.keys()]
class ReleaseTypeScore:
def __init__(self, group, layout, label, cell):
row, column = cell # it uses 2 cells (r,c and r,c+1)
self.group = group
self.layout = layout
self.label = QtGui.QLabel(self.group)
self.label.setText(label)
self.layout.addWidget(self.label, row, column, 1, 1)
self.slider = QtGui.QSlider(self.group)
self.slider.setMaximum(100)
self.slider.setOrientation(QtCore.Qt.Horizontal)
self.layout.addWidget(self.slider, row, column + 1, 1, 1)
self.reset()
def setValue(self, value):
self.slider.setValue(int(value * 100))
def value(self):
return float(self.slider.value()) / 100.0
def reset(self):
self.setValue(_DEFAULT_SCORE)
class RowColIter:
def __init__(self, max_cells, max_cols=6, step=2):
assert(max_cols % step == 0)
self.step = step
self.cols = max_cols
self.rows = int((max_cells - 1) / (self.cols / step)) + 1
self.current = (-1, 0)
def __iter__(self):
return self
def next(self):
row, col = self.current
row += 1
if row == self.rows:
col += self.step
row = 0
self.current = (row, col)
return self.current
class ReleasesOptionsPage(OptionsPage):
@@ -36,29 +90,43 @@ class ReleasesOptionsPage(OptionsPage):
ACTIVE = True
options = [
config.TextOption("setting", "release_type_scores", "Album 0.5 Single 0.5 EP 0.5 Compilation 0.5 Soundtrack 0.5 Spokenword 0.5 Interview 0.5 Audiobook 0.5 Live 0.5 Remix 0.5 Other 0.5"),
config.ListOption("setting", "release_type_scores", _release_type_scores),
config.ListOption("setting", "preferred_release_countries", []),
config.ListOption("setting", "preferred_release_formats", []),
]
_release_type_sliders = {}
def __init__(self, parent=None):
super(ReleasesOptionsPage, self).__init__(parent)
self.ui = Ui_ReleasesOptionsPage()
self.ui.setupUi(self)
self.ui.reset_preferred_types_btn.clicked.connect(self.reset_preferred_types)
self._release_type_sliders["Album"] = self.ui.prefer_album_score
self._release_type_sliders["Single"] = self.ui.prefer_single_score
self._release_type_sliders["EP"] = self.ui.prefer_ep_score
self._release_type_sliders["Compilation"] = self.ui.prefer_compilation_score
self._release_type_sliders["Soundtrack"] = self.ui.prefer_soundtrack_score
self._release_type_sliders["Spokenword"] = self.ui.prefer_spokenword_score
self._release_type_sliders["Interview"] = self.ui.prefer_interview_score
self._release_type_sliders["Audiobook"] = self.ui.prefer_audiobook_score
self._release_type_sliders["Live"] = self.ui.prefer_live_score
self._release_type_sliders["Remix"] = self.ui.prefer_remix_score
self._release_type_sliders["Other"] = self.ui.prefer_other_score
self._release_type_sliders = {}
def add_slider(name, griditer, context):
label = ugettext_attr(name, context)
self._release_type_sliders[name] = \
ReleaseTypeScore(self.ui.type_group,
self.ui.gridLayout,
label,
griditer.next())
griditer = RowColIter(len(RELEASE_PRIMARY_GROUPS) +
len(RELEASE_SECONDARY_GROUPS) + 1) # +1 for Reset button
for name in RELEASE_PRIMARY_GROUPS:
add_slider(name, griditer, context=u'release_group_primary_type')
for name in RELEASE_SECONDARY_GROUPS:
add_slider(name, griditer, context=u'release_group_secondary_type')
self.reset_preferred_types_btn = QtGui.QPushButton(self.ui.type_group)
self.reset_preferred_types_btn.setText(_("Reset all"))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.reset_preferred_types_btn.sizePolicy().hasHeightForWidth())
self.reset_preferred_types_btn.setSizePolicy(sizePolicy)
r, c = griditer.next()
self.ui.gridLayout.addWidget(self.reset_preferred_types_btn, r, c, 1, 2)
self.reset_preferred_types_btn.clicked.connect(self.reset_preferred_types)
self.ui.add_countries.clicked.connect(self.add_preferred_countries)
self.ui.remove_countries.clicked.connect(self.remove_preferred_countries)
@@ -70,9 +138,10 @@ class ReleasesOptionsPage(OptionsPage):
self.ui.preferred_format_list.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
def load(self):
scores = load_release_type_scores(config.setting["release_type_scores"])
scores = dict(config.setting["release_type_scores"])
for (release_type, release_type_slider) in self._release_type_sliders.iteritems():
release_type_slider.setValue(int(scores.get(release_type, 0.5) * 100))
release_type_slider.setValue(scores.get(release_type,
_DEFAULT_SCORE))
self._load_list_items("preferred_release_countries", RELEASE_COUNTRIES,
self.ui.country_list, self.ui.preferred_country_list)
@@ -80,17 +149,17 @@ class ReleasesOptionsPage(OptionsPage):
self.ui.format_list, self.ui.preferred_format_list)
def save(self):
scores = {}
scores = []
for (release_type, release_type_slider) in self._release_type_sliders.iteritems():
scores[release_type] = float(release_type_slider.value()) / 100.0
config.setting["release_type_scores"] = save_release_type_scores(scores)
scores.append((release_type, release_type_slider.value()))
config.setting["release_type_scores"] = scores
self._save_list_items("preferred_release_countries", self.ui.preferred_country_list)
self._save_list_items("preferred_release_formats", self.ui.preferred_format_list)
def reset_preferred_types(self):
for release_type_slider in self._release_type_sliders.values():
release_type_slider.setValue(50)
release_type_slider.reset()
def add_preferred_countries(self):
self._move_selected_items(self.ui.country_list, self.ui.preferred_country_list)
@@ -114,7 +183,11 @@ class ReleasesOptionsPage(OptionsPage):
def _load_list_items(self, setting, source, list1, list2):
if setting == "preferred_release_countries":
source_list = [(c[0], ugettext_countries(c[1])) for c in source.items()]
source_list = [(c[0], ugettext_countries(c[1])) for c in
source.items()]
elif setting == "preferred_release_formats":
source_list = [(c[0], ugettext_attr(c[1], u"medium_format")) for c
in source.items()]
else:
source_list = [(c[0], _(c[1])) for c in source.items()]
source_list.sort(key=itemgetter(1), cmp=strcoll)

View File

@@ -83,12 +83,11 @@ class Ui_CoverOptionsPage(object):
self.label_2.setObjectName(_fromUtf8("label_2"))
self.verticalLayout_3.addWidget(self.label_2)
self.caa_types_selector_1 = QtGui.QListWidget(self.gb_caa)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Preferred)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.caa_types_selector_1.sizePolicy().hasHeightForWidth())
self.caa_types_selector_1.setSizePolicy(sizePolicy)
self.caa_types_selector_1.setMaximumSize(QtCore.QSize(16777215, 80))
self.caa_types_selector_1.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.caa_types_selector_1.setTabKeyNavigation(True)
self.caa_types_selector_1.setProperty("showDropIndicator", False)

View File

@@ -21,113 +21,6 @@ class Ui_ReleasesOptionsPage(object):
self.gridLayout = QtGui.QGridLayout(self.type_group)
self.gridLayout.setVerticalSpacing(6)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.label = QtGui.QLabel(self.type_group)
self.label.setObjectName(_fromUtf8("label"))
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.prefer_album_score = QtGui.QSlider(self.type_group)
self.prefer_album_score.setMaximum(100)
self.prefer_album_score.setProperty("value", 50)
self.prefer_album_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_album_score.setObjectName(_fromUtf8("prefer_album_score"))
self.gridLayout.addWidget(self.prefer_album_score, 0, 1, 1, 1)
self.label_2 = QtGui.QLabel(self.type_group)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.gridLayout.addWidget(self.label_2, 0, 2, 1, 1)
self.prefer_single_score = QtGui.QSlider(self.type_group)
self.prefer_single_score.setMaximum(100)
self.prefer_single_score.setProperty("value", 50)
self.prefer_single_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_single_score.setObjectName(_fromUtf8("prefer_single_score"))
self.gridLayout.addWidget(self.prefer_single_score, 0, 3, 1, 1)
self.label_3 = QtGui.QLabel(self.type_group)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.gridLayout.addWidget(self.label_3, 0, 4, 1, 1)
self.prefer_ep_score = QtGui.QSlider(self.type_group)
self.prefer_ep_score.setMaximum(100)
self.prefer_ep_score.setProperty("value", 50)
self.prefer_ep_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_ep_score.setObjectName(_fromUtf8("prefer_ep_score"))
self.gridLayout.addWidget(self.prefer_ep_score, 0, 5, 1, 1)
self.label_7 = QtGui.QLabel(self.type_group)
self.label_7.setObjectName(_fromUtf8("label_7"))
self.gridLayout.addWidget(self.label_7, 1, 0, 1, 1)
self.prefer_compilation_score = QtGui.QSlider(self.type_group)
self.prefer_compilation_score.setMaximum(100)
self.prefer_compilation_score.setProperty("value", 50)
self.prefer_compilation_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_compilation_score.setObjectName(_fromUtf8("prefer_compilation_score"))
self.gridLayout.addWidget(self.prefer_compilation_score, 1, 1, 1, 1)
self.label_8 = QtGui.QLabel(self.type_group)
self.label_8.setObjectName(_fromUtf8("label_8"))
self.gridLayout.addWidget(self.label_8, 1, 2, 1, 1)
self.prefer_soundtrack_score = QtGui.QSlider(self.type_group)
self.prefer_soundtrack_score.setMaximum(100)
self.prefer_soundtrack_score.setProperty("value", 50)
self.prefer_soundtrack_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_soundtrack_score.setObjectName(_fromUtf8("prefer_soundtrack_score"))
self.gridLayout.addWidget(self.prefer_soundtrack_score, 1, 3, 1, 1)
self.label_9 = QtGui.QLabel(self.type_group)
self.label_9.setObjectName(_fromUtf8("label_9"))
self.gridLayout.addWidget(self.label_9, 1, 4, 1, 1)
self.prefer_spokenword_score = QtGui.QSlider(self.type_group)
self.prefer_spokenword_score.setMaximum(100)
self.prefer_spokenword_score.setProperty("value", 50)
self.prefer_spokenword_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_spokenword_score.setObjectName(_fromUtf8("prefer_spokenword_score"))
self.gridLayout.addWidget(self.prefer_spokenword_score, 1, 5, 1, 1)
self.label_10 = QtGui.QLabel(self.type_group)
self.label_10.setObjectName(_fromUtf8("label_10"))
self.gridLayout.addWidget(self.label_10, 2, 0, 1, 1)
self.prefer_interview_score = QtGui.QSlider(self.type_group)
self.prefer_interview_score.setMaximum(100)
self.prefer_interview_score.setProperty("value", 50)
self.prefer_interview_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_interview_score.setObjectName(_fromUtf8("prefer_interview_score"))
self.gridLayout.addWidget(self.prefer_interview_score, 2, 1, 1, 1)
self.label_11 = QtGui.QLabel(self.type_group)
self.label_11.setObjectName(_fromUtf8("label_11"))
self.gridLayout.addWidget(self.label_11, 2, 2, 1, 1)
self.prefer_audiobook_score = QtGui.QSlider(self.type_group)
self.prefer_audiobook_score.setMaximum(100)
self.prefer_audiobook_score.setProperty("value", 50)
self.prefer_audiobook_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_audiobook_score.setObjectName(_fromUtf8("prefer_audiobook_score"))
self.gridLayout.addWidget(self.prefer_audiobook_score, 2, 3, 1, 1)
self.label_12 = QtGui.QLabel(self.type_group)
self.label_12.setObjectName(_fromUtf8("label_12"))
self.gridLayout.addWidget(self.label_12, 2, 4, 1, 1)
self.prefer_live_score = QtGui.QSlider(self.type_group)
self.prefer_live_score.setMaximum(100)
self.prefer_live_score.setProperty("value", 50)
self.prefer_live_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_live_score.setObjectName(_fromUtf8("prefer_live_score"))
self.gridLayout.addWidget(self.prefer_live_score, 2, 5, 1, 1)
self.label_13 = QtGui.QLabel(self.type_group)
self.label_13.setObjectName(_fromUtf8("label_13"))
self.gridLayout.addWidget(self.label_13, 3, 0, 1, 1)
self.prefer_remix_score = QtGui.QSlider(self.type_group)
self.prefer_remix_score.setMaximum(100)
self.prefer_remix_score.setProperty("value", 50)
self.prefer_remix_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_remix_score.setObjectName(_fromUtf8("prefer_remix_score"))
self.gridLayout.addWidget(self.prefer_remix_score, 3, 1, 1, 1)
self.label_14 = QtGui.QLabel(self.type_group)
self.label_14.setObjectName(_fromUtf8("label_14"))
self.gridLayout.addWidget(self.label_14, 3, 2, 1, 1)
self.prefer_other_score = QtGui.QSlider(self.type_group)
self.prefer_other_score.setMaximum(100)
self.prefer_other_score.setSliderPosition(50)
self.prefer_other_score.setOrientation(QtCore.Qt.Horizontal)
self.prefer_other_score.setObjectName(_fromUtf8("prefer_other_score"))
self.gridLayout.addWidget(self.prefer_other_score, 3, 3, 1, 1)
self.reset_preferred_types_btn = QtGui.QPushButton(self.type_group)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.reset_preferred_types_btn.sizePolicy().hasHeightForWidth())
self.reset_preferred_types_btn.setSizePolicy(sizePolicy)
self.reset_preferred_types_btn.setObjectName(_fromUtf8("reset_preferred_types_btn"))
self.gridLayout.addWidget(self.reset_preferred_types_btn, 4, 5, 1, 1)
self.verticalLayout_3.addWidget(self.type_group)
self.country_group = QtGui.QGroupBox(ReleasesOptionsPage)
self.country_group.setObjectName(_fromUtf8("country_group"))
@@ -209,18 +102,6 @@ class Ui_ReleasesOptionsPage(object):
def retranslateUi(self, ReleasesOptionsPage):
self.type_group.setTitle(_("Preferred release types"))
self.label.setText(_("Album"))
self.label_2.setText(_("Single"))
self.label_3.setText(_("EP"))
self.label_7.setText(_("Compilation"))
self.label_8.setText(_("Soundtrack"))
self.label_9.setText(_("Spokenword"))
self.label_10.setText(_("Interview"))
self.label_11.setText(_("Audiobook"))
self.label_12.setText(_("Live"))
self.label_13.setText(_("Remix"))
self.label_14.setText(_("Other"))
self.reset_preferred_types_btn.setText(_("Reset all"))
self.country_group.setTitle(_("Preferred release countries"))
self.add_countries.setText(_(">"))
self.remove_countries.setText(_("<"))

View File

@@ -267,22 +267,6 @@ def rot13(input):
return u''.join(unichr(rot_13.encoding_map.get(ord(c), ord(c))) for c in input)
def load_release_type_scores(setting):
scores = {}
values = setting.split()
for i in range(0, len(values), 2):
try:
score = float(values[i + 1])
except IndexError:
score = 0.0
scores[values[i]] = score
return scores
def save_release_type_scores(scores):
return " ".join(["%s %.2f" % v for v in scores.iteritems()])
def parse_amazon_url(url):
"""Extract host and asin from an amazon url.
It returns a dict with host and asin keys on success, None else

0
po/attributes/.gitignore vendored Normal file
View File

View File

@@ -424,8 +424,8 @@ def _get_option_name(obj):
raise Exception("No such command class")
class picard_update_countries(Command):
description = "Regenerate countries.py"
class picard_update_constants(Command):
description = "Regenerate attributes.py and countries.py"
user_options = [
('skip-pull', None, "skip the tx pull steps"),
]
@@ -443,22 +443,22 @@ class picard_update_countries(Command):
from babel.messages import pofile
countries = dict()
if not self.skip_pull:
txpull_cmd = [
tx_executable,
'pull',
'--force',
'--resource=musicbrainz.countries',
'--resource=musicbrainz.attributes,musicbrainz.countries',
'--source',
'--language=none',
]
self.spawn(txpull_cmd)
potfile = os.path.join('po', 'countries', 'countries.pot')
countries = dict()
countries_potfile = os.path.join('po', 'countries', 'countries.pot')
isocode_comment = u'iso.code:'
with open(potfile, 'rb') as f:
log.info('Parsing %s' % potfile)
with open(countries_potfile, 'rb') as f:
log.info('Parsing %s' % countries_potfile)
po = pofile.read_po(f)
for message in po:
if not message.id or not isinstance(message.id, unicode):
@@ -472,6 +472,28 @@ class picard_update_countries(Command):
else:
sys.exit('Failed to extract any country code/name !')
attributes = dict()
attributes_potfile = os.path.join('po', 'attributes', 'attributes.pot')
extract_attributes = (
u'DB:cover_art_archive.art_type/name',
u'DB:medium_format/name',
u'DB:release_group_primary_type/name',
u'DB:release_group_secondary_type/name',
)
with open(attributes_potfile, 'rb') as f:
log.info('Parsing %s' % attributes_potfile)
po = pofile.read_po(f)
for message in po:
if not message.id or not isinstance(message.id, unicode):
continue
for loc, pos in message.locations:
if loc in extract_attributes:
attributes[u"%s:%03d" % (loc, pos)] = message.id
if attributes:
self.attributes_py_file(attributes)
else:
sys.exit('Failed to extract any attribute !')
def countries_py_file(self, countries):
header = (u"# -*- coding: utf-8 -*-\n"
u"# Automatically generated - don't edit.\n"
@@ -492,6 +514,26 @@ class picard_update_countries(Command):
log.info("%s was rewritten (%d countries)" % (filename,
len(countries)))
def attributes_py_file(self, attributes):
header = (u"# -*- coding: utf-8 -*-\n"
u"# Automatically generated - don't edit.\n"
u"# Use `python setup.py {option}` to update it.\n"
u"\n"
u"MB_ATTRIBUTES = {{\n")
line = u" u'{key}': u'{value}',\n"
footer = u"}}\n"
filename = os.path.join('picard', 'attributes.py')
with open(filename, 'w') as attributes_py:
def write_utf8(s, **kwargs):
attributes_py.write(s.format(**kwargs).encode('utf-8'))
write_utf8(header, option=_get_option_name(self))
for key, value in sorted(attributes.items(), key=lambda (k,v): k):
write_utf8(line, key=key, value=value.replace("'", "\\'"))
write_utf8(footer)
log.info("%s was rewritten (%d attributes)" % (filename,
len(attributes)))
def cflags_to_include_dirs(cflags):
cflags = cflags.split()
@@ -507,6 +549,7 @@ def _picard_get_locale_files():
path_domain = {
'po': 'picard',
os.path.join('po', 'countries'): 'picard-countries',
os.path.join('po', 'attributes'): 'picard-attributes',
}
for path, domain in path_domain.iteritems():
for filepath in glob.glob(os.path.join(path, '*.po')):
@@ -537,7 +580,7 @@ args2 = {
'clean_ui': picard_clean_ui,
'install': picard_install,
'install_locales': picard_install_locales,
'update_countries': picard_update_countries,
'update_constants': picard_update_constants,
'get_po_files': picard_get_po_files,
'regen_pot_file': picard_regen_pot_file,
},

View File

@@ -92,36 +92,6 @@ class FormatTimeTest(unittest.TestCase):
self.assertEqual("2:59", util.format_time(179499))
class LoadReleaseTypeScoresTest(unittest.TestCase):
def test_valid(self):
release_type_score_config = "Album 1.0 Single 0.5 EP 0.5 Compilation 0.5 Soundtrack 0.5 Spokenword 0.5 Interview 0.2 Audiobook 0.0 Live 0.5 Remix 0.4 Other 0.0"
release_type_scores = util.load_release_type_scores(release_type_score_config)
self.assertEqual(1.0, release_type_scores["Album"])
self.assertEqual(0.5, release_type_scores["Single"])
self.assertEqual(0.2, release_type_scores["Interview"])
self.assertEqual(0.0, release_type_scores["Audiobook"])
self.assertEqual(0.4, release_type_scores["Remix"])
def test_invalid(self):
release_type_score_config = "Album 1.0 Other"
release_type_scores = util.load_release_type_scores(release_type_score_config)
self.assertEqual(1.0, release_type_scores["Album"])
self.assertEqual(0.0, release_type_scores["Other"])
class SaveReleaseTypeScoresTest(unittest.TestCase):
def test(self):
expected = "Album 1.00 Single 0.50 Other 0.00"
scores = {"Album": 1.0, "Single": 0.5, "Other": 0.0}
saved_scores = util.save_release_type_scores(scores)
self.assertTrue("Album 1.00" in saved_scores)
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):

View File

@@ -162,17 +162,11 @@
<item>
<widget class="QListWidget" name="caa_types_selector_1">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>

View File

@@ -20,239 +20,6 @@
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Album</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="prefer_album_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Single</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSlider" name="prefer_single_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_3">
<property name="text">
<string>EP</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QSlider" name="prefer_ep_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Compilation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="prefer_compilation_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Soundtrack</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QSlider" name="prefer_soundtrack_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Spokenword</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QSlider" name="prefer_spokenword_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Interview</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSlider" name="prefer_interview_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Audiobook</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QSlider" name="prefer_audiobook_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Live</string>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QSlider" name="prefer_live_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Remix</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSlider" name="prefer_remix_score">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Other</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QSlider" name="prefer_other_score">
<property name="maximum">
<number>100</number>
</property>
<property name="sliderPosition">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="5">
<widget class="QPushButton" name="reset_preferred_types_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Reset all</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>