You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jellyfin/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs

369 lines
14 KiB

#nullable disable
#pragma warning disable CS1591
using System;
8 years ago
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
8 years ago
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
8 years ago
using MediaBrowser.Controller.Persistence;
10 months ago
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
8 years ago
namespace Emby.Server.Implementations.Data
{
public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
{
private readonly IUserManager _userManager;
public SqliteUserDataRepository(
ILogger<SqliteUserDataRepository> logger,
IServerConfigurationManager config,
IUserManager userManager)
: base(logger)
8 years ago
{
_userManager = userManager;
DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "library.db");
8 years ago
}
/// <summary>
/// Opens the connection to the database.
8 years ago
/// </summary>
public override void Initialize()
8 years ago
{
base.Initialize();
5 years ago
using (var connection = GetConnection())
{
var userDatasTableExists = TableExists(connection, "UserDatas");
var userDataTableExists = TableExists(connection, "userdata");
var users = userDatasTableExists ? null : _userManager.Users;
using var transaction = connection.BeginTransaction();
connection.Execute(string.Join(
';',
"create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null, AudioStreamIndex INT, SubtitleStreamIndex INT)",
"drop index if exists idx_userdata",
"drop index if exists idx_userdata1",
"drop index if exists idx_userdata2",
"drop index if exists userdataindex1",
"drop index if exists userdataindex",
"drop index if exists userdataindex3",
"drop index if exists userdataindex4",
"create unique index if not exists UserDatasIndex1 on UserDatas (key, userId)",
"create index if not exists UserDatasIndex2 on UserDatas (key, userId, played)",
"create index if not exists UserDatasIndex3 on UserDatas (key, userId, playbackPositionTicks)",
"create index if not exists UserDatasIndex4 on UserDatas (key, userId, isFavorite)"));
if (!userDataTableExists)
{
transaction.Commit();
return;
}
8 years ago
var existingColumnNames = GetColumnNames(connection, "userdata");
AddColumn(connection, "userdata", "InternalUserId", "int", existingColumnNames);
AddColumn(connection, "userdata", "AudioStreamIndex", "int", existingColumnNames);
AddColumn(connection, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
if (userDatasTableExists)
{
return;
}
ImportUserIds(connection, users);
connection.Execute("INSERT INTO UserDatas (key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex) SELECT key, InternalUserId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex from userdata where InternalUserId not null");
transaction.Commit();
}
}
10 months ago
private void ImportUserIds(SqliteConnection db, IEnumerable<User> users)
{
var userIdsWithUserData = GetAllUserIdsWithUserData(db);
using (var statement = db.PrepareStatement("update userdata set InternalUserId=@InternalUserId where UserId=@UserId"))
{
foreach (var user in users)
{
if (!userIdsWithUserData.Contains(user.Id))
{
continue;
}
statement.TryBind("@UserId", user.Id);
statement.TryBind("@InternalUserId", user.InternalId);
10 months ago
statement.ExecuteNonQuery();
}
}
}
10 months ago
private List<Guid> GetAllUserIdsWithUserData(SqliteConnection db)
{
var list = new List<Guid>();
using (var statement = PrepareStatement(db, "select DISTINCT UserId from UserData where UserId not null"))
{
foreach (var row in statement.ExecuteQuery())
{
try
{
10 months ago
list.Add(row.GetGuid(0));
}
catch (Exception ex)
{
Logger.LogError(ex, "Error while getting user");
}
}
}
return list;
}
/// <inheritdoc />
public void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken)
8 years ago
{
ArgumentNullException.ThrowIfNull(userData);
if (userId <= 0)
8 years ago
{
throw new ArgumentNullException(nameof(userId));
8 years ago
}
ArgumentException.ThrowIfNullOrEmpty(key);
8 years ago
PersistUserData(userId, key, userData, cancellationToken);
8 years ago
}
/// <inheritdoc />
public void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken)
8 years ago
{
ArgumentNullException.ThrowIfNull(userData);
if (userId <= 0)
8 years ago
{
throw new ArgumentNullException(nameof(userId));
8 years ago
}
PersistAllUserData(userId, userData, cancellationToken);
8 years ago
}
/// <summary>
/// Persists the user data.
/// </summary>
/// <param name="internalUserId">The user id.</param>
8 years ago
/// <param name="key">The key.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
8 years ago
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
using (var transaction = connection.BeginTransaction())
8 years ago
{
SaveUserData(connection, internalUserId, key, userData);
transaction.Commit();
8 years ago
}
}
10 months ago
private static void SaveUserData(SqliteConnection db, long internalUserId, string key, UserItemData userData)
8 years ago
{
using (var statement = db.PrepareStatement("replace into UserDatas (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
8 years ago
{
statement.TryBind("@userId", internalUserId);
statement.TryBind("@key", key);
8 years ago
if (userData.Rating.HasValue)
{
statement.TryBind("@rating", userData.Rating.Value);
}
else
{
statement.TryBindNull("@rating");
}
statement.TryBind("@played", userData.Played);
statement.TryBind("@playCount", userData.PlayCount);
statement.TryBind("@isFavorite", userData.IsFavorite);
statement.TryBind("@playbackPositionTicks", userData.PlaybackPositionTicks);
if (userData.LastPlayedDate.HasValue)
{
statement.TryBind("@lastPlayedDate", userData.LastPlayedDate.Value.ToDateTimeParamValue());
}
else
{
statement.TryBindNull("@lastPlayedDate");
}
if (userData.AudioStreamIndex.HasValue)
{
statement.TryBind("@AudioStreamIndex", userData.AudioStreamIndex.Value);
}
else
{
statement.TryBindNull("@AudioStreamIndex");
}
if (userData.SubtitleStreamIndex.HasValue)
{
statement.TryBind("@SubtitleStreamIndex", userData.SubtitleStreamIndex.Value);
}
else
{
statement.TryBindNull("@SubtitleStreamIndex");
}
statement.ExecuteNonQuery();
8 years ago
}
}
/// <summary>
/// Persist all user data for the specified user.
8 years ago
/// </summary>
private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
8 years ago
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
using (var transaction = connection.BeginTransaction())
8 years ago
{
foreach (var userItemData in userDataList)
{
SaveUserData(connection, internalUserId, userItemData.Key, userItemData);
}
transaction.Commit();
8 years ago
}
}
/// <summary>
/// Gets the user data.
/// </summary>
/// <param name="userId">The user id.</param>
8 years ago
/// <param name="key">The key.</param>
/// <returns>Task{UserItemData}.</returns>
/// <exception cref="ArgumentNullException">
8 years ago
/// userId
/// or
/// key.
8 years ago
/// </exception>
public UserItemData GetUserData(long userId, string key)
8 years ago
{
if (userId <= 0)
8 years ago
{
throw new ArgumentNullException(nameof(userId));
8 years ago
}
5 years ago
ArgumentException.ThrowIfNullOrEmpty(key);
8 years ago
10 months ago
using (var connection = GetConnection())
8 years ago
{
5 years ago
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId"))
8 years ago
{
statement.TryBind("@UserId", userId);
5 years ago
statement.TryBind("@Key", key);
5 years ago
foreach (var row in statement.ExecuteQuery())
{
return ReadRow(row);
}
8 years ago
}
5 years ago
return null;
8 years ago
}
}
public UserItemData GetUserData(long userId, List<string> keys)
8 years ago
{
ArgumentNullException.ThrowIfNull(keys);
8 years ago
if (keys.Count == 0)
{
return null;
}
return GetUserData(userId, keys[0]);
8 years ago
}
/// <summary>
/// Return all user-data associated with the given user.
8 years ago
/// </summary>
/// <param name="userId">The internal user id.</param>
/// <returns>The list of user item data.</returns>
public List<UserItemData> GetAllUserData(long userId)
8 years ago
{
if (userId <= 0)
8 years ago
{
throw new ArgumentNullException(nameof(userId));
8 years ago
}
var list = new List<UserItemData>();
using (var connection = GetConnection())
8 years ago
{
5 years ago
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId"))
8 years ago
{
statement.TryBind("@UserId", userId);
5 years ago
foreach (var row in statement.ExecuteQuery())
{
list.Add(ReadRow(row));
8 years ago
}
}
}
return list;
}
/// <summary>
/// Read a row from the specified reader into the provided userData object.
8 years ago
/// </summary>
/// <param name="reader">The list of result set values.</param>
/// <returns>The user item data.</returns>
10 months ago
private UserItemData ReadRow(SqliteDataReader reader)
8 years ago
{
var userData = new UserItemData();
userData.Key = reader[0].ToString();
4 years ago
// userData.UserId = reader[1].ReadGuidFromBlob();
8 years ago
if (reader.TryGetDouble(2, out var rating))
8 years ago
{
userData.Rating = rating;
8 years ago
}
10 months ago
userData.Played = reader.GetBoolean(3);
userData.PlayCount = reader.GetInt32(4);
userData.IsFavorite = reader.GetBoolean(5);
userData.PlaybackPositionTicks = reader.GetInt64(6);
8 years ago
if (reader.TryReadDateTime(7, out var lastPlayedDate))
8 years ago
{
userData.LastPlayedDate = lastPlayedDate;
8 years ago
}
if (reader.TryGetInt32(8, out var audioStreamIndex))
8 years ago
{
userData.AudioStreamIndex = audioStreamIndex;
8 years ago
}
if (reader.TryGetInt32(9, out var subtitleStreamIndex))
8 years ago
{
userData.SubtitleStreamIndex = subtitleStreamIndex;
8 years ago
}
return userData;
}
}
}