From 39da88b9ac3fa0c8a42e7fc992d13847c67dc596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 19 Nov 2006 18:12:59 +0100 Subject: [PATCH 1/4] * Move picard.plugins.picardmutagen to picard.formats * IFileOpener.open_file now returns only one file * Removed IFileOpener.can_open_file --- picard/file.py | 31 ++++++-- .../picardmutagen => formats}/__init__.py | 20 ++--- .../picardmutagen => formats}/apev2.py | 0 .../{plugins/picardmutagen => formats}/asf.py | 2 +- .../{plugins/picardmutagen => formats}/id3.py | 2 +- .../{plugins/picardmutagen => formats}/mp4.py | 0 .../mutagenext/__init__.py | 0 .../mutagenext/asf.py | 0 .../mutagenext/compatid3.py | 0 .../mutagenext/optimfrog.py | 0 .../picardmutagen => formats}/vorbis.py | 0 picard/plugins/__init__.py | 5 +- picard/tagger.py | 75 +++++++------------ 13 files changed, 62 insertions(+), 73 deletions(-) rename picard/{plugins/picardmutagen => formats}/__init__.py (87%) rename picard/{plugins/picardmutagen => formats}/apev2.py (100%) rename picard/{plugins/picardmutagen => formats}/asf.py (97%) rename picard/{plugins/picardmutagen => formats}/id3.py (99%) rename picard/{plugins/picardmutagen => formats}/mp4.py (100%) rename picard/{plugins/picardmutagen => formats}/mutagenext/__init__.py (100%) rename picard/{plugins/picardmutagen => formats}/mutagenext/asf.py (100%) rename picard/{plugins/picardmutagen => formats}/mutagenext/compatid3.py (100%) rename picard/{plugins/picardmutagen => formats}/mutagenext/optimfrog.py (100%) rename picard/{plugins/picardmutagen => formats}/vorbis.py (100%) diff --git a/picard/file.py b/picard/file.py index a0a8cf129..292672c4f 100644 --- a/picard/file.py +++ b/picard/file.py @@ -27,6 +27,13 @@ from picard.util import LockableObject, needs_write_lock, needs_read_lock, encod class File(LockableObject): + __id_counter = 0 + + @staticmethod + def new_id(): + File.__id_counter += 1 + return File.__id_counter + NEW = 0 CHANGED = 1 TO_BE_SAVED = 2 @@ -38,23 +45,31 @@ class File(LockableObject): self.filename = filename self.base_filename = os.path.basename(filename) self.state = File.NEW + self.orig_metadata = Metadata() - self.metadata = Metadata() + self.user_metadata = Metadata() + self.server_metadata = Metadata() + self.metadata = self.user_metadata + + self.orig_metadata["~#length"] = 0 + self.orig_metadata["title"] = os.path.basename(self.filename) + + self.user_metadata.copy(self.orig_metadata) + self.server_metadata.copy(self.orig_metadata) + self.similarity = 1.0 self.parent = None def __str__(self): return '' % (self.id, self.base_filename) - __id_counter = 0 - - @staticmethod - def new_id(): - File.__id_counter += 1 - return File.__id_counter + def load(self): + """Save the metadata.""" + self.read() + #raise NotImplementedError def save(self): - """Save the file.""" + """Save the metadata.""" raise NotImplementedError def save_images(self): diff --git a/picard/plugins/picardmutagen/__init__.py b/picard/formats/__init__.py similarity index 87% rename from picard/plugins/picardmutagen/__init__.py rename to picard/formats/__init__.py index 285b9507b..b978fff04 100644 --- a/picard/plugins/picardmutagen/__init__.py +++ b/picard/formats/__init__.py @@ -79,19 +79,19 @@ mutagen._util.delete_bytes = _delete_bytes from picard.api import IFileOpener from picard.component import Component, implements -from picard.plugins.picardmutagen.asf import MutagenASFFile -from picard.plugins.picardmutagen.mp4 import MP4File -from picard.plugins.picardmutagen.id3 import ( +from picard.formats.asf import MutagenASFFile +from picard.formats.mp4 import MP4File +from picard.formats.id3 import ( MP3File, TrueAudioFile, ) -from picard.plugins.picardmutagen.apev2 import ( +from picard.formats.apev2 import ( MonkeysAudioFile, MusepackFile, OptimFROGFile, WavPackFile, ) -from picard.plugins.picardmutagen.vorbis import ( +from picard.formats.vorbis import ( FLACFile, OggFLACFile, OggSpeexFile, @@ -128,16 +128,8 @@ class MutagenComponent(Component): return [(key, value[1]) for key, value in self.__supported_formats.items()] - def can_open_file(self, filename): - for ext in self.__supported_formats.keys(): - if filename.lower().endswith(ext): - return True - return False - def open_file(self, filename): for ext in self.__supported_formats.keys(): if filename.lower().endswith(ext): - file = self.__supported_formats[ext][0](filename) - file.read() - return (file,) + return self.__supported_formats[ext][0](filename) return None diff --git a/picard/plugins/picardmutagen/apev2.py b/picard/formats/apev2.py similarity index 100% rename from picard/plugins/picardmutagen/apev2.py rename to picard/formats/apev2.py diff --git a/picard/plugins/picardmutagen/asf.py b/picard/formats/asf.py similarity index 97% rename from picard/plugins/picardmutagen/asf.py rename to picard/formats/asf.py index 0eabcf797..99fe7951b 100644 --- a/picard/plugins/picardmutagen/asf.py +++ b/picard/formats/asf.py @@ -21,7 +21,7 @@ from picard.file import File from picard.util import encode_filename -from picard.plugins.picardmutagen.mutagenext.asf import ASF +from picard.formats.mutagenext.asf import ASF class MutagenASFFile(File): diff --git a/picard/plugins/picardmutagen/id3.py b/picard/formats/id3.py similarity index 99% rename from picard/plugins/picardmutagen/id3.py rename to picard/formats/id3.py index 9427d06d7..bb16e1b1e 100644 --- a/picard/plugins/picardmutagen/id3.py +++ b/picard/formats/id3.py @@ -22,7 +22,7 @@ import mutagen.mp3 import mutagen.trueaudio from mutagen import id3 from picard.file import File -from picard.plugins.picardmutagen.mutagenext import compatid3 +from picard.formats.mutagenext import compatid3 from picard.util import encode_filename class ID3File(File): diff --git a/picard/plugins/picardmutagen/mp4.py b/picard/formats/mp4.py similarity index 100% rename from picard/plugins/picardmutagen/mp4.py rename to picard/formats/mp4.py diff --git a/picard/plugins/picardmutagen/mutagenext/__init__.py b/picard/formats/mutagenext/__init__.py similarity index 100% rename from picard/plugins/picardmutagen/mutagenext/__init__.py rename to picard/formats/mutagenext/__init__.py diff --git a/picard/plugins/picardmutagen/mutagenext/asf.py b/picard/formats/mutagenext/asf.py similarity index 100% rename from picard/plugins/picardmutagen/mutagenext/asf.py rename to picard/formats/mutagenext/asf.py diff --git a/picard/plugins/picardmutagen/mutagenext/compatid3.py b/picard/formats/mutagenext/compatid3.py similarity index 100% rename from picard/plugins/picardmutagen/mutagenext/compatid3.py rename to picard/formats/mutagenext/compatid3.py diff --git a/picard/plugins/picardmutagen/mutagenext/optimfrog.py b/picard/formats/mutagenext/optimfrog.py similarity index 100% rename from picard/plugins/picardmutagen/mutagenext/optimfrog.py rename to picard/formats/mutagenext/optimfrog.py diff --git a/picard/plugins/picardmutagen/vorbis.py b/picard/formats/vorbis.py similarity index 100% rename from picard/plugins/picardmutagen/vorbis.py rename to picard/formats/vorbis.py diff --git a/picard/plugins/__init__.py b/picard/plugins/__init__.py index 1bdbd1567..58067efcb 100644 --- a/picard/plugins/__init__.py +++ b/picard/plugins/__init__.py @@ -19,6 +19,5 @@ """Built-in plugins.""" -import picard.plugins.cuesheet -import picard.plugins.csv_opener -import picard.plugins.picardmutagen +#import picard.plugins.cuesheet +#import picard.plugins.csv_opener diff --git a/picard/tagger.py b/picard/tagger.py index 5627010d8..04e29477a 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -31,6 +31,7 @@ import imp import picard.resources import picard.plugins +import picard.formats import picard.tagz from picard import musicdns @@ -248,53 +249,35 @@ class Tagger(QtGui.QApplication, ComponentManager, Component): formats.extend(opener.get_supported_formats()) return formats - def add_files(self, files): - """Load and add files.""" - files = map(os.path.normpath, files) - self.log.debug(u"Adding files %r", files) - filenames = [] - for filename in files: - not_found = True - for file in self.files: - if file.filename == filename: - not_found = False - break - if not_found: - for opener in self.file_openers: - if opener.can_open_file(filename): - filenames.append((filename, opener.open_file)) - if filenames: - self.thread_assist.spawn(self.__add_files_thread, filenames, - thread=self.load_thread) - - def __add_files_thread(self, filenames): - """Load the files.""" - files = [] - for filename, opener in filenames: - try: - files.extend(opener(filename)) - except: - import traceback; traceback.print_exc() - while files: - self.thread_assist.proxy_to_main(self.__add_files_finished, - files[:100]) - files = files[100:] - - def __add_files_finished(self, files): - """Add loaded files to the tagger.""" - for file in files: - self.files.append(file) - album_id = file.metadata["musicbrainz_albumid"] - if album_id: - album = self.get_album_by_id(album_id) - if not album: - album = self.load_album(album_id) - if album.loaded: - self.match_files_to_album([file], album) - else: - self._move_to_album.append((file, album)) - if not file.parent: + def add_files(self, filenames): + """Add files to the tagger.""" + self.log.debug(u"Adding files %r", filenames) + for filename in filenames: + filename = os.path.normpath(filename) + if self.get_file_by_filename(filename): + continue + for opener in self.file_openers: + file = opener.open_file(filename) + if not file: + continue file.move(self.unmatched_files) + self.files.append(file) + self.thread_assist.spawn( + self.__load_file_thread, file, thread=self.load_thread) + + def __load_file_thread(self, file): + """Load metadata from the file.""" + self.log.debug(u"Loading file %r", file.filename) + file.load() + self.thread_assist.proxy_to_main(self.__load_file_finished, file) + + def __load_file_finished(self, file): + """Move loaded file to right album/cluster.""" + file.update() + album_id = file.metadata["musicbrainz_albumid"] + if album_id: + album = self.load_album(album_id) + self.move_files_to_album([file], album) def add_directory(self, directory): """Add all files from the directory ``directory`` to the tagger.""" From 536a233aa339c991bb615112b3eaf77efbed5a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 19 Nov 2006 18:40:47 +0100 Subject: [PATCH 2/4] Update Tagger.__read_directory_thread --- picard/api.py | 3 --- picard/tagger.py | 18 ++++++++---------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/picard/api.py b/picard/api.py index 52049bd44..fdccbe480 100644 --- a/picard/api.py +++ b/picard/api.py @@ -38,9 +38,6 @@ class IFileOpener(Interface): def get_supported_formats(self): pass - def can_open_file(self, filename): - pass - def open_file(self, filename): pass diff --git a/picard/tagger.py b/picard/tagger.py index 04e29477a..31e97591a 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -288,23 +288,21 @@ class Tagger(QtGui.QApplication, ComponentManager, Component): def __read_directory_thread(self, directory): directories = [encode_filename(directory)] while directories: - files = [] directory = directories.pop() self.log.debug(u"Reading directory %r", directory) - self.thread_assist.proxy_to_main(self.__set_status_bar_message, - N_("Reading directory %s ..."), - directory) + self.thread_assist.proxy_to_main( + self.__set_status_bar_message, + N_("Reading directory %s ..."), decode_filename(directory)) + filenames = [] for name in os.listdir(directory): name = os.path.join(directory, name) if os.path.isdir(name): directories.append(name) else: - files.append(decode_filename(name)) - while files: - self.thread_assist.proxy_to_main(self.add_files, files[:100]) - files = files[100:] - self.thread_assist.proxy_to_main(self.__set_status_bar_message, - N_("Done")) + filenames.append(decode_filename(name)) + if filenames: + self.thread_assist.proxy_to_main(self.add_files, filenames) + self.thread_assist.proxy_to_main(self.__clear_status_bar_message) def get_file_by_id(self, id): """Get file by a file ID.""" From 7aacf597aa220830d0149c129c7192da80123e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Tue, 21 Nov 2006 22:59:27 +0100 Subject: [PATCH 3/4] Added PENDING file state. Display files in colors based on their state. --- picard/file.py | 14 ++++++------ picard/tagger.py | 8 +++---- picard/ui/itemviews.py | 48 +++++++++++++++++++++++++++++------------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/picard/file.py b/picard/file.py index 292672c4f..593e20eab 100644 --- a/picard/file.py +++ b/picard/file.py @@ -34,17 +34,18 @@ class File(LockableObject): File.__id_counter += 1 return File.__id_counter - NEW = 0 - CHANGED = 1 - TO_BE_SAVED = 2 - SAVED = 3 + PENDING = 0 + NORMAL = 1 + CHANGED = 2 + ERROR = 3 + SAVED = 4 def __init__(self, filename): LockableObject.__init__(self) self.id = self.new_id() self.filename = filename self.base_filename = os.path.basename(filename) - self.state = File.NEW + self.state = File.PENDING self.orig_metadata = Metadata() self.user_metadata = Metadata() @@ -66,7 +67,7 @@ class File(LockableObject): def load(self): """Save the metadata.""" self.read() - #raise NotImplementedError + self.state = File.NORMAL def save(self): """Save the metadata.""" @@ -142,7 +143,6 @@ class File(LockableObject): similarity = metadata1.compare(metadata2) self.lock_for_write() self.similarity = similarity - self.state = self.CHANGED self.unlock() if signal: self.log.debug(u"Updating file %s", self) diff --git a/picard/tagger.py b/picard/tagger.py index 31e97591a..08d7503da 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -365,12 +365,9 @@ class Tagger(QtGui.QApplication, ComponentManager, Component): def save(self, objects): """Save the specified objects.""" - files = [] - for file in self.get_files_from_objects(objects): - file.state = File.TO_BE_SAVED - files.append(file) self.set_wait_cursor() - self.thread_assist.spawn(self.__save_thread, files) + self.thread_assist.spawn(self.__save_thread, + self.get_files_from_objects(objects)) def __rename_file(self, file): file.lock_for_read() @@ -445,6 +442,7 @@ class Tagger(QtGui.QApplication, ComponentManager, Component): failed = False try: file.save() + file.state = File.SAVED old_filename = self.__rename_file(file) if (self.config.setting["move_files"] and self.config.setting["move_additional_files"]): diff --git a/picard/ui/itemviews.py b/picard/ui/itemviews.py index 15befcfb3..e7e651431 100644 --- a/picard/ui/itemviews.py +++ b/picard/ui/itemviews.py @@ -28,15 +28,6 @@ from picard.config import TextOption __all__ = ["FileTreeView", "AlbumTreeView"] -def matchColor(similarity): - colors = ((255, 255, 255), (223, 125, 125)) - res = [0, 0, 0] - #similarity = (1 - similarity) * (1 - similarity) - similarity = 1 - similarity - for i in range(3): - res[i] = colors[0][i] + (colors[1][i] - colors[0][i]) * similarity - return QtGui.QColor(res[0], res[1], res[2]) - class BaseTreeView(QtGui.QTreeWidget): options = [ @@ -76,6 +67,8 @@ class BaseTreeView(QtGui.QTreeWidget): self.contextMenu.addAction(self.main_window.save_action) self.contextMenu.addAction(self.main_window.remove_action) + self.__file_state_colors[File.NORMAL] = self.tagger.palette().text().color() + self.objectToItem = {} self.itemToObject = {} @@ -115,6 +108,25 @@ class BaseTreeView(QtGui.QTreeWidget): def get_item_from_object(self, obj): return self.objectToItem[obj] + __file_state_colors = { + File.PENDING: QtGui.QColor(128, 128, 128), + File.NORMAL: QtGui.QColor(0, 0, 0), + File.CHANGED: QtGui.QColor(0, 0, 64), + File.ERROR: QtGui.QColor(128, 0, 0), + File.SAVED: QtGui.QColor(0, 128, 0), + } + + def get_file_state_color(self, state): + return self.__file_state_colors[state] + + def get_file_match_color(self, similarity): + c1 = (255, 255, 255) + c2 = (223, 125, 125) + return QtGui.QColor( + c2[0] + (c1[0] - c2[0]) * similarity, + c2[1] + (c1[1] - c2[1]) * similarity, + c2[2] + (c1[2] - c2[2]) * similarity) + def supportedDropActions(self): return QtCore.Qt.MoveAction | QtCore.Qt.CopyAction @@ -275,9 +287,11 @@ class FileTreeView(BaseTreeView): item.setText(0, metadata[u"title"]) item.setText(1, format_time(metadata.get(u"~#length", 0))) item.setText(2, metadata[u"artist"]) - color = matchColor(file.similarity) + fg_color = self.get_file_state_color(file.state) + bg_color = self.get_file_match_color(file.similarity) for i in range(3): - item.setBackgroundColor(i, color) + item.setTextColor(i, fg_color) + item.setBackgroundColor(i, bg_color) def update_file(self, file): try: @@ -385,6 +399,7 @@ class AlbumTreeView(BaseTreeView): item = self.get_item_from_object(track) if track.is_linked(): file = track.linked_file + state = file.state if file.state == File.SAVED: similarity = 1.0 icon = self.icon_saved @@ -393,13 +408,16 @@ class AlbumTreeView(BaseTreeView): icon = self.matchIcons[int(similarity * 5 + 0.5)] else: similarity = 1 + state = File.NORMAL icon = self.noteIcon - - color = matchColor(similarity) + # Colors + fg_color = self.get_file_state_color(state) + bg_color = self.get_file_match_color(similarity) for i in range(3): - item.setBackgroundColor(i, color) + item.setTextColor(i, fg_color) + item.setBackgroundColor(i, bg_color) + # Icon item.setIcon(0, icon) - self._set_album_metadata(track.album) def add_album(self, album): From 48e8c49d58e54bf23b4afa62320de9198bf36a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Wed, 22 Nov 2006 22:15:10 +0100 Subject: [PATCH 4/4] Set "pending" status before fingerprinting the file. --- picard/tagger.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/picard/tagger.py b/picard/tagger.py index 08d7503da..a475b9e22 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -691,6 +691,9 @@ class Tagger(QtGui.QApplication, ComponentManager, Component): def analyze(self, objs): """Analyze the selected files.""" files = self.get_files_from_objects(objs) + for file in files: + file.state = File.PENDING + file.update() self.thread_assist.spawn(self.__analyze_thread, files, thread=self._analyze_thread) @@ -713,6 +716,8 @@ class Tagger(QtGui.QApplication, ComponentManager, Component): from picard.musicdns.webservice import TrackFilter, Query ws = self.get_web_service(host="ofa.musicdns.org", pathPrefix="/ofa") for file in files: + if file.state != File.PENDING: + continue file.lock_for_read() try: filename = file.filename @@ -755,6 +760,9 @@ class Tagger(QtGui.QApplication, ComponentManager, Component): self.__lookup_puid(file) else: self.log.debug("Fingerprint looked up, no PUID found.") + if file.state == File.PENDING: + file.state = File.NORMAL + file.update() def cluster(self, objs): """Group files with similar metadata to 'clusters'."""