mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-03 15:13:57 +00:00
Translate foreign artist names.
This commit is contained in:
@@ -28,6 +28,7 @@ from picard.metadata import Metadata
|
||||
from picard.dataobj import DataObject
|
||||
from picard.track import Track
|
||||
from picard.artist import Artist
|
||||
from picard.util import translate_artist
|
||||
|
||||
class AlbumLoadError(Exception):
|
||||
pass
|
||||
@@ -81,6 +82,8 @@ class Album(DataObject):
|
||||
except WebServiceError, e:
|
||||
self.hasLoadError = True
|
||||
raise AlbumLoadError, e
|
||||
|
||||
translate = self.config.setting["translate_artist_names"]
|
||||
|
||||
self.lock()
|
||||
|
||||
@@ -88,8 +91,11 @@ class Album(DataObject):
|
||||
self.metadata["album"] = release.title
|
||||
self.metadata["artist"] = release.artist.name
|
||||
self.metadata["artist_sortname"] = release.artist.sortName
|
||||
self.metadata["albumartist"] = release.artist.name
|
||||
self.metadata["albumartist_sortname"] = release.artist.sortName
|
||||
if translate:
|
||||
self.metadata["artist"] = translate_artist(
|
||||
self.metadata["artist"], self.metadata["artist_sortname"])
|
||||
self.metadata["albumartist"] = self.metadata["artist"]
|
||||
self.metadata["albumartist_sortname"] = self.metadata["artist_sortname"]
|
||||
self.metadata["musicbrainz_albumid"] = extractUuid(release.id)
|
||||
self.metadata["musicbrainz_artistid"] = extractUuid(release.artist.id)
|
||||
self.metadata["musicbrainz_albumartistid"] = \
|
||||
@@ -149,27 +155,33 @@ class Album(DataObject):
|
||||
script = None
|
||||
|
||||
self.name = release.title
|
||||
self.artist = Artist(release.artist.id, release.artist.name)
|
||||
self.artist = Artist(self.metadata["musicbrainz_artistid"],
|
||||
self.metadata["artist"])
|
||||
|
||||
self.duration = 0
|
||||
self.tracks = []
|
||||
tracknum = 1
|
||||
for track in release.tracks:
|
||||
if track.artist:
|
||||
artist = Artist(extractUuid(track.artist.id),
|
||||
track.artist.name)
|
||||
artist_id = extractUuid(track.artist.id)
|
||||
artist_name = track.artist.name
|
||||
artist_sortname = track.artist.sortName
|
||||
if translate:
|
||||
artist_name = translate_artist(artist_name, artist_sortname)
|
||||
else:
|
||||
artist = Artist(extractUuid(release.artist.id),
|
||||
release.artist.name)
|
||||
tr = Track(extractUuid(track.id), track.title, artist, self)
|
||||
artist_id = self.metadata["musicbrainz_artistid"]
|
||||
artist_name = self.metadata["artist"]
|
||||
artist_sortname = self.metadata["artist_sortname"]
|
||||
print artist_name
|
||||
tr = Track(extractUuid(track.id), track.title,
|
||||
Artist(artist_id, artist_name), self)
|
||||
tr.duration = track.duration or 0
|
||||
tr.metadata.copy(self.metadata)
|
||||
tr.metadata["title"] = track.title
|
||||
if track.artist:
|
||||
tr.metadata["artist"] = artist.name
|
||||
tr.metadata["artist_sortname"] = track.artist.sortName
|
||||
tr.metadata["musicbrainz_artistid"] = extractUuid(artist.id)
|
||||
tr.metadata["musicbrainz_trackid"] = extractUuid(track.id)
|
||||
tr.metadata["artist"] = artist_name
|
||||
tr.metadata["artist_sortname"] = artist_sortname
|
||||
tr.metadata["musicbrainz_artistid"] = artist_id
|
||||
tr.metadata["musicbrainz_trackid"] = tr.id
|
||||
tr.metadata["tracknumber"] = str(tracknum)
|
||||
tr.metadata["~#length"] = tr.duration
|
||||
# Metadata processor plugins
|
||||
|
||||
@@ -26,6 +26,7 @@ from picard.ui.options import (
|
||||
advanced,
|
||||
cdlookup,
|
||||
general,
|
||||
metadata,
|
||||
naming,
|
||||
proxy,
|
||||
scripting,
|
||||
|
||||
47
picard/ui/options/metadata.py
Normal file
47
picard/ui/options/metadata.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
# Copyright (C) 2006 Lukáš Lalinský
|
||||
#
|
||||
# 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 QtCore, QtGui
|
||||
from picard.api import IOptionsPage
|
||||
from picard.component import Component, implements
|
||||
from picard.config import BoolOption, TextOption
|
||||
|
||||
class MetadataOptionsPage(Component):
|
||||
|
||||
implements(IOptionsPage)
|
||||
|
||||
options = [
|
||||
BoolOption("setting", "translate_artist_names", False),
|
||||
]
|
||||
|
||||
def get_page_info(self):
|
||||
return _("Metadata"), "metadata", None, 20
|
||||
|
||||
def get_page_widget(self, parent=None):
|
||||
self.widget = QtGui.QWidget(parent)
|
||||
from picard.ui.ui_options_metadata import Ui_Form
|
||||
self.ui = Ui_Form()
|
||||
self.ui.setupUi(self.widget)
|
||||
self.ui.translate_artist_names.setChecked(
|
||||
self.config.setting["translate_artist_names"])
|
||||
return self.widget
|
||||
|
||||
def save_options(self):
|
||||
self.config.setting["translate_artist_names"] = \
|
||||
self.ui.translate_artist_names.isChecked()
|
||||
45
picard/ui/ui_options_metadata.py
Normal file
45
picard/ui/ui_options_metadata.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'ui\options_metadata.ui'
|
||||
#
|
||||
# Created: Sun Oct 15 13:45:25 2006
|
||||
# by: PyQt4 UI code generator 4.0
|
||||
# E:\projects\picard-qt\setup.py build_ui
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
import sys
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
class Ui_Form(object):
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName("Form")
|
||||
Form.resize(QtCore.QSize(QtCore.QRect(0,0,350,300).size()).expandedTo(Form.minimumSizeHint()))
|
||||
|
||||
self.vboxlayout = QtGui.QVBoxLayout(Form)
|
||||
self.vboxlayout.setMargin(9)
|
||||
self.vboxlayout.setSpacing(2)
|
||||
self.vboxlayout.setObjectName("vboxlayout")
|
||||
|
||||
self.rename_files = QtGui.QGroupBox(Form)
|
||||
self.rename_files.setObjectName("rename_files")
|
||||
|
||||
self.gridlayout = QtGui.QGridLayout(self.rename_files)
|
||||
self.gridlayout.setMargin(9)
|
||||
self.gridlayout.setSpacing(2)
|
||||
self.gridlayout.setObjectName("gridlayout")
|
||||
|
||||
self.translate_artist_names = QtGui.QCheckBox(self.rename_files)
|
||||
self.translate_artist_names.setObjectName("translate_artist_names")
|
||||
self.gridlayout.addWidget(self.translate_artist_names,0,0,1,1)
|
||||
self.vboxlayout.addWidget(self.rename_files)
|
||||
|
||||
spacerItem = QtGui.QSpacerItem(61,201,QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding)
|
||||
self.vboxlayout.addItem(spacerItem)
|
||||
|
||||
self.retranslateUi(Form)
|
||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||
|
||||
def retranslateUi(self, Form):
|
||||
self.rename_files.setTitle(_("Metadata"))
|
||||
self.translate_artist_names.setText(_("Translate foreign artist names to English where possible"))
|
||||
@@ -175,3 +175,21 @@ def make_short_filename(prefix, filename, length=250, max_length=250,
|
||||
parts.reverse()
|
||||
return os.path.join(*parts)
|
||||
|
||||
def reverse_sortname(sortname):
|
||||
chunks = map(unicode.strip, sortname.split(u","))
|
||||
if len(chunks) == 2:
|
||||
return u"%s %s" % (chunks[1], chunks[0])
|
||||
elif len(chunks) == 3:
|
||||
return u"%s %s %s" % (chunks[2], chunks[1], chunks[0])
|
||||
elif len(chunks) == 4:
|
||||
return u"%s %s, %s %s" % (chunks[1], chunks[0], chunks[3], chunks[2])
|
||||
else:
|
||||
return sortname.strip()
|
||||
|
||||
def translate_artist(name, sortname):
|
||||
for c in name:
|
||||
ctg = unicodedata.category(c)
|
||||
if (ctg[0] not in ("P", "Z") and ctg != "Nd" and
|
||||
unicodedata.name(c).find("LATIN") == -1):
|
||||
return " & ".join(map(reverse_sortname, sortname.split("&")))
|
||||
return name
|
||||
|
||||
@@ -73,3 +73,24 @@ class ShortFilenameTest(unittest.TestCase):
|
||||
def test_too_long(self):
|
||||
self.failUnlessRaises(IOError, util.make_short_filename, "/home/me/", os.path.join("a1234567890", "b1234567890"), 10)
|
||||
|
||||
|
||||
class TranslateArtistTest(unittest.TestCase):
|
||||
|
||||
def test_latin(self):
|
||||
self.failUnlessEqual(u"Jean Michel Jarre", util.translate_artist(u"Jean Michel Jarre", u"Jarre, Jean Michel"))
|
||||
self.failIfEqual(u"Jarre, Jean Michel", util.translate_artist(u"Jean Michel Jarre", u"Jarre, Jean Michel"))
|
||||
|
||||
def test_kanji(self):
|
||||
self.failUnlessEqual(u"Tetsuya Komuro", util.translate_artist(u"小室哲哉", u"Komuro, Tetsuya"))
|
||||
self.failIfEqual(u"Komuro, Tetsuya", util.translate_artist(u"小室哲哉", u"Komuro, Tetsuya"))
|
||||
self.failIfEqual(u"小室哲哉", util.translate_artist(u"小室哲哉", u"Komuro, Tetsuya"))
|
||||
|
||||
def test_kanji2(self):
|
||||
self.failUnlessEqual(u"Ayumi Hamasaki & Keiko", util.translate_artist(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko"))
|
||||
self.failIfEqual(u"浜崎あゆみ & KEIKO", util.translate_artist(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko"))
|
||||
self.failIfEqual(u"Hamasaki, Ayumi & Keiko", util.translate_artist(u"浜崎あゆみ & KEIKO", u"Hamasaki, Ayumi & Keiko"))
|
||||
|
||||
def test_cyrillic(self):
|
||||
self.failUnlessEqual(u"Pyotr Ilyich Tchaikovsky", util.translate_artist(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich"))
|
||||
self.failIfEqual(u"Tchaikovsky, Pyotr Ilyich", util.translate_artist(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich"))
|
||||
self.failIfEqual(u"Пётр Ильич Чайковский", util.translate_artist(u"Пётр Ильич Чайковский", u"Tchaikovsky, Pyotr Ilyich"))
|
||||
|
||||
65
ui/options_metadata.ui
Normal file
65
ui/options_metadata.ui
Normal file
@@ -0,0 +1,65 @@
|
||||
<ui version="4.0" >
|
||||
<author></author>
|
||||
<comment></comment>
|
||||
<exportmacro></exportmacro>
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>350</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="rename_files" >
|
||||
<property name="title" >
|
||||
<string>Metadata</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QCheckBox" name="translate_artist_names" >
|
||||
<property name="text" >
|
||||
<string>Translate foreign artist names to English where possible</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>61</width>
|
||||
<height>201</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<pixmapfunction></pixmapfunction>
|
||||
<tabstops>
|
||||
<tabstop>translate_artist_names</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user