Reconcile verified account IDs and make language repairs observable
Magent CI/CD / verify (push) Successful in 1m50s
Magent CI/CD / deploy-prod (push) Skipped
Magent CI/CD / deploy-beta (push) Skipped

This commit is contained in:
2026-09-11 19:56:32 +12:00
parent 38169b881e
commit de25255ea8
25 changed files with 593 additions and 160 deletions
+45 -35
View File
@@ -1,4 +1,9 @@
import logging
from collections import Counter
from contextlib import closing
from .. import db
from .jellyfin_identity import source_key
from .identity_review import normalized_id, name_key
from fastapi import HTTPException
@@ -6,18 +11,14 @@ from ..clients.jellyfin import JellyfinClient
from ..db import (
create_user_if_missing,
get_user_by_username,
set_user_email,
set_user_auth_provider,
set_user_jellyseerr_id,
)
from ..runtime import get_runtime_settings
from .jellyfin_identity import link_user
from .user_cache import (
build_jellyseerr_candidate_map,
extract_jellyseerr_user_email,
find_matching_jellyseerr_user,
get_cached_jellyseerr_users,
match_jellyseerr_user_id,
save_jellyfin_users_cache,
)
@@ -36,43 +37,52 @@ async def sync_jellyfin_users() -> int:
# Jellyfin is the canonical source for local user objects; Seerr IDs are
# matched as enrichment when possible.
jellyseerr_users = get_cached_jellyseerr_users()
candidate_map = build_jellyseerr_candidate_map(jellyseerr_users or [])
imported = 0
name_counts = Counter(name_key(row.get('Name')) for row in users if isinstance(row, dict))
with closing(db._connect()) as conn:
links = [dict(zip(('local_id', 'jf_id'), row)) for row in conn.execute(
'SELECT local_user_id,jellyfin_user_id FROM jellyfin_user_links WHERE source=?', (source_key(runtime.jellyfin_base_url),))]
for user in users:
if not isinstance(user, dict):
continue
name = user.get("Name")
if not name:
name, jf_id = user.get('Name'), normalized_id(user.get('Id'))
if not name or not jf_id or name_counts[name_key(name)] != 1:
continue
matched_id = match_jellyseerr_user_id(name, candidate_map) if candidate_map else None
matched_seerr_user = find_matching_jellyseerr_user(name, jellyseerr_users or [])
matched_email = extract_jellyseerr_user_email(matched_seerr_user)
created = create_user_if_missing(
name,
"jellyfin-user",
role="user",
email=matched_email,
auth_provider="jellyfin",
jellyseerr_user_id=matched_id,
)
if created:
imported += 1
else:
matches = [row for row in (jellyseerr_users or []) if normalized_id(row.get('jellyfinUserId')) == jf_id]
if len(matches) > 1:
continue
matched = matches[0] if matches else None
matched_id = matched.get('id') if matched else None
owners = [row['local_id'] for row in links if normalized_id(row['jf_id']) == jf_id]
if len(owners) > 1:
continue
existing = db.get_user_by_id(owners[0]) if owners else None
if not existing and matched_id is not None:
candidates = [row for row in db.get_all_users() if row.get('jellyseerr_user_id') == matched_id]
if len(candidates) > 1:
continue
existing = candidates[0] if candidates else None
if not existing:
existing = get_user_by_username(name)
if (
existing
and str(existing.get("role") or "user").strip().lower() != "admin"
and str(existing.get("auth_provider") or "local").strip().lower() != "jellyfin"
):
set_user_auth_provider(name, "jellyfin")
if matched_id is not None:
set_user_jellyseerr_id(name, matched_id)
if matched_email:
set_user_email(name, matched_email)
if user.get("Id"):
local_user = get_user_by_username(name)
if local_user and local_user.get("auth_provider") == "jellyfin":
link_user(name, str(user["Id"]), runtime.jellyfin_base_url)
if existing:
existing_links = [normalized_id(row['jf_id']) for row in links if row['local_id'] == existing['id']]
if existing_links and any(value != jf_id for value in existing_links):
continue
if existing.get('role') == 'admin' or existing.get('auth_provider') == 'local':
continue
canonical = existing['username']
# Never overwrite a stored Seerr identity on name evidence.
if existing.get('jellyseerr_user_id') not in (None, matched_id):
continue
set_user_auth_provider(canonical, 'jellyfin')
else:
canonical = name
if create_user_if_missing(canonical, 'jellyfin-user', auth_provider='jellyfin',
jellyseerr_user_id=matched_id, email=extract_jellyseerr_user_email(matched)):
imported += 1
if matched_id is not None:
set_user_jellyseerr_id(canonical, matched_id)
link_user(canonical, jf_id, runtime.jellyfin_base_url)
return imported