Merge pull request #1431 from phw/PICARD-34-acoustid-fingerprint-indicator

PICARD-34: AcoustID fingerprint indicator
This commit is contained in:
Philipp Wolfer
2020-01-14 17:26:26 +01:00
committed by GitHub
15 changed files with 12395 additions and 11728 deletions

View File

@@ -62,18 +62,24 @@ class AcoustIDManager(QtCore.QObject):
del self._fingerprints[file]
self._check_unsubmitted()
def is_submitted(self, file):
submission = self._fingerprints.get(file)
if submission:
return not submission.recordingid or submission.orig_recordingid == submission.recordingid
return True
def _unsubmitted(self):
for submission in self._fingerprints.values():
for file, submission in self._fingerprints.items():
if submission.recordingid and submission.orig_recordingid != submission.recordingid:
yield submission
yield (file, submission)
def _check_unsubmitted(self):
enabled = next(self._unsubmitted(), None) is not None
self.tagger.window.enable_submit(enabled)
def submit(self):
fingerprints = list(self._unsubmitted())
if not fingerprints:
submissions = list(self._unsubmitted())
if not submissions:
self._check_unsubmitted()
return
log.debug("AcoustID: submitting ...")
@@ -81,10 +87,11 @@ class AcoustIDManager(QtCore.QObject):
N_('Submitting AcoustIDs ...'),
echo=None
)
fingerprints = [fingerprint for _, fingerprint in submissions]
self.tagger.acoustid_api.submit_acoustid_fingerprints(fingerprints,
partial(self.__fingerprint_submission_finished, fingerprints))
partial(self.__fingerprint_submission_finished, submissions))
def __fingerprint_submission_finished(self, fingerprints, document, http, error):
def __fingerprint_submission_finished(self, submissions, document, http, error):
if error:
try:
error = load_json(document)
@@ -111,6 +118,7 @@ class AcoustIDManager(QtCore.QObject):
echo=None,
timeout=3000
)
for submission in fingerprints:
for file, submission in submissions:
submission.orig_recordingid = submission.recordingid
file.update()
self._check_unsubmitted()

View File

@@ -531,6 +531,7 @@ class File(QtCore.QObject, Item):
if not recording_id:
recording_id = self.metadata['musicbrainz_recordingid']
self.tagger.acoustidmanager.update(self, recording_id)
self.update_item()
@classmethod
def supports_tag(cls, name):

File diff suppressed because it is too large Load Diff

View File

@@ -137,6 +137,7 @@ class Track(DataObject, Item):
def add_file(self, file):
if file not in self.linked_files:
track_will_expand = self.num_linked_files == 1
self.linked_files.append(file)
self.num_linked_files += 1
self.update_file_metadata(file)
@@ -144,6 +145,9 @@ class Track(DataObject, Item):
self.album._add_file(self, file)
file.metadata_images_changed.connect(self.update_orig_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()
def update_file_metadata(self, file):
if file not in self.linked_files:

View File

@@ -65,6 +65,9 @@ from picard.ui.scriptsmenu import ScriptsMenu
from picard.ui.widgets.tristatesortheaderview import TristateSortHeaderView
COLUMN_ICON_SIZE = 16
class BaseAction(QtWidgets.QAction):
NAME = "Unknown"
MENU = []
@@ -137,8 +140,12 @@ class MainPanel(QtWidgets.QSplitter):
(N_('Barcode'), 'barcode'),
(N_('Media'), 'media'),
(N_('Genre'), 'genre'),
(N_('Fingerprint status'), '~fingerprint'),
]
TITLE_COLUMN = 0
FINGERPRINT_COLUMN = 13
def __init__(self, window, parent=None):
super().__init__(parent)
self.window = window
@@ -195,6 +202,8 @@ class MainPanel(QtWidgets.QSplitter):
FileItem.icon_file_pending = QtGui.QIcon(":/images/file-pending.png")
FileItem.icon_error = icontheme.lookup('dialog-error', icontheme.ICON_SIZE_MENU)
FileItem.icon_saved = QtGui.QIcon(":/images/track-saved.png")
FileItem.icon_fingerprint = icontheme.lookup('fingerprint', icontheme.ICON_SIZE_MENU)
FileItem.icon_fingerprint_gray = icontheme.lookup('fingerprint-gray', icontheme.ICON_SIZE_MENU)
FileItem.match_icons = [
QtGui.QIcon(":/images/match-50.png"),
QtGui.QIcon(":/images/match-60.png"),
@@ -256,6 +265,35 @@ class MainPanel(QtWidgets.QSplitter):
self.update_current_view()
def paint_fingerprint_icon(painter, rect, icon):
if not icon:
return
size = COLUMN_ICON_SIZE
padding_h = (rect.width() - size) / 2
padding_v = (rect.height() - size) / 2
target_rect = QtCore.QRect(rect.x() + padding_h, rect.y() + padding_v, size, size)
painter.drawPixmap(target_rect, icon.pixmap(size, size))
class FingerprintColumnWidget(QtWidgets.QWidget):
def __init__(self, file, parent=None):
super().__init__(parent=parent)
self._file = file
def paintEvent(self, event=None):
painter = QtGui.QPainter(self)
paint_fingerprint_icon(painter, event.rect(), self.decide_icon())
def decide_icon(self):
if getattr(self._file, 'acoustid_fingerprint', None):
if self.tagger.acoustidmanager.is_submitted(self._file):
return FileItem.icon_fingerprint_gray
else:
return FileItem.icon_fingerprint
else:
return None
class ConfigurableColumnsHeader(TristateSortHeaderView):
def __init__(self, parent=None):
@@ -268,6 +306,7 @@ class ConfigurableColumnsHeader(TristateSortHeaderView):
self.setStretchLastSection(True)
self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.setSectionsClickable(False)
self.sortIndicatorChanged.connect(self.on_sort_indicator_changed)
# enable sorting, but don't actually use it by default
# XXX it would be nice to be able to go to the 'no sort' mode, but the
@@ -283,6 +322,11 @@ class ConfigurableColumnsHeader(TristateSortHeaderView):
if self.sectionSize(column) == 0:
self.resizeSection(column, self.defaultSectionSize())
self._visible_columns.add(column)
if column == MainPanel.FINGERPRINT_COLUMN:
self.setSectionResizeMode(column, QtWidgets.QHeaderView.Fixed)
self.parent().resizeColumnToContents(column)
else:
self.setSectionResizeMode(column, QtWidgets.QHeaderView.Interactive)
elif column in self._visible_columns:
self._visible_columns.remove(column)
@@ -317,6 +361,19 @@ class ConfigurableColumnsHeader(TristateSortHeaderView):
def restore_defaults(self):
self.parent().restore_default_columns()
def paintSection(self, painter, rect, index):
if index == MainPanel.FINGERPRINT_COLUMN:
painter.save()
super().paintSection(painter, rect, index)
painter.restore()
paint_fingerprint_icon(painter, rect, FileItem.icon_fingerprint_gray)
else:
super().paintSection(painter, rect, index)
def on_sort_indicator_changed(self, index, order):
if index == MainPanel.FINGERPRINT_COLUMN:
self.setSortIndicator(-1, QtCore.Qt.AscendingOrder)
class BaseTreeView(QtWidgets.QTreeWidget):
@@ -326,7 +383,8 @@ class BaseTreeView(QtWidgets.QTreeWidget):
self.window = window
self.panel = parent
self.setHeaderLabels([_(h) for h, n in MainPanel.columns])
self.setHeaderLabels([_(h) if n != '~fingerprint' else ''
for h, n in MainPanel.columns])
self.restore_state()
self.setAcceptDrops(True)
@@ -716,7 +774,7 @@ class FileTreeView(BaseTreeView):
self.set_clusters_text()
def set_clusters_text(self):
self.clusters.setText(0, '%s (%d)' % (_("Clusters"), len(self.tagger.clusters)))
self.clusters.setText(MainPanel.TITLE_COLUMN, '%s (%d)' % (_("Clusters"), len(self.tagger.clusters)))
class AlbumTreeView(BaseTreeView):
@@ -736,7 +794,7 @@ class AlbumTreeView(BaseTreeView):
self.insertTopLevelItem(0, item)
else:
item = AlbumItem(album, True, self)
item.setIcon(0, AlbumItem.icon_cd)
item.setIcon(MainPanel.TITLE_COLUMN, AlbumItem.icon_cd)
for i, column in enumerate(MainPanel.columns):
font = item.font(i)
font.setBold(True)
@@ -777,7 +835,7 @@ class ClusterItem(TreeItem):
def __init__(self, *args):
super().__init__(*args)
self.setIcon(0, ClusterItem.icon_dir)
self.setIcon(MainPanel.TITLE_COLUMN, ClusterItem.icon_dir)
def update(self):
for i, column in enumerate(MainPanel.columns):
@@ -843,22 +901,22 @@ class AlbumItem(TreeItem):
for item in items: # Update after insertChildren so that setExpanded works
item.update(update_album=False)
if album.errors:
self.setIcon(0, AlbumItem.icon_error)
self.setToolTip(0, _("Error"))
self.setIcon(MainPanel.TITLE_COLUMN, AlbumItem.icon_error)
self.setToolTip(MainPanel.TITLE_COLUMN, _("Error"))
elif album.is_complete():
if album.is_modified():
self.setIcon(0, AlbumItem.icon_cd_saved_modified)
self.setToolTip(0, _("Album modified and complete"))
self.setIcon(MainPanel.TITLE_COLUMN, AlbumItem.icon_cd_saved_modified)
self.setToolTip(MainPanel.TITLE_COLUMN, _("Album modified and complete"))
else:
self.setIcon(0, AlbumItem.icon_cd_saved)
self.setToolTip(0, _("Album unchanged and complete"))
self.setIcon(MainPanel.TITLE_COLUMN, AlbumItem.icon_cd_saved)
self.setToolTip(MainPanel.TITLE_COLUMN, _("Album unchanged and complete"))
else:
if album.is_modified():
self.setIcon(0, AlbumItem.icon_cd_modified)
self.setToolTip(0, _("Album modified"))
self.setIcon(MainPanel.TITLE_COLUMN, AlbumItem.icon_cd_modified)
self.setToolTip(MainPanel.TITLE_COLUMN, _("Album modified"))
else:
self.setIcon(0, AlbumItem.icon_cd)
self.setToolTip(0, _("Album unchanged"))
self.setIcon(MainPanel.TITLE_COLUMN, AlbumItem.icon_cd)
self.setToolTip(MainPanel.TITLE_COLUMN, _("Album unchanged"))
for i, column in enumerate(MainPanel.columns):
self.setText(i, album.column(column[1]))
if selection_changed:
@@ -885,17 +943,26 @@ class TrackItem(TreeItem):
def update(self, update_album=True, update_files=True):
track = self.obj
tree_widget = self.treeWidget()
if track.num_linked_files == 1:
file = track.linked_files[0]
file.item = self
color = TrackItem.track_colors[file.state]
bgcolor = get_match_color(file.similarity, TreeItem.base_color)
icon = FileItem.decide_file_icon(file)
self.setToolTip(0, _(FileItem.decide_file_icon_info(file)))
self.setToolTip(MainPanel.TITLE_COLUMN, _(FileItem.decide_file_icon_info(file)))
self.takeChildren()
self.setExpanded(False)
self.setToolTip(MainPanel.FINGERPRINT_COLUMN, FileItem.decide_fingerprint_icon_info(file))
if tree_widget:
if not tree_widget.itemWidget(self, MainPanel.FINGERPRINT_COLUMN):
tree_widget.setItemWidget(self, MainPanel.FINGERPRINT_COLUMN,
FingerprintColumnWidget(file=file))
else:
self.setToolTip(0, "")
self.setToolTip(MainPanel.TITLE_COLUMN, "")
self.setToolTip(MainPanel.FINGERPRINT_COLUMN, "")
if tree_widget:
tree_widget.setItemWidget(self, MainPanel.FINGERPRINT_COLUMN, None)
if track.ignored_for_completeness():
color = TreeItem.text_color_secondary
else:
@@ -929,10 +996,10 @@ class TrackItem(TreeItem):
self.addChildren(items)
self.setExpanded(True)
if track.error:
self.setIcon(0, TrackItem.icon_error)
self.setToolTip(0, track.error)
self.setIcon(MainPanel.TITLE_COLUMN, TrackItem.icon_error)
self.setToolTip(MainPanel.TITLE_COLUMN, track.error)
else:
self.setIcon(0, icon)
self.setIcon(MainPanel.TITLE_COLUMN, icon)
for i, column in enumerate(MainPanel.columns):
self.setText(i, track.column(column[1]))
self.setForeground(i, color)
@@ -947,13 +1014,19 @@ class FileItem(TreeItem):
def update(self, update_track=True):
file = self.obj
self.setIcon(0, FileItem.decide_file_icon(file))
self.setIcon(MainPanel.TITLE_COLUMN, FileItem.decide_file_icon(file))
self.setToolTip(MainPanel.FINGERPRINT_COLUMN, self.decide_fingerprint_icon_info(file))
color = FileItem.file_colors[file.state]
bgcolor = get_match_color(file.similarity, TreeItem.base_color)
for i, column in enumerate(MainPanel.columns):
self.setText(i, file.column(column[1]))
self.setForeground(i, color)
self.setBackground(i, bgcolor)
tree_widget = self.treeWidget()
if tree_widget:
if not tree_widget.itemWidget(self, MainPanel.FINGERPRINT_COLUMN):
tree_widget.setItemWidget(self, MainPanel.FINGERPRINT_COLUMN,
FingerprintColumnWidget(file=file))
if self.isSelected():
TreeItem.window.update_selection()
@@ -989,3 +1062,13 @@ class FileItem(TreeItem):
return FileItem.match_icons_info[int(file.similarity * 5 + 0.5)]
elif file.state == File.PENDING:
return N_("Pending")
@staticmethod
def decide_fingerprint_icon_info(file):
if getattr(file, 'acoustid_fingerprint', None):
if QtCore.QObject.tagger.acoustidmanager.is_submitted(file):
return _('Fingerprint has already been submitted')
else:
return _('Unsubmitted fingerprint')
else:
return _('No fingerprint was calculated for this file, use "Scan" or "Generate AcoustID fingerprints" to calculate the fingerprint.')

View File

@@ -501,7 +501,7 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
self.analyze_action.setShortcut(QtGui.QKeySequence(_("Ctrl+Y")))
self.analyze_action.triggered.connect(self.analyze)
self.generate_fingerprints_action = QtWidgets.QAction(icontheme.lookup('picard-fingerprint'), _("&Generate AcoustID fingerprints"), self)
self.generate_fingerprints_action = QtWidgets.QAction(icontheme.lookup('fingerprint'), _("&Generate AcoustID fingerprints"), self)
self.generate_fingerprints_action.setStatusTip(_("Generate the AcoustID audio fingerprints for the selected files without doing a lookup"))
self.generate_fingerprints_action.setEnabled(False)
self.generate_fingerprints_action.setToolTip(_('Generate the AcoustID audio fingerprints for the selected files'))

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 58.999 58.999"
style="enable-background:new 0 0 58.999 58.999;"
xml:space="preserve"
sodipodi:docname="fingerprint-gray.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
inkscape:export-filename="/home/phw/devel/musicbrainz/picard/resources/images/16x16/fingerprint-gray.png"
inkscape:export-xdpi="26.030001"
inkscape:export-ydpi="26.030001"><metadata
id="metadata55"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs53" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1016"
id="namedview51"
showgrid="false"
inkscape:zoom="4.0000678"
inkscape:cx="-25.99956"
inkscape:cy="25.99956"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="Capa_1" /><g
id="g18"
style="fill:#a0a0a0;fill-opacity:1"><path
style="fill:#a0a0a0;fill-opacity:1"
d="M29.083,26.038c-0.53,0.154-0.836,0.708-0.683,1.239c0.027,0.093,2.697,9.591,1.962,30.687 c-0.02,0.553,0.412,1.016,0.964,1.034c0.012,0.001,0.024,0.001,0.036,0.001c0.536,0,0.979-0.425,0.998-0.965 c0.747-21.414-1.925-30.919-2.038-31.313C30.169,26.19,29.616,25.888,29.083,26.038z"
id="path2" /><path
style="fill:#a0a0a0;fill-opacity:1"
d="M54.228,16.479C49.992,6.469,40.232,0,29.362,0c-3.587,0-7.085,0.701-10.396,2.083 c-1.834,0.765-3.567,1.675-5.151,2.704c-0.463,0.302-0.595,0.921-0.294,1.384c0.302,0.463,0.922,0.595,1.384,0.294 c1.483-0.964,3.108-1.817,4.831-2.536C22.803,2.648,26.041,2,29.362,2c10.064,0,19.102,5.989,23.023,15.259 c1.63,3.854,2.511,10.95,2.891,14.838c0.051,0.517,0.485,0.902,0.994,0.902c0.032,0,0.065-0.002,0.099-0.005 c0.549-0.054,0.951-0.543,0.897-1.093C56.789,27.015,55.875,20.372,54.228,16.479z"
id="path4" /><path
style="fill:#a0a0a0;fill-opacity:1"
d="M10.851,9.711c0.394-0.387,0.397-1.021,0.01-1.414c-0.387-0.393-1.02-0.397-1.414-0.01 C4.069,13.585-1.288,23.438,3.73,40.284c0.129,0.435,0.526,0.715,0.958,0.715c0.094,0,0.19-0.014,0.285-0.042 c0.529-0.157,0.831-0.714,0.673-1.243C1.899,27.132,3.698,16.758,10.851,9.711z"
id="path6" /><path
style="fill:#a0a0a0;fill-opacity:1"
d="M30.13,6.014c-3.06-0.119-6.038,0.432-8.853,1.606C11.471,11.711,6.257,22.494,9.15,32.704 c0.289,1.021,0.585,2.107,0.744,3.189c0.358,2.449,0.377,6.956,0.377,14.105c0,0.553,0.447,1,1,1s1-0.447,1-1 c0-7.458-0.019-11.804-0.398-14.396c-0.176-1.202-0.491-2.36-0.799-3.444c-2.617-9.236,2.1-18.992,10.973-22.693 c2.548-1.063,5.259-1.546,8.012-1.454C37.17,8.267,45.23,14.535,47.661,21.7c2.104,6.199,2.122,16.76,1.861,26.271 c-0.015,0.552,0.421,1.012,0.973,1.027c0.542-0.01,1.012-0.419,1.027-0.973c0.265-9.689,0.238-20.471-1.967-26.969 C46.838,13.049,38.124,6.3,30.13,6.014z"
id="path8" /><path
style="fill:#a0a0a0;fill-opacity:1"
d="M21.935,16.337c0.452-0.317,0.562-0.941,0.244-1.393c-0.316-0.452-0.941-0.563-1.393-0.244 c-5.15,3.616-7.388,10.563-5.565,17.295c0.021,0.073,2.051,7.462,2.051,19.004c0,0.553,0.447,1,1,1s1-0.447,1-1 c0-11.813-2.038-19.232-2.123-19.535C15.548,25.55,17.472,19.471,21.935,16.337z"
id="path10" /><path
style="fill:#a0a0a0;fill-opacity:1"
d="M29.362,12c-1.087,0-2.169,0.117-3.218,0.349c-0.539,0.119-0.88,0.652-0.761,1.192 c0.119,0.539,0.646,0.873,1.192,0.761c0.907-0.2,1.845-0.302,2.786-0.302c5.233,0,9.933,3.114,11.972,7.935 c2.43,5.74,2.589,14.953,2.028,31.029c-0.02,0.552,0.412,1.016,0.964,1.034c0.012,0.001,0.024,0.001,0.036,0.001 c0.536,0,0.979-0.425,0.998-0.965c0.584-16.723,0.406-25.758-2.185-31.879C40.823,15.594,35.401,12,29.362,12z"
id="path12" /><path
style="fill:#a0a0a0;fill-opacity:1"
d="M39.272,48.006c0.004-0.553-0.44-1.003-0.993-1.007c-0.002,0-0.005,0-0.007,0 c-0.549,0-0.996,0.443-1,0.993c-0.005,0.752,0.02,1.85,0.047,3.036c0.039,1.725,0.083,3.68,0.04,4.937 c-0.02,0.552,0.412,1.015,0.965,1.033c0.012,0.001,0.023,0.001,0.035,0.001c0.536,0,0.979-0.425,0.998-0.966 c0.046-1.313,0.001-3.299-0.039-5.051C39.291,49.82,39.267,48.743,39.272,48.006z"
id="path14" /><path
style="fill:#a0a0a0;fill-opacity:1"
d="M29.362,19c-1.062,0-2.098,0.208-3.081,0.618c-3.892,1.624-5.783,6.015-4.4,10.214 c0.754,2.289,1.391,14.272,1.391,26.167c0,0.553,0.447,1,1,1s1-0.447,1-1c0-10.707-0.57-23.995-1.49-26.792 c-1.054-3.197,0.353-6.526,3.27-7.743C27.789,21.156,28.566,21,29.362,21c2.415,0,4.584,1.438,5.529,3.67 c1.867,4.419,2.422,12.419,2.558,18.353c0.013,0.544,0.458,0.977,1,0.977c0.007,0,0.016,0,0.023,0 c0.552-0.013,0.989-0.471,0.977-1.023c-0.141-6.106-0.726-14.375-2.72-19.093C35.475,20.917,32.583,19,29.362,19z"
id="path16" /></g><g
id="g20" /><g
id="g22" /><g
id="g24" /><g
id="g26" /><g
id="g28" /><g
id="g30" /><g
id="g32" /><g
id="g34" /><g
id="g36" /><g
id="g38" /><g
id="g40" /><g
id="g42" /><g
id="g44" /><g
id="g46" /><g
id="g48" /></svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -16,7 +16,7 @@
viewBox="0 0 58.999 58.999"
style="enable-background:new 0 0 58.999 58.999;"
xml:space="preserve"
sodipodi:docname="picard-fingerprint.svg"
sodipodi:docname="fingerprint.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
id="metadata55"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -20,6 +20,10 @@
<file>images/16x16/edit-cut@2x.png</file>
<file>images/16x16/edit-paste.png</file>
<file>images/16x16/edit-paste@2x.png</file>
<file>images/16x16/fingerprint-gray.png</file>
<file>images/16x16/fingerprint-gray@2x.png</file>
<file>images/16x16/fingerprint.png</file>
<file>images/16x16/fingerprint@2x.png</file>
<file>images/16x16/folder.png</file>
<file>images/16x16/folder@2x.png</file>
<file>images/16x16/go-down.png</file>
@@ -59,8 +63,6 @@
<file>images/16x16/picard-cluster@2x.png</file>
<file>images/16x16/picard-edit-tags.png</file>
<file>images/16x16/picard-edit-tags@2x.png</file>
<file>images/16x16/picard-fingerprint.png</file>
<file>images/16x16/picard-fingerprint@2x.png</file>
<file>images/16x16/play-music.png</file>
<file>images/16x16/play-music@2x.png</file>
<file>images/16x16/play.png</file>

View File

@@ -0,0 +1,53 @@
from unittest.mock import MagicMock
from test.picardtestcase import PicardTestCase
from picard.acoustid.manager import AcoustIDManager
from picard.file import File
class AcoustIDManagerTest(PicardTestCase):
def setUp(self):
super().setUp()
self.acoustidmanager = AcoustIDManager()
self.tagger.window = MagicMock()
self.tagger.window.enable_submit = MagicMock()
def test_add_invalid(self):
file = File('foo.flac')
self.acoustidmanager.add(file, '00000000-0000-0000-0000-000000000001')
self.tagger.window.enable_submit.assert_not_called()
def test_add_and_update(self):
file = File('foo.flac')
file.acoustid_fingerprint = 'foo'
file.acoustid_length = 120
self.acoustidmanager.add(file, '00000000-0000-0000-0000-000000000001')
self.tagger.window.enable_submit.assert_called_with(False)
self.acoustidmanager.update(file, '00000000-0000-0000-0000-000000000002')
self.tagger.window.enable_submit.assert_called_with(True)
self.acoustidmanager.update(file, '00000000-0000-0000-0000-000000000001')
self.tagger.window.enable_submit.assert_called_with(False)
def test_add_and_remove(self):
file = File('foo.flac')
file.acoustid_fingerprint = 'foo'
file.acoustid_length = 120
self.acoustidmanager.add(file, '00000000-0000-0000-0000-000000000001')
self.tagger.window.enable_submit.assert_called_with(False)
self.acoustidmanager.update(file, '00000000-0000-0000-0000-000000000002')
self.tagger.window.enable_submit.assert_called_with(True)
self.acoustidmanager.remove(file)
self.tagger.window.enable_submit.assert_called_with(False)
def test_is_submitted(self):
file = File('foo.flac')
file.acoustid_fingerprint = 'foo'
file.acoustid_length = 120
self.assertTrue(self.acoustidmanager.is_submitted(file))
self.acoustidmanager.add(file, '00000000-0000-0000-0000-000000000001')
self.assertTrue(self.acoustidmanager.is_submitted(file))
self.acoustidmanager.update(file, '00000000-0000-0000-0000-000000000002')
self.assertFalse(self.acoustidmanager.is_submitted(file))
self.acoustidmanager.update(file, '')
self.assertTrue(self.acoustidmanager.is_submitted(file))