mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-18 06:25:33 +00:00
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:
committed by
Philipp Wolfer
parent
f5aa26095e
commit
4d85c2e31f
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -87,6 +87,7 @@ TAGS = {
|
||||
'catalognumber': 'Foo',
|
||||
'comment:': 'Foo',
|
||||
'comment:foo': 'Foo',
|
||||
'comment:deu:foo': 'Foo',
|
||||
'compilation': '1',
|
||||
'composer': 'Foo',
|
||||
'composersort': 'Foo',
|
||||
|
||||
@@ -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
18
test/test_util_tags.py
Normal 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'))
|
||||
Reference in New Issue
Block a user