mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-26 10:25:16 +00:00
331 lines
12 KiB
Python
331 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
import os.path
|
|
import shutil
|
|
from tempfile import mkstemp
|
|
import unittest
|
|
|
|
import mutagen
|
|
|
|
from test.picardtestcase import PicardTestCase
|
|
|
|
from picard import config
|
|
import picard.formats
|
|
from picard.formats import ext_to_format
|
|
from picard.metadata import Metadata
|
|
|
|
|
|
settings = {
|
|
'clear_existing_tags': False,
|
|
'embed_only_one_front_image': False,
|
|
'enabled_plugins': '',
|
|
'id3v23_join_with': '/',
|
|
'id3v2_encoding': 'utf-8',
|
|
'rating_steps': 6,
|
|
'rating_user_email': 'users@musicbrainz.org',
|
|
'remove_ape_from_mp3': False,
|
|
'remove_id3_from_flac': False,
|
|
'remove_images_from_tags': False,
|
|
'save_images_to_tags': True,
|
|
'write_id3v1': True,
|
|
'write_id3v23': False,
|
|
'itunes_compatible_grouping': False,
|
|
}
|
|
|
|
|
|
def save_metadata(filename, metadata):
|
|
f = picard.formats.open_(filename)
|
|
f._save(filename, metadata)
|
|
|
|
|
|
def load_metadata(filename):
|
|
f = picard.formats.open_(filename)
|
|
return f._load(filename)
|
|
|
|
|
|
def save_and_load_metadata(filename, metadata):
|
|
"""Save new metadata to a file and load it again."""
|
|
f = picard.formats.open_(filename)
|
|
loaded_metadata = f._load(filename)
|
|
f._copy_loaded_metadata(loaded_metadata)
|
|
f._save(filename, metadata)
|
|
return load_metadata(filename)
|
|
|
|
|
|
def load_raw(filename):
|
|
return mutagen.File(filename)
|
|
|
|
|
|
TAGS = {
|
|
'albumartist': 'Foo',
|
|
'albumartistsort': 'Foo',
|
|
'album': 'Foo Bar',
|
|
'albumsort': 'Foo',
|
|
'arranger': 'Foo',
|
|
'artist': 'Foo',
|
|
'artistsort': 'Foo',
|
|
'asin': 'Foo',
|
|
'barcode': 'Foo',
|
|
'bpm': '80',
|
|
'catalognumber': 'Foo',
|
|
'comment:': 'Foo',
|
|
'comment:foo': 'Foo',
|
|
'compilation': '1',
|
|
'composer': 'Foo',
|
|
'composersort': 'Foo',
|
|
'conductor': 'Foo',
|
|
'copyright': 'Foo',
|
|
'date': '2004',
|
|
'discnumber': '1',
|
|
'discsubtitle': 'Foo',
|
|
'djmixer': 'Foo',
|
|
'encodedby': 'Foo',
|
|
'encodersettings': 'Foo',
|
|
'engineer': 'Foo',
|
|
'gapless': '1',
|
|
'genre': 'Foo',
|
|
'grouping': 'Foo',
|
|
'isrc': 'Foo',
|
|
'key': 'E#m',
|
|
'label': 'Foo',
|
|
'lyricist': 'Foo',
|
|
'lyrics': 'Foo',
|
|
'media': 'Foo',
|
|
'mixer': 'Foo',
|
|
'mood': 'Foo',
|
|
'movement': 'Foo',
|
|
'movementnumber': '2',
|
|
'movementtotal': '8',
|
|
'musicbrainz_albumartistid': '00000000-0000-0000-0000-000000000000',
|
|
'musicbrainz_albumid': '00000000-0000-0000-0000-000000000000',
|
|
'musicbrainz_artistid': '00000000-0000-0000-0000-000000000000',
|
|
'musicbrainz_discid': 'HJRFvVfxx0MU_6v8v9swQUxDmZQ-',
|
|
'musicbrainz_originalalbumid': '00000000-0000-0000-0000-000000000000',
|
|
'musicbrainz_originalartistid': '00000000-0000-0000-0000-000000000000',
|
|
'musicbrainz_releasegroupid': '00000000-0000-0000-0000-000000000000',
|
|
'musicbrainz_trackid': '00000000-0000-0000-0000-000000000000',
|
|
'musicbrainz_trmid': 'Foo',
|
|
'musicbrainz_workid': '00000000-0000-0000-0000-000000000000',
|
|
'musicip_fingerprint': 'Foo',
|
|
'musicip_puid': '00000000-0000-0000-0000-000000000000',
|
|
'originaldate': '1980-01-20',
|
|
'originalyear': '1980',
|
|
'performer:guest vocal': 'Foo',
|
|
'podcast': '1',
|
|
'podcasturl': 'Foo',
|
|
'producer': 'Foo',
|
|
'releasecountry': 'XW',
|
|
'releasestatus': 'Foo',
|
|
'releasetype': 'Foo',
|
|
'remixer': 'Foo',
|
|
'show': 'Foo',
|
|
'showmovement': '1',
|
|
'showsort': 'Foo',
|
|
'subtitle': 'Foo',
|
|
'title': 'Foo',
|
|
'titlesort': 'Foo',
|
|
'totaldiscs': '2',
|
|
'totaltracks': '10',
|
|
'tracknumber': '2',
|
|
'website': 'http://example.com',
|
|
'work': 'Foo'
|
|
}
|
|
|
|
|
|
def skipUnlessTestfile(func):
|
|
def _decorator(self, *args, **kwargs):
|
|
if not self.testfile:
|
|
raise unittest.SkipTest("No test file set")
|
|
func(self, *args, **kwargs)
|
|
return _decorator
|
|
|
|
|
|
# prevent unittest to run tests in those classes
|
|
class CommonTests:
|
|
|
|
class BaseFileTestCase(PicardTestCase):
|
|
testfile = None
|
|
testfile_ext = None
|
|
testfile_path = None
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
config.setting = settings.copy()
|
|
if self.testfile:
|
|
_name, self.testfile_ext = os.path.splitext(self.testfile)
|
|
self.testfile_path = os.path.join('test', 'data', self.testfile)
|
|
self.testfile_ext = os.path.splitext(self.testfile)[1]
|
|
self.filename = self.copy_of_original_testfile()
|
|
self.format = ext_to_format(self.testfile_ext[1:])
|
|
|
|
def copy_of_original_testfile(self):
|
|
return self.copy_file_tmp(self.testfile_path, self.testfile_ext)
|
|
|
|
def copy_file_tmp(self, filename, ext):
|
|
fd, copy = mkstemp(suffix=ext)
|
|
self.addCleanup(os.unlink, copy)
|
|
os.close(fd)
|
|
shutil.copy(filename, copy)
|
|
return copy
|
|
|
|
class SimpleFormatsTestCase(BaseFileTestCase):
|
|
|
|
expected_info = None
|
|
|
|
@skipUnlessTestfile
|
|
def test_can_open_and_save(self):
|
|
metadata = save_and_load_metadata(self.filename, Metadata())
|
|
self.assertTrue(metadata['~format'])
|
|
|
|
@skipUnlessTestfile
|
|
def test_info(self):
|
|
if not self.expected_info:
|
|
raise unittest.SkipTest("Ratings not supported")
|
|
metadata = save_and_load_metadata(self.filename, Metadata())
|
|
for key, expected_value in self.expected_info.items():
|
|
value = metadata.length if key == 'length' else metadata[key]
|
|
self.assertEqual(value, expected_value)
|
|
|
|
class TagFormatsTestCase(SimpleFormatsTestCase):
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.tags = TAGS.copy()
|
|
self.setup_tags()
|
|
|
|
def setup_tags(self):
|
|
if self.testfile:
|
|
supports_tag = self.format.supports_tag
|
|
self.unsupported_tags = {tag: val for tag, val in self.tags.items() if not supports_tag(tag)}
|
|
self.remove_tags(self.unsupported_tags.keys())
|
|
|
|
def set_tags(self, dict_tag_value=None):
|
|
if dict_tag_value:
|
|
self.tags.update(dict_tag_value)
|
|
|
|
def remove_tags(self, tag_list=None):
|
|
for tag in tag_list:
|
|
del self.tags[tag]
|
|
|
|
@skipUnlessTestfile
|
|
def test_simple_tags(self):
|
|
metadata = Metadata(self.tags)
|
|
loaded_metadata = save_and_load_metadata(self.filename, metadata)
|
|
for (key, value) in self.tags.items():
|
|
self.assertEqual(loaded_metadata[key], value, '%s: %r != %r' % (key, loaded_metadata[key], value))
|
|
|
|
@skipUnlessTestfile
|
|
def test_save_does_not_modify_metadata(self):
|
|
tags = dict(self.tags)
|
|
if self.supports_ratings:
|
|
tags['~rating'] = '3'
|
|
metadata = Metadata(tags)
|
|
save_metadata(self.filename, metadata)
|
|
for (key, value) in tags.items():
|
|
self.assertEqual(metadata[key], value, '%s: %r != %r' % (key, metadata[key], value))
|
|
|
|
@skipUnlessTestfile
|
|
def test_unsupported_tags(self):
|
|
metadata = Metadata(self.unsupported_tags)
|
|
loaded_metadata = save_and_load_metadata(self.filename, metadata)
|
|
for tag in self.unsupported_tags:
|
|
self.assertTrue(tag not in loaded_metadata, '%s: %r != None' % (tag, loaded_metadata[tag]))
|
|
|
|
@skipUnlessTestfile
|
|
def test_preserve_unchanged_tags(self):
|
|
metadata = Metadata(self.tags)
|
|
save_metadata(self.filename, metadata)
|
|
loaded_metadata = save_and_load_metadata(self.filename, Metadata())
|
|
for (key, value) in self.tags.items():
|
|
self.assertEqual(loaded_metadata[key], value, '%s: %r != %r' % (key, loaded_metadata[key], value))
|
|
|
|
@skipUnlessTestfile
|
|
def test_delete_simple_tags(self):
|
|
metadata = Metadata(self.tags)
|
|
if self.supports_ratings:
|
|
metadata['~rating'] = 1
|
|
original_metadata = save_and_load_metadata(self.filename, metadata)
|
|
del metadata['albumartist']
|
|
if self.supports_ratings:
|
|
del metadata['~rating']
|
|
new_metadata = save_and_load_metadata(self.filename, metadata)
|
|
self.assertIn('albumartist', original_metadata.keys())
|
|
self.assertNotIn('albumartist', new_metadata.keys())
|
|
if self.supports_ratings:
|
|
self.assertIn('~rating', original_metadata.keys())
|
|
self.assertNotIn('~rating', new_metadata.keys())
|
|
|
|
@skipUnlessTestfile
|
|
def test_delete_non_existant_tags(self):
|
|
metadata = Metadata()
|
|
del metadata['albumartist']
|
|
del metadata['performer:drums']
|
|
del metadata['totaltracks']
|
|
new_metadata = save_and_load_metadata(self.filename, metadata)
|
|
self.assertNotIn('albumartist', new_metadata.keys())
|
|
self.assertNotIn('performer:drums', new_metadata.keys())
|
|
self.assertNotIn('totaltracks', new_metadata.keys())
|
|
|
|
@skipUnlessTestfile
|
|
def test_delete_complex_tags(self):
|
|
metadata = Metadata(self.tags)
|
|
original_metadata = save_and_load_metadata(self.filename, metadata)
|
|
del metadata['totaldiscs']
|
|
new_metadata = save_and_load_metadata(self.filename, metadata)
|
|
|
|
self.assertIn('totaldiscs', original_metadata)
|
|
if self.testfile_ext == '.m4a':
|
|
self.assertEqual(u'0', new_metadata['totaldiscs'])
|
|
else:
|
|
self.assertNotIn('totaldiscs', new_metadata)
|
|
|
|
@skipUnlessTestfile
|
|
def test_delete_performer(self):
|
|
if 'performer:guest vocal' in self.tags:
|
|
metadata = Metadata(self.tags)
|
|
metadata['performer:piano'] = 'Foo'
|
|
|
|
original_metadata = save_and_load_metadata(self.filename, metadata)
|
|
del metadata['performer:piano']
|
|
new_metadata = save_and_load_metadata(self.filename, metadata)
|
|
|
|
self.assertIn('performer:guest vocal', original_metadata)
|
|
self.assertIn('performer:guest vocal', new_metadata)
|
|
self.assertIn('performer:piano', original_metadata)
|
|
self.assertNotIn('performer:piano', new_metadata)
|
|
|
|
@skipUnlessTestfile
|
|
def test_ratings(self):
|
|
if not self.supports_ratings:
|
|
raise unittest.SkipTest("Ratings not supported")
|
|
for rating in range(6):
|
|
rating = 1
|
|
metadata = Metadata()
|
|
metadata['~rating'] = rating
|
|
loaded_metadata = save_and_load_metadata(self.filename, metadata)
|
|
self.assertEqual(int(loaded_metadata['~rating']), rating, '~rating: %r != %r' % (loaded_metadata['~rating'], rating))
|
|
|
|
@skipUnlessTestfile
|
|
def test_invalid_rating_email(self):
|
|
if not self.supports_ratings:
|
|
raise unittest.SkipTest("Ratings not supported")
|
|
metadata = Metadata()
|
|
metadata['~rating'] = 3
|
|
config.setting['rating_user_email'] = '{in\tvälid}'
|
|
loaded_metadata = save_and_load_metadata(self.filename, metadata)
|
|
self.assertEqual(loaded_metadata['~rating'], metadata['~rating'])
|
|
|
|
@skipUnlessTestfile
|
|
def test_guess_format(self):
|
|
temp_file = self.copy_of_original_testfile()
|
|
audio = picard.formats.guess_format(temp_file)
|
|
audio_original = picard.formats.open_(self.filename)
|
|
self.assertEqual(type(audio), type(audio_original))
|
|
|
|
@skipUnlessTestfile
|
|
def test_split_ext(self):
|
|
f = picard.formats.open_(self.filename)
|
|
self.assertEqual(f._fixed_splitext(f.filename), os.path.splitext(f.filename))
|
|
self.assertEqual(f._fixed_splitext(f.EXTENSIONS[0]), ('', f.EXTENSIONS[0]))
|
|
self.assertEqual(f._fixed_splitext('.test'), os.path.splitext('.test'))
|
|
self.assertNotEqual(f._fixed_splitext(f.EXTENSIONS[0]), os.path.splitext(f.EXTENSIONS[0]))
|