96 lines
4.0 KiB
Python
96 lines
4.0 KiB
Python
import os
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
os.environ['MUSIC_WORKSHOP_DB_PATH'] = str(
|
|
Path(tempfile.gettempdir()) / f'music_workshop_metadata_normalization_{next(tempfile._get_candidate_names())}.db'
|
|
)
|
|
|
|
from backend.app.metadata_normalization import MetadataNormalizationService, can_ingest_metadata, parse_artist_string
|
|
from backend.app.task_store import TaskStore
|
|
|
|
|
|
class MetadataNormalizationTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.db_path = Path(os.environ['MUSIC_WORKSHOP_DB_PATH'])
|
|
if self.db_path.exists():
|
|
self.db_path.unlink()
|
|
self.task_store = TaskStore(self.db_path)
|
|
self.task = self.task_store.create_task_if_idle(
|
|
{
|
|
'input': '/tmp/input',
|
|
'output': '/tmp/output',
|
|
'trash': '/tmp/trash'
|
|
}
|
|
)
|
|
self.service = MetadataNormalizationService(self.task_store)
|
|
|
|
def test_parse_artist_string_supports_common_delimiters(self):
|
|
self.assertEqual(parse_artist_string('A / B')['tokens'], ['A', 'B'])
|
|
self.assertEqual(parse_artist_string('A; B')['tokens'], ['A', 'B'])
|
|
self.assertEqual(parse_artist_string('A & B')['tokens'], ['A', 'B'])
|
|
self.assertEqual(parse_artist_string('A feat. B')['tokens'], ['A', 'B'])
|
|
self.assertEqual(parse_artist_string('A、B')['tokens'], ['A', 'B'])
|
|
|
|
def test_single_artist_album_derives_album_artist(self):
|
|
item = self._insert_item('track-01.flac', {'title': 'Song 1', 'artist': 'Artist A', 'album': 'Album X'})
|
|
self._insert_item('track-02.flac', {'title': 'Song 2', 'artist': 'Artist A', 'album': 'Album X'})
|
|
|
|
normalized = self.service.normalize_item(item)
|
|
self.assertEqual(normalized['album_artist'], 'Artist A')
|
|
self.assertEqual(normalized['normalization_strategy'], 'single_artist')
|
|
self.assertTrue(can_ingest_metadata({**normalized, 'title': 'Song 1'}))
|
|
|
|
def test_feat_album_uses_dominant_primary_artist(self):
|
|
item = self._insert_item('track-01.flac', {'title': 'Song 1', 'artist': 'Artist A feat. Guest', 'album': 'Album X'})
|
|
self._insert_item('track-02.flac', {'title': 'Song 2', 'artist': 'Artist A', 'album': 'Album X'})
|
|
self._insert_item('track-03.flac', {'title': 'Song 3', 'artist': 'Artist A & Another', 'album': 'Album X'})
|
|
|
|
normalized = self.service.normalize_item(item)
|
|
self.assertEqual(normalized['album_artist'], 'Artist A')
|
|
self.assertEqual(normalized['normalization_strategy'], 'main_artist_feat')
|
|
|
|
def test_compilation_album_sets_various_artists(self):
|
|
item = self._insert_item('track-01.flac', {'title': 'Song 1', 'artist': 'Artist A', 'album': 'Top Hits 2025'})
|
|
self._insert_item('track-02.flac', {'title': 'Song 2', 'artist': 'Artist B', 'album': 'Top Hits 2025'})
|
|
self._insert_item('track-03.flac', {'title': 'Song 3', 'artist': 'Artist C', 'album': 'Top Hits 2025'})
|
|
|
|
normalized = self.service.normalize_item(item)
|
|
self.assertEqual(normalized['album_artist'], 'Various Artists')
|
|
self.assertEqual(normalized['compilation'], 1)
|
|
self.assertEqual(normalized['normalization_strategy'], 'compilation')
|
|
|
|
def test_existing_album_artist_is_preserved(self):
|
|
item = self._insert_item(
|
|
'track-01.flac',
|
|
{'title': 'Song 1', 'artist': '阿信', 'album': 'Solo Album', 'album_artist': '五月天'}
|
|
)
|
|
|
|
normalized = self.service.normalize_item(item)
|
|
self.assertEqual(normalized['album_artist'], '五月天')
|
|
self.assertEqual(normalized['normalization_strategy'], 'source_preserved')
|
|
|
|
def _insert_item(self, filename: str, matched_metadata_json: dict):
|
|
return self.task_store.insert_task_item(
|
|
self.task['task_id'],
|
|
original_path=f'/tmp/input/{filename}',
|
|
current_file_path=f'/tmp/input/{filename}',
|
|
relative_path=f'Artist/Album/{filename}',
|
|
filename=filename,
|
|
extension='.flac',
|
|
size_bytes=123456,
|
|
modified_at='2024-01-01T00:00:00Z',
|
|
local_cover=None,
|
|
local_lyric=None,
|
|
scan_status='queued',
|
|
scan_reason=None,
|
|
scan_message=None,
|
|
match_status='matched_fallback',
|
|
matched_metadata_json=matched_metadata_json
|
|
)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|