Skip to content

Commit 90b386a

Browse files
authored
fix(plex): skip watchlist target when username collision is detected (#5403)
1 parent fa6d304 commit 90b386a

1 file changed

Lines changed: 11 additions & 6 deletions

File tree

src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,13 @@ private async Task<OmbiUser> EnsureOmbiUser(PlexCommunityUser plexUser, UserMana
312312
return null;
313313
}
314314

315-
var existing = await ResolveExistingUser(plexUser, resolvedNumericId, ct);
315+
var (existing, usernameCollision) = await ResolveExistingUser(plexUser, resolvedNumericId, ct);
316316
if (existing != null) return existing;
317+
// A username collision means another Ombi row already owns this username but we
318+
// refuse to adopt it (e.g. a stale ghost bound to a different numeric id). Creating
319+
// a new row would either trip the unique-username constraint or produce a duplicate
320+
// — skip the target and let the sweep handle the ghost.
321+
if (usernameCollision) return null;
317322

318323
return await CreateOmbiUserIfEligible(plexUser, resolvedNumericId, userManagement);
319324
}
@@ -345,7 +350,7 @@ private static bool IsBanned(PlexCommunityUser plexUser, string resolvedNumericI
345350
return false;
346351
}
347352

348-
private async Task<OmbiUser> ResolveExistingUser(PlexCommunityUser plexUser, string resolvedNumericId, CancellationToken ct)
353+
private async Task<(OmbiUser user, bool usernameCollision)> ResolveExistingUser(PlexCommunityUser plexUser, string resolvedNumericId, CancellationToken ct)
349354
{
350355
// Primary lookup: numeric plex.tv id (the canonical identifier). New rows only
351356
// ever get the numeric id, and existing legacy rows have it too.
@@ -357,7 +362,7 @@ private async Task<OmbiUser> ResolveExistingUser(PlexCommunityUser plexUser, str
357362
}
358363
if (existing != null || string.IsNullOrWhiteSpace(plexUser.username))
359364
{
360-
return existing;
365+
return (existing, false);
361366
}
362367

363368
// Username fallback: picks up rows whose ProviderUserId is stale / UUID-shaped
@@ -368,16 +373,16 @@ private async Task<OmbiUser> ResolveExistingUser(PlexCommunityUser plexUser, str
368373
// drives the NotAFriend sweep instead.
369374
var usernameMatch = await _ombiUserManager.Users.FirstOrDefaultAsync(
370375
x => x.UserType == UserType.PlexUser && x.UserName == plexUser.username, ct);
371-
if (usernameMatch == null) return null;
376+
if (usernameMatch == null) return (null, false);
372377

373378
if (!IsUsernameAdoptionSafe(usernameMatch.ProviderUserId, plexUser.id, resolvedNumericId))
374379
{
375380
_logger.LogWarning(
376381
"Plex user '{Username}' ({PlexUserId}) collides with existing Ombi user bound to {ProviderUserId}; skipping",
377382
plexUser.username, plexUser.id, usernameMatch.ProviderUserId);
378-
return null;
383+
return (null, true);
379384
}
380-
return usernameMatch;
385+
return (usernameMatch, false);
381386
}
382387

383388
// A username match is only safe to adopt when we can convince ourselves it's the same

0 commit comments

Comments
 (0)