DeviceManager.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Jellyfin.Data.Entities;
  6. using Jellyfin.Data.Entities.Security;
  7. using Jellyfin.Data.Enums;
  8. using Jellyfin.Data.Events;
  9. using MediaBrowser.Controller.Devices;
  10. using MediaBrowser.Controller.Library;
  11. using MediaBrowser.Model.Devices;
  12. using MediaBrowser.Model.Querying;
  13. using MediaBrowser.Model.Session;
  14. using Microsoft.EntityFrameworkCore;
  15. namespace Jellyfin.Server.Implementations.Devices
  16. {
  17. public class DeviceManager : IDeviceManager
  18. {
  19. private readonly JellyfinDbProvider _dbProvider;
  20. private readonly IUserManager _userManager;
  21. private readonly ConcurrentDictionary<string, ClientCapabilities> _capabilitiesMap = new ();
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="DeviceManager"/> class.
  24. /// </summary>
  25. /// <param name="dbProvider">The database provider.</param>
  26. /// <param name="userManager">The user manager.</param>
  27. public DeviceManager(JellyfinDbProvider dbProvider, IUserManager userManager)
  28. {
  29. _dbProvider = dbProvider;
  30. _userManager = userManager;
  31. }
  32. /// <inheritdoc />
  33. public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>>? DeviceOptionsUpdated;
  34. /// <inheritdoc />
  35. public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
  36. {
  37. _capabilitiesMap[deviceId] = capabilities;
  38. }
  39. /// <inheritdoc />
  40. public async Task UpdateDeviceOptions(string deviceId, DeviceOptions options)
  41. {
  42. await using var dbContext = _dbProvider.CreateContext();
  43. await dbContext.Database
  44. .ExecuteSqlRawAsync($"UPDATE [DeviceOptions] SET [CustomName] = ${options.CustomName}")
  45. .ConfigureAwait(false);
  46. DeviceOptionsUpdated?.Invoke(this, new GenericEventArgs<Tuple<string, DeviceOptions>>(new Tuple<string, DeviceOptions>(deviceId, options)));
  47. }
  48. /// <inheritdoc />
  49. public DeviceOptions? GetDeviceOptions(string deviceId)
  50. {
  51. using var dbContext = _dbProvider.CreateContext();
  52. return dbContext.DeviceOptions
  53. .AsQueryable()
  54. .FirstOrDefault(d => d.DeviceId == deviceId);
  55. }
  56. /// <inheritdoc />
  57. public ClientCapabilities GetCapabilities(string id)
  58. {
  59. return _capabilitiesMap.TryGetValue(id, out ClientCapabilities? result)
  60. ? result
  61. : new ClientCapabilities();
  62. }
  63. /// <inheritdoc />
  64. public async Task<DeviceInfo?> GetDevice(string id)
  65. {
  66. await using var dbContext = _dbProvider.CreateContext();
  67. var device = await dbContext.Devices
  68. .AsQueryable()
  69. .Where(d => d.DeviceId == id)
  70. .OrderByDescending(d => d.DateLastActivity)
  71. .Include(d => d.User)
  72. .FirstOrDefaultAsync()
  73. .ConfigureAwait(false);
  74. var deviceInfo = device == null ? null : ToDeviceInfo(device);
  75. return deviceInfo;
  76. }
  77. /// <inheritdoc />
  78. public async Task<QueryResult<DeviceInfo>> GetDevices(DeviceQuery query)
  79. {
  80. await using var dbContext = _dbProvider.CreateContext();
  81. var sessions = dbContext.Devices
  82. .AsQueryable()
  83. .OrderBy(d => d.DeviceId)
  84. .ThenByDescending(d => d.DateLastActivity)
  85. .AsAsyncEnumerable();
  86. // TODO: DeviceQuery doesn't seem to be used from client. Not even Swagger.
  87. if (query.SupportsSync.HasValue)
  88. {
  89. var val = query.SupportsSync.Value;
  90. sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == val);
  91. }
  92. if (!query.UserId.Equals(Guid.Empty))
  93. {
  94. var user = _userManager.GetUserById(query.UserId);
  95. sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId));
  96. }
  97. var array = await sessions.Select(ToDeviceInfo).ToArrayAsync();
  98. return new QueryResult<DeviceInfo>(array);
  99. }
  100. /// <inheritdoc />
  101. public bool CanAccessDevice(User user, string deviceId)
  102. {
  103. if (user == null)
  104. {
  105. throw new ArgumentException("user not found");
  106. }
  107. if (string.IsNullOrEmpty(deviceId))
  108. {
  109. throw new ArgumentNullException(nameof(deviceId));
  110. }
  111. if (user.HasPermission(PermissionKind.EnableAllDevices) || user.HasPermission(PermissionKind.IsAdministrator))
  112. {
  113. return true;
  114. }
  115. return user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparer.OrdinalIgnoreCase)
  116. || !GetCapabilities(deviceId).SupportsPersistentIdentifier;
  117. }
  118. private DeviceInfo ToDeviceInfo(Device authInfo)
  119. {
  120. var caps = GetCapabilities(authInfo.DeviceId);
  121. return new DeviceInfo
  122. {
  123. AppName = authInfo.AppName,
  124. AppVersion = authInfo.AppVersion,
  125. Id = authInfo.DeviceId,
  126. LastUserId = authInfo.UserId,
  127. LastUserName = authInfo.User.Username,
  128. Name = authInfo.DeviceName,
  129. DateLastActivity = authInfo.DateLastActivity,
  130. IconUrl = caps.IconUrl
  131. };
  132. }
  133. }
  134. }