From 574e7a90c11fdc44c7c8096180c3d3ba2e15e9b9 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Thu, 20 Jan 2022 23:33:43 +0100 Subject: [PATCH] PICARD-2396: Submit metadata to AcoustID on duration mismatch If a file's duration does clearly not match the MB recording duration allow AcoustID submission anyway. But instead of submitting the recording MBID submit only the metadata. This prevents clear mismatches while still allowing fingerprint submission. The submitted metadata can give a hint on the recording. --- picard/acoustid/manager.py | 6 ++-- picard/webservice/api_helpers.py | 26 +++++++++++--- test/test_api_helpers.py | 59 +++++++++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/picard/acoustid/manager.py b/picard/acoustid/manager.py index 243380e04..d1186b232 100644 --- a/picard/acoustid/manager.py +++ b/picard/acoustid/manager.py @@ -31,8 +31,8 @@ from picard import log from picard.util import load_json -# Maximum difference between file duration and MB metadata length. -# If the match is above this threshold the fingerprint will not get submitted. +# Maximum difference between file duration and MB recording length. +# If the match is above this threshold the MBID will not get submitted. # Compare also acoustid/const.py in acoustid-server sources FINGERPRINT_MAX_ALLOWED_LENGTH_DIFF_MS = 30000 @@ -111,7 +111,7 @@ class AcoustIDManager(QtCore.QObject): def _unsubmitted(self, reset=False): for file, submission in self._submissions.items(): - if not submission.is_submitted and submission.valid_duration: + if not submission.is_submitted: if reset: submission.attempts = 0 yield (file, submission) diff --git a/picard/webservice/api_helpers.py b/picard/webservice/api_helpers.py index f8393ba75..ba4a36830 100644 --- a/picard/webservice/api_helpers.py +++ b/picard/webservice/api_helpers.py @@ -297,12 +297,28 @@ class AcoustIdAPIHelper(APIHelper): def _submissions_to_args(submissions): config = get_config() args = {'user': config.setting["acoustid_apikey"]} + + def set_arg(name, i, value): + if value: + args[".".join((name, str(i)))] = value + for i, submission in enumerate(submissions): - args['fingerprint.%d' % i] = submission.fingerprint - args['duration.%d' % i] = str(submission.duration) - args['mbid.%d' % i] = submission.recordingid - if submission.puid: - args['puid.%d' % i] = submission.puid + set_arg('fingerprint', i, submission.fingerprint) + set_arg('duration', i, str(submission.duration)) + set_arg('puid', i, submission.puid) + if submission.valid_duration: + set_arg('mbid', i, submission.recordingid) + else: + metadata = submission.metadata + set_arg('track', i, metadata['title']) + set_arg('artist', i, metadata['artist']) + set_arg('album', i, metadata['album']) + set_arg('albumartist', i, metadata['albumartist']) + year = metadata['year'] or metadata['date'][:4] + if year and year.isdecimal(): + set_arg('year', i, year) + set_arg('trackno', i, metadata['tracknumber']) + set_arg('discno', i, metadata['discnumber']) return args def submit_acoustid_fingerprints(self, submissions, handler): diff --git a/test/test_api_helpers.py b/test/test_api_helpers.py index c57030c91..5955d7cee 100644 --- a/test/test_api_helpers.py +++ b/test/test_api_helpers.py @@ -27,6 +27,7 @@ from unittest.mock import MagicMock from test.picardtestcase import PicardTestCase from picard.acoustid.manager import Submission +from picard.metadata import Metadata from picard.webservice import WebService from picard.webservice.api_helpers import ( AcoustIdAPIHelper, @@ -239,11 +240,11 @@ class AcoustdIdAPITest(PicardTestCase): def test_submissions_to_args(self): submissions = [ - Submission('f1', 1, recordingid='r1', metadata={'musicip_puid': 'p1'}), - Submission('f2', 2, recordingid='r2', metadata={'musicip_puid': 'p2'}), + Submission('f1', 1, recordingid='or1', metadata=Metadata(musicip_puid='p1')), + Submission('f2', 2, recordingid='or2', metadata=Metadata(musicip_puid='p2')), ] - submissions[0].orig_recordingid = 'or1' - submissions[1].orig_recordingid = 'or2' + submissions[0].recordingid = 'r1' + submissions[1].recordingid = 'r2' result = self.api._submissions_to_args(submissions) expected = { 'user': 'apikey', @@ -251,3 +252,53 @@ class AcoustdIdAPITest(PicardTestCase): 'fingerprint.1': 'f2', 'duration.1': '2', 'mbid.1': 'r2', 'puid.1': 'p2' } self.assertEqual(result, expected) + + def test_submissions_to_args_invalid_duration(self): + metadata1 = Metadata({ + 'title': 'The Track', + 'artist': 'The Artist', + 'album': 'The Album', + 'albumartist': 'The Album Artist', + 'tracknumber': '4', + 'discnumber': '2', + }, length=100000) + metadata2 = Metadata({ + 'year': '2022' + }, length=100000) + metadata3 = Metadata({ + 'date': '1980-08-30' + }, length=100000) + metadata4 = Metadata({ + 'date': '08-30' + }, length=100000) + submissions = [ + Submission('f1', 500000, recordingid='or1', metadata=metadata1), + Submission('f2', 500000, recordingid='or2', metadata=metadata2), + Submission('f3', 500000, recordingid='or3', metadata=metadata3), + Submission('f4', 500000, recordingid='or4', metadata=metadata4), + ] + submissions[0].recordingid = 'r1' + submissions[1].recordingid = 'r2' + submissions[1].recordingid = 'r3' + submissions[1].recordingid = 'r4' + result = self.api._submissions_to_args(submissions) + expected = { + 'user': 'apikey', + 'fingerprint.0': 'f1', + 'duration.0': '500000', + 'track.0': metadata1['title'], + 'artist.0': metadata1['artist'], + 'album.0': metadata1['album'], + 'albumartist.0': metadata1['albumartist'], + 'trackno.0': metadata1['tracknumber'], + 'discno.0': metadata1['discnumber'], + 'fingerprint.1': 'f2', + 'duration.1': '500000', + 'year.1': '2022', + 'fingerprint.2': 'f3', + 'duration.2': '500000', + 'year.2': '1980', + 'fingerprint.3': 'f4', + 'duration.3': '500000', + } + self.assertEqual(result, expected)