mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-08 17:43:58 +00:00
Add Id3Encoding enum
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
|
||||
|
||||
from collections import defaultdict
|
||||
from enum import IntEnum
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@@ -82,12 +83,25 @@ __ID3_IMAGE_TYPE_MAP = dict(__IMAGE_TYPES)
|
||||
__ID3_REVERSE_IMAGE_TYPE_MAP = dict([(v, k) for k, v in __IMAGE_TYPES])
|
||||
|
||||
|
||||
class Id3Encoding(IntEnum):
|
||||
LATIN1 = 0
|
||||
UTF16 = 1
|
||||
UTF16BE = 2
|
||||
UTF8 = 3
|
||||
|
||||
def from_config(id3v2_encoding):
|
||||
return {
|
||||
'utf-8': Id3Encoding.UTF8,
|
||||
'utf-16': Id3Encoding.UTF16
|
||||
}.get(id3v2_encoding, Id3Encoding.LATIN1)
|
||||
|
||||
|
||||
def id3text(text, encoding):
|
||||
"""Returns a string which only contains code points which can
|
||||
be encododed with the given numeric id3 encoding.
|
||||
"""
|
||||
|
||||
if encoding == 0:
|
||||
if encoding == Id3Encoding.LATIN1:
|
||||
return text.encode("latin1", "replace").decode("latin1")
|
||||
return text
|
||||
|
||||
@@ -259,7 +273,7 @@ class ID3File(File):
|
||||
tags = file.tags or {}
|
||||
config = get_config()
|
||||
itunes_compatible = config.setting['itunes_compatible_grouping']
|
||||
rating_user_email = id3text(config.setting['rating_user_email'], 0)
|
||||
rating_user_email = id3text(config.setting['rating_user_email'], Id3Encoding.LATIN1)
|
||||
rating_steps = config.setting['rating_steps']
|
||||
# upgrade custom 2.3 frames to 2.4
|
||||
for old, new in self.__upgrade.items():
|
||||
@@ -391,28 +405,28 @@ class ID3File(File):
|
||||
if images_to_save:
|
||||
tags.delall('APIC')
|
||||
|
||||
encoding = {'utf-8': 3, 'utf-16': 1}.get(config.setting['id3v2_encoding'], 0)
|
||||
encoding = Id3Encoding.from_config(config.setting['id3v2_encoding'])
|
||||
|
||||
if 'tracknumber' in metadata:
|
||||
if 'totaltracks' in metadata:
|
||||
text = '%s/%s' % (metadata['tracknumber'], metadata['totaltracks'])
|
||||
else:
|
||||
text = metadata['tracknumber']
|
||||
tags.add(id3.TRCK(encoding=0, text=id3text(text, 0)))
|
||||
tags.add(id3.TRCK(encoding=Id3Encoding.LATIN1, text=id3text(text, Id3Encoding.LATIN1)))
|
||||
|
||||
if 'discnumber' in metadata:
|
||||
if 'totaldiscs' in metadata:
|
||||
text = '%s/%s' % (metadata['discnumber'], metadata['totaldiscs'])
|
||||
else:
|
||||
text = metadata['discnumber']
|
||||
tags.add(id3.TPOS(encoding=0, text=id3text(text, 0)))
|
||||
tags.add(id3.TPOS(encoding=Id3Encoding.LATIN1, text=id3text(text, Id3Encoding.LATIN1)))
|
||||
|
||||
if 'movementnumber' in metadata:
|
||||
if 'movementtotal' in metadata:
|
||||
text = '%s/%s' % (metadata['movementnumber'], metadata['movementtotal'])
|
||||
else:
|
||||
text = metadata['movementnumber']
|
||||
tags.add(id3.MVIN(encoding=0, text=id3text(text, 0)))
|
||||
tags.add(id3.MVIN(encoding=Id3Encoding.LATIN1, text=id3text(text, Id3Encoding.LATIN1)))
|
||||
|
||||
# This is necessary because mutagens HashKey for APIC frames only
|
||||
# includes the FrameID (APIC) and description - it's basically
|
||||
@@ -427,10 +441,10 @@ class ID3File(File):
|
||||
else:
|
||||
desctag = "(%i)" % counters[desc]
|
||||
counters[desc] += 1
|
||||
tags.add(id3.APIC(encoding=0,
|
||||
tags.add(id3.APIC(encoding=Id3Encoding.LATIN1,
|
||||
mime=image.mimetype,
|
||||
type=image_type_as_id3_num(image.maintype),
|
||||
desc=id3text(desctag, 0),
|
||||
desc=id3text(desctag, Id3Encoding.LATIN1),
|
||||
data=image.data))
|
||||
|
||||
tmcl = mutagen.id3.TMCL(encoding=encoding, people=[])
|
||||
@@ -457,7 +471,7 @@ class ID3File(File):
|
||||
(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]))
|
||||
tags.add(id3.COMM(encoding=Id3Encoding.LATIN1, desc=desc, lang='eng', text=[v + '\x00' for v in values]))
|
||||
else:
|
||||
tags.add(id3.COMM(encoding=encoding, desc=desc, lang=lang, text=values))
|
||||
elif name.startswith('lyrics:') or name == 'lyrics':
|
||||
@@ -473,7 +487,7 @@ class ID3File(File):
|
||||
elif name == 'musicbrainz_recordingid':
|
||||
tags.add(id3.UFID(owner='http://musicbrainz.org', data=bytes(values[0], 'ascii')))
|
||||
elif name == '~rating':
|
||||
rating_user_email = id3text(config.setting['rating_user_email'], 0)
|
||||
rating_user_email = id3text(config.setting['rating_user_email'], Id3Encoding.LATIN1)
|
||||
# Search for an existing POPM frame to get the current playcount
|
||||
for frame in tags.values():
|
||||
if frame.FrameID == 'POPM' and frame.email == rating_user_email:
|
||||
@@ -597,7 +611,7 @@ class ID3File(File):
|
||||
tags.delall(real_name)
|
||||
tags.delall('TXXX:' + self.__rtranslate_freetext[name])
|
||||
elif real_name == 'POPM':
|
||||
rating_user_email = id3text(config.setting['rating_user_email'], 0)
|
||||
rating_user_email = id3text(config.setting['rating_user_email'], Id3Encoding.LATIN1)
|
||||
for key, frame in list(tags.items()):
|
||||
if frame.FrameID == 'POPM' and frame.email == rating_user_email:
|
||||
del tags[key]
|
||||
|
||||
@@ -644,11 +644,17 @@ class AIFFTest(CommonId3Tests.Id3TestCase):
|
||||
|
||||
|
||||
class Id3UtilTest(PicardTestCase):
|
||||
def test_id3encoding_from_config(self):
|
||||
self.assertEqual(id3.Id3Encoding.LATIN1, id3.Id3Encoding.from_config('iso-8859-1'))
|
||||
self.assertEqual(id3.Id3Encoding.UTF16, id3.Id3Encoding.from_config('utf-16'))
|
||||
self.assertEqual(id3.Id3Encoding.UTF8, id3.Id3Encoding.from_config('utf-8'))
|
||||
|
||||
def test_id3text(self):
|
||||
teststring = '日本語testÖäß'
|
||||
self.assertEqual(id3.id3text(teststring, 0), '???testÖäß')
|
||||
self.assertEqual(id3.id3text(teststring, 1), teststring)
|
||||
self.assertEqual(id3.id3text(teststring, 3), teststring)
|
||||
self.assertEqual(id3.id3text(teststring, id3.Id3Encoding.LATIN1), '???testÖäß')
|
||||
self.assertEqual(id3.id3text(teststring, id3.Id3Encoding.UTF16), teststring)
|
||||
self.assertEqual(id3.id3text(teststring, id3.Id3Encoding.UTF16BE), teststring)
|
||||
self.assertEqual(id3.id3text(teststring, id3.Id3Encoding.UTF8), teststring)
|
||||
|
||||
def test_image_type_from_id3_num(self):
|
||||
self.assertEqual(id3.image_type_from_id3_num(0), 'other')
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Copyright (C) 2013, 2018 Laurent Monin
|
||||
# Copyright (C) 2016 Christoph Reiter
|
||||
# Copyright (C) 2018 Wieland Hoffmann
|
||||
# Copyright (C) 2019 Philipp Wolfer
|
||||
# Copyright (C) 2019, 2021 Philipp Wolfer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -27,23 +27,17 @@ from mutagen import id3
|
||||
|
||||
from test.picardtestcase import PicardTestCase
|
||||
|
||||
from picard.formats.id3 import id3text
|
||||
from picard.formats.id3 import Id3Encoding
|
||||
from picard.formats.mutagenext import compatid3
|
||||
|
||||
|
||||
class UpdateToV23Test(PicardTestCase):
|
||||
|
||||
def test_id3text(self):
|
||||
self.assertEqual(id3text("\u1234", 0), "?")
|
||||
self.assertEqual(id3text("\u1234", 1), "\u1234")
|
||||
self.assertEqual(id3text("\u1234", 2), "\u1234")
|
||||
self.assertEqual(id3text("\u1234", 3), "\u1234")
|
||||
|
||||
def test_keep_some_v24_tag(self):
|
||||
tags = compatid3.CompatID3()
|
||||
tags.add(id3.TSOP(encoding=0, text=["foo"]))
|
||||
tags.add(id3.TSOA(encoding=0, text=["foo"]))
|
||||
tags.add(id3.TSOT(encoding=0, text=["foo"]))
|
||||
tags.add(id3.TSOP(encoding=Id3Encoding.LATIN1, text=["foo"]))
|
||||
tags.add(id3.TSOA(encoding=Id3Encoding.LATIN1, text=["foo"]))
|
||||
tags.add(id3.TSOT(encoding=Id3Encoding.LATIN1, text=["foo"]))
|
||||
tags.update_to_v23()
|
||||
self.assertEqual(tags["TSOP"].text, ["foo"])
|
||||
self.assertEqual(tags["TSOA"].text, ["foo"])
|
||||
@@ -51,7 +45,7 @@ class UpdateToV23Test(PicardTestCase):
|
||||
|
||||
def test_tdrc(self):
|
||||
tags = compatid3.CompatID3()
|
||||
tags.add(id3.TDRC(encoding=1, text="2003-04-05 12:03"))
|
||||
tags.add(id3.TDRC(encoding=Id3Encoding.UTF16, text="2003-04-05 12:03"))
|
||||
tags.update_to_v23()
|
||||
self.assertEqual(tags["TYER"].text, ["2003"])
|
||||
self.assertEqual(tags["TDAT"].text, ["0504"])
|
||||
@@ -59,30 +53,30 @@ class UpdateToV23Test(PicardTestCase):
|
||||
|
||||
def test_tdor(self):
|
||||
tags = compatid3.CompatID3()
|
||||
tags.add(id3.TDOR(encoding=1, text="2003-04-05 12:03"))
|
||||
tags.add(id3.TDOR(encoding=Id3Encoding.UTF16, text="2003-04-05 12:03"))
|
||||
tags.update_to_v23()
|
||||
self.assertEqual(tags["TORY"].text, ["2003"])
|
||||
|
||||
def test_genre_from_v24_1(self):
|
||||
tags = compatid3.CompatID3()
|
||||
tags.add(id3.TCON(encoding=1, text=["4", "Rock"]))
|
||||
tags.add(id3.TCON(encoding=Id3Encoding.UTF16, text=["4", "Rock"]))
|
||||
tags.update_to_v23()
|
||||
self.assertEqual(tags["TCON"].text, ["Disco", "Rock"])
|
||||
|
||||
def test_genre_from_v24_2(self):
|
||||
tags = compatid3.CompatID3()
|
||||
tags.add(id3.TCON(encoding=1, text=["RX", "3", "CR"]))
|
||||
tags.add(id3.TCON(encoding=Id3Encoding.UTF16, text=["RX", "3", "CR"]))
|
||||
tags.update_to_v23()
|
||||
self.assertEqual(tags["TCON"].text, ["Remix", "Dance", "Cover"])
|
||||
|
||||
def test_genre_from_v23_1(self):
|
||||
tags = compatid3.CompatID3()
|
||||
tags.add(id3.TCON(encoding=1, text=["(4)Rock"]))
|
||||
tags.add(id3.TCON(encoding=Id3Encoding.UTF16, text=["(4)Rock"]))
|
||||
tags.update_to_v23()
|
||||
self.assertEqual(tags["TCON"].text, ["Disco", "Rock"])
|
||||
|
||||
def test_genre_from_v23_2(self):
|
||||
tags = compatid3.CompatID3()
|
||||
tags.add(id3.TCON(encoding=1, text=["(RX)(3)(CR)"]))
|
||||
tags.add(id3.TCON(encoding=Id3Encoding.UTF16, text=["(RX)(3)(CR)"]))
|
||||
tags.update_to_v23()
|
||||
self.assertEqual(tags["TCON"].text, ["Remix", "Dance", "Cover"])
|
||||
|
||||
Reference in New Issue
Block a user