mirror of
https://github.com/fergalmoran/picard.git
synced 2026-02-21 07:04:02 +00:00
merge upstream
This commit is contained in:
57
contrib/plugins/titlecase.py
Normal file
57
contrib/plugins/titlecase.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# Copyright 2007 Javier Kohen
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation
|
||||
|
||||
import unicodedata
|
||||
|
||||
def iswbound(char):
|
||||
"""Returns whether the given character is a word boundary."""
|
||||
category = unicodedata.category(char)
|
||||
# If it's a space separator or punctuation
|
||||
return 'Zs' == category or 'Sk' == category or 'P' == category[0]
|
||||
|
||||
def utitle(string):
|
||||
"""Title-case a string using a less destructive method than str.title."""
|
||||
new_string = string[0].capitalize()
|
||||
cap = False
|
||||
for i in xrange(1, len(string)):
|
||||
s = string[i]
|
||||
# Special case apostrophe in the middle of a word.
|
||||
if u"'" == s and string[i-1].isalpha(): cap = False
|
||||
elif iswbound(s): cap = True
|
||||
elif cap and s.isalpha():
|
||||
cap = False
|
||||
s = s.capitalize()
|
||||
else: cap = False
|
||||
new_string += s
|
||||
return new_string
|
||||
|
||||
def title(string, locale="utf-8"):
|
||||
"""Title-case a string using a less destructive method than str.title."""
|
||||
if not string: return u""
|
||||
# if the string is all uppercase, lowercase it - Erich/Javier
|
||||
# Lots of Japanese songs use entirely upper-case English titles,
|
||||
# so I don't like this change... - JoeW
|
||||
#if string == string.upper(): string = string.lower()
|
||||
if not isinstance(string, unicode):
|
||||
string = string.decode(locale)
|
||||
return utitle(string)
|
||||
|
||||
|
||||
PLUGIN_NAME = "Title Case"
|
||||
PLUGIN_API_VERSIONS = ["0.9", "0.10", "0.11"]
|
||||
PLUGIN_DESCRIPTION = "Capitalize First Character In Every Word Of A Title"
|
||||
from picard.metadata import (
|
||||
register_track_metadata_processor,
|
||||
register_album_metadata_processor,
|
||||
)
|
||||
|
||||
def title_case(tagger, metadata, release, track=None):
|
||||
for name, value in metadata.rawitems():
|
||||
if name in ["title", "album", "artist"]:
|
||||
metadata[name] = [title(x) for x in value]
|
||||
|
||||
register_track_metadata_processor(title_case)
|
||||
register_album_metadata_processor(title_case)
|
||||
@@ -301,3 +301,39 @@ RELEASE_COUNTRIES = {
|
||||
u'XW': N_('[Worldwide]'),
|
||||
u'YU': N_('Yugoslavia (historical, 1918-1992)'),
|
||||
}
|
||||
|
||||
# List of available user interface languages
|
||||
UI_LANGUAGES = [
|
||||
(u'ca', u'Català', N_(u'Catalan')),
|
||||
(u'cs', u'Čeština', N_(u'Czech')),
|
||||
(u'cy', u'Cymraeg', N_(u'Welsh')),
|
||||
(u'da', u'Dansk', N_(u'Danish')),
|
||||
(u'de', u'Deutsch', N_(u'German')),
|
||||
(u'en', u'English', N_(u'English')),
|
||||
(u'en_CA', u'English (Canada)', N_(u'English (Canada)')),
|
||||
(u'en_GB', u'English (UK)', N_(u'English (UK)')),
|
||||
(u'es', u'Español', N_(u'Spanish')),
|
||||
(u'fa', u'فارسی', N_(u'Persian')),
|
||||
(u'fi', u'Suomi', N_(u'Finnish')),
|
||||
(u'fr', u'Français', N_(u'French')),
|
||||
(u'fy', u'Frysk', N_(u'Frisian')),
|
||||
(u'he', u'עברית', N_(u'Hebrew')),
|
||||
(u'hu', u'Magyar', N_(u'Hungarian')),
|
||||
(u'is', u'Íslenska', N_(u'Islandic')),
|
||||
(u'it', u'Italiano', N_(u'Italian')),
|
||||
(u'kn', u'ಕನ್ನಡ ', N_(u'Kannada')),
|
||||
(u'ko', u'한국어', N_(u'Korean')),
|
||||
(u'lt', u'Lietuvių', N_(u'Lithuanian')),
|
||||
(u'nb', u'Norsk bokmål', N_(u'Norwegian Bokmal')),
|
||||
(u'nl', u'Nederlands', N_(u'Dutch')),
|
||||
(u'pl', u'Polski', N_(u'Polish')),
|
||||
(u'pt', u'Português', N_(u'Portuguese')),
|
||||
(u'pt_BR', u'Português do Brasil', N_(u'Brazilian Portuguese')),
|
||||
(u'ro', u'Română', N_(u'Romanian')),
|
||||
(u'ru', u'Pyccĸий', N_(u'Russian')),
|
||||
(u'sco', u'Scots leid', N_(u'Scots')),
|
||||
(u'sk', u'Slovenčina', N_(u'Slovak')),
|
||||
(u'sl', u'Slovenščina', N_(u'Slovenian')),
|
||||
(u'sv', u'Svenska', N_(u'Swedish')),
|
||||
(u'zh_CN', u'中文', N_(u'Chinese')),
|
||||
]
|
||||
|
||||
@@ -52,6 +52,14 @@ class APEv2File(File):
|
||||
metadata = Metadata()
|
||||
if file.tags:
|
||||
for origname, values in file.tags.items():
|
||||
if origname.lower().startswith("cover art") and values.kind == mutagen.apev2.BINARY:
|
||||
if '\0' in values.value:
|
||||
descr, data = values.value.split('\0', 1)
|
||||
if data.startswith('\xff\xd8\xff\xe0'):
|
||||
mime = 'image/jpeg'
|
||||
else:
|
||||
mime = 'image/png'
|
||||
metadata.add_image(mime, data)
|
||||
# skip EXTERNAL and BINARY values
|
||||
if values.kind != mutagen.apev2.TEXT:
|
||||
continue
|
||||
@@ -89,6 +97,10 @@ class APEv2File(File):
|
||||
tags = mutagen.apev2.APEv2()
|
||||
if settings["clear_existing_tags"]:
|
||||
tags.clear()
|
||||
elif settings['save_images_to_tags']:
|
||||
for name, value in tags.items():
|
||||
if name.lower().startswith('cover art') and value.kind == mutagen.apev2.BINARY:
|
||||
del tags[name]
|
||||
temp = {}
|
||||
for name, value in metadata.items():
|
||||
if name.startswith("~"):
|
||||
@@ -122,6 +134,16 @@ class APEv2File(File):
|
||||
temp.setdefault(name, []).append(value)
|
||||
for name, values in temp.items():
|
||||
tags[str(name)] = values
|
||||
if settings['save_images_to_tags']:
|
||||
for mime, data in metadata.images:
|
||||
cover_filename = 'Cover Art (Front).'
|
||||
if mime == 'image/jpeg':
|
||||
cover_filename += 'jpg'
|
||||
else:
|
||||
cover_filename += 'png'
|
||||
tags['Cover Art (Front)'] = cover_filename + '\0' + data
|
||||
break # can't save more than one item with the same name
|
||||
# (mp3tags does this, but it's against the specs)
|
||||
tags.save(encode_filename(filename))
|
||||
|
||||
class MusepackFile(APEv2File):
|
||||
|
||||
@@ -49,9 +49,10 @@ def _decamelcase(text):
|
||||
return re.sub(r'([A-Z])', r' \1', text).strip()
|
||||
|
||||
|
||||
_REPLACE_MAP = {'TurntableS': 'Turntable(s)'}
|
||||
_EXTRA_ATTRS = ['Guest', 'Additional', 'Minor']
|
||||
def _parse_attributes(attrs):
|
||||
attrs = map(_decamelcase, attrs)
|
||||
attrs = [_decamelcase(_REPLACE_MAP.get(a, a)) for a in attrs]
|
||||
prefix = ' '.join([a for a in attrs if a in _EXTRA_ATTRS])
|
||||
attrs = [a for a in attrs if a not in _EXTRA_ATTRS]
|
||||
if len(attrs) > 1:
|
||||
|
||||
@@ -204,6 +204,9 @@ class Tagger(QtGui.QApplication):
|
||||
|
||||
def setup_gettext(self, localedir):
|
||||
"""Setup locales, load translations, install gettext functions."""
|
||||
if self.config.setting["ui_language"]:
|
||||
os.environ['LANGUAGE'] = ''
|
||||
os.environ['LANG'] = self.config.setting["ui_language"]
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, os.environ["LANG"])
|
||||
|
||||
@@ -17,9 +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.config import BoolOption
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from picard.config import BoolOption, TextOption
|
||||
from picard.ui.options import OptionsPage, register_options_page
|
||||
from picard.ui.ui_options_interface import Ui_InterfaceOptionsPage
|
||||
from picard.const import UI_LANGUAGES
|
||||
import operator
|
||||
|
||||
|
||||
class InterfaceOptionsPage(OptionsPage):
|
||||
@@ -34,23 +37,39 @@ class InterfaceOptionsPage(OptionsPage):
|
||||
BoolOption("setting", "toolbar_show_labels", True),
|
||||
BoolOption("setting", "toolbar_multiselect", False),
|
||||
BoolOption("setting", "use_adv_search_syntax", False),
|
||||
TextOption("setting", "ui_language", u""),
|
||||
]
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(InterfaceOptionsPage, self).__init__(parent)
|
||||
self.ui = Ui_InterfaceOptionsPage()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.ui_language.addItem(_('System default'), QtCore.QVariant(''))
|
||||
language_list = [(l[0], l[1], _(l[2])) for l in UI_LANGUAGES]
|
||||
for lang_code, native, translation in sorted(language_list, key=operator.itemgetter(2)):
|
||||
if native and native != translation:
|
||||
name = u'%s (%s)' % (translation, native)
|
||||
else:
|
||||
name = translation
|
||||
self.ui.ui_language.addItem(name, QtCore.QVariant(lang_code))
|
||||
|
||||
def load(self):
|
||||
self.ui.toolbar_show_labels.setChecked(self.config.setting["toolbar_show_labels"])
|
||||
self.ui.toolbar_multiselect.setChecked(self.config.setting["toolbar_multiselect"])
|
||||
self.ui.use_adv_search_syntax.setChecked(self.config.setting["use_adv_search_syntax"])
|
||||
current_ui_language = QtCore.QVariant(self.config.setting["ui_language"])
|
||||
self.ui.ui_language.setCurrentIndex(self.ui.ui_language.findData(current_ui_language))
|
||||
|
||||
def save(self):
|
||||
self.config.setting["toolbar_show_labels"] = self.ui.toolbar_show_labels.isChecked()
|
||||
self.config.setting["toolbar_multiselect"] = self.ui.toolbar_multiselect.isChecked()
|
||||
self.config.setting["use_adv_search_syntax"] = self.ui.use_adv_search_syntax.isChecked()
|
||||
self.tagger.window.update_toolbar_style()
|
||||
new_language = self.ui.ui_language.itemData(self.ui.ui_language.currentIndex()).toString()
|
||||
if new_language != self.config.setting["ui_language"]:
|
||||
self.config.setting["ui_language"] = self.ui.ui_language.itemData(self.ui.ui_language.currentIndex()).toString()
|
||||
dialog = QtGui.QMessageBox(QtGui.QMessageBox.Information, _('Language changed'), _('You have changed the interface language. You have to restart Picard in order for the change to take effect.'), QtGui.QMessageBox.Ok, self)
|
||||
dialog.exec_()
|
||||
|
||||
|
||||
register_options_page(InterfaceOptionsPage)
|
||||
|
||||
@@ -49,8 +49,9 @@ class MetadataOptionsPage(OptionsPage):
|
||||
self.ui.setupUi(self)
|
||||
self.connect(self.ui.va_name_default, QtCore.SIGNAL("clicked()"), self.set_va_name_default)
|
||||
self.connect(self.ui.nat_name_default, QtCore.SIGNAL("clicked()"), self.set_nat_name_default)
|
||||
self.ui.preferred_release_country.addItem(N_("None"), QtCore.QVariant(""))
|
||||
for country, name in sorted(RELEASE_COUNTRIES.items(), key=operator.itemgetter(1)):
|
||||
self.ui.preferred_release_country.addItem(_("None"), QtCore.QVariant(""))
|
||||
country_list = [(c[0], _(c[1])) for c in RELEASE_COUNTRIES.items()]
|
||||
for country, name in sorted(country_list, key=operator.itemgetter(1)):
|
||||
self.ui.preferred_release_country.addItem(name, QtCore.QVariant(country))
|
||||
|
||||
def load(self):
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'ui/options_interface.ui'
|
||||
#
|
||||
# Created: Fri Mar 28 08:55:16 2008
|
||||
# by: PyQt4 UI code generator 4.3
|
||||
# Created: Sat Jan 24 18:12:02 2009
|
||||
# by: PyQt4 UI code generator 4.4.3
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -12,32 +12,36 @@ from PyQt4 import QtCore, QtGui
|
||||
class Ui_InterfaceOptionsPage(object):
|
||||
def setupUi(self, InterfaceOptionsPage):
|
||||
InterfaceOptionsPage.setObjectName("InterfaceOptionsPage")
|
||||
InterfaceOptionsPage.resize(QtCore.QSize(QtCore.QRect(0,0,276,196).size()).expandedTo(InterfaceOptionsPage.minimumSizeHint()))
|
||||
|
||||
InterfaceOptionsPage.resize(287, 200)
|
||||
self.vboxlayout = QtGui.QVBoxLayout(InterfaceOptionsPage)
|
||||
self.vboxlayout.setObjectName("vboxlayout")
|
||||
|
||||
self.groupBox_2 = QtGui.QGroupBox(InterfaceOptionsPage)
|
||||
self.groupBox_2.setObjectName("groupBox_2")
|
||||
|
||||
self.vboxlayout1 = QtGui.QVBoxLayout(self.groupBox_2)
|
||||
self.vboxlayout1.setObjectName("vboxlayout1")
|
||||
|
||||
self.toolbar_show_labels = QtGui.QCheckBox(self.groupBox_2)
|
||||
self.toolbar_show_labels.setObjectName("toolbar_show_labels")
|
||||
self.vboxlayout1.addWidget(self.toolbar_show_labels)
|
||||
|
||||
self.toolbar_multiselect = QtGui.QCheckBox(self.groupBox_2)
|
||||
self.toolbar_multiselect.setObjectName("toolbar_multiselect")
|
||||
self.vboxlayout1.addWidget(self.toolbar_multiselect)
|
||||
|
||||
self.use_adv_search_syntax = QtGui.QCheckBox(self.groupBox_2)
|
||||
self.use_adv_search_syntax.setObjectName("use_adv_search_syntax")
|
||||
self.vboxlayout1.addWidget(self.use_adv_search_syntax)
|
||||
self.label = QtGui.QLabel(self.groupBox_2)
|
||||
self.label.setObjectName("label")
|
||||
self.vboxlayout1.addWidget(self.label)
|
||||
self.horizontalLayout = QtGui.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.ui_language = QtGui.QComboBox(self.groupBox_2)
|
||||
self.ui_language.setObjectName("ui_language")
|
||||
self.horizontalLayout.addWidget(self.ui_language)
|
||||
spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.vboxlayout1.addLayout(self.horizontalLayout)
|
||||
self.vboxlayout.addWidget(self.groupBox_2)
|
||||
|
||||
spacerItem = QtGui.QSpacerItem(220,61,QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding)
|
||||
self.vboxlayout.addItem(spacerItem)
|
||||
spacerItem1 = QtGui.QSpacerItem(220, 61, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||
self.vboxlayout.addItem(spacerItem1)
|
||||
|
||||
self.retranslateUi(InterfaceOptionsPage)
|
||||
QtCore.QMetaObject.connectSlotsByName(InterfaceOptionsPage)
|
||||
@@ -47,4 +51,5 @@ class Ui_InterfaceOptionsPage(object):
|
||||
self.toolbar_show_labels.setText(_("Show text labels under icons"))
|
||||
self.toolbar_multiselect.setText(_("Allow selection of multiple directories"))
|
||||
self.use_adv_search_syntax.setText(_("Use advanced query syntax"))
|
||||
self.label.setText(_("User interface language:"))
|
||||
|
||||
|
||||
2746
po/en_CA.po
2746
po/en_CA.po
File diff suppressed because it is too large
Load Diff
2905
po/en_GB.po
2905
po/en_GB.po
File diff suppressed because it is too large
Load Diff
2368
po/picard.pot
2368
po/picard.pot
File diff suppressed because it is too large
Load Diff
3016
po/pt_BR.po
3016
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
2622
po/zh_CN.po
2622
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>276</width>
|
||||
<height>196</height>
|
||||
<width>287</width>
|
||||
<height>200</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
@@ -37,6 +37,33 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>User interface language:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QComboBox" name="ui_language" />
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -45,7 +72,7 @@
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>220</width>
|
||||
<height>61</height>
|
||||
|
||||
Reference in New Issue
Block a user