< Summary - Jellyfin

Information
Class: Jellyfin.Server.Implementations.Devices.DeviceManager
Assembly: Jellyfin.Server.Implementations
File(s): /srv/git/jellyfin/Jellyfin.Server.Implementations/Devices/DeviceManager.cs
Line coverage
40%
Covered lines: 32
Uncovered lines: 47
Coverable lines: 79
Total lines: 258
Line coverage: 40.5%
Branch coverage
32%
Covered branches: 9
Total branches: 28
Branch coverage: 32.1%
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(...)50%4.04486.66%
SaveCapabilities(...)100%210%
GetDeviceOptions(...)100%22100%
GetCapabilities(...)50%22100%
GetDevice(...)0%620%
GetDevices(...)50%4.07483.33%
GetDeviceInfos(...)100%210%
GetDevicesForUser(...)0%2040%
CanAccessDevice(...)33.33%7.33666.66%
ToDeviceInfo(...)0%2040%

File(s)

/srv/git/jellyfin/Jellyfin.Server.Implementations/Devices/DeviceManager.cs

#LineLine coverage
 1using System;
 2using System.Collections.Concurrent;
 3using System.Collections.Generic;
 4using System.Linq;
 5using System.Threading.Tasks;
 6using Jellyfin.Data.Entities;
 7using Jellyfin.Data.Entities.Security;
 8using Jellyfin.Data.Enums;
 9using Jellyfin.Data.Events;
 10using Jellyfin.Data.Queries;
 11using Jellyfin.Extensions;
 12using MediaBrowser.Common.Extensions;
 13using MediaBrowser.Controller.Devices;
 14using MediaBrowser.Controller.Library;
 15using MediaBrowser.Model.Devices;
 16using MediaBrowser.Model.Querying;
 17using MediaBrowser.Model.Session;
 18using Microsoft.EntityFrameworkCore;
 19
 20namespace Jellyfin.Server.Implementations.Devices
 21{
 22    /// <summary>
 23    /// Manages the creation, updating, and retrieval of devices.
 24    /// </summary>
 25    public class DeviceManager : IDeviceManager
 26    {
 27        private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
 28        private readonly IUserManager _userManager;
 2229        private readonly ConcurrentDictionary<string, ClientCapabilities> _capabilitiesMap = new();
 30        private readonly ConcurrentDictionary<int, Device> _devices;
 31        private readonly ConcurrentDictionary<string, DeviceOptions> _deviceOptions;
 32
 33        /// <summary>
 34        /// Initializes a new instance of the <see cref="DeviceManager"/> class.
 35        /// </summary>
 36        /// <param name="dbProvider">The database provider.</param>
 37        /// <param name="userManager">The user manager.</param>
 38        public DeviceManager(IDbContextFactory<JellyfinDbContext> dbProvider, IUserManager userManager)
 39        {
 2240            _dbProvider = dbProvider;
 2241            _userManager = userManager;
 2242            _devices = new ConcurrentDictionary<int, Device>();
 2243            _deviceOptions = new ConcurrentDictionary<string, DeviceOptions>();
 44
 2245            using var dbContext = _dbProvider.CreateDbContext();
 4446            foreach (var device in dbContext.Devices
 2247                         .OrderBy(d => d.Id)
 2248                         .AsEnumerable())
 49            {
 050                _devices.TryAdd(device.Id, device);
 51            }
 52
 4453            foreach (var deviceOption in dbContext.DeviceOptions
 2254                         .OrderBy(d => d.Id)
 2255                         .AsEnumerable())
 56            {
 057                _deviceOptions.TryAdd(deviceOption.DeviceId, deviceOption);
 58            }
 2259        }
 60
 61        /// <inheritdoc />
 62        public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>>? DeviceOptionsUpdated;
 63
 64        /// <inheritdoc />
 65        public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
 66        {
 067            _capabilitiesMap[deviceId] = capabilities;
 068        }
 69
 70        /// <inheritdoc />
 71        public async Task UpdateDeviceOptions(string deviceId, string deviceName)
 72        {
 73            DeviceOptions? deviceOptions;
 74            var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 75            await using (dbContext.ConfigureAwait(false))
 76            {
 77                deviceOptions = await dbContext.DeviceOptions.FirstOrDefaultAsync(dev => dev.DeviceId == deviceId).Confi
 78                if (deviceOptions is null)
 79                {
 80                    deviceOptions = new DeviceOptions(deviceId);
 81                    dbContext.DeviceOptions.Add(deviceOptions);
 82                }
 83
 84                deviceOptions.CustomName = deviceName;
 85                await dbContext.SaveChangesAsync().ConfigureAwait(false);
 86            }
 87
 88            _deviceOptions[deviceId] = deviceOptions;
 89
 90            DeviceOptionsUpdated?.Invoke(this, new GenericEventArgs<Tuple<string, DeviceOptions>>(new Tuple<string, Devi
 91        }
 92
 93        /// <inheritdoc />
 94        public async Task<Device> CreateDevice(Device device)
 95        {
 96            var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 97            await using (dbContext.ConfigureAwait(false))
 98            {
 99                dbContext.Devices.Add(device);
 100                await dbContext.SaveChangesAsync().ConfigureAwait(false);
 101                _devices.TryAdd(device.Id, device);
 102            }
 103
 104            return device;
 105        }
 106
 107        /// <inheritdoc />
 108        public DeviceOptions GetDeviceOptions(string deviceId)
 109        {
 16110            _deviceOptions.TryGetValue(deviceId, out var deviceOptions);
 111
 16112            return deviceOptions ?? new DeviceOptions(deviceId);
 113        }
 114
 115        /// <inheritdoc />
 116        public ClientCapabilities GetCapabilities(string deviceId)
 117        {
 16118            return _capabilitiesMap.TryGetValue(deviceId, out ClientCapabilities? result)
 16119                ? result
 16120                : new ClientCapabilities();
 121        }
 122
 123        /// <inheritdoc />
 124        public DeviceInfo? GetDevice(string id)
 125        {
 0126            var device = _devices.Values.Where(d => d.DeviceId == id).OrderByDescending(d => d.DateLastActivity).FirstOr
 0127            _deviceOptions.TryGetValue(id, out var deviceOption);
 128
 0129            var deviceInfo = device is null ? null : ToDeviceInfo(device, deviceOption);
 0130            return deviceInfo;
 131        }
 132
 133        /// <inheritdoc />
 134        public QueryResult<Device> GetDevices(DeviceQuery query)
 135        {
 128136            IEnumerable<Device> devices = _devices.Values
 128137                .Where(device => !query.UserId.HasValue || device.UserId.Equals(query.UserId.Value))
 128138                .Where(device => query.DeviceId is null || device.DeviceId == query.DeviceId)
 128139                .Where(device => query.AccessToken is null || device.AccessToken == query.AccessToken)
 128140                .OrderBy(d => d.Id)
 128141                .ToList();
 128142            var count = devices.Count();
 143
 128144            if (query.Skip.HasValue)
 145            {
 0146                devices = devices.Skip(query.Skip.Value);
 147            }
 148
 128149            if (query.Limit.HasValue)
 150            {
 0151                devices = devices.Take(query.Limit.Value);
 152            }
 153
 128154            return new QueryResult<Device>(query.Skip, count, devices.ToList());
 155        }
 156
 157        /// <inheritdoc />
 158        public QueryResult<DeviceInfo> GetDeviceInfos(DeviceQuery query)
 159        {
 0160            var devices = GetDevices(query);
 161
 0162            return new QueryResult<DeviceInfo>(
 0163                devices.StartIndex,
 0164                devices.TotalRecordCount,
 0165                devices.Items.Select(device => ToDeviceInfo(device)).ToList());
 166        }
 167
 168        /// <inheritdoc />
 169        public QueryResult<DeviceInfo> GetDevicesForUser(Guid? userId)
 170        {
 0171            IEnumerable<Device> devices = _devices.Values
 0172                .OrderByDescending(d => d.DateLastActivity)
 0173                .ThenBy(d => d.DeviceId);
 174
 0175            if (!userId.IsNullOrEmpty())
 176            {
 0177                var user = _userManager.GetUserById(userId.Value);
 0178                if (user is null)
 179                {
 0180                    throw new ResourceNotFoundException();
 181                }
 182
 0183                devices = devices.Where(i => CanAccessDevice(user, i.DeviceId));
 184            }
 185
 0186            var array = devices.Select(device =>
 0187                {
 0188                    _deviceOptions.TryGetValue(device.DeviceId, out var option);
 0189                    return ToDeviceInfo(device, option);
 0190                }).ToArray();
 191
 0192            return new QueryResult<DeviceInfo>(array);
 193        }
 194
 195        /// <inheritdoc />
 196        public async Task DeleteDevice(Device device)
 197        {
 198            _devices.TryRemove(device.Id, out _);
 199            var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 200            await using (dbContext.ConfigureAwait(false))
 201            {
 202                dbContext.Devices.Remove(device);
 203                await dbContext.SaveChangesAsync().ConfigureAwait(false);
 204            }
 205        }
 206
 207        /// <inheritdoc />
 208        public async Task UpdateDevice(Device device)
 209        {
 210            var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 211            await using (dbContext.ConfigureAwait(false))
 212            {
 213                dbContext.Devices.Update(device);
 214                await dbContext.SaveChangesAsync().ConfigureAwait(false);
 215            }
 216
 217            _devices[device.Id] = device;
 218        }
 219
 220        /// <inheritdoc />
 221        public bool CanAccessDevice(User user, string deviceId)
 222        {
 16223            ArgumentNullException.ThrowIfNull(user);
 16224            ArgumentException.ThrowIfNullOrEmpty(deviceId);
 225
 16226            if (user.HasPermission(PermissionKind.EnableAllDevices) || user.HasPermission(PermissionKind.IsAdministrator
 227            {
 16228                return true;
 229            }
 230
 0231            return user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparison.OrdinalIgnoreCa
 0232                   || !GetCapabilities(deviceId).SupportsPersistentIdentifier;
 233        }
 234
 235        private DeviceInfo ToDeviceInfo(Device authInfo, DeviceOptions? options = null)
 236        {
 0237            var caps = GetCapabilities(authInfo.DeviceId);
 0238            var user = _userManager.GetUserById(authInfo.UserId);
 0239            if (user is null)
 240            {
 0241                throw new ResourceNotFoundException("User with UserId " + authInfo.UserId + " not found");
 242            }
 243
 0244            return new DeviceInfo
 0245            {
 0246                AppName = authInfo.AppName,
 0247                AppVersion = authInfo.AppVersion,
 0248                Id = authInfo.DeviceId,
 0249                LastUserId = authInfo.UserId,
 0250                LastUserName = user.Username,
 0251                Name = authInfo.DeviceName,
 0252                DateLastActivity = authInfo.DateLastActivity,
 0253                IconUrl = caps.IconUrl,
 0254                CustomName = options?.CustomName,
 0255            };
 256        }
 257    }
 258}