Fixes
Backfill scanned items but badges stayed at 0/1
AchievementBadgeService used raw strings as dictionary keys for user profiles. Backfill forced user IDs into dashed GUID format, but the web UI passed them through however ApiClient.getCurrentUserId() returned them (sometimes dashless). Progress was being written under one key and read from another.
- Added
NormalizeUserId()(canonical dashed GUID) and applied it at every public entry point ofAchievementBadgeService. Load()now canonicalizes existing profile keys on startup and merges duplicate profiles viaMergeProfiles(), so users who already ran backfill on v1.4.6 will have their data automatically recovered on first load of v1.4.7.
Live playback never registered to badges
PlaybackCompletionTracker was registered as AddSingleton<>() but nothing actually resolved it. Singletons are lazy, so the constructor (which subscribed to SessionManager.PlaybackProgress / PlaybackStopped) never ran. The tracker was effectively dead.
PlaybackCompletionTrackernow implementsIHostedService. Event subscription moved from the constructor toStartAsync(), so Jellyfin's host instantiates it at startup.- Dropped dead
PlaybackActivityTrackerregistration. - Added diagnostic logging for previously silent extraction failures (missing userId, missing item, zero runtime/position).
Upgrade
Install from the plugin catalog or drop the zip into plugins/AchievementBadges/ and restart Jellyfin. No manual data migration needed - existing badges/counters are preserved and re-keyed on first load.