mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-06 08:34:01 +00:00
PICARD-2584: Moved AcoustID recording parsing into helper class
This commit is contained in:
@@ -34,10 +34,7 @@ import json
|
||||
from PyQt6 import QtCore
|
||||
|
||||
from picard import log
|
||||
from picard.acoustid.json_helpers import (
|
||||
max_source_count,
|
||||
parse_recording,
|
||||
)
|
||||
from picard.acoustid.recordings import RecordingResolver
|
||||
from picard.config import get_config
|
||||
from picard.const import (
|
||||
DEFAULT_FPCALC_THREADS,
|
||||
@@ -49,6 +46,7 @@ from picard.util import (
|
||||
find_executable,
|
||||
win_prefix_longpath,
|
||||
)
|
||||
from picard.webservice.api_helpers import AcoustIdAPIHelper
|
||||
|
||||
|
||||
def get_score(node):
|
||||
@@ -76,7 +74,7 @@ AcoustIDTask = namedtuple('AcoustIDTask', ('file', 'next_func'))
|
||||
|
||||
class AcoustIDClient(QtCore.QObject):
|
||||
|
||||
def __init__(self, acoustid_api):
|
||||
def __init__(self, acoustid_api: AcoustIdAPIHelper):
|
||||
super().__init__()
|
||||
self._queue = deque()
|
||||
self._running = 0
|
||||
@@ -108,42 +106,15 @@ class AcoustIDClient(QtCore.QObject):
|
||||
mparms,
|
||||
echo=None
|
||||
)
|
||||
task.next_func(doc, http, error)
|
||||
else:
|
||||
try:
|
||||
recording_list = doc['recordings'] = []
|
||||
status = document['status']
|
||||
if status == 'ok':
|
||||
results = document.get('results') or []
|
||||
for result in results:
|
||||
recordings = result.get('recordings') or []
|
||||
max_sources = max_source_count(recordings)
|
||||
result_score = get_score(result)
|
||||
for recording in recordings:
|
||||
parsed_recording = parse_recording(recording)
|
||||
if parsed_recording is not None:
|
||||
# Calculate a score based on result score and sources for this
|
||||
# recording relative to other recordings in this result
|
||||
score = min(recording.get('sources', 1) / max_sources, 1.0) * 100
|
||||
parsed_recording['score'] = score * result_score
|
||||
parsed_recording['acoustid'] = result['id']
|
||||
recording_list.append(parsed_recording)
|
||||
|
||||
if results:
|
||||
if not recording_list:
|
||||
# Set AcoustID in tags if there was no matching recording
|
||||
task.file.metadata['acoustid_id'] = results[0]['id']
|
||||
task.file.update()
|
||||
log.debug(
|
||||
"AcoustID: Found no matching recordings for '%s',"
|
||||
" setting acoustid_id tag to %r",
|
||||
task.file.filename, results[0]['id']
|
||||
)
|
||||
else:
|
||||
log.debug(
|
||||
"AcoustID: Lookup successful for '%s' (recordings: %d)",
|
||||
task.file.filename,
|
||||
len(recording_list)
|
||||
)
|
||||
resolver = RecordingResolver(self._acoustid_api.webservice)
|
||||
resolver.resolve(
|
||||
document,
|
||||
partial(self._on_recording_resolve_finish, task, document, http))
|
||||
else:
|
||||
mparms = {
|
||||
'error': document['error']['message'],
|
||||
@@ -157,11 +128,31 @@ class AcoustIDClient(QtCore.QObject):
|
||||
mparms,
|
||||
echo=None
|
||||
)
|
||||
task.next_func(doc, http, error)
|
||||
except (AttributeError, KeyError, TypeError) as e:
|
||||
log.error("AcoustID: Error reading response", exc_info=True)
|
||||
error = e
|
||||
task.next_func(doc, http, e)
|
||||
|
||||
task.next_func(doc, http, error)
|
||||
def _on_recording_resolve_finish(self, task, document, http, result=None, error=None):
|
||||
recording_list = document['recordings'] = result
|
||||
if not recording_list:
|
||||
results = document.get('results')
|
||||
if results:
|
||||
# Set AcoustID in tags if there was no matching recording
|
||||
task.file.metadata['acoustid_id'] = results[0]['id']
|
||||
task.file.update()
|
||||
log.debug(
|
||||
"AcoustID: Found no matching recordings for '%s',"
|
||||
" setting acoustid_id tag to %r",
|
||||
task.file.filename, results[0]['id']
|
||||
)
|
||||
else:
|
||||
log.debug(
|
||||
"AcoustID: Lookup successful for '%s' (recordings: %d)",
|
||||
task.file.filename,
|
||||
len(recording_list)
|
||||
)
|
||||
task.next_func(document, http, error)
|
||||
|
||||
def _lookup_fingerprint(self, task, result=None, error=None):
|
||||
if task.file.state == File.REMOVED:
|
||||
|
||||
59
picard/acoustid/recordings.py
Normal file
59
picard/acoustid/recordings.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
#
|
||||
# Copyright (C) 2023 Philipp Wolfer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
from picard.acoustid.json_helpers import (
|
||||
max_source_count,
|
||||
parse_recording,
|
||||
)
|
||||
from picard.webservice import WebService
|
||||
from picard.webservice.api_helpers import MBAPIHelper
|
||||
|
||||
|
||||
class RecordingResolver:
|
||||
|
||||
def __init__(self, ws: WebService) -> None:
|
||||
self.mbapi = MBAPIHelper(ws)
|
||||
|
||||
def resolve(self, doc: dict, callback: callable) -> None:
|
||||
recording_map = {}
|
||||
results = doc.get('results') or []
|
||||
for result in results:
|
||||
recordings = result.get('recordings') or []
|
||||
max_sources = max_source_count(recordings)
|
||||
result_score = get_score(result)
|
||||
for recording in recordings:
|
||||
parsed_recording = parse_recording(recording)
|
||||
if parsed_recording is not None:
|
||||
# Calculate a score based on result score and sources for this
|
||||
# recording relative to other recordings in this result
|
||||
score = min(recording.get('sources', 1) / max_sources, 1.0) * 100
|
||||
parsed_recording['score'] = score * result_score
|
||||
parsed_recording['acoustid'] = result.get('id')
|
||||
recording_map[parsed_recording['id']] = parsed_recording
|
||||
|
||||
# TODO: Load recording details for recordings without metadata
|
||||
callback(recording_map.values())
|
||||
|
||||
|
||||
def get_score(node):
|
||||
try:
|
||||
return float(node.get('score', 1.0))
|
||||
except (TypeError, ValueError):
|
||||
return 1.0
|
||||
Reference in New Issue
Block a user