jellyfin/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs

231 lines
10 KiB
C#
Raw Normal View History

2020-07-01 03:44:41 +02:00
using System;
2020-08-01 20:56:32 +02:00
using System.Collections.Generic;
2020-07-01 03:44:41 +02:00
using System.IO;
using System.Linq;
2020-07-01 03:44:41 +02:00
using System.Text.Json;
using System.Text.Json.Serialization;
2023-08-21 15:31:02 +02:00
using Emby.Server.Implementations.Data;
2020-07-01 03:44:41 +02:00
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Server.Implementations;
using MediaBrowser.Controller;
2020-11-11 15:17:19 +01:00
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
2023-08-21 15:31:02 +02:00
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
2020-07-01 03:44:41 +02:00
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Migrations.Routines
{
/// <summary>
/// The migration routine for migrating the display preferences database to EF Core.
/// </summary>
public class MigrateDisplayPreferencesDb : IMigrationRoutine
{
private const string DbFilename = "displaypreferences.db";
private readonly ILogger<MigrateDisplayPreferencesDb> _logger;
private readonly IServerApplicationPaths _paths;
2023-01-16 18:14:44 +01:00
private readonly IDbContextFactory<JellyfinDbContext> _provider;
2020-07-01 03:44:41 +02:00
private readonly JsonSerializerOptions _jsonOptions;
2020-11-11 15:17:19 +01:00
private readonly IUserManager _userManager;
2020-07-01 03:44:41 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="MigrateDisplayPreferencesDb"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="paths">The server application paths.</param>
/// <param name="provider">The database provider.</param>
2020-11-11 15:17:19 +01:00
/// <param name="userManager">The user manager.</param>
public MigrateDisplayPreferencesDb(
ILogger<MigrateDisplayPreferencesDb> logger,
IServerApplicationPaths paths,
2023-01-16 18:14:44 +01:00
IDbContextFactory<JellyfinDbContext> provider,
2020-11-11 15:17:19 +01:00
IUserManager userManager)
2020-07-01 03:44:41 +02:00
{
_logger = logger;
_paths = paths;
_provider = provider;
2020-11-11 15:17:19 +01:00
_userManager = userManager;
2020-07-01 03:44:41 +02:00
_jsonOptions = new JsonSerializerOptions();
_jsonOptions.Converters.Add(new JsonStringEnumConverter());
}
/// <inheritdoc />
public Guid Id => Guid.Parse("06387815-C3CC-421F-A888-FB5F9992BEA8");
/// <inheritdoc />
public string Name => "MigrateDisplayPreferencesDatabase";
2020-07-24 02:36:36 +02:00
/// <inheritdoc />
public bool PerformOnNewInstall => false;
2020-07-01 03:44:41 +02:00
/// <inheritdoc />
public void Perform()
{
HomeSectionType[] defaults =
{
HomeSectionType.SmallLibraryTiles,
HomeSectionType.Resume,
HomeSectionType.ResumeAudio,
HomeSectionType.LiveTv,
HomeSectionType.NextUp,
HomeSectionType.LatestMedia,
HomeSectionType.None,
};
2020-08-01 20:56:32 +02:00
var chromecastDict = new Dictionary<string, ChromecastVersion>(StringComparer.OrdinalIgnoreCase)
{
{ "stable", ChromecastVersion.Stable },
{ "nightly", ChromecastVersion.Unstable },
{ "unstable", ChromecastVersion.Unstable }
};
var displayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var customDisplayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
2020-07-01 03:44:41 +02:00
var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
2023-08-21 15:31:02 +02:00
using (var connection = new SqliteConnection($"Filename={dbFilePath}"))
2020-07-01 03:44:41 +02:00
{
2023-09-16 07:27:22 +02:00
connection.Open();
using var dbContext = _provider.CreateDbContext();
2020-07-01 03:44:41 +02:00
var results = connection.Query("SELECT * FROM userdisplaypreferences");
foreach (var result in results)
{
2023-08-21 15:31:02 +02:00
var dto = JsonSerializer.Deserialize<DisplayPreferencesDto>(result.GetStream(3), _jsonOptions);
2022-12-05 15:00:20 +01:00
if (dto is null)
2020-08-25 15:33:58 +02:00
{
continue;
}
2023-08-21 15:31:02 +02:00
var itemId = result.GetGuid(1);
var dtoUserId = itemId;
var client = result.GetString(2);
var displayPreferencesKey = $"{dtoUserId}|{itemId}|{client}";
if (displayPrefs.Contains(displayPreferencesKey))
{
// Duplicate display preference.
continue;
}
displayPrefs.Add(displayPreferencesKey);
2020-11-11 15:17:19 +01:00
var existingUser = _userManager.GetUserById(dtoUserId);
2022-12-05 15:00:20 +01:00
if (existingUser is null)
2020-11-11 15:17:19 +01:00
{
_logger.LogWarning("User with ID {UserId} does not exist in the database, skipping migration.", dtoUserId);
2020-11-11 15:17:19 +01:00
continue;
}
var chromecastVersion = dto.CustomPrefs.TryGetValue("chromecastVersion", out var version)
&& !string.IsNullOrEmpty(version)
2020-08-01 20:56:32 +02:00
? chromecastDict[version]
: ChromecastVersion.Stable;
dto.CustomPrefs.Remove("chromecastVersion");
2020-07-01 03:44:41 +02:00
var displayPreferences = new DisplayPreferences(dtoUserId, itemId, client)
2020-07-01 03:44:41 +02:00
{
IndexBy = Enum.TryParse<IndexingKind>(dto.IndexBy, true, out var indexBy) ? indexBy : null,
2020-07-01 03:44:41 +02:00
ShowBackdrop = dto.ShowBackdrop,
ShowSidebar = dto.ShowSidebar,
ScrollDirection = dto.ScrollDirection,
2020-07-24 22:30:54 +02:00
ChromecastVersion = chromecastVersion,
2021-04-20 13:43:05 +02:00
SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length) && int.TryParse(length, out var skipForwardLength)
? skipForwardLength
2020-07-24 22:30:54 +02:00
: 30000,
SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && int.TryParse(length, out var skipBackwardLength)
2021-04-20 13:43:05 +02:00
? skipBackwardLength
: 10000,
2023-04-06 19:30:17 +02:00
EnableNextVideoInfoOverlay = !dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) || string.IsNullOrEmpty(enabled) || bool.Parse(enabled),
2020-07-28 02:50:58 +02:00
DashboardTheme = dto.CustomPrefs.TryGetValue("dashboardtheme", out var theme) ? theme : string.Empty,
TvHome = dto.CustomPrefs.TryGetValue("tvhome", out var home) ? home : string.Empty
2020-07-01 03:44:41 +02:00
};
dto.CustomPrefs.Remove("skipForwardLength");
dto.CustomPrefs.Remove("skipBackLength");
dto.CustomPrefs.Remove("enableNextVideoInfoOverlay");
dto.CustomPrefs.Remove("dashboardtheme");
dto.CustomPrefs.Remove("tvhome");
2020-07-01 03:44:41 +02:00
for (int i = 0; i < 7; i++)
{
var key = "homesection" + i;
dto.CustomPrefs.TryGetValue(key, out var homeSection);
2020-07-01 03:44:41 +02:00
displayPreferences.HomeSections.Add(new HomeSection
{
Order = i,
Type = Enum.TryParse<HomeSectionType>(homeSection, true, out var type) ? type : defaults[i]
});
dto.CustomPrefs.Remove(key);
2020-07-01 03:44:41 +02:00
}
var defaultLibraryPrefs = new ItemDisplayPreferences(displayPreferences.UserId, Guid.Empty, displayPreferences.Client)
{
SortBy = dto.SortBy ?? "SortName",
SortOrder = dto.SortOrder,
RememberIndexing = dto.RememberIndexing,
RememberSorting = dto.RememberSorting,
};
dbContext.Add(defaultLibraryPrefs);
foreach (var key in dto.CustomPrefs.Keys.Where(key => key.StartsWith("landing-", StringComparison.Ordinal)))
{
2020-12-05 00:00:11 +01:00
if (!Guid.TryParse(key.AsSpan().Slice("landing-".Length), out var landingItemId))
{
continue;
}
2020-12-05 00:00:11 +01:00
var libraryDisplayPreferences = new ItemDisplayPreferences(displayPreferences.UserId, landingItemId, displayPreferences.Client)
{
SortBy = dto.SortBy ?? "SortName",
SortOrder = dto.SortOrder,
RememberIndexing = dto.RememberIndexing,
RememberSorting = dto.RememberSorting,
};
if (Enum.TryParse<ViewType>(dto.ViewType, true, out var viewType))
{
libraryDisplayPreferences.ViewType = viewType;
}
dto.CustomPrefs.Remove(key);
dbContext.ItemDisplayPreferences.Add(libraryDisplayPreferences);
}
foreach (var (key, value) in dto.CustomPrefs)
{
// Custom display preferences can have a key collision.
var indexKey = $"{displayPreferences.UserId}|{itemId}|{displayPreferences.Client}|{key}";
if (!customDisplayPrefs.Contains(indexKey))
{
dbContext.Add(new CustomItemDisplayPreferences(displayPreferences.UserId, itemId, displayPreferences.Client, key, value));
customDisplayPrefs.Add(indexKey);
}
}
2020-07-01 03:44:41 +02:00
dbContext.Add(displayPreferences);
}
dbContext.SaveChanges();
}
try
{
File.Move(dbFilePath, dbFilePath + ".old");
var journalPath = dbFilePath + "-journal";
if (File.Exists(journalPath))
{
File.Move(journalPath, dbFilePath + ".old-journal");
}
}
catch (IOException e)
{
_logger.LogError(e, "Error renaming legacy display preferences database to 'displaypreferences.db.old'");
}
}
}
}