UserDataChangeNotifier.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using MediaBrowser.Controller.Entities;
  8. using MediaBrowser.Controller.Library;
  9. using MediaBrowser.Controller.Session;
  10. using MediaBrowser.Model.Entities;
  11. using MediaBrowser.Model.Session;
  12. using Microsoft.Extensions.Hosting;
  13. namespace 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. private readonly ISessionManager _sessionManager;
  22. private readonly IUserDataManager _userDataManager;
  23. private readonly IUserManager _userManager;
  24. private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new();
  25. private readonly object _syncLock = new();
  26. private Timer? _updateTimer;
  27. /// <summary>
  28. /// Initializes a new instance of the <see cref="UserDataChangeNotifier"/> class.
  29. /// </summary>
  30. /// <param name="userDataManager">The <see cref="IUserDataManager"/>.</param>
  31. /// <param name="sessionManager">The <see cref="ISessionManager"/>.</param>
  32. /// <param name="userManager">The <see cref="IUserManager"/>.</param>
  33. public UserDataChangeNotifier(
  34. IUserDataManager userDataManager,
  35. ISessionManager sessionManager,
  36. IUserManager userManager)
  37. {
  38. _userDataManager = userDataManager;
  39. _sessionManager = sessionManager;
  40. _userManager = userManager;
  41. }
  42. /// <inheritdoc />
  43. public Task StartAsync(CancellationToken cancellationToken)
  44. {
  45. _userDataManager.UserDataSaved += OnUserDataManagerUserDataSaved;
  46. return Task.CompletedTask;
  47. }
  48. /// <inheritdoc />
  49. public Task StopAsync(CancellationToken cancellationToken)
  50. {
  51. _userDataManager.UserDataSaved -= OnUserDataManagerUserDataSaved;
  52. return Task.CompletedTask;
  53. }
  54. private void OnUserDataManagerUserDataSaved(object? sender, UserDataSaveEventArgs e)
  55. {
  56. if (e.SaveReason == UserDataSaveReason.PlaybackProgress)
  57. {
  58. return;
  59. }
  60. lock (_syncLock)
  61. {
  62. if (_updateTimer is null)
  63. {
  64. _updateTimer = new Timer(
  65. UpdateTimerCallback,
  66. null,
  67. UpdateDuration,
  68. Timeout.Infinite);
  69. }
  70. else
  71. {
  72. _updateTimer.Change(UpdateDuration, Timeout.Infinite);
  73. }
  74. if (!_changedItems.TryGetValue(e.UserId, out List<BaseItem>? keys))
  75. {
  76. keys = new List<BaseItem>();
  77. _changedItems[e.UserId] = keys;
  78. }
  79. keys.Add(e.Item);
  80. var baseItem = e.Item;
  81. // Go up one level for indicators
  82. if (baseItem is not null)
  83. {
  84. var parent = baseItem.GetOwner() ?? baseItem.GetParent();
  85. if (parent is not null)
  86. {
  87. keys.Add(parent);
  88. }
  89. }
  90. }
  91. }
  92. private async void UpdateTimerCallback(object? state)
  93. {
  94. List<KeyValuePair<Guid, List<BaseItem>>> changes;
  95. lock (_syncLock)
  96. {
  97. // Remove dupes in case some were saved multiple times
  98. changes = _changedItems.ToList();
  99. _changedItems.Clear();
  100. if (_updateTimer is not null)
  101. {
  102. _updateTimer.Dispose();
  103. _updateTimer = null;
  104. }
  105. }
  106. foreach (var (userId, changedItems) in changes)
  107. {
  108. await _sessionManager.SendMessageToUserSessions(
  109. [userId],
  110. SessionMessageType.UserDataChanged,
  111. () => GetUserDataChangeInfo(userId, changedItems),
  112. default).ConfigureAwait(false);
  113. }
  114. }
  115. private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List<BaseItem> changedItems)
  116. {
  117. var user = _userManager.GetUserById(userId);
  118. return new UserDataChangeInfo
  119. {
  120. UserId = userId.ToString("N", CultureInfo.InvariantCulture),
  121. UserDataList = changedItems
  122. .DistinctBy(x => x.Id)
  123. .Select(i =>
  124. {
  125. var dto = _userDataManager.GetUserDataDto(i, user);
  126. dto.ItemId = i.Id.ToString("N", CultureInfo.InvariantCulture);
  127. return dto;
  128. })
  129. .ToArray()
  130. };
  131. }
  132. /// <inheritdoc />
  133. public void Dispose()
  134. {
  135. _updateTimer?.Dispose();
  136. _updateTimer = null;
  137. }
  138. }
  139. }