1
0
Fork 0
mirror of https://github.com/Radarr/Radarr synced 2024-12-26 09:49:00 +00:00

Fixed: Fix Movies, Lists with Invalid Profile on Migration

This commit is contained in:
Qstick 2020-07-30 22:46:18 -04:00
parent eff03a7d2c
commit 4d193b2279
2 changed files with 529 additions and 0 deletions

View file

@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration
{
[TestFixture]
public class fix_invalid_profile_referencesFixture : MigrationTest<fix_invalid_profile_references>
{
private void AddDefaultProfile(fix_invalid_profile_references m, string name, int profileId)
{
var allowed = new Quality[] { Quality.WEBDL720p };
var items = Quality.DefaultQualityDefinitions
.OrderBy(v => v.Weight)
.Select(v => new { Quality = (int)v.Quality, Allowed = allowed.Contains(v.Quality) })
.ToList();
var profile = new
{
Id = profileId,
Name = name,
FormatItems = new List<ProfileFormatItem>().ToJson(),
Cutoff = (int)Quality.WEBDL720p,
Items = items.ToJson(),
Language = (int)Language.English,
MinFormatScore = 0,
CutOffFormatScore = 0
};
m.Insert.IntoTable("Profiles").Row(profile);
}
private void AddMovie(fix_invalid_profile_references m, string movieTitle, int tmdbId, int profileId)
{
var movie = new
{
Id = tmdbId,
Monitored = true,
Title = movieTitle,
CleanTitle = movieTitle,
Status = MovieStatusType.Announced,
MinimumAvailability = MovieStatusType.Announced,
Images = new[] { new { CoverType = "Poster" } }.ToJson(),
Recommendations = new[] { 1 }.ToJson(),
HasPreDBEntry = false,
Runtime = 90,
OriginalLanguage = 1,
ProfileId = profileId,
MovieFileId = 1,
Path = string.Format("/Movies/{0}", movieTitle),
TitleSlug = movieTitle,
TmdbId = tmdbId
};
m.Insert.IntoTable("Movies").Row(movie);
}
private void AddCustomFormat(fix_invalid_profile_references c, int id, string name)
{
var customFormat = new
{
Id = id,
Name = name,
Specifications = "[]"
};
c.Insert.IntoTable("CustomFormats").Row(customFormat);
}
[Test]
public void should_add_default_profiles_if_none_exist_but_movies_exist()
{
var profileId = 18;
var db = WithMigrationTestDb(c =>
{
AddMovie(c, "movie", 123456, profileId);
});
var items = db.Query<Movie179>("SELECT Id, ProfileId FROM Movies");
var profiles = db.Query<Profile179>("SELECT Id FROM Profiles");
items.Should().HaveCount(1);
profiles.Should().HaveCount(6);
items.First().ProfileId.Should().BeOneOf(profiles.Select(p => p.Id));
}
[Test]
public void should_not_add_default_profiles_if_one_exist()
{
var profileId = 18;
var db = WithMigrationTestDb(c =>
{
AddDefaultProfile(c, "My Custom Profile", profileId);
AddMovie(c, "movie", 123456, 17);
});
var items = db.Query<Movie179>("SELECT Id, ProfileId FROM Movies");
var profiles = db.Query<Profile179>("SELECT Id FROM Profiles");
items.Should().HaveCount(1);
profiles.Should().HaveCount(1);
items.First().ProfileId.Should().BeOneOf(profiles.Select(p => p.Id));
}
[Test]
public void should_add_custom_formats_to_default_profiles_if_some_exist()
{
var profileId = 18;
var formatId = 3;
var db = WithMigrationTestDb(c =>
{
AddCustomFormat(c, formatId, "SomeFormat");
AddMovie(c, "movie", 123456, profileId);
});
var items = db.Query<Movie179>("SELECT Id, ProfileId FROM Movies");
var profiles = db.Query<Profile179>("SELECT Id, FormatItems FROM Profiles");
items.Should().HaveCount(1);
profiles.Should().HaveCount(6);
profiles.First().FormatItems.Should().HaveCount(1);
profiles.First().FormatItems.First().Format.Should().Be(formatId);
items.First().ProfileId.Should().BeOneOf(profiles.Select(p => p.Id));
}
[Test]
public void should_not_change_movies_with_valid_profile()
{
var profileId = 2;
var db = WithMigrationTestDb(c =>
{
AddDefaultProfile(c, "My Custom Profile", profileId);
AddMovie(c, "movie", 123456, profileId);
});
var items = db.Query<Movie179>("SELECT Id, ProfileId FROM Movies");
items.Should().HaveCount(1);
items.First().ProfileId.Should().Be(profileId);
}
[Test]
public void should_change_movies_with_bad_profile_id()
{
var profileId = 2;
var db = WithMigrationTestDb(c =>
{
AddDefaultProfile(c, "My Custom Profile", profileId);
AddMovie(c, "movie", 123456, 1);
});
var items = db.Query<Movie179>("SELECT Id, ProfileId FROM Movies");
items.Should().HaveCount(1);
items.First().ProfileId.Should().Be(profileId);
}
[Test]
public void should_change_to_most_common_valid_profile_in_library()
{
var commonProfileId = 2;
var otherProfileId = 3;
var db = WithMigrationTestDb(c =>
{
AddDefaultProfile(c, "My Custom Profile", commonProfileId);
AddDefaultProfile(c, "My Custom Profile 2", otherProfileId);
AddMovie(c, "movie1", 123451, 1);
AddMovie(c, "movie2", 123452, 1);
AddMovie(c, "movie3", 123453, 1);
AddMovie(c, "movie4", 123454, 1);
AddMovie(c, "movie5", 123455, commonProfileId);
AddMovie(c, "movie6", 123456, commonProfileId);
AddMovie(c, "movie7", 123457, commonProfileId);
AddMovie(c, "movie8", 123458, otherProfileId);
AddMovie(c, "movie9", 123459, otherProfileId);
});
var items = db.Query<Movie179>("SELECT Id, ProfileId FROM Movies");
items.Should().HaveCount(9);
items.Where(x => x.ProfileId == commonProfileId).Should().HaveCount(7);
}
}
public class Movie179
{
public int Id { get; set; }
public int ProfileId { get; set; }
}
public class Profile179
{
public int Id { get; set; }
public List<ProfileFormatItem179> FormatItems { get; set; }
}
public class ProfileFormatItem179
{
public int Id { get; set; }
public int Format { get; set; }
public int Score { get; set; }
}
}

View file

@ -0,0 +1,308 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Dapper;
using FluentMigrator;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(180)]
public class fix_invalid_profile_references : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(FixMovies);
}
private void FixMovies(IDbConnection conn, IDbTransaction tran)
{
var profiles = GetProfileIds(conn);
var movieRows = conn.Query<ProfileEntity179>($"SELECT Id, ProfileId FROM Movies");
var listRows = conn.Query<ProfileEntity179>($"SELECT Id, ProfileId FROM NetImport");
// Only process if there are lists or movies existing in the DB
if (movieRows.Any() || listRows.Any())
{
//If there are no Profiles lets add the defaults
if (!profiles.Any())
{
InsertDefaultQualityProfiles(conn, tran);
profiles = GetProfileIds(conn);
}
var mostCommonProfileId = 0;
//If we have some movies, lets determine the most common profile used and use it for the bad entries
if (movieRows.Any())
{
mostCommonProfileId = movieRows.Select(x => x.ProfileId)
.Where(x => profiles.Contains(x))
.GroupBy(p => p)
.OrderByDescending(g => g.Count())
.Select(g => g.Key)
.FirstOrDefault();
}
// If all the movie profiles are bad or there are no movies, just use the first profile for bad movies and lsits
if (mostCommonProfileId == 0)
{
mostCommonProfileId = profiles.First();
}
//Correct any Movies that reference profiles that are null
var sql = $"UPDATE Movies SET ProfileId = {mostCommonProfileId} WHERE Id IN(SELECT Movies.Id FROM Movies LEFT OUTER JOIN Profiles ON Movies.ProfileId = Profiles.Id WHERE Profiles.Id IS NULL)";
conn.Execute(sql, transaction: tran);
//Correct any Lists that reference profiles that are null
sql = $"UPDATE NetImport SET ProfileId = {mostCommonProfileId} WHERE Id IN(SELECT NetImport.Id FROM NetImport LEFT OUTER JOIN Profiles ON NetImport.ProfileId = Profiles.Id WHERE Profiles.Id IS NULL)";
conn.Execute(sql, transaction: tran);
}
}
private List<int> GetProfileIds(IDbConnection conn)
{
return conn.Query<QualityProfile180>("SELECT Id From Profiles").Select(p => p.Id).ToList();
}
private void InsertDefaultQualityProfiles(IDbConnection conn, IDbTransaction tran)
{
var profiles = GetDefaultQualityProfiles(conn);
var formatItemConverter = new EmbeddedDocumentConverter<List<ProfileFormatItem180>>(new CustomFormatIntConverter());
var profileItemConverter = new EmbeddedDocumentConverter<List<QualityProfileItem111>>(new QualityIntConverter());
var profileId = 1;
foreach (var profile in profiles.OrderBy(p => p.Id))
{
using (IDbCommand insertNewLanguageProfileCmd = conn.CreateCommand())
{
insertNewLanguageProfileCmd.Transaction = tran;
insertNewLanguageProfileCmd.CommandText = "INSERT INTO Profiles (Id, Name, Cutoff, Items, Language, FormatItems, MinFormatScore, CutoffFormatScore, UpgradeAllowed) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
insertNewLanguageProfileCmd.AddParameter(profileId);
insertNewLanguageProfileCmd.AddParameter(profile.Name);
insertNewLanguageProfileCmd.AddParameter(profile.Cutoff);
var paramItems = insertNewLanguageProfileCmd.CreateParameter();
profileItemConverter.SetValue(paramItems, profile.Items);
insertNewLanguageProfileCmd.Parameters.Add(paramItems);
insertNewLanguageProfileCmd.AddParameter(profile.Language.Id);
var paramFormats = insertNewLanguageProfileCmd.CreateParameter();
formatItemConverter.SetValue(paramFormats, profile.FormatItems);
insertNewLanguageProfileCmd.Parameters.Add(paramFormats);
insertNewLanguageProfileCmd.AddParameter(profile.MinFormatScore);
insertNewLanguageProfileCmd.AddParameter(profile.CutoffFormatScore);
insertNewLanguageProfileCmd.AddParameter(profile.UpgradeAllowed);
insertNewLanguageProfileCmd.ExecuteNonQuery();
}
profileId += 1;
}
}
private List<QualityProfile180> GetDefaultQualityProfiles(IDbConnection conn)
{
var profiles = new List<QualityProfile180>();
//Grab custom formats if any exist and add them to the new profiles
var formats = conn.Query<CustomFormat180>($"SELECT Id FROM CustomFormats").ToList();
profiles.Add(GetDefaultProfile("Any",
formats,
Quality.Bluray480p,
Quality.WORKPRINT,
Quality.CAM,
Quality.TELESYNC,
Quality.TELECINE,
Quality.DVDSCR,
Quality.REGIONAL,
Quality.SDTV,
Quality.DVD,
Quality.DVDR,
Quality.HDTV720p,
Quality.HDTV1080p,
Quality.HDTV2160p,
Quality.WEBDL480p,
Quality.WEBRip480p,
Quality.WEBDL720p,
Quality.WEBRip720p,
Quality.WEBDL1080p,
Quality.WEBRip1080p,
Quality.WEBDL2160p,
Quality.WEBRip2160p,
Quality.Bluray480p,
Quality.Bluray576p,
Quality.Bluray720p,
Quality.Bluray1080p,
Quality.Bluray2160p,
Quality.Remux1080p,
Quality.Remux2160p,
Quality.BRDISK));
profiles.Add(GetDefaultProfile("SD",
formats,
Quality.Bluray480p,
Quality.WORKPRINT,
Quality.CAM,
Quality.TELESYNC,
Quality.TELECINE,
Quality.DVDSCR,
Quality.REGIONAL,
Quality.SDTV,
Quality.DVD,
Quality.WEBDL480p,
Quality.WEBRip480p,
Quality.Bluray480p,
Quality.Bluray576p));
profiles.Add(GetDefaultProfile("HD-720p",
formats,
Quality.Bluray720p,
Quality.HDTV720p,
Quality.WEBDL720p,
Quality.WEBRip720p,
Quality.Bluray720p));
profiles.Add(GetDefaultProfile("HD-1080p",
formats,
Quality.Bluray1080p,
Quality.HDTV1080p,
Quality.WEBDL1080p,
Quality.WEBRip1080p,
Quality.Bluray1080p,
Quality.Remux1080p));
profiles.Add(GetDefaultProfile("Ultra-HD",
formats,
Quality.Remux2160p,
Quality.HDTV2160p,
Quality.WEBDL2160p,
Quality.WEBRip2160p,
Quality.Bluray2160p,
Quality.Remux2160p));
profiles.Add(GetDefaultProfile("HD - 720p/1080p",
formats,
Quality.Bluray720p,
Quality.HDTV720p,
Quality.HDTV1080p,
Quality.WEBDL720p,
Quality.WEBRip720p,
Quality.WEBDL1080p,
Quality.WEBRip1080p,
Quality.Bluray720p,
Quality.Bluray1080p,
Quality.Remux1080p));
return profiles;
}
private QualityProfile180 GetDefaultProfile(string name, List<CustomFormat180> formats, Quality cutoff = null, params Quality[] allowed)
{
var groupedQualites = Quality.DefaultQualityDefinitions.GroupBy(q => q.Weight);
var items = new List<QualityProfileItem111>();
var groupId = 1000;
var profileCutoff = cutoff == null ? Quality.Unknown.Id : cutoff.Id;
foreach (var group in groupedQualites)
{
if (group.Count() == 1)
{
var quality = group.First().Quality;
items.Add(new QualityProfileItem111 { Quality = group.First().Quality, Allowed = allowed.Contains(quality), Items = new List<QualityProfileItem111>() });
continue;
}
var groupAllowed = group.Any(g => allowed.Contains(g.Quality));
items.Add(new QualityProfileItem111
{
Id = groupId,
Name = group.First().GroupName,
Items = group.Select(g => new QualityProfileItem111
{
Quality = g.Quality,
Allowed = groupAllowed,
Items = new List<QualityProfileItem111>()
}).ToList(),
Allowed = groupAllowed
});
if (group.Any(g => g.Quality.Id == profileCutoff))
{
profileCutoff = groupId;
}
groupId++;
}
var formatItems = formats.Select(format => new ProfileFormatItem180
{
Id = format.Id,
Score = 0,
Format = format.Id
}).ToList();
var qualityProfile = new QualityProfile180
{
Name = name,
Cutoff = profileCutoff,
Items = items,
Language = Language.English,
MinFormatScore = 0,
CutoffFormatScore = 0,
UpgradeAllowed = 0,
FormatItems = formatItems
};
return qualityProfile;
}
private class ProfileEntity179
{
public int Id { get; set; }
public int ProfileId { get; set; }
}
private class QualityProfile180
{
public int Id { get; set; }
public string Name { get; set; }
public int Cutoff { get; set; }
public int MinFormatScore { get; set; }
public int CutoffFormatScore { get; set; }
public int UpgradeAllowed { get; set; }
public Language Language { get; set; }
public List<ProfileFormatItem180> FormatItems { get; set; }
public List<QualityProfileItem111> Items { get; set; }
}
private class QualityProfileItem111
{
public int Id { get; set; }
public string Name { get; set; }
public Quality Quality { get; set; }
public List<QualityProfileItem111> Items { get; set; }
public bool Allowed { get; set; }
}
private class ProfileFormatItem180
{
public int Id { get; set; }
public int Format { get; set; }
public int Score { get; set; }
}
private class CustomFormat180
{
public int Id { get; set; }
}
}
}