PICARD-1589: Support language for ID3 comments.

Adds syntax "comment:{language}:{description}" in addition to existing "comment:{description}" for comment tag names.
This commit is contained in:
Philipp Wolfer
2019-09-04 18:51:03 +02:00
committed by Philipp Wolfer
parent f5aa26095e
commit 4d85c2e31f
5 changed files with 54 additions and 6 deletions

View File

@@ -46,6 +46,7 @@ from picard.util import (
encode_filename,
sanitize_date,
)
from picard.util.tags import parse_comment_tag
id3.GRP1 = compatid3.GRP1
@@ -254,7 +255,11 @@ class ID3File(File):
elif frameid == 'COMM':
for text in frame.text:
if text:
metadata.add('%s:%s' % (name, frame.desc), text)
if frame.lang == 'eng':
name = '%s:%s' % (name, frame.desc)
else:
name = '%s:%s:%s' % (name, frame.lang, frame.desc)
metadata.add(name, text)
else:
metadata.add(name, frame)
elif frameid == 'TIT1':
@@ -411,12 +416,12 @@ class ID3File(File):
else:
tmcl.people.append([role, value])
elif name.startswith('comment:'):
desc = name.split(':', 1)[1]
(lang, desc) = parse_comment_tag(name)
if desc.lower()[:4] == 'itun':
tags.delall('COMM:' + desc)
tags.add(id3.COMM(encoding=0, desc=desc, lang='eng', text=[v + '\x00' for v in values]))
else:
tags.add(id3.COMM(encoding=encoding, desc=desc, lang='eng', text=values))
tags.add(id3.COMM(encoding=encoding, desc=desc, lang=lang, text=values))
elif name.startswith('lyrics:') or name == 'lyrics':
if ':' in name:
desc = name.split(':', 1)[1]
@@ -528,10 +533,11 @@ class ID3File(File):
if people[0] == role:
frame.people.remove(people)
elif name.startswith('comment:'):
desc = name.split(':', 1)[1]
(lang, desc) = parse_comment_tag(name)
if desc.lower()[:4] != 'itun':
for key, frame in list(tags.items()):
if frame.FrameID == 'COMM' and frame.desc == desc:
if (frame.FrameID == 'COMM' and frame.desc == desc
and frame.lang == lang):
del tags[key]
elif name.startswith('lyrics:') or name == 'lyrics':
if ':' in name:

View File

@@ -17,6 +17,9 @@
# 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
TAG_NAMES = {
'acoustid_fingerprint': N_('AcoustID Fingerprint'),
'acoustid_id': N_('AcoustID'),
@@ -32,7 +35,7 @@ TAG_NAMES = {
'barcode': N_('Barcode'),
'bpm': N_('BPM'),
'catalognumber': N_('Catalog Number'),
'comment:': N_('Comment'),
'comment': N_('Comment'),
'compilation': N_('Compilation (iTunes)'),
'composer': N_('Composer'),
'composersort': N_('Composer Sort Order'),
@@ -127,3 +130,19 @@ def display_tag_name(name):
if desc:
return '%s [%s]' % (_(TAG_NAMES.get(name, name)), desc)
return _(TAG_NAMES.get(name, name))
RE_COMMENT_LANG = re.compile('^([a-zA-Z]{3}):')
def parse_comment_tag(name): # noqa: E302
"""
Parses a tag name like "comment:XXX:desc", where XXX is the language.
If language is not set ("comment:desc") "eng" is assumed as default.
Returns a (lang, desc) tuple.
"""
desc = name.split(':', 1)[1]
lang = 'eng'
match = RE_COMMENT_LANG.match(desc)
if match:
lang = match.group(1)
desc = desc[4:]
return (lang, desc)

View File

@@ -87,6 +87,7 @@ TAGS = {
'catalognumber': 'Foo',
'comment:': 'Foo',
'comment:foo': 'Foo',
'comment:deu:foo': 'Foo',
'compilation': '1',
'composer': 'Foo',
'composersort': 'Foo',

View File

@@ -101,14 +101,18 @@ class CommonId3Tests:
def test_comment_delete(self):
metadata = Metadata(self.tags)
metadata['comment:bar'] = 'Foo'
metadata['comment:XXX:withlang'] = 'Foo'
original_metadata = save_and_load_metadata(self.filename, metadata)
del metadata['comment:bar']
del metadata['comment:XXX:withlang']
new_metadata = save_and_load_metadata(self.filename, metadata)
self.assertIn('comment:foo', original_metadata)
self.assertIn('comment:bar', original_metadata)
self.assertIn('comment:XXX:withlang', original_metadata)
self.assertIn('comment:foo', new_metadata)
self.assertNotIn('comment:bar', new_metadata)
self.assertNotIn('comment:XXX:withlang', new_metadata)
@skipUnlessTestfile
def test_id3v23_simple_tags(self):

18
test/test_util_tags.py Normal file
View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from test.picardtestcase import PicardTestCase
from picard.util.tags import (
display_tag_name,
parse_comment_tag,
)
class UtilTagsTest(PicardTestCase):
def test_display_tag_name(self):
self.assertEqual('Artist', display_tag_name('artist'))
self.assertEqual('Lyrics', display_tag_name('lyrics:'))
self.assertEqual('Comment [Foo]', display_tag_name('comment:Foo'))
def test_parse_comment_tag(self):
self.assertEqual(('XXX', 'foo'), parse_comment_tag('comment:XXX:foo'))
self.assertEqual(('eng', 'foo'), parse_comment_tag('comment:foo'))