fix post-download tagging, and enable tagging for hifi

This commit is contained in:
elmerohueso 2026-05-01 12:56:44 -06:00
parent 5880e32a92
commit f9f47f978e
6 changed files with 95 additions and 4 deletions

View file

@ -282,24 +282,30 @@ class HiFiClient:
def _parse_track(self, item: dict) -> Dict:
artist_name = 'Unknown Artist'
artist_id = None
artists_raw = item.get('artists', item.get('artist'))
if isinstance(artists_raw, list):
names = []
for a in artists_raw:
if isinstance(a, dict):
names.append(a.get('name', ''))
if artist_id is None:
artist_id = a.get('id')
elif isinstance(a, str):
names.append(a)
artist_name = ', '.join(n for n in names if n) or 'Unknown Artist'
elif isinstance(artists_raw, dict):
artist_name = artists_raw.get('name', 'Unknown Artist')
artist_id = artists_raw.get('id')
elif isinstance(artists_raw, str):
artist_name = artists_raw
album_raw = item.get('album', {})
album_name = ''
album_id = None
if isinstance(album_raw, dict):
album_name = album_raw.get('title', album_raw.get('name', ''))
album_id = album_raw.get('id')
elif isinstance(album_raw, str):
album_name = album_raw
@ -308,6 +314,8 @@ class HiFiClient:
return {
'id': item.get('id'),
'artist_id': artist_id,
'album_id': album_id,
'title': item.get('title', item.get('name', 'Unknown')),
'artist': artist_name,
'album': album_name,

View file

@ -257,6 +257,8 @@ def get_source_tag_names(source: str) -> Dict[str, Optional[str]]:
return {"track": None, "artist": None, "album": None}
if source_name == "discogs":
return {"track": None, "artist": None, "album": None}
if source_name == "hifi":
return {"track": "HIFI_TRACK_ID", "artist": "HIFI_ARTIST_ID", "album": None}
return {"track": None, "artist": None, "album": None}
@ -272,6 +274,8 @@ def get_library_source_id_columns(source: str) -> Dict[str, Optional[str]]:
return {"artist": "soul_id", "album": "soul_id", "track": "soul_id", "track_album": "album_soul_id"}
if source_name == "discogs":
return {"artist": "discogs_id", "album": "discogs_id", "track": None}
if source_name == "hifi":
return {"artist": "hifi_artist_id", "album": None, "track": "hifi_track_id"}
return {}

View file

@ -37,6 +37,7 @@ def build_metadata_enrichment_runtime(
deezer_worker: Any | None = None,
audiodb_worker: Any | None = None,
tidal_client: Any | None = None,
hifi_client: Any | None = None,
qobuz_enrichment_worker: Any | None = None,
lastfm_worker: Any | None = None,
genius_worker: Any | None = None,
@ -49,6 +50,7 @@ def build_metadata_enrichment_runtime(
deezer_worker=deezer_worker,
audiodb_worker=audiodb_worker,
tidal_client=tidal_client,
hifi_client=hifi_client,
qobuz_enrichment_worker=qobuz_enrichment_worker,
lastfm_worker=lastfm_worker,
genius_worker=genius_worker,

View file

@ -124,12 +124,14 @@ SOURCE_TAG_CONFIG = {
"AUDIODB_TRACK_ID": "audiodb.tags.track_id",
"TIDAL_TRACK_ID": "tidal.tags.track_id",
"TIDAL_ARTIST_ID": "tidal.tags.artist_id",
"HIFI_TRACK_ID": "hifi.tags.track_id",
"HIFI_ARTIST_ID": "hifi.tags.artist_id",
"QOBUZ_TRACK_ID": "qobuz.tags.track_id",
"QOBUZ_ARTIST_ID": "qobuz.tags.artist_id",
"GENIUS_TRACK_ID": "genius.tags.track_id",
}
DEFAULT_SOURCE_ORDER = ["musicbrainz", "deezer", "audiodb", "tidal", "qobuz", "lastfm", "genius"]
DEFAULT_SOURCE_ORDER = ["musicbrainz", "deezer", "audiodb", "tidal", "hifi", "qobuz", "lastfm", "genius"]
ID3_TAG_MAP = {
"MUSICBRAINZ_RECORDING_ID": ("UFID", "http://musicbrainz.org"),
@ -465,6 +467,41 @@ def _process_tidal_source(pp: dict, metadata: dict, cfg, runtime, track_title: s
pp["release_year"] = td_release[:4]
def _process_hifi_source(pp: dict, metadata: dict, cfg, runtime, track_title: str, artist_name: str) -> None:
if cfg.get("hifi.embed_tags", True) is False:
return
if not track_title or not artist_name:
return
hifi_client = getattr(runtime, "hifi_client", None)
if not hifi_client:
return
hifi_results = _call_source_lookup("HiFi track", hifi_client.search_tracks, track_title, artist_name)
if hifi_results and len(hifi_results) > 0:
hifi_track = hifi_results[0]
if _names_match(hifi_track.get("title", ""), track_title):
hifi_track_id = hifi_track.get("id")
if hifi_track_id:
pp["id_tags"]["HIFI_TRACK_ID"] = str(hifi_track_id)
hifi_artist_id = hifi_track.get("artist_id")
if hifi_artist_id:
pp["id_tags"]["HIFI_ARTIST_ID"] = str(hifi_artist_id)
if hifi_track_id:
hifi_details = _call_source_lookup("HiFi track details", hifi_client.get_track_info, hifi_track_id)
if hifi_details:
hifi_isrc = hifi_details.get("isrc")
if hifi_isrc:
pp["hifi_isrc"] = hifi_isrc
if not pp["release_year"]:
hifi_album_id = hifi_track.get("album_id")
if hifi_album_id:
hifi_album = _call_source_lookup("HiFi album", hifi_client.get_album, hifi_album_id)
if hifi_album:
hifi_release = str(hifi_album.get("release_date", "") or "")
if len(hifi_release) >= 4 and hifi_release[:4].isdigit():
pp["release_year"] = hifi_release[:4]
def _process_qobuz_source(pp: dict, metadata: dict, cfg, runtime, track_title: str, artist_name: str) -> None:
if cfg.get("qobuz.embed_tags", True) is False:
return
@ -572,6 +609,8 @@ def _process_source_enrichment(source_name: str, pp: dict, metadata: dict, cfg,
_process_audiodb_source(pp, metadata, cfg, runtime, track_title, artist_name)
elif source_name == "tidal":
_process_tidal_source(pp, metadata, cfg, runtime, track_title, artist_name)
elif source_name == "hifi":
_process_hifi_source(pp, metadata, cfg, runtime, track_title, artist_name)
elif source_name == "qobuz":
_process_qobuz_source(pp, metadata, cfg, runtime, track_title, artist_name)
elif source_name == "lastfm":
@ -699,6 +738,8 @@ def _write_embedded_metadata(audio_file, metadata: dict, pp: dict, cfg, symbols)
isrc_candidates.append(("Deezer", pp["deezer_isrc"]))
if pp["tidal_isrc"] and _tag_enabled(cfg, "tidal.tags.isrc"):
isrc_candidates.append(("Tidal", pp["tidal_isrc"]))
if pp["hifi_isrc"] and _tag_enabled(cfg, "hifi.tags.isrc"):
isrc_candidates.append(("HiFi", pp["hifi_isrc"]))
if pp["qobuz_isrc"] and _tag_enabled(cfg, "qobuz.tags.isrc"):
isrc_candidates.append(("Qobuz", pp["qobuz_isrc"]))
if isrc_candidates:
@ -968,6 +1009,7 @@ def embed_source_ids(audio_file, metadata: dict, context: dict = None, runtime=N
"audiodb_genre": None,
"tidal_isrc": None,
"tidal_copyright": None,
"hifi_isrc": None,
"qobuz_isrc": None,
"qobuz_copyright": None,
"qobuz_label": None,

View file

@ -23,6 +23,7 @@ def test_build_import_pipeline_runtime_exposes_expected_contract():
"deezer_worker",
"audiodb_worker",
"tidal_client",
"hifi_client",
"qobuz_enrichment_worker",
"lastfm_worker",
"genius_worker",
@ -38,6 +39,7 @@ def test_build_metadata_enrichment_runtime_exposes_expected_contract():
"deezer_worker": object(),
"audiodb_worker": object(),
"tidal_client": object(),
"hifi_client": object(),
"qobuz_enrichment_worker": object(),
"lastfm_worker": object(),
"genius_worker": object(),

View file

@ -14313,7 +14313,18 @@ def _enhance_file_metadata(file_path: str, context: dict, artist: dict, album_in
context,
artist,
album_info,
runtime=metadata_runtime or _build_metadata_enrichment_runtime(),
runtime=metadata_runtime or _build_metadata_enrichment_runtime(
mb_worker=mb_worker,
deezer_worker=deezer_worker,
audiodb_worker=audiodb_worker,
tidal_client=tidal_client,
qobuz_enrichment_worker=qobuz_enrichment_worker,
lastfm_worker=lastfm_worker,
genius_worker=genius_worker,
spotify_enrichment_worker=spotify_enrichment_worker,
itunes_enrichment_worker=itunes_enrichment_worker,
hifi_client=soulseek_client.hifi if soulseek_client else None,
),
)
@ -14407,7 +14418,18 @@ def _post_process_matched_download_with_verification(context_key, context, file_
task_id,
batch_id,
_build_import_pipeline_runtime(),
_build_metadata_enrichment_runtime(),
_build_metadata_enrichment_runtime(
mb_worker=mb_worker,
deezer_worker=deezer_worker,
audiodb_worker=audiodb_worker,
tidal_client=tidal_client,
qobuz_enrichment_worker=qobuz_enrichment_worker,
lastfm_worker=lastfm_worker,
genius_worker=genius_worker,
spotify_enrichment_worker=spotify_enrichment_worker,
itunes_enrichment_worker=itunes_enrichment_worker,
hifi_client=soulseek_client.hifi if soulseek_client else None,
),
)
@ -14520,7 +14542,18 @@ def _post_process_matched_download(context_key, context, file_path):
context,
file_path,
_build_import_pipeline_runtime(),
metadata_runtime=_build_metadata_enrichment_runtime(),
metadata_runtime=_build_metadata_enrichment_runtime(
mb_worker=mb_worker,
deezer_worker=deezer_worker,
audiodb_worker=audiodb_worker,
tidal_client=tidal_client,
qobuz_enrichment_worker=qobuz_enrichment_worker,
lastfm_worker=lastfm_worker,
genius_worker=genius_worker,
spotify_enrichment_worker=spotify_enrichment_worker,
itunes_enrichment_worker=itunes_enrichment_worker,
hifi_client=soulseek_client.hifi if soulseek_client else None,
),
)
# Track stale transfer keys (completed in slskd but no context — e.g., from before app restart)