UserDataManager.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. using MediaBrowser.Common.Events;
  2. using MediaBrowser.Common.Kernel;
  3. using MediaBrowser.Controller.Entities;
  4. using MediaBrowser.Model.Connectivity;
  5. using MediaBrowser.Model.Logging;
  6. using System;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace MediaBrowser.Controller.Library
  10. {
  11. /// <summary>
  12. /// Class UserDataManager
  13. /// </summary>
  14. public class UserDataManager : BaseManager<Kernel>
  15. {
  16. #region Events
  17. /// <summary>
  18. /// Occurs when [playback start].
  19. /// </summary>
  20. public event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
  21. /// <summary>
  22. /// Occurs when [playback progress].
  23. /// </summary>
  24. public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
  25. /// <summary>
  26. /// Occurs when [playback stopped].
  27. /// </summary>
  28. public event EventHandler<PlaybackProgressEventArgs> PlaybackStopped;
  29. #endregion
  30. /// <summary>
  31. /// The _logger
  32. /// </summary>
  33. private readonly ILogger _logger;
  34. /// <summary>
  35. /// Initializes a new instance of the <see cref="UserDataManager" /> class.
  36. /// </summary>
  37. /// <param name="kernel">The kernel.</param>
  38. /// <param name="logger">The logger.</param>
  39. public UserDataManager(Kernel kernel, ILogger logger)
  40. : base(kernel)
  41. {
  42. _logger = logger;
  43. }
  44. /// <summary>
  45. /// Used to report that playback has started for an item
  46. /// </summary>
  47. /// <param name="user">The user.</param>
  48. /// <param name="item">The item.</param>
  49. /// <param name="clientType">Type of the client.</param>
  50. /// <param name="deviceName">Name of the device.</param>
  51. /// <exception cref="System.ArgumentNullException"></exception>
  52. public void OnPlaybackStart(User user, BaseItem item, ClientType clientType, string deviceName)
  53. {
  54. if (user == null)
  55. {
  56. throw new ArgumentNullException();
  57. }
  58. if (item == null)
  59. {
  60. throw new ArgumentNullException();
  61. }
  62. Kernel.UserManager.UpdateNowPlayingItemId(user, clientType, deviceName, item);
  63. // Nothing to save here
  64. // Fire events to inform plugins
  65. EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
  66. {
  67. Argument = item,
  68. User = user
  69. }, _logger);
  70. }
  71. /// <summary>
  72. /// Used to report playback progress for an item
  73. /// </summary>
  74. /// <param name="user">The user.</param>
  75. /// <param name="item">The item.</param>
  76. /// <param name="positionTicks">The position ticks.</param>
  77. /// <param name="clientType">Type of the client.</param>
  78. /// <param name="deviceName">Name of the device.</param>
  79. /// <returns>Task.</returns>
  80. /// <exception cref="System.ArgumentNullException"></exception>
  81. public async Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceName)
  82. {
  83. if (user == null)
  84. {
  85. throw new ArgumentNullException();
  86. }
  87. if (item == null)
  88. {
  89. throw new ArgumentNullException();
  90. }
  91. Kernel.UserManager.UpdateNowPlayingItemId(user, clientType, deviceName, item, positionTicks);
  92. if (positionTicks.HasValue)
  93. {
  94. var data = item.GetUserData(user, true);
  95. UpdatePlayState(item, data, positionTicks.Value, false);
  96. await SaveUserDataForItem(user, item, data).ConfigureAwait(false);
  97. }
  98. EventHelper.QueueEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
  99. {
  100. Argument = item,
  101. User = user,
  102. PlaybackPositionTicks = positionTicks
  103. }, _logger);
  104. }
  105. /// <summary>
  106. /// Used to report that playback has ended for an item
  107. /// </summary>
  108. /// <param name="user">The user.</param>
  109. /// <param name="item">The item.</param>
  110. /// <param name="positionTicks">The position ticks.</param>
  111. /// <param name="clientType">Type of the client.</param>
  112. /// <param name="deviceName">Name of the device.</param>
  113. /// <returns>Task.</returns>
  114. /// <exception cref="System.ArgumentNullException"></exception>
  115. public async Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceName)
  116. {
  117. if (user == null)
  118. {
  119. throw new ArgumentNullException();
  120. }
  121. if (item == null)
  122. {
  123. throw new ArgumentNullException();
  124. }
  125. Kernel.UserManager.RemoveNowPlayingItemId(user, clientType, deviceName, item);
  126. var data = item.GetUserData(user, true);
  127. if (positionTicks.HasValue)
  128. {
  129. UpdatePlayState(item, data, positionTicks.Value, true);
  130. }
  131. else
  132. {
  133. // If the client isn't able to report this, then we'll just have to make an assumption
  134. data.PlayCount++;
  135. data.Played = true;
  136. }
  137. await SaveUserDataForItem(user, item, data).ConfigureAwait(false);
  138. EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackProgressEventArgs
  139. {
  140. Argument = item,
  141. User = user,
  142. PlaybackPositionTicks = positionTicks
  143. }, _logger);
  144. }
  145. /// <summary>
  146. /// Updates playstate position for an item but does not save
  147. /// </summary>
  148. /// <param name="item">The item</param>
  149. /// <param name="data">User data for the item</param>
  150. /// <param name="positionTicks">The current playback position</param>
  151. /// <param name="incrementPlayCount">Whether or not to increment playcount</param>
  152. private void UpdatePlayState(BaseItem item, UserItemData data, long positionTicks, bool incrementPlayCount)
  153. {
  154. // If a position has been reported, and if we know the duration
  155. if (positionTicks > 0 && item.RunTimeTicks.HasValue && item.RunTimeTicks > 0)
  156. {
  157. var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
  158. // Don't track in very beginning
  159. if (pctIn < Kernel.Configuration.MinResumePct)
  160. {
  161. positionTicks = 0;
  162. incrementPlayCount = false;
  163. }
  164. // If we're at the end, assume completed
  165. else if (pctIn > Kernel.Configuration.MaxResumePct || positionTicks >= item.RunTimeTicks.Value)
  166. {
  167. positionTicks = 0;
  168. data.Played = true;
  169. }
  170. else
  171. {
  172. // Enforce MinResumeDuration
  173. var durationSeconds = TimeSpan.FromTicks(item.RunTimeTicks.Value).TotalSeconds;
  174. if (durationSeconds < Kernel.Configuration.MinResumeDurationSeconds)
  175. {
  176. positionTicks = 0;
  177. data.Played = true;
  178. }
  179. }
  180. }
  181. data.PlaybackPositionTicks = positionTicks;
  182. if (incrementPlayCount)
  183. {
  184. data.PlayCount++;
  185. data.LastPlayedDate = DateTime.UtcNow;
  186. }
  187. }
  188. /// <summary>
  189. /// Saves user data for an item
  190. /// </summary>
  191. /// <param name="user">The user.</param>
  192. /// <param name="item">The item.</param>
  193. /// <param name="data">The data.</param>
  194. public Task SaveUserDataForItem(User user, BaseItem item, UserItemData data)
  195. {
  196. item.AddOrUpdateUserData(user, data);
  197. return Kernel.UserDataRepository.SaveUserData(item, CancellationToken.None);
  198. }
  199. }
  200. }