< Summary - Jellyfin

Information
Class: Jellyfin.Server.Migrations.Routines.RemoveDuplicateExtras
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 27
Coverable lines: 27
Total lines: 76
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 4
Branch coverage: 0%
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%210%
Perform()0%2040%

File(s)

/srv/git/jellyfin/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.IO;
 4using System.Linq;
 5using Emby.Server.Implementations.Data;
 6using MediaBrowser.Controller;
 7using Microsoft.Data.Sqlite;
 8using Microsoft.Extensions.Logging;
 9
 10namespace Jellyfin.Server.Migrations.Routines;
 11
 12/// <summary>
 13/// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself.
 14/// </summary>
 15#pragma warning disable CS0618 // Type or member is obsolete
 16[JellyfinMigration("2025-04-20T08:00:00", nameof(RemoveDuplicateExtras), "ACBE17B7-8435-4A83-8B64-6FCF162CB9BD")]
 17internal class RemoveDuplicateExtras : IMigrationRoutine
 18#pragma warning restore CS0618 // Type or member is obsolete
 19{
 20    private const string DbFilename = "library.db";
 21    private readonly ILogger<RemoveDuplicateExtras> _logger;
 22    private readonly IServerApplicationPaths _paths;
 23
 24    public RemoveDuplicateExtras(ILogger<RemoveDuplicateExtras> logger, IServerApplicationPaths paths)
 25    {
 026        _logger = logger;
 027        _paths = paths;
 028    }
 29
 30    /// <inheritdoc/>
 31    public void Perform()
 32    {
 033        var dataPath = _paths.DataPath;
 034        var dbPath = Path.Combine(dataPath, DbFilename);
 035        using var connection = new SqliteConnection($"Filename={dbPath}");
 036        connection.Open();
 037        using (var transaction = connection.BeginTransaction())
 38        {
 39            // Query the database for the ids of duplicate extras
 040            var queryResult = connection.Query("SELECT t1.Path FROM TypedBaseItems AS t1, TypedBaseItems AS t2 WHERE t1.
 041            var bads = string.Join(", ", queryResult.Select(x => x.GetString(0)));
 42
 43            // Do nothing if no duplicate extras were detected
 044            if (bads.Length == 0)
 45            {
 046                _logger.LogInformation("No duplicate extras detected, skipping migration.");
 047                return;
 48            }
 49
 50            // Back up the database before deleting any entries
 051            for (int i = 1; ; i++)
 52            {
 053                var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i);
 054                if (!File.Exists(bakPath))
 55                {
 56                    try
 57                    {
 058                        File.Copy(dbPath, bakPath);
 059                        _logger.LogInformation("Library database backed up to {BackupPath}", bakPath);
 060                        break;
 61                    }
 062                    catch (Exception ex)
 63                    {
 064                        _logger.LogError(ex, "Cannot make a backup of {Library} at path {BackupPath}", DbFilename, bakPa
 065                        throw;
 66                    }
 67                }
 68            }
 69
 70            // Delete all duplicate extras
 071            _logger.LogInformation("Removing found duplicated extras for the following items: {DuplicateExtras}", bads);
 072            connection.Execute("DELETE FROM TypedBaseItems WHERE rowid IN (SELECT t1.rowid FROM TypedBaseItems AS t1, Ty
 073            transaction.Commit();
 074        }
 075    }
 76}