Add MusicWorkshop application
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user