2020-05-14 23:30:28 +00:00
using System ;
2020-05-15 18:49:45 +00:00
using System.Globalization ;
2020-05-14 23:30:28 +00:00
using System.IO ;
using MediaBrowser.Controller ;
using Microsoft.Extensions.Logging ;
using SQLitePCL.pretty ;
namespace Jellyfin.Server.Migrations.Routines
{
/// <summary>
/// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself.
/// </summary>
2020-05-15 18:32:56 +00:00
internal class RemoveDuplicateExtras : IMigrationRoutine
2020-05-14 23:30:28 +00:00
{
private const string DbFilename = "library.db" ;
2020-06-06 00:15:56 +00:00
private readonly ILogger < RemoveDuplicateExtras > _logger ;
2020-05-14 23:30:28 +00:00
private readonly IServerApplicationPaths _paths ;
2020-05-15 18:49:45 +00:00
public RemoveDuplicateExtras ( ILogger < RemoveDuplicateExtras > logger , IServerApplicationPaths paths )
2020-05-14 23:30:28 +00:00
{
_logger = logger ;
_paths = paths ;
}
/// <inheritdoc/>
public Guid Id = > Guid . Parse ( "{ACBE17B7-8435-4A83-8B64-6FCF162CB9BD}" ) ;
/// <inheritdoc/>
2020-05-15 18:49:45 +00:00
public string Name = > "RemoveDuplicateExtras" ;
2020-05-14 23:30:28 +00:00
2020-07-20 13:45:24 +00:00
/// <inheritdoc/>
public bool PerformOnNewInstall = > false ;
2020-05-14 23:30:28 +00:00
/// <inheritdoc/>
public void Perform ( )
{
var dataPath = _paths . DataPath ;
2020-05-15 18:49:45 +00:00
var dbPath = Path . Combine ( dataPath , DbFilename ) ;
2020-05-14 23:30:28 +00:00
using ( var connection = SQLite3 . Open (
2020-05-15 18:49:45 +00:00
dbPath ,
2020-05-14 23:30:28 +00:00
ConnectionFlags . ReadWrite ,
null ) )
{
2020-05-15 19:10:41 +00:00
// Query the database for the ids of duplicate extras
2020-05-14 23:30:28 +00:00
var queryResult = connection . Query ( "SELECT t1.Path FROM TypedBaseItems AS t1, TypedBaseItems AS t2 WHERE t1.Path=t2.Path AND t1.Type!=t2.Type AND t1.Type='MediaBrowser.Controller.Entities.Video'" ) ;
var bads = string . Join ( ", " , queryResult . SelectScalarString ( ) ) ;
2020-05-15 19:10:41 +00:00
// Do nothing if no duplicate extras were detected
if ( bads . Length = = 0 )
{
_logger . LogInformation ( "No duplicate extras detected, skipping migration." ) ;
return ;
}
// Back up the database before deleting any entries
for ( int i = 1 ; ; i + + )
2020-05-14 23:30:28 +00:00
{
2020-05-15 19:10:41 +00:00
var bakPath = string . Format ( CultureInfo . InvariantCulture , "{0}.bak{1}" , dbPath , i ) ;
if ( ! File . Exists ( bakPath ) )
2020-05-15 18:49:45 +00:00
{
2020-05-15 19:10:41 +00:00
try
2020-05-15 18:49:45 +00:00
{
2020-05-15 19:10:41 +00:00
File . Copy ( dbPath , bakPath ) ;
_logger . LogInformation ( "Library database backed up to {BackupPath}" , bakPath ) ;
break ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Cannot make a backup of {Library} at path {BackupPath}" , DbFilename , bakPath ) ;
throw ;
2020-05-15 18:49:45 +00:00
}
}
2020-05-14 23:30:28 +00:00
}
2020-05-15 19:10:41 +00:00
// Delete all duplicate extras
_logger . LogInformation ( "Removing found duplicated extras for the following items: {DuplicateExtras}" , bads ) ;
connection . Execute ( "DELETE FROM TypedBaseItems WHERE rowid IN (SELECT t1.rowid FROM TypedBaseItems AS t1, TypedBaseItems AS t2 WHERE t1.Path=t2.Path AND t1.Type!=t2.Type AND t1.Type='MediaBrowser.Controller.Entities.Video')" ) ;
2020-05-14 23:30:28 +00:00
}
}
}
}