< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.EntryPoints.UserDataChangeNotifier
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
Line coverage
26%
Covered lines: 13
Uncovered lines: 36
Coverable lines: 49
Total lines: 161
Line coverage: 26.5%
Branch coverage
6%
Covered branches: 1
Total branches: 16
Branch coverage: 6.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
StartAsync(...)100%11100%
StopAsync(...)100%11100%
OnUserDataManagerUserDataSaved(...)0%156120%
GetUserDataChangeInfo(...)0%620%
Dispose()50%22100%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using MediaBrowser.Controller.Entities;
 7using MediaBrowser.Controller.Library;
 8using MediaBrowser.Controller.Session;
 9using MediaBrowser.Model.Entities;
 10using MediaBrowser.Model.Session;
 11using Microsoft.Extensions.Hosting;
 12
 13namespace Emby.Server.Implementations.EntryPoints
 14{
 15    /// <summary>
 16    /// <see cref="IHostedService"/> responsible for notifying users when associated item data is updated.
 17    /// </summary>
 18    public sealed class UserDataChangeNotifier : IHostedService, IDisposable
 19    {
 20        private const int UpdateDuration = 500;
 21
 22        private readonly ISessionManager _sessionManager;
 23        private readonly IUserDataManager _userDataManager;
 24        private readonly IUserManager _userManager;
 25
 2226        private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new();
 2227        private readonly object _syncLock = new();
 28
 29        private Timer? _updateTimer;
 30
 31        /// <summary>
 32        /// Initializes a new instance of the <see cref="UserDataChangeNotifier"/> class.
 33        /// </summary>
 34        /// <param name="userDataManager">The <see cref="IUserDataManager"/>.</param>
 35        /// <param name="sessionManager">The <see cref="ISessionManager"/>.</param>
 36        /// <param name="userManager">The <see cref="IUserManager"/>.</param>
 37        public UserDataChangeNotifier(
 38            IUserDataManager userDataManager,
 39            ISessionManager sessionManager,
 40            IUserManager userManager)
 41        {
 2242            _userDataManager = userDataManager;
 2243            _sessionManager = sessionManager;
 2244            _userManager = userManager;
 2245        }
 46
 47        /// <inheritdoc />
 48        public Task StartAsync(CancellationToken cancellationToken)
 49        {
 2250            _userDataManager.UserDataSaved += OnUserDataManagerUserDataSaved;
 51
 2252            return Task.CompletedTask;
 53        }
 54
 55        /// <inheritdoc />
 56        public Task StopAsync(CancellationToken cancellationToken)
 57        {
 2258            _userDataManager.UserDataSaved -= OnUserDataManagerUserDataSaved;
 59
 2260            return Task.CompletedTask;
 61        }
 62
 63        private void OnUserDataManagerUserDataSaved(object? sender, UserDataSaveEventArgs e)
 64        {
 065            if (e.SaveReason == UserDataSaveReason.PlaybackProgress)
 66            {
 067                return;
 68            }
 69
 070            lock (_syncLock)
 71            {
 072                if (_updateTimer is null)
 73                {
 074                    _updateTimer = new Timer(
 075                        UpdateTimerCallback,
 076                        null,
 077                        UpdateDuration,
 078                        Timeout.Infinite);
 79                }
 80                else
 81                {
 082                    _updateTimer.Change(UpdateDuration, Timeout.Infinite);
 83                }
 84
 085                if (!_changedItems.TryGetValue(e.UserId, out List<BaseItem>? keys))
 86                {
 087                    keys = new List<BaseItem>();
 088                    _changedItems[e.UserId] = keys;
 89                }
 90
 091                keys.Add(e.Item);
 92
 093                var baseItem = e.Item;
 94
 95                // Go up one level for indicators
 096                if (baseItem is not null)
 97                {
 098                    var parent = baseItem.GetOwner() ?? baseItem.GetParent();
 99
 0100                    if (parent is not null)
 101                    {
 0102                        keys.Add(parent);
 103                    }
 104                }
 0105            }
 0106        }
 107
 108        private async void UpdateTimerCallback(object? state)
 109        {
 110            List<KeyValuePair<Guid, List<BaseItem>>> changes;
 111            lock (_syncLock)
 112            {
 113                // Remove dupes in case some were saved multiple times
 114                changes = _changedItems.ToList();
 115                _changedItems.Clear();
 116
 117                if (_updateTimer is not null)
 118                {
 119                    _updateTimer.Dispose();
 120                    _updateTimer = null;
 121                }
 122            }
 123
 124            foreach (var (userId, changedItems) in changes)
 125            {
 126                await _sessionManager.SendMessageToUserSessions(
 127                    [userId],
 128                    SessionMessageType.UserDataChanged,
 129                    () => GetUserDataChangeInfo(userId, changedItems),
 130                    default).ConfigureAwait(false);
 131            }
 132        }
 133
 134        private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List<BaseItem> changedItems)
 135        {
 0136            var user = _userManager.GetUserById(userId)
 0137                ?? throw new ArgumentException("Invalid user ID", nameof(userId));
 138
 0139            return new UserDataChangeInfo
 0140            {
 0141                UserId = userId,
 0142                UserDataList = changedItems
 0143                    .DistinctBy(x => x.Id)
 0144                    .Select(i =>
 0145                    {
 0146                        var dto = _userDataManager.GetUserDataDto(i, user);
 0147                        dto.ItemId = i.Id;
 0148                        return dto;
 0149                    })
 0150                    .ToArray()
 0151            };
 152        }
 153
 154        /// <inheritdoc />
 155        public void Dispose()
 156        {
 22157            _updateTimer?.Dispose();
 22158            _updateTimer = null;
 22159        }
 160    }
 161}