< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Data.SqliteUserDataRepository
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
Line coverage
28%
Covered lines: 41
Uncovered lines: 103
Coverable lines: 144
Total lines: 369
Line coverage: 28.4%
Branch coverage
11%
Covered branches: 5
Total branches: 44
Branch coverage: 11.3%
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%
Initialize()33.33%6.67673.52%
ImportUserIds(...)0%2040%
GetAllUserIdsWithUserData(...)0%620%
SaveUserData(...)0%620%
SaveAllUserData(...)0%620%
PersistUserData(...)100%210%
SaveUserData(...)0%7280%
PersistAllUserData(...)0%620%
GetUserData(...)50%4.1481.81%
GetUserData(...)50%2.06275%
GetAllUserData(...)0%2040%
ReadRow(...)0%7280%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Generic;
 7using System.IO;
 8using System.Threading;
 9using Jellyfin.Data.Entities;
 10using MediaBrowser.Controller.Configuration;
 11using MediaBrowser.Controller.Entities;
 12using MediaBrowser.Controller.Library;
 13using MediaBrowser.Controller.Persistence;
 14using Microsoft.Data.Sqlite;
 15using Microsoft.Extensions.Logging;
 16
 17namespace Emby.Server.Implementations.Data
 18{
 19    public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
 20    {
 21        private readonly IUserManager _userManager;
 22
 23        public SqliteUserDataRepository(
 24            ILogger<SqliteUserDataRepository> logger,
 25            IServerConfigurationManager config,
 26            IUserManager userManager)
 2227            : base(logger)
 28        {
 2229            _userManager = userManager;
 30
 2231            DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "library.db");
 2232        }
 33
 34        /// <summary>
 35        /// Opens the connection to the database.
 36        /// </summary>
 37        public override void Initialize()
 38        {
 2239            base.Initialize();
 40
 2241            using (var connection = GetConnection())
 42            {
 2243                var userDatasTableExists = TableExists(connection, "UserDatas");
 2244                var userDataTableExists = TableExists(connection, "userdata");
 45
 2246                var users = userDatasTableExists ? null : _userManager.Users;
 2247                using var transaction = connection.BeginTransaction();
 2248                connection.Execute(string.Join(
 2249                    ';',
 2250                    "create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null
 2251                    "drop index if exists idx_userdata",
 2252                    "drop index if exists idx_userdata1",
 2253                    "drop index if exists idx_userdata2",
 2254                    "drop index if exists userdataindex1",
 2255                    "drop index if exists userdataindex",
 2256                    "drop index if exists userdataindex3",
 2257                    "drop index if exists userdataindex4",
 2258                    "create unique index if not exists UserDatasIndex1 on UserDatas (key, userId)",
 2259                    "create index if not exists UserDatasIndex2 on UserDatas (key, userId, played)",
 2260                    "create index if not exists UserDatasIndex3 on UserDatas (key, userId, playbackPositionTicks)",
 2261                    "create index if not exists UserDatasIndex4 on UserDatas (key, userId, isFavorite)",
 2262                    "create index if not exists UserDatasIndex5 on UserDatas (key, userId, lastPlayedDate)"));
 63
 2264                if (!userDataTableExists)
 65                {
 2266                    transaction.Commit();
 2267                    return;
 68                }
 69
 070                var existingColumnNames = GetColumnNames(connection, "userdata");
 71
 072                AddColumn(connection, "userdata", "InternalUserId", "int", existingColumnNames);
 073                AddColumn(connection, "userdata", "AudioStreamIndex", "int", existingColumnNames);
 074                AddColumn(connection, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
 75
 076                if (userDatasTableExists)
 77                {
 078                    return;
 79                }
 80
 081                ImportUserIds(connection, users);
 82
 083                connection.Execute("INSERT INTO UserDatas (key, userId, rating, played, playCount, isFavorite, playbackP
 84
 085                transaction.Commit();
 86            }
 2287        }
 88
 89        private void ImportUserIds(ManagedConnection db, IEnumerable<User> users)
 90        {
 091            var userIdsWithUserData = GetAllUserIdsWithUserData(db);
 92
 093            using (var statement = db.PrepareStatement("update userdata set InternalUserId=@InternalUserId where UserId=
 94            {
 095                foreach (var user in users)
 96                {
 097                    if (!userIdsWithUserData.Contains(user.Id))
 98                    {
 99                        continue;
 100                    }
 101
 0102                    statement.TryBind("@UserId", user.Id);
 0103                    statement.TryBind("@InternalUserId", user.InternalId);
 104
 0105                    statement.ExecuteNonQuery();
 106                }
 107            }
 0108        }
 109
 110        private List<Guid> GetAllUserIdsWithUserData(ManagedConnection db)
 111        {
 0112            var list = new List<Guid>();
 113
 0114            using (var statement = PrepareStatement(db, "select DISTINCT UserId from UserData where UserId not null"))
 115            {
 0116                foreach (var row in statement.ExecuteQuery())
 117                {
 118                    try
 119                    {
 0120                        list.Add(row.GetGuid(0));
 0121                    }
 0122                    catch (Exception ex)
 123                    {
 0124                        Logger.LogError(ex, "Error while getting user");
 0125                    }
 126                }
 127            }
 128
 0129            return list;
 130        }
 131
 132        /// <inheritdoc />
 133        public void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken)
 134        {
 0135            ArgumentNullException.ThrowIfNull(userData);
 136
 0137            if (userId <= 0)
 138            {
 0139                throw new ArgumentNullException(nameof(userId));
 140            }
 141
 0142            ArgumentException.ThrowIfNullOrEmpty(key);
 143
 0144            PersistUserData(userId, key, userData, cancellationToken);
 0145        }
 146
 147        /// <inheritdoc />
 148        public void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken)
 149        {
 0150            ArgumentNullException.ThrowIfNull(userData);
 151
 0152            if (userId <= 0)
 153            {
 0154                throw new ArgumentNullException(nameof(userId));
 155            }
 156
 0157            PersistAllUserData(userId, userData, cancellationToken);
 0158        }
 159
 160        /// <summary>
 161        /// Persists the user data.
 162        /// </summary>
 163        /// <param name="internalUserId">The user id.</param>
 164        /// <param name="key">The key.</param>
 165        /// <param name="userData">The user data.</param>
 166        /// <param name="cancellationToken">The cancellation token.</param>
 167        public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellati
 168        {
 0169            cancellationToken.ThrowIfCancellationRequested();
 170
 0171            using (var connection = GetConnection())
 0172            using (var transaction = connection.BeginTransaction())
 173            {
 0174                SaveUserData(connection, internalUserId, key, userData);
 0175                transaction.Commit();
 0176            }
 0177        }
 178
 179        private static void SaveUserData(ManagedConnection db, long internalUserId, string key, UserItemData userData)
 180        {
 0181            using (var statement = db.PrepareStatement("replace into UserDatas (key, userId, rating,played,playCount,isF
 182            {
 0183                statement.TryBind("@userId", internalUserId);
 0184                statement.TryBind("@key", key);
 185
 0186                if (userData.Rating.HasValue)
 187                {
 0188                    statement.TryBind("@rating", userData.Rating.Value);
 189                }
 190                else
 191                {
 0192                    statement.TryBindNull("@rating");
 193                }
 194
 0195                statement.TryBind("@played", userData.Played);
 0196                statement.TryBind("@playCount", userData.PlayCount);
 0197                statement.TryBind("@isFavorite", userData.IsFavorite);
 0198                statement.TryBind("@playbackPositionTicks", userData.PlaybackPositionTicks);
 199
 0200                if (userData.LastPlayedDate.HasValue)
 201                {
 0202                    statement.TryBind("@lastPlayedDate", userData.LastPlayedDate.Value.ToDateTimeParamValue());
 203                }
 204                else
 205                {
 0206                    statement.TryBindNull("@lastPlayedDate");
 207                }
 208
 0209                if (userData.AudioStreamIndex.HasValue)
 210                {
 0211                    statement.TryBind("@AudioStreamIndex", userData.AudioStreamIndex.Value);
 212                }
 213                else
 214                {
 0215                    statement.TryBindNull("@AudioStreamIndex");
 216                }
 217
 0218                if (userData.SubtitleStreamIndex.HasValue)
 219                {
 0220                    statement.TryBind("@SubtitleStreamIndex", userData.SubtitleStreamIndex.Value);
 221                }
 222                else
 223                {
 0224                    statement.TryBindNull("@SubtitleStreamIndex");
 225                }
 226
 0227                statement.ExecuteNonQuery();
 0228            }
 0229        }
 230
 231        /// <summary>
 232        /// Persist all user data for the specified user.
 233        /// </summary>
 234        private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellation
 235        {
 0236            cancellationToken.ThrowIfCancellationRequested();
 237
 0238            using (var connection = GetConnection())
 0239            using (var transaction = connection.BeginTransaction())
 240            {
 0241                foreach (var userItemData in userDataList)
 242                {
 0243                    SaveUserData(connection, internalUserId, userItemData.Key, userItemData);
 244                }
 245
 0246                transaction.Commit();
 0247            }
 0248        }
 249
 250        /// <summary>
 251        /// Gets the user data.
 252        /// </summary>
 253        /// <param name="userId">The user id.</param>
 254        /// <param name="key">The key.</param>
 255        /// <returns>Task{UserItemData}.</returns>
 256        /// <exception cref="ArgumentNullException">
 257        /// userId
 258        /// or
 259        /// key.
 260        /// </exception>
 261        public UserItemData GetUserData(long userId, string key)
 262        {
 2263            if (userId <= 0)
 264            {
 0265                throw new ArgumentNullException(nameof(userId));
 266            }
 267
 2268            ArgumentException.ThrowIfNullOrEmpty(key);
 269
 2270            using (var connection = GetConnection(true))
 271            {
 2272                using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite
 273                {
 2274                    statement.TryBind("@UserId", userId);
 2275                    statement.TryBind("@Key", key);
 276
 4277                    foreach (var row in statement.ExecuteQuery())
 278                    {
 0279                        return ReadRow(row);
 280                    }
 281                }
 282
 2283                return null;
 284            }
 2285        }
 286
 287        public UserItemData GetUserData(long userId, List<string> keys)
 288        {
 2289            ArgumentNullException.ThrowIfNull(keys);
 290
 2291            if (keys.Count == 0)
 292            {
 0293                return null;
 294            }
 295
 2296            return GetUserData(userId, keys[0]);
 297        }
 298
 299        /// <summary>
 300        /// Return all user-data associated with the given user.
 301        /// </summary>
 302        /// <param name="userId">The internal user id.</param>
 303        /// <returns>The list of user item data.</returns>
 304        public List<UserItemData> GetAllUserData(long userId)
 305        {
 0306            if (userId <= 0)
 307            {
 0308                throw new ArgumentNullException(nameof(userId));
 309            }
 310
 0311            var list = new List<UserItemData>();
 312
 0313            using (var connection = GetConnection())
 314            {
 0315                using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite
 316                {
 0317                    statement.TryBind("@UserId", userId);
 318
 0319                    foreach (var row in statement.ExecuteQuery())
 320                    {
 0321                        list.Add(ReadRow(row));
 322                    }
 323                }
 324            }
 325
 0326            return list;
 327        }
 328
 329        /// <summary>
 330        /// Read a row from the specified reader into the provided userData object.
 331        /// </summary>
 332        /// <param name="reader">The list of result set values.</param>
 333        /// <returns>The user item data.</returns>
 334        private UserItemData ReadRow(SqliteDataReader reader)
 335        {
 0336            var userData = new UserItemData
 0337            {
 0338                Key = reader.GetString(0)
 0339            };
 340
 0341            if (reader.TryGetDouble(2, out var rating))
 342            {
 0343                userData.Rating = rating;
 344            }
 345
 0346            userData.Played = reader.GetBoolean(3);
 0347            userData.PlayCount = reader.GetInt32(4);
 0348            userData.IsFavorite = reader.GetBoolean(5);
 0349            userData.PlaybackPositionTicks = reader.GetInt64(6);
 350
 0351            if (reader.TryReadDateTime(7, out var lastPlayedDate))
 352            {
 0353                userData.LastPlayedDate = lastPlayedDate;
 354            }
 355
 0356            if (reader.TryGetInt32(8, out var audioStreamIndex))
 357            {
 0358                userData.AudioStreamIndex = audioStreamIndex;
 359            }
 360
 0361            if (reader.TryGetInt32(9, out var subtitleStreamIndex))
 362            {
 0363                userData.SubtitleStreamIndex = subtitleStreamIndex;
 364            }
 365
 0366            return userData;
 367        }
 368    }
 369}