PICARD-34: Fingerprint indicators shows submission status

The fingerprint indicator is shown in red for unsubmitted fingerprints and gray for submitted fingerprints. The tooltip explains the status.
This commit is contained in:
Philipp Wolfer
2020-01-12 20:04:14 +01:00
parent 7df2c2cb96
commit 63cb098006
15 changed files with 12304 additions and 11716 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

@@ -199,7 +199,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('picard-fingerprint', icontheme.ICON_SIZE_MENU)
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"),
@@ -282,7 +283,10 @@ class FingerprintColumnWidget(QtWidgets.QWidget):
def decide_icon(self):
if getattr(self._file, 'acoustid_fingerprint', None):
return FileItem.icon_fingerprint
if self.tagger.acoustidmanager.is_submitted(self._file):
return FileItem.icon_fingerprint_gray
else:
return FileItem.icon_fingerprint
else:
return None
@@ -359,7 +363,7 @@ class ConfigurableColumnsHeader(TristateSortHeaderView):
painter.save()
super().paintSection(painter, rect, index)
painter.restore()
paint_fingerprint_icon(painter, rect, FileItem.icon_fingerprint)
paint_fingerprint_icon(painter, rect, FileItem.icon_fingerprint_gray)
else:
super().paintSection(painter, rect, index)
@@ -1058,10 +1062,10 @@ class FileItem(TreeItem):
@staticmethod
def decide_fingerprint_icon_info(file):
acoustid = file.metadata['acoustid_id']
if acoustid:
return _('AcoustID: %s' % acoustid)
elif getattr(file, 'acoustid_fingerprint', None):
return _('Fingerprint calculated')
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))