curds: Add a Track Tag class

This is similar to the Album tag and stores its data in the same
dictionary.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2019-03-05 15:52:07 -05:00
parent 04ac4398e2
commit 60f56ba3b7
4 changed files with 93 additions and 136 deletions

View File

@ -1,7 +1,10 @@
# Copyright 2019 (c) Anna Schumaker.
import mutagen
import os
import re
tag_map = dict()
discno_map = { "one" : "1", "two" : "2", "three" : "3", "four" : "4", "five" : "5" }
tag_map = dict()
class Tag:
def extract(info, key, default):
@ -32,3 +35,29 @@ class Album(Tag):
def lookup(info):
return Tag.lookup(Album(info))
class Track(Tag):
def __init__(self, path):
info = mutagen.File(path)
self.path = path
self.title = Tag.extract(info, "title", os.path.basename(path))
self.artist = Tag.extract(info, "artist", "Unknown Artist")
self.tracknumber = int(Tag.extract(info, "tracknumber", 0))
self.discnumber = int(Tag.extract(info, "discnumber", 1))
self.length = info.info.length
self.album = Album.lookup(info)
# Try to detect discnumbers that are embedded in the album name
discno = Tag.extract(info, "album", "Unknown Album")[len(self.album.album):]
match = re.search("([1-9][0-9]*)|one|two|three|four|five", discno.lower())
if match:
self.discnumber = int(discno_map.get(match.group(), match.group()))
def __hash__(self):
return hash((hash(self.album), self.title, self.artist,
self.tracknumber, self.discnumber))
def add(path):
track = Track(path)
return track if Tag.lookup(track) == track else None

63
curds/test_tags_track.py Normal file
View File

@ -0,0 +1,63 @@
# Copyright 2019 (c) Anna Schumaker.
import os
import tags
import unittest
test_tracks = os.path.abspath("./trier/Test Album")
class TestTrackTag(unittest.TestCase):
def test_track_init_basic(self):
p = [ test_tracks, "01 - Test Track.ogg" ]
t = tags.Track(os.path.join(*p))
self.assertIsInstance(t, tags.Tag)
self.assertEqual(t.path, os.path.join(*p))
self.assertEqual(t.title, "Test Track")
self.assertEqual(t.artist, "Test Artist")
self.assertEqual(t.tracknumber, 1)
self.assertEqual(t.discnumber, 1)
self.assertEqual(t.length, 10)
self.assertEqual(t.album.album, "Test Album")
self.assertEqual(t.album.date, 2019)
self.assertEqual(t.album.genre, "Test")
self.assertEqual(t.album.albumartist, "Test Artist")
self.assertEqual(hash(t), hash((hash(t.album), "Test Track", "Test Artist", 1, 1)))
def test_track_init_empty(self):
p = [ test_tracks, "00 - Empty Track.ogg" ]
t = tags.Track(os.path.join(*p))
self.assertIsInstance(t, tags.Tag)
self.assertEqual(t.path, os.path.join(*p))
self.assertEqual(t.title, "00 - Empty Track.ogg")
self.assertEqual(t.artist, "Unknown Artist")
self.assertEqual(t.tracknumber, 0)
self.assertEqual(t.length, 0)
self.assertEqual(t.discnumber, 1)
self.assertEqual(t.album.album, "Unknown Album")
self.assertEqual(t.album.date, 0)
self.assertEqual(t.album.genre, "Unknown")
self.assertEqual(t.album.albumartist, "Unknown Artist")
self.assertEqual(hash(t), hash((hash(t.album), "00 - Empty Track.ogg", "Unknown Artist", 0, 1)))
def test_track_discno_detect(self):
join = os.path.join
self.assertEqual(tags.Track(join(test_tracks, "02 - Test {Disc 2}.ogg")).discnumber, 2)
self.assertEqual(tags.Track(join(test_tracks, "03 - Test [Disk One].ogg")).discnumber, 1)
self.assertEqual(tags.Track(join(test_tracks, "04 - Test (Disc Two).ogg")).discnumber, 2)
self.assertEqual(tags.Track(join(test_tracks, "05 - Test - Disc Three.ogg")).discnumber, 3)
self.assertEqual(tags.Track(join(test_tracks, "06 - Test;CD Four.ogg")).discnumber, 4)
self.assertEqual(tags.Track(join(test_tracks, "07 - Test;CdFive.ogg")).discnumber, 5)
self.assertEqual(tags.Track(join(test_tracks, "08 - Test CD 6/10.ogg")).discnumber, 6)
self.assertEqual(tags.Track(join(test_tracks, "09 - Test {Disc 02}.ogg")).discnumber, 2)
self.assertEqual(tags.Track(join(test_tracks, "10 - Test {Disc 20}.ogg")).discnumber, 20)
def test_track_add(self):
tags.tag_map.clear()
t = tags.Track.add(os.path.join(test_tracks, "01 - Test Track.ogg"))
self.assertIsNotNone(t)
self.assertIn(t, tags.tag_map.values())
self.assertIn(t.album, tags.tag_map.values())
for i in range(10):
u = tags.Track.add(os.path.join(test_tracks, "01 - Test Track.ogg"))
self.assertIsNone(u)
self.assertEqual(len(tags.tag_map), 2)

View File

@ -1,94 +0,0 @@
# Copyright 2019 (c) Anna Schumaker.
import concurrent.futures
import mutagen
import os
import track
import unittest
test_tracks = os.path.abspath("./trier/Test Album")
test_library = os.path.abspath("./trier/Test Library")
class TestTrackClass(unittest.TestCase):
def setUp(self):
track.track_list.clear()
def test_init_basic(self):
p = [ test_tracks, "01 - Test Track.ogg" ]
t = track.Track(os.path.join(*p))
self.assertEqual(t.path, os.path.join(*p))
self.assertEqual(t.title, "Test Track")
self.assertEqual(t.artist, "Test Artist")
self.assertEqual(t.tracknumber, 1)
self.assertEqual(t.length, 10)
self.assertEqual(t.discnumber, 1)
self.assertEqual(t.album.album, "Test Album")
self.assertEqual(t.album.date, 2019)
self.assertEqual(t.album.genre, "Test")
self.assertEqual(t.album.albumartist, "Test Artist")
self.assertEqual(len(track.track_list), 0)
def test_init_empty(self):
p = [ test_tracks, "00 - Empty Track.ogg" ]
t = track.Track(os.path.join(*p))
self.assertEqual(t.path, os.path.join(*p))
self.assertEqual(t.title, "00 - Empty Track.ogg")
self.assertEqual(t.artist, "Unknown Artist")
self.assertEqual(t.tracknumber , 0)
self.assertEqual(t.length, 0)
self.assertEqual(t.discnumber, 1)
self.assertEqual(t.album.album, "Unknown Album")
self.assertEqual(t.album.date, 0)
self.assertEqual(t.album.genre, "Unknown")
self.assertEqual(t.album.albumartist, "Unknown Artist")
self.assertEqual(len(track.track_list), 0)
def test_init_discno_detect(self):
join = os.path.join
self.assertEqual(track.Track(join(test_tracks, "02 - Test {Disc 2}.ogg")).discnumber, 2)
self.assertEqual(track.Track(join(test_tracks, "03 - Test [Disk One].ogg")).discnumber, 1)
self.assertEqual(track.Track(join(test_tracks, "04 - Test (Disc Two).ogg")).discnumber, 2)
self.assertEqual(track.Track(join(test_tracks, "05 - Test - Disc Three.ogg")).discnumber, 3)
self.assertEqual(track.Track(join(test_tracks, "06 - Test;CD Four.ogg")).discnumber, 4)
self.assertEqual(track.Track(join(test_tracks, "07 - Test;CdFive.ogg")).discnumber, 5)
self.assertEqual(track.Track(join(test_tracks, "08 - Test CD 6/10.ogg")).discnumber, 6)
self.assertEqual(track.Track(join(test_tracks, "09 - Test {Disc 02}.ogg")).discnumber, 2)
self.assertEqual(track.Track(join(test_tracks, "10 - Test {Disc 20}.ogg")).discnumber, 20)
def test_track_add(self):
fs = track.add(os.path.join(test_tracks, "01 - Test Track.ogg"))
self.assertEqual(len(track.track_list), 0)
t = fs.result()
self.assertIsNotNone(t)
self.assertEqual(len(track.track_list), 1)
self.assertIn(t, track.track_list)
self.assertIsNone(track.add(os.path.join(test_tracks, "01 - Test Track.ogg")).result())
self.assertEqual(len(track.track_list), 1)
self.assertIsNone(track.add("99 - No Such Track.ogg").result())
self.assertEqual(len(track.track_list), 1)
fs = track.add(os.path.join(test_tracks, "02 - Test {Disc 2}.ogg"))
u = fs.result()
self.assertIsNotNone(u)
self.assertEqual(len(track.track_list), 2)
self.assertIn(u, track.track_list)
def test_parallel_add(self):
count = 0
futures = [ ]
done = [ ]
none = [ ]
for dirname, subdirs, files in os.walk(test_library):
for f in files:
futures.append(track.add(os.path.join(dirname, f)))
futures.append(track.add(os.path.join(dirname, f)))
count += 1
self.assertLess(len(track.track_list), count)
for fs in concurrent.futures.as_completed(futures):
if fs.result() is not None:
done.append(fs.result())
else:
none.append(fs.result())
self.assertEqual(len(done), count)
self.assertEqual(len(none), count)
self.assertEqual(len(track.track_list), count)

View File

@ -1,41 +0,0 @@
# Copyright 2019 (c) Anna Schumaker.
import tags
import concurrent.futures
import mutagen
import os
import re
discno_map = { "one" : "1", "two" : "2", "three" : "3", "four" : "4", "five" : "5" }
track_list = [ ]
track_pool = concurrent.futures.ThreadPoolExecutor(max_workers = 1)
class Track:
def __init__(self, path):
fileinfo = mutagen.File(path)
self.path = path
self.title = fileinfo.get("title", [os.path.basename(path)])[0]
self.artist = fileinfo.get("artist", ["Unknown Artist"])[0]
self.tracknumber = int(fileinfo.get("tracknumber", ["0"])[0])
self.length = fileinfo.info.length
self.album = tags.Album.lookup(fileinfo)
self.discnumber = int(fileinfo.get("discnumber", ["1"])[0])
# Try to detect discnumbers that are embedded in the album name
discno = fileinfo.get("album", ["Unknown Album"])[0][len(self.album.album):]
match = re.search("([1-9][0-9]*)|one|two|three|four|five", discno.lower())
if match:
self.discnumber = int(discno_map.get(match.group(), match.group()))
def __add(path):
try:
if not any(t.path == path for t in track_list):
track = Track(path)
track_list.append(track)
return track
except mutagen.MutagenError:
return None
def add(path):
return track_pool.submit(__add, path)