Let Track inherit from FileList

Unifies handling of objects with linked files. Simplifies code, e.g. cover art propagation to parent objects
This commit is contained in:
Philipp Wolfer
2020-12-01 10:01:00 +01:00
parent f83044a5d0
commit a54090a960
12 changed files with 60 additions and 83 deletions

View File

@@ -168,7 +168,7 @@ class Album(DataObject, Item):
if track_discids:
track.metadata['musicbrainz_discid'] = track_discids
track.update()
for file in track.linked_files:
for file in track.files:
file.metadata['musicbrainz_discid'] = track_discids
file.update()
@@ -399,7 +399,7 @@ class Album(DataObject, Item):
self._new_metadata.strip_whitespace()
for track in self.tracks:
for file in list(track.linked_files):
for file in list(track.files):
file.move(self.unmatched_files)
self.metadata = self._new_metadata
self.orig_metadata.copy(self.metadata)
@@ -632,7 +632,7 @@ class Album(DataObject, Item):
def is_modified(self):
if self.tracks:
for track in self.tracks:
for file in track.linked_files:
for file in track.files:
if not file.is_saved():
return True
return False
@@ -640,7 +640,7 @@ class Album(DataObject, Item):
def get_num_unsaved_files(self):
count = 0
for track in self.tracks:
for file in track.linked_files:
for file in track.files:
if not file.is_saved():
count += 1
return count
@@ -746,7 +746,7 @@ class NatAlbum(Album):
for track in self.tracks:
if old_album_title == track.metadata["album"]:
track.metadata["album"] = self.metadata["album"]
for file in track.linked_files:
for file in track.files:
track.update_file_metadata(file)
self.enable_update_metadata_images(True)
super().update(update_tracks)

View File

@@ -750,7 +750,7 @@ class Tagger(QtWidgets.QApplication):
elif isinstance(obj, NonAlbumTrack):
self.remove_nat(obj)
elif isinstance(obj, Track):
files.extend(obj.linked_files)
files.extend(obj.files)
elif isinstance(obj, Album):
self.window.set_statusbar_message(
N_("Removing album %(id)s: %(artist)s - %(album)s"),

View File

@@ -48,6 +48,7 @@ from picard import (
config,
log,
)
from picard.cluster import FileList
from picard.const import (
DATA_TRACK_TITLE,
SILENCE_TRACK_TITLE,
@@ -73,12 +74,9 @@ from picard.util.imagelist import (
ImageList,
add_metadata_images,
remove_metadata_images,
update_metadata_images,
)
from picard.util.textencoding import asciipunct
from picard.ui.item import Item
_TRANSLATE_TAGS = {
"hip hop": "Hip-Hop",
@@ -137,39 +135,42 @@ class TrackArtist(DataObject):
super().__init__(ta_id)
class Track(DataObject, Item):
class Track(DataObject, FileList):
metadata_images_changed = QtCore.pyqtSignal()
def __init__(self, track_id, album=None):
DataObject.__init__(self, track_id)
FileList.__init__(self)
self.album = album
self.linked_files = []
self.num_linked_files = 0
self.metadata = Metadata()
self.orig_metadata = Metadata()
self.scripted_metadata = Metadata()
self._track_artists = []
def __repr__(self):
return '<Track %s %r>' % (self.id, self.metadata["title"])
@property
def linked_files(self):
"""For backward compatibility with old code in plugins"""
return self.files
def add_file(self, file):
if file not in self.linked_files:
if file not in self.files:
track_will_expand = self.num_linked_files == 1
self.linked_files.append(file)
self.files.append(file)
self.num_linked_files += 1
self.update_file_metadata(file)
add_metadata_images(self, [file])
self.album._add_file(self, file)
file.metadata_images_changed.connect(self.update_orig_metadata_images)
file.metadata_images_changed.connect(self.update_metadata_images)
run_file_post_addition_to_track_processors(self, file)
if track_will_expand:
# Files get expanded, ensure the existing item renders correctly
self.linked_files[0].update_item()
self.files[0].update_item()
def update_file_metadata(self, file):
if file not in self.linked_files:
if file not in self.files:
return
# Run the scripts for the file to allow usage of
# file specific metadata and variables
@@ -191,12 +192,12 @@ class Track(DataObject, Item):
self.update()
def remove_file(self, file):
if file not in self.linked_files:
if file not in self.files:
return
self.linked_files.remove(file)
self.files.remove(file)
self.num_linked_files -= 1
file.copy_metadata(file.orig_metadata, preserve_deleted=False)
file.metadata_images_changed.disconnect(self.update_orig_metadata_images)
file.metadata_images_changed.disconnect(self.update_metadata_images)
self.album._remove_file(self, file)
remove_metadata_images(self, [file])
run_file_post_removal_from_track_processors(self, file)
@@ -218,23 +219,19 @@ class Track(DataObject, Item):
if self.item:
self.item.update()
def iterfiles(self, save=False):
for file in self.linked_files:
yield file
def is_linked(self):
return self.num_linked_files > 0
def can_save(self):
"""Return if this object can be saved."""
for file in self.linked_files:
for file in self.files:
if file.can_save():
return True
return False
def can_remove(self):
"""Return if this object can be removed."""
for file in self.linked_files:
for file in self.files:
if file.can_remove():
return True
return False
@@ -254,7 +251,7 @@ class Track(DataObject, Item):
elif column in m:
return m[column]
elif self.num_linked_files == 1:
return self.linked_files[0].column(column)
return self.files[0].column(column)
def is_video(self):
return self.metadata['~video'] == '1'
@@ -348,15 +345,9 @@ class Track(DataObject, Item):
genre = [join_genres.join(genre)]
self.metadata['genre'] = genre
def update_orig_metadata_images(self):
update_metadata_images(self)
self.metadata_images_changed.emit()
def keep_original_images(self):
for file in self.linked_files:
file.keep_original_images()
self.update_orig_metadata_images()
if self.linked_files:
super().keep_original_images()
if self.files:
self.metadata.images = self.orig_metadata.images.copy()
else:
self.metadata.images = ImageList()
@@ -411,7 +402,7 @@ class NonAlbumTrack(Track):
return
try:
self._parse_recording(recording)
for file in self.linked_files:
for file in self.files:
self.update_file_metadata(file)
except Exception:
self._set_error(traceback.format_exc())

View File

@@ -55,7 +55,6 @@ from picard.coverart.image import (
CoverArtImageError,
)
from picard.file import File
from picard.track import Track
from picard.util import imageinfo
from picard.util.lrucache import LRUCache
@@ -488,29 +487,16 @@ class CoverArtBox(QtWidgets.QGroupBox):
album.update_metadata_images()
album.update(False)
elif isinstance(self.item, FileList):
cluster = self.item
cluster.enable_update_metadata_images(False)
set_image(cluster, coverartimage)
for file in cluster.iterfiles():
filelist = self.item
filelist.enable_update_metadata_images(False)
set_image(filelist, coverartimage)
for file in filelist.iterfiles():
set_image(file, coverartimage)
file.metadata_images_changed.emit()
file.update(signal=False)
cluster.enable_update_metadata_images(True)
cluster.update_metadata_images()
cluster.update()
elif isinstance(self.item, Track):
track = self.item
track.album.enable_update_metadata_images(False)
set_image(track, coverartimage)
track.metadata_images_changed.emit()
for file in track.iterfiles():
set_image(file, coverartimage)
file.metadata_images_changed.emit()
file.update(signal=False)
track.album.enable_update_metadata_images(True)
track.album.update_metadata_images()
track.album.update(False)
track.update()
filelist.enable_update_metadata_images(True)
filelist.update_metadata_images()
filelist.update()
elif isinstance(self.item, File):
file = self.item
set_image(file, coverartimage)

View File

@@ -399,7 +399,7 @@ class TrackInfoDialog(InfoDialog):
tabWidget.setTabText(tab_index, _("&Info"))
text = ngettext("%i file in this track", "%i files in this track",
track.num_linked_files) % track.num_linked_files
info_files = [format_file_info(file_) for file_ in track.linked_files]
info_files = [format_file_info(file_) for file_ in track.files]
text += '<hr />' + '<hr />'.join(info_files)
self.ui.info.setText(text)

View File

@@ -1018,7 +1018,7 @@ class TrackItem(TreeItem):
def update(self, update_album=True, update_files=True):
track = self.obj
if track.num_linked_files == 1:
file = track.linked_files[0]
file = track.files[0]
file.item = self
color = TrackItem.track_colors[file.state]
bgcolor = get_match_color(file.similarity, TreeItem.base_color)
@@ -1053,14 +1053,14 @@ class TrackItem(TreeItem):
oldnum = newnum
for i in range(oldnum): # update existing items
item = self.child(i)
file = track.linked_files[i]
file = track.files[i]
item.obj = file
file.item = item
item.update(update_track=False)
if newnum > oldnum: # add new items
items = []
for i in range(newnum - 1, oldnum - 1, -1):
item = FileItem(track.linked_files[i], False)
item = FileItem(track.files[i], False)
item.update(update_track=False)
items.append(item)
self.addChildren(items)

View File

@@ -1077,7 +1077,7 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
def show_more_tracks(self):
obj = self.selected_objects[0]
if isinstance(obj, Track):
obj = obj.linked_files[0]
obj = obj.files[0]
dialog = TrackSearchDialog(self)
dialog.load_similar_tracks(obj)
dialog.exec_()
@@ -1193,7 +1193,7 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
self.set_statusbar_message(msg, mparms, echo=None, history=None)
elif isinstance(obj, Track):
if obj.num_linked_files == 1:
file = obj.linked_files[0]
file = obj.files[0]
if file.has_error():
msg = N_("%(filename)s (%(similarity)d%%) (error: %(error)s)")
mparms = {

View File

@@ -395,7 +395,7 @@ class MetadataBox(QtWidgets.QTableWidget):
track_albums = set()
for file in self.files:
objects = [file]
if file.parent in self.tracks and len(self.files & set(file.parent.linked_files)) == 1:
if file.parent in self.tracks and len(self.files & set(file.parent.files)) == 1:
objects.append(file.parent)
file_tracks.append(file.parent)
track_albums.add(file.parent.album)
@@ -503,7 +503,7 @@ class MetadataBox(QtWidgets.QTableWidget):
files.add(obj)
elif isinstance(obj, Track):
tracks.add(obj)
files.update(obj.linked_files)
files.update(obj.files)
elif isinstance(obj, Cluster) and obj.can_edit_tags():
objects.add(obj)
files.update(obj.files)
@@ -511,7 +511,7 @@ class MetadataBox(QtWidgets.QTableWidget):
objects.add(obj)
tracks.update(obj.tracks)
for track in obj.tracks:
files.update(track.linked_files)
files.update(track.files)
objects.update(files)
objects.update(tracks)
self.selection_dirty = False

View File

@@ -95,7 +95,7 @@ class RatingWidget(QtWidgets.QWidget):
track = self._track
rating = str(self._rating)
track.metadata["~rating"] = rating
for file in track.linked_files:
for file in track.files:
file.metadata["~rating"] = rating
if config.setting["submit_ratings"]:
ratings = {("recording", track.id): self._rating}

View File

@@ -82,4 +82,4 @@ class ScriptsMenu(QtWidgets.QMenu):
yield from self._get_metadata_objects(obj.tracks)
yield from self._get_metadata_objects(obj.unmatched_files.iterfiles())
if isinstance(obj, Track):
yield from self._get_metadata_objects(obj.linked_files)
yield from self._get_metadata_objects(obj.files)

View File

@@ -147,12 +147,12 @@ def _get_state(obj):
if isinstance(obj, Album):
for track in obj.tracks:
state.sources.append(track)
state.sources += track.linked_files
state.sources += track.files
state.sources += obj.unmatched_files.files
state.update_new_metadata = True
state.update_orig_metadata = True
elif isinstance(obj, Track):
state.sources = obj.linked_files
state.sources = obj.files
state.update_orig_metadata = True
elif isinstance(obj, Cluster):
state.sources = obj.files

View File

@@ -88,22 +88,22 @@ class UpdateMetadataImagesTest(PicardTestCase):
def test_update_track_images(self):
track = Track('00000000-0000-0000-0000-000000000000')
track.linked_files = list(self.test_files)
track.files = list(self.test_files)
update_metadata_images(track)
self.assertEqual(set(self.test_images), set(track.orig_metadata.images))
self.assertFalse(track.orig_metadata.has_common_images)
track.linked_files.remove(self.test_files[2])
track.files.remove(self.test_files[2])
update_metadata_images(track)
self.assertEqual(set(self.test_images), set(track.orig_metadata.images))
self.assertFalse(track.orig_metadata.has_common_images)
track.linked_files.remove(self.test_files[0])
track.files.remove(self.test_files[0])
update_metadata_images(track)
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
self.assertTrue(track.orig_metadata.has_common_images)
track.linked_files.append(self.test_files[2])
track.files.append(self.test_files[2])
update_metadata_images(track)
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
self.assertTrue(track.orig_metadata.has_common_images)
@@ -111,9 +111,9 @@ class UpdateMetadataImagesTest(PicardTestCase):
def test_update_album_images(self):
album = Album('00000000-0000-0000-0000-000000000000')
track1 = Track('00000000-0000-0000-0000-000000000001')
track1.linked_files.append(self.test_files[0])
track1.files.append(self.test_files[0])
track2 = Track('00000000-0000-0000-0000-000000000002')
track2.linked_files.append(self.test_files[1])
track2.files.append(self.test_files[1])
album.tracks = [track1, track2]
album.unmatched_files.files.append(self.test_files[2])
update_metadata_images(album)
@@ -171,27 +171,27 @@ class RemoveMetadataImagesTest(PicardTestCase):
def test_remove_from_track(self):
track = Track('00000000-0000-0000-0000-000000000000')
track.linked_files = list(self.test_files)
track.files = list(self.test_files)
update_metadata_images(track)
track.linked_files.remove(self.test_files[0])
track.files.remove(self.test_files[0])
remove_metadata_images(track, [self.test_files[0]])
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
self.assertTrue(track.orig_metadata.has_common_images)
def test_remove_from_track_with_common_images(self):
track = Track('00000000-0000-0000-0000-000000000000')
track.linked_files = list(self.test_files[1:])
track.files = list(self.test_files[1:])
update_metadata_images(track)
track.linked_files.remove(self.test_files[1])
track.files.remove(self.test_files[1])
remove_metadata_images(track, [self.test_files[1]])
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
self.assertTrue(track.orig_metadata.has_common_images)
def test_remove_from_empty_track(self):
track = Track('00000000-0000-0000-0000-000000000000')
track.linked_files.append(File('test1.flac'))
track.files.append(File('test1.flac'))
update_metadata_images(track)
remove_metadata_images(track, [track.linked_files[0]])
remove_metadata_images(track, [track.files[0]])
self.assertEqual(set(), set(track.orig_metadata.images))
self.assertTrue(track.orig_metadata.has_common_images)