Merged develop in
This commit is contained in:
commit
d12a79892f
1443 changed files with 215765 additions and 44113 deletions
|
|
@ -1,17 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.KavitaPlus.Metadata;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Enums.UserPreferences;
|
||||
using API.Entities.History;
|
||||
using API.Entities.Interfaces;
|
||||
using API.Entities.Metadata;
|
||||
using API.Entities.MetadataMatching;
|
||||
using API.Entities.Person;
|
||||
using API.Entities.Scrobble;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
|
||||
namespace API.Data;
|
||||
|
||||
|
|
@ -36,6 +43,7 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
public DbSet<ServerSetting> ServerSetting { get; set; } = null!;
|
||||
public DbSet<AppUserPreferences> AppUserPreferences { get; set; } = null!;
|
||||
public DbSet<SeriesMetadata> SeriesMetadata { get; set; } = null!;
|
||||
[Obsolete]
|
||||
public DbSet<CollectionTag> CollectionTag { get; set; } = null!;
|
||||
public DbSet<AppUserBookmark> AppUserBookmark { get; set; } = null!;
|
||||
public DbSet<ReadingList> ReadingList { get; set; } = null!;
|
||||
|
|
@ -64,7 +72,12 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
public DbSet<ExternalRecommendation> ExternalRecommendation { get; set; } = null!;
|
||||
public DbSet<ManualMigrationHistory> ManualMigrationHistory { get; set; } = null!;
|
||||
public DbSet<SeriesBlacklist> SeriesBlacklist { get; set; } = null!;
|
||||
|
||||
public DbSet<AppUserCollection> AppUserCollection { get; set; } = null!;
|
||||
public DbSet<ChapterPeople> ChapterPeople { get; set; } = null!;
|
||||
public DbSet<SeriesMetadataPeople> SeriesMetadataPeople { get; set; } = null!;
|
||||
public DbSet<EmailHistory> EmailHistory { get; set; } = null!;
|
||||
public DbSet<MetadataSettings> MetadataSettings { get; set; } = null!;
|
||||
public DbSet<MetadataFieldMapping> MetadataFieldMapping { get; set; } = null!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
|
@ -114,10 +127,22 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
.Property(b => b.Locale)
|
||||
.IsRequired(true)
|
||||
.HasDefaultValue("en");
|
||||
builder.Entity<AppUserPreferences>()
|
||||
.Property(b => b.AniListScrobblingEnabled)
|
||||
.HasDefaultValue(true);
|
||||
builder.Entity<AppUserPreferences>()
|
||||
.Property(b => b.WantToReadSync)
|
||||
.HasDefaultValue(true);
|
||||
builder.Entity<AppUserPreferences>()
|
||||
.Property(b => b.AllowAutomaticWebtoonReaderDetection)
|
||||
.HasDefaultValue(true);
|
||||
|
||||
builder.Entity<Library>()
|
||||
.Property(b => b.AllowScrobbling)
|
||||
.HasDefaultValue(true);
|
||||
builder.Entity<Library>()
|
||||
.Property(b => b.AllowMetadataMatching)
|
||||
.HasDefaultValue(true);
|
||||
|
||||
builder.Entity<Chapter>()
|
||||
.Property(b => b.WebLinks)
|
||||
|
|
@ -149,6 +174,85 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
.WithOne(s => s.ExternalSeriesMetadata)
|
||||
.HasForeignKey<ExternalSeriesMetadata>(em => em.SeriesId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<AppUserCollection>()
|
||||
.Property(b => b.AgeRating)
|
||||
.HasDefaultValue(AgeRating.Unknown);
|
||||
|
||||
// Configure the many-to-many relationship for Movie and Person
|
||||
builder.Entity<ChapterPeople>()
|
||||
.HasKey(cp => new { cp.ChapterId, cp.PersonId, cp.Role });
|
||||
|
||||
builder.Entity<ChapterPeople>()
|
||||
.HasOne(cp => cp.Chapter)
|
||||
.WithMany(c => c.People)
|
||||
.HasForeignKey(cp => cp.ChapterId);
|
||||
|
||||
builder.Entity<ChapterPeople>()
|
||||
.HasOne(cp => cp.Person)
|
||||
.WithMany(p => p.ChapterPeople)
|
||||
.HasForeignKey(cp => cp.PersonId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
|
||||
builder.Entity<SeriesMetadataPeople>()
|
||||
.HasKey(smp => new { smp.SeriesMetadataId, smp.PersonId, smp.Role });
|
||||
|
||||
builder.Entity<SeriesMetadataPeople>()
|
||||
.HasOne(smp => smp.SeriesMetadata)
|
||||
.WithMany(sm => sm.People)
|
||||
.HasForeignKey(smp => smp.SeriesMetadataId);
|
||||
|
||||
builder.Entity<SeriesMetadataPeople>()
|
||||
.HasOne(smp => smp.Person)
|
||||
.WithMany(p => p.SeriesMetadataPeople)
|
||||
.HasForeignKey(smp => smp.PersonId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<SeriesMetadataPeople>()
|
||||
.Property(b => b.OrderWeight)
|
||||
.HasDefaultValue(0);
|
||||
|
||||
builder.Entity<MetadataSettings>()
|
||||
.Property(x => x.AgeRatingMappings)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, JsonSerializerOptions.Default),
|
||||
v => JsonSerializer.Deserialize<Dictionary<string, AgeRating>>(v, JsonSerializerOptions.Default) ?? new Dictionary<string, AgeRating>()
|
||||
);
|
||||
|
||||
// Ensure blacklist is stored as a JSON array
|
||||
builder.Entity<MetadataSettings>()
|
||||
.Property(x => x.Blacklist)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, JsonSerializerOptions.Default),
|
||||
v => JsonSerializer.Deserialize<List<string>>(v, JsonSerializerOptions.Default) ?? new List<string>()
|
||||
);
|
||||
builder.Entity<MetadataSettings>()
|
||||
.Property(x => x.Whitelist)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, JsonSerializerOptions.Default),
|
||||
v => JsonSerializer.Deserialize<List<string>>(v, JsonSerializerOptions.Default) ?? new List<string>()
|
||||
);
|
||||
builder.Entity<MetadataSettings>()
|
||||
.Property(x => x.Overrides)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, JsonSerializerOptions.Default),
|
||||
v => JsonSerializer.Deserialize<List<MetadataSettingField>>(v, JsonSerializerOptions.Default) ?? new List<MetadataSettingField>()
|
||||
);
|
||||
|
||||
// Configure one-to-many relationship
|
||||
builder.Entity<MetadataSettings>()
|
||||
.HasMany(x => x.FieldMappings)
|
||||
.WithOne(x => x.MetadataSettings)
|
||||
.HasForeignKey(x => x.MetadataSettingsId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<MetadataSettings>()
|
||||
.Property(b => b.Enabled)
|
||||
.HasDefaultValue(true);
|
||||
builder.Entity<MetadataSettings>()
|
||||
.Property(b => b.EnableCoverImage)
|
||||
.HasDefaultValue(true);
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
|
|
@ -156,10 +260,15 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
{
|
||||
if (e.FromQuery || e.Entry.State != EntityState.Added || e.Entry.Entity is not IEntityDate entity) return;
|
||||
|
||||
entity.Created = DateTime.Now;
|
||||
entity.LastModified = DateTime.Now;
|
||||
entity.CreatedUtc = DateTime.UtcNow;
|
||||
entity.LastModifiedUtc = DateTime.UtcNow;
|
||||
|
||||
// This allows for mocking
|
||||
if (entity.Created == DateTime.MinValue)
|
||||
{
|
||||
entity.Created = DateTime.Now;
|
||||
entity.CreatedUtc = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnEntityStateChanged(object? sender, EntityStateChangedEventArgs e)
|
||||
|
|
|
|||
|
|
@ -15,9 +15,8 @@ public static class MigrateLibrariesToHaveAllFileTypes
|
|||
{
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.Library.AnyAsync(l => l.LibraryFileTypes.Count == 0))
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateLibrariesToHaveAllFileTypes"))
|
||||
{
|
||||
logger.LogCritical("Running MigrateLibrariesToHaveAllFileTypes migration - Completed. This is not an error");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3,7 +3,11 @@ using System.Linq;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.Filtering.v2;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Helpers;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
|
@ -21,10 +25,14 @@ public static class MigrateSmartFilterEncoding
|
|||
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateSmartFilterEncoding"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running MigrateSmartFilterEncoding migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
|
||||
var smartFilters = dataContext.AppUserSmartFilter.ToList();
|
||||
var smartFilters = await dataContext.AppUserSmartFilter.ToListAsync();
|
||||
foreach (var filter in smartFilters)
|
||||
{
|
||||
if (!ShouldMigrateFilter(filter.Filter)) continue;
|
||||
|
|
@ -38,6 +46,14 @@ public static class MigrateSmartFilterEncoding
|
|||
await unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateSmartFilterEncoding",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running MigrateSmartFilterEncoding migration - Completed. This is not an error");
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -21,10 +21,11 @@ public static class MigrateEmailTemplates
|
|||
var files = directoryService.GetFiles(directoryService.CustomizedTemplateDirectory);
|
||||
if (files.Any())
|
||||
{
|
||||
logger.LogCritical("Running MigrateEmailTemplates migration - Completed. This is not an error");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running MigrateEmailTemplates migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Write files to directory
|
||||
await DownloadAndWriteToFile(EmailChange, Path.Join(directoryService.CustomizedTemplateDirectory, "EmailChange.html"), logger);
|
||||
await DownloadAndWriteToFile(EmailConfirm, Path.Join(directoryService.CustomizedTemplateDirectory, "EmailConfirm.html"), logger);
|
||||
|
|
@ -33,8 +34,7 @@ public static class MigrateEmailTemplates
|
|||
await DownloadAndWriteToFile(EmailTest, Path.Join(directoryService.CustomizedTemplateDirectory, "EmailTest.html"), logger);
|
||||
|
||||
|
||||
|
||||
logger.LogCritical("Running MigrateEmailTemplates migration - Please be patient, this may take some time. This is not an error");
|
||||
logger.LogCritical("Running MigrateEmailTemplates migration - Completed. This is not an error");
|
||||
}
|
||||
|
||||
private static async Task DownloadAndWriteToFile(string url, string filePath, ILogger<Program> logger)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
@ -16,8 +17,6 @@ public static class MigrateManualHistory
|
|||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync())
|
||||
{
|
||||
logger.LogCritical(
|
||||
"Running MigrateManualHistory migration - Completed. This is not an error");
|
||||
return;
|
||||
}
|
||||
|
||||
42
API/Data/ManualMigrations/v0.7.14/MigrateVolumeLookupName.cs
Normal file
42
API/Data/ManualMigrations/v0.7.14/MigrateVolumeLookupName.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
public static class MigrateVolumeLookupName
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateVolumeLookupName"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateVolumeLookupName migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Update all volumes to have LookupName as after this migration, name isn't used for lookup
|
||||
var volumes = dataContext.Volume.ToList();
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
volume.LookupName = volume.Name;
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateVolumeLookupName",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
logger.LogCritical(
|
||||
"Running MigrateVolumeLookupName migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -13,8 +13,13 @@ namespace API.Data.ManualMigrations;
|
|||
/// </summary>
|
||||
public static class MigrateVolumeNumber
|
||||
{
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, DataContext dataContext, ILogger<Program> logger)
|
||||
public static async Task Migrate(DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateVolumeNumber"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (await dataContext.Volume.AnyAsync(v => v.MaxNumber > 0))
|
||||
{
|
||||
logger.LogCritical(
|
||||
|
|
@ -20,6 +20,12 @@ public static class MigrateWantToReadExport
|
|||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateWantToReadExport"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var importFile = Path.Join(directoryService.ConfigDirectory, "want-to-read-migration.csv");
|
||||
if (File.Exists(importFile))
|
||||
{
|
||||
|
|
@ -6,6 +6,7 @@ using API.Data.Repositories;
|
|||
using API.Entities;
|
||||
using API.Services;
|
||||
using CsvHelper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
|
@ -15,8 +16,14 @@ namespace API.Data.ManualMigrations;
|
|||
/// </summary>
|
||||
public static class MigrateWantToReadImport
|
||||
{
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, IDirectoryService directoryService, ILogger<Program> logger)
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, DataContext dataContext, IDirectoryService directoryService, ILogger<Program> logger)
|
||||
{
|
||||
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateWantToReadImport"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var importFile = Path.Join(directoryService.ConfigDirectory, "want-to-read-migration.csv");
|
||||
var outputFile = Path.Join(directoryService.ConfigDirectory, "imported-want-to-read-migration.csv");
|
||||
|
||||
|
|
@ -14,6 +14,10 @@ public static class MigrateUserLibrarySideNavStream
|
|||
{
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateUserLibrarySideNavStream"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var usersWithLibraryStreams = await dataContext.AppUser
|
||||
.Include(u => u.SideNavStreams)
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 migration to move loose leaf chapters into their own volume and retain user progress.
|
||||
/// </summary>
|
||||
public static class MigrateLooseLeafChapters
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, IDirectoryService directoryService, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateLooseLeafChapters"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateLooseLeafChapters migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var settings = await unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
var extension = settings.EncodeMediaAs.GetExtension();
|
||||
|
||||
var progress = await dataContext.AppUserProgresses
|
||||
.Join(dataContext.Chapter, p => p.ChapterId, c => c.Id, (p, c) => new UserProgressCsvRecord
|
||||
{
|
||||
IsSpecial = c.IsSpecial,
|
||||
AppUserId = p.AppUserId,
|
||||
PagesRead = p.PagesRead,
|
||||
Range = c.Range,
|
||||
Number = c.Number,
|
||||
MinNumber = c.MinNumber,
|
||||
SeriesId = p.SeriesId,
|
||||
VolumeId = p.VolumeId,
|
||||
ProgressId = p.Id
|
||||
})
|
||||
.Where(d => !d.IsSpecial)
|
||||
.Join(dataContext.Volume, d => d.VolumeId, v => v.Id, (d, v) => new
|
||||
{
|
||||
ProgressRecord = d,
|
||||
Volume = v
|
||||
})
|
||||
.Where(d => d.Volume.Name == "0")
|
||||
.ToListAsync();
|
||||
|
||||
// First, group all the progresses into different series
|
||||
logger.LogCritical("Migrating {Count} progress events to new Volume structure for Loose leafs - This may take over 10 minutes depending on size of DB. Please wait", progress.Count);
|
||||
var progressesGroupedBySeries = progress
|
||||
.GroupBy(p => p.ProgressRecord.SeriesId);
|
||||
|
||||
foreach (var seriesGroup in progressesGroupedBySeries)
|
||||
{
|
||||
// Get each series and move the loose leafs from the old volume to the new Volume
|
||||
var seriesId = seriesGroup.Key;
|
||||
|
||||
// Handle All Loose Leafs
|
||||
var looseLeafsInSeries = seriesGroup
|
||||
.Where(p => !p.ProgressRecord.IsSpecial)
|
||||
.ToList();
|
||||
|
||||
// Get distinct Volumes by Id. For each one, create it then create the progress events
|
||||
var distinctVolumes = looseLeafsInSeries.DistinctBy(d => d.Volume.Id);
|
||||
foreach (var distinctVolume in distinctVolumes)
|
||||
{
|
||||
// Create a new volume for each series with the appropriate number (-100000)
|
||||
var chapters = await dataContext.Chapter
|
||||
.Where(c => c.VolumeId == distinctVolume.Volume.Id && !c.IsSpecial).ToListAsync();
|
||||
|
||||
var newVolume = new VolumeBuilder(Parser.LooseLeafVolume)
|
||||
.WithSeriesId(seriesId)
|
||||
.WithCreated(distinctVolume.Volume.Created)
|
||||
.WithLastModified(distinctVolume.Volume.LastModified)
|
||||
.Build();
|
||||
|
||||
newVolume.Pages = chapters.Sum(c => c.Pages);
|
||||
newVolume.WordCount = chapters.Sum(c => c.WordCount);
|
||||
newVolume.MinHoursToRead = chapters.Sum(c => c.MinHoursToRead);
|
||||
newVolume.MaxHoursToRead = chapters.Sum(c => c.MaxHoursToRead);
|
||||
newVolume.AvgHoursToRead = chapters.Sum(c => c.AvgHoursToRead);
|
||||
dataContext.Volume.Add(newVolume);
|
||||
await dataContext.SaveChangesAsync(); // Save changes to generate the newVolumeId
|
||||
|
||||
// Migrate the progress event to the new volume
|
||||
var oldVolumeProgresses = await dataContext.AppUserProgresses
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
foreach (var oldProgress in oldVolumeProgresses)
|
||||
{
|
||||
oldProgress.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
|
||||
logger.LogInformation("Moving {Count} chapters from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
chapters.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
|
||||
// Move the loose leaf chapters from the old volume to the new Volume
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
// Update the VolumeId on the existing progress event
|
||||
chapter.VolumeId = newVolume.Id;
|
||||
|
||||
// We need to migrate cover images as well
|
||||
//UpdateCoverImage(directoryService, logger, chapter, extension, newVolume);
|
||||
}
|
||||
|
||||
|
||||
var oldVolumeBookmarks = await dataContext.AppUserBookmark
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
logger.LogInformation("Moving {Count} existing Bookmarks from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
oldVolumeBookmarks.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
foreach (var bookmark in oldVolumeBookmarks)
|
||||
{
|
||||
bookmark.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
|
||||
var oldVolumePersonalToC = await dataContext.AppUserTableOfContent
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
oldVolumePersonalToC.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
foreach (var pToc in oldVolumePersonalToC)
|
||||
{
|
||||
pToc.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
var oldVolumeReadingListItems = await dataContext.ReadingListItem
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
oldVolumeReadingListItems.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
foreach (var readingListItem in oldVolumeReadingListItems)
|
||||
{
|
||||
readingListItem.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// Save changes after processing all series
|
||||
if (dataContext.ChangeTracker.HasChanges())
|
||||
{
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateLooseLeafChapters",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateLooseLeafChapters migration - Completed. This is not an error");
|
||||
}
|
||||
|
||||
private static void UpdateCoverImage(IDirectoryService directoryService, ILogger<Program> logger, Chapter chapter,
|
||||
string extension, Volume newVolume)
|
||||
{
|
||||
var existingCover = ImageService.GetChapterFormat(chapter.Id, chapter.VolumeId) + extension;
|
||||
var newCover = ImageService.GetChapterFormat(chapter.Id, newVolume.Id) + extension;
|
||||
try
|
||||
{
|
||||
if (!chapter.CoverImageLocked)
|
||||
{
|
||||
// First rename existing cover
|
||||
File.Copy(Path.Join(directoryService.CoverImageDirectory, existingCover), Path.Join(directoryService.CoverImageDirectory, newCover));
|
||||
chapter.CoverImage = newCover;
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Unable to rename {OldCover} to {NewCover}, this cover will need manual refresh", existingCover, newCover);
|
||||
}
|
||||
}
|
||||
}
|
||||
207
API/Data/ManualMigrations/v0.8.0/ManualMigrateMixedSpecials.cs
Normal file
207
API/Data/ManualMigrations/v0.8.0/ManualMigrateMixedSpecials.cs
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
public class UserProgressCsvRecord
|
||||
{
|
||||
public bool IsSpecial { get; set; }
|
||||
public int AppUserId { get; set; }
|
||||
public int PagesRead { get; set; }
|
||||
public string Range { get; set; }
|
||||
public string Number { get; set; }
|
||||
public float MinNumber { get; set; }
|
||||
public int SeriesId { get; set; }
|
||||
public int VolumeId { get; set; }
|
||||
public int ProgressId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 migration to move Specials into their own volume and retain user progress.
|
||||
/// </summary>
|
||||
public static class MigrateMixedSpecials
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, IDirectoryService directoryService, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateMixedSpecials"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running ManualMigrateMixedSpecials migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// First, group all the progresses into different series
|
||||
// Get each series and move the specials from old volume to the new Volume()
|
||||
// Create a new progress event from existing and store the Id of existing progress event to delete it
|
||||
// Save per series
|
||||
|
||||
var settings = await unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
var extension = settings.EncodeMediaAs.GetExtension();
|
||||
|
||||
var progress = await dataContext.AppUserProgresses
|
||||
.Join(dataContext.Chapter, p => p.ChapterId, c => c.Id, (p, c) => new UserProgressCsvRecord
|
||||
{
|
||||
IsSpecial = c.IsSpecial,
|
||||
AppUserId = p.AppUserId,
|
||||
PagesRead = p.PagesRead,
|
||||
Range = c.Range,
|
||||
Number = c.Number,
|
||||
MinNumber = c.MinNumber,
|
||||
SeriesId = p.SeriesId,
|
||||
VolumeId = p.VolumeId,
|
||||
ProgressId = p.Id
|
||||
})
|
||||
.Where(d => d.IsSpecial || d.Number == "0")
|
||||
.Join(dataContext.Volume, d => d.VolumeId, v => v.Id,
|
||||
(d, v) => new
|
||||
{
|
||||
ProgressRecord = d,
|
||||
Volume = v
|
||||
})
|
||||
.Where(d => d.Volume.Name == "0")
|
||||
.ToListAsync();
|
||||
|
||||
// First, group all the progresses into different series
|
||||
logger.LogCritical("Migrating {Count} progress events to new Volume structure for Specials - This may take over 10 minutes depending on size of DB. Please wait", progress.Count);
|
||||
var progressesGroupedBySeries = progress.GroupBy(p => p.ProgressRecord.SeriesId);
|
||||
|
||||
foreach (var seriesGroup in progressesGroupedBySeries)
|
||||
{
|
||||
// Get each series and move the specials from the old volume to the new Volume
|
||||
var seriesId = seriesGroup.Key;
|
||||
|
||||
// Handle All Specials
|
||||
var specialsInSeries = seriesGroup
|
||||
.Where(p => p.ProgressRecord.IsSpecial)
|
||||
.ToList();
|
||||
|
||||
// Get distinct Volumes by Id. For each one, create it then create the progress events
|
||||
var distinctVolumes = specialsInSeries.DistinctBy(d => d.Volume.Id);
|
||||
foreach (var distinctVolume in distinctVolumes)
|
||||
{
|
||||
// Create a new volume for each series with the appropriate number (-100000)
|
||||
var chapters = await dataContext.Chapter
|
||||
.Where(c => c.VolumeId == distinctVolume.Volume.Id && c.IsSpecial).ToListAsync();
|
||||
|
||||
var newVolume = new VolumeBuilder(Parser.SpecialVolume)
|
||||
.WithSeriesId(seriesId)
|
||||
.WithCreated(distinctVolume.Volume.Created)
|
||||
.WithLastModified(distinctVolume.Volume.LastModified)
|
||||
.Build();
|
||||
|
||||
newVolume.Pages = chapters.Sum(c => c.Pages);
|
||||
newVolume.WordCount = chapters.Sum(c => c.WordCount);
|
||||
newVolume.MinHoursToRead = chapters.Sum(c => c.MinHoursToRead);
|
||||
newVolume.MaxHoursToRead = chapters.Sum(c => c.MaxHoursToRead);
|
||||
newVolume.AvgHoursToRead = chapters.Sum(c => c.AvgHoursToRead);
|
||||
|
||||
dataContext.Volume.Add(newVolume);
|
||||
await dataContext.SaveChangesAsync(); // Save changes to generate the newVolumeId
|
||||
|
||||
// Migrate the progress event to the new volume
|
||||
var oldVolumeProgresses = await dataContext.AppUserProgresses
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
foreach (var oldProgress in oldVolumeProgresses)
|
||||
{
|
||||
oldProgress.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
|
||||
logger.LogInformation("Moving {Count} chapters from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
chapters.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
|
||||
// Move the special chapters from the old volume to the new Volume
|
||||
foreach (var specialChapter in chapters)
|
||||
{
|
||||
// Update the VolumeId on the existing progress event
|
||||
specialChapter.VolumeId = newVolume.Id;
|
||||
|
||||
//UpdateCoverImage(directoryService, logger, specialChapter, extension, newVolume);
|
||||
}
|
||||
|
||||
var oldVolumeBookmarks = await dataContext.AppUserBookmark
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
logger.LogInformation("Moving {Count} existing Bookmarks from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
oldVolumeBookmarks.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
foreach (var bookmark in oldVolumeBookmarks)
|
||||
{
|
||||
bookmark.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
|
||||
var oldVolumePersonalToC = await dataContext.AppUserTableOfContent
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
oldVolumePersonalToC.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
foreach (var pToc in oldVolumePersonalToC)
|
||||
{
|
||||
pToc.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
var oldVolumeReadingListItems = await dataContext.ReadingListItem
|
||||
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
|
||||
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
|
||||
oldVolumeReadingListItems.Count, distinctVolume.Volume.Id, newVolume.Id);
|
||||
foreach (var readingListItem in oldVolumeReadingListItems)
|
||||
{
|
||||
readingListItem.VolumeId = newVolume.Id;
|
||||
}
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Save changes after processing all series
|
||||
if (dataContext.ChangeTracker.HasChanges())
|
||||
{
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateMixedSpecials",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
logger.LogCritical(
|
||||
"Running ManualMigrateMixedSpecials migration - Completed. This is not an error");
|
||||
}
|
||||
|
||||
private static void UpdateCoverImage(IDirectoryService directoryService, ILogger<Program> logger, Chapter specialChapter,
|
||||
string extension, Volume newVolume)
|
||||
{
|
||||
// We need to migrate cover images as well
|
||||
var existingCover = ImageService.GetChapterFormat(specialChapter.Id, specialChapter.VolumeId) + extension;
|
||||
var newCover = ImageService.GetChapterFormat(specialChapter.Id, newVolume.Id) + extension;
|
||||
try
|
||||
{
|
||||
|
||||
if (!specialChapter.CoverImageLocked)
|
||||
{
|
||||
// First rename existing cover
|
||||
File.Copy(Path.Join(directoryService.CoverImageDirectory, existingCover), Path.Join(directoryService.CoverImageDirectory, newCover));
|
||||
specialChapter.CoverImage = newCover;
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Unable to rename {OldCover} to {NewCover}, this cover will need manual refresh", existingCover, newCover);
|
||||
}
|
||||
}
|
||||
}
|
||||
90
API/Data/ManualMigrations/v0.8.0/MigrateChapterFields.cs
Normal file
90
API/Data/ManualMigrations/v0.8.0/MigrateChapterFields.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Introduced in v0.8.0, this migrates the existing Chapter and Volume 0 -> Parser defined, MangaFile.FileName
|
||||
/// </summary>
|
||||
public static class MigrateChapterFields
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateChapterFields"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterFields migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Update all volumes only have specials in them (rare)
|
||||
var volumesWithJustSpecials = dataContext.Volume
|
||||
.Include(v => v.Chapters)
|
||||
.Where(v => v.Name == "0" && v.Chapters.All(c => c.IsSpecial))
|
||||
.ToList();
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterFields migration - Updating {Count} volumes that only have specials in them", volumesWithJustSpecials.Count);
|
||||
foreach (var volume in volumesWithJustSpecials)
|
||||
{
|
||||
volume.Name = $"{Parser.SpecialVolumeNumber}";
|
||||
volume.MinNumber = Parser.SpecialVolumeNumber;
|
||||
volume.MaxNumber = Parser.SpecialVolumeNumber;
|
||||
}
|
||||
|
||||
// Update all volumes that only have loose leafs in them
|
||||
var looseLeafVolumes = dataContext.Volume
|
||||
.Include(v => v.Chapters)
|
||||
.Where(v => v.Name == "0" && v.Chapters.All(c => !c.IsSpecial))
|
||||
.ToList();
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterFields migration - Updating {Count} volumes that only have loose leaf chapters in them", looseLeafVolumes.Count);
|
||||
foreach (var volume in looseLeafVolumes)
|
||||
{
|
||||
volume.Name = $"{Parser.DefaultChapterNumber}";
|
||||
volume.MinNumber = Parser.DefaultChapterNumber;
|
||||
volume.MaxNumber = Parser.DefaultChapterNumber;
|
||||
}
|
||||
|
||||
// Update all MangaFile
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterFields migration - Updating all MangaFiles");
|
||||
foreach (var mangaFile in dataContext.MangaFile)
|
||||
{
|
||||
mangaFile.FileName = Parser.RemoveExtensionIfSupported(mangaFile.FilePath);
|
||||
}
|
||||
|
||||
var looseLeafChapters = await dataContext.Chapter.Where(c => c.Number == "0").ToListAsync();
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterFields migration - Updating {Count} loose leaf chapters", looseLeafChapters.Count);
|
||||
foreach (var chapter in looseLeafChapters)
|
||||
{
|
||||
chapter.Number = Parser.DefaultChapter;
|
||||
chapter.MinNumber = Parser.DefaultChapterNumber;
|
||||
chapter.MaxNumber = Parser.DefaultChapterNumber;
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateChapterFields",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterFields migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
51
API/Data/ManualMigrations/v0.8.0/MigrateChapterNumber.cs
Normal file
51
API/Data/ManualMigrations/v0.8.0/MigrateChapterNumber.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// Introduced in v0.8.0, this migrates the existing Chapter Range -> Chapter Min/Max Number
|
||||
/// </summary>
|
||||
public static class MigrateChapterNumber
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateChapterNumber"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterNumber migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Get all volumes
|
||||
foreach (var chapter in dataContext.Chapter)
|
||||
{
|
||||
if (chapter.IsSpecial)
|
||||
{
|
||||
chapter.MinNumber = Parser.DefaultChapterNumber;
|
||||
chapter.MaxNumber = Parser.DefaultChapterNumber;
|
||||
continue;
|
||||
}
|
||||
chapter.MinNumber = Parser.MinNumberFromRange(chapter.Range);
|
||||
chapter.MaxNumber = Parser.MaxNumberFromRange(chapter.Range);
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateChapterNumber",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterNumber migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
57
API/Data/ManualMigrations/v0.8.0/MigrateChapterRange.cs
Normal file
57
API/Data/ManualMigrations/v0.8.0/MigrateChapterRange.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 changed the range to that it doesn't have filename by default
|
||||
/// </summary>
|
||||
public static class MigrateChapterRange
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateChapterRange"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterRange migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var chapters = await dataContext.Chapter.ToListAsync();
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
if (Parser.MinNumberFromRange(chapter.Range).Is(0.0f))
|
||||
{
|
||||
chapter.Range = chapter.GetNumberTitle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Save changes after processing all series
|
||||
if (dataContext.ChangeTracker.HasChanges())
|
||||
{
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateChapterRange",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
logger.LogCritical(
|
||||
"Running MigrateChapterRange migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data.Repositories;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.History;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 refactored User Collections
|
||||
/// </summary>
|
||||
public static class MigrateCollectionTagToUserCollections
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateCollectionTagToUserCollections") ||
|
||||
!await dataContext.AppUser.AnyAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateCollectionTagToUserCollections migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Find the first user that is an admin
|
||||
var defaultAdmin = await unitOfWork.UserRepository.GetDefaultAdminUser(AppUserIncludes.Collections);
|
||||
if (defaultAdmin == null)
|
||||
{
|
||||
await CompleteMigration(dataContext, logger);
|
||||
return;
|
||||
}
|
||||
|
||||
// For all collectionTags, move them over to said user
|
||||
var existingCollections = await dataContext.CollectionTag
|
||||
.OrderBy(c => c.NormalizedTitle)
|
||||
.Includes(CollectionTagIncludes.SeriesMetadataWithSeries)
|
||||
.ToListAsync();
|
||||
foreach (var existingCollectionTag in existingCollections)
|
||||
{
|
||||
var collection = new AppUserCollection()
|
||||
{
|
||||
Title = existingCollectionTag.Title,
|
||||
NormalizedTitle = existingCollectionTag.Title.Normalize(),
|
||||
CoverImage = existingCollectionTag.CoverImage,
|
||||
CoverImageLocked = existingCollectionTag.CoverImageLocked,
|
||||
Promoted = existingCollectionTag.Promoted,
|
||||
AgeRating = AgeRating.Unknown,
|
||||
Summary = existingCollectionTag.Summary,
|
||||
Items = existingCollectionTag.SeriesMetadatas.Select(s => s.Series).ToList()
|
||||
};
|
||||
|
||||
collection.AgeRating = await unitOfWork.SeriesRepository.GetMaxAgeRatingFromSeriesAsync(collection.Items.Select(s => s.Id));
|
||||
defaultAdmin.Collections.Add(collection);
|
||||
}
|
||||
unitOfWork.UserRepository.Update(defaultAdmin);
|
||||
|
||||
await unitOfWork.CommitAsync();
|
||||
|
||||
await CompleteMigration(dataContext, logger);
|
||||
}
|
||||
|
||||
private static async Task CompleteMigration(DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateCollectionTagToUserCollections",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateCollectionTagToUserCollections migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 ensured that MangaFile Path is normalized. This will normalize existing data to avoid churn.
|
||||
/// </summary>
|
||||
public static class MigrateDuplicateDarkTheme
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateDuplicateDarkTheme"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateDuplicateDarkTheme migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var darkThemes = await dataContext.SiteTheme.Where(t => t.Name == "Dark").ToListAsync();
|
||||
|
||||
if (darkThemes.Count > 1)
|
||||
{
|
||||
var correctDarkTheme = darkThemes.First(d => !string.IsNullOrEmpty(d.Description));
|
||||
|
||||
// Get users
|
||||
var users = await dataContext.AppUser
|
||||
.Include(u => u.UserPreferences)
|
||||
.ThenInclude(p => p.Theme)
|
||||
.Where(u => u.UserPreferences.Theme.Name == "Dark")
|
||||
.ToListAsync();
|
||||
|
||||
// Find any users that have a duplicate Dark theme as default and switch to the correct one
|
||||
foreach (var user in users)
|
||||
{
|
||||
if (string.IsNullOrEmpty(user.UserPreferences.Theme.Description))
|
||||
{
|
||||
user.UserPreferences.Theme = correctDarkTheme;
|
||||
}
|
||||
}
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
// Now remove the bad themes
|
||||
dataContext.SiteTheme.RemoveRange(darkThemes.Where(d => string.IsNullOrEmpty(d.Description)));
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateDuplicateDarkTheme",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateDuplicateDarkTheme migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
46
API/Data/ManualMigrations/v0.8.0/MigrateMangaFilePath.cs
Normal file
46
API/Data/ManualMigrations/v0.8.0/MigrateMangaFilePath.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 ensured that MangaFile Path is normalized. This will normalize existing data to avoid churn.
|
||||
/// </summary>
|
||||
public static class MigrateMangaFilePath
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateMangaFilePath"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateMangaFilePath migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
|
||||
foreach(var file in dataContext.MangaFile)
|
||||
{
|
||||
file.FilePath = Parser.NormalizePath(file.FilePath);
|
||||
}
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateMangaFilePath",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateMangaFilePath migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
124
API/Data/ManualMigrations/v0.8.0/MigrateProgressExport.cs
Normal file
124
API/Data/ManualMigrations/v0.8.0/MigrateProgressExport.cs
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration.Attributes;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
public class ProgressExport
|
||||
{
|
||||
[Name("Library Id")]
|
||||
public int LibraryId { get; set; }
|
||||
|
||||
[Name("Library Name")]
|
||||
public string LibraryName { get; set; }
|
||||
|
||||
[Name("Series Name")]
|
||||
public string SeriesName { get; set; }
|
||||
|
||||
[Name("Volume Number")]
|
||||
public string VolumeRange { get; set; }
|
||||
|
||||
[Name("Volume LookupName")]
|
||||
public string VolumeLookupName { get; set; }
|
||||
|
||||
[Name("Chapter Number")]
|
||||
public string ChapterRange { get; set; }
|
||||
|
||||
[Name("FileName")]
|
||||
public string MangaFileName { get; set; }
|
||||
|
||||
[Name("FilePath")]
|
||||
public string MangaFilePath { get; set; }
|
||||
|
||||
[Name("AppUser Name")]
|
||||
public string AppUserName { get; set; }
|
||||
|
||||
[Name("AppUser Id")]
|
||||
public int AppUserId { get; set; }
|
||||
|
||||
[Name("Pages Read")]
|
||||
public int PagesRead { get; set; }
|
||||
|
||||
[Name("BookScrollId")]
|
||||
public string BookScrollId { get; set; }
|
||||
|
||||
[Name("Progress Created")]
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
[Name("Progress LastModified")]
|
||||
public DateTime LastModified { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 - Progress is extracted and saved in a csv
|
||||
/// </summary>
|
||||
public static class MigrateProgressExport
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IDirectoryService directoryService, ILogger<Program> logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateProgressExport"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateProgressExport migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var data = await dataContext.AppUserProgresses
|
||||
.Join(dataContext.Series, progress => progress.SeriesId, series => series.Id, (progress, series) => new { progress, series })
|
||||
.Join(dataContext.Volume, ps => ps.progress.VolumeId, volume => volume.Id, (ps, volume) => new { ps.progress, ps.series, volume })
|
||||
.Join(dataContext.Chapter, psv => psv.progress.ChapterId, chapter => chapter.Id, (psv, chapter) => new { psv.progress, psv.series, psv.volume, chapter })
|
||||
.Join(dataContext.MangaFile, psvc => psvc.chapter.Id, mangaFile => mangaFile.ChapterId, (psvc, mangaFile) => new { psvc.progress, psvc.series, psvc.volume, psvc.chapter, mangaFile })
|
||||
.Join(dataContext.AppUser, psvcm => psvcm.progress.AppUserId, appUser => appUser.Id, (psvcm, appUser) => new
|
||||
{
|
||||
LibraryId = psvcm.series.LibraryId,
|
||||
LibraryName = psvcm.series.Library.Name,
|
||||
SeriesName = psvcm.series.Name,
|
||||
VolumeRange = psvcm.volume.MinNumber + "-" + psvcm.volume.MaxNumber,
|
||||
VolumeLookupName = psvcm.volume.Name,
|
||||
ChapterRange = psvcm.chapter.Range,
|
||||
MangaFileName = psvcm.mangaFile.FileName,
|
||||
MangaFilePath = psvcm.mangaFile.FilePath,
|
||||
AppUserName = appUser.UserName,
|
||||
AppUserId = appUser.Id,
|
||||
PagesRead = psvcm.progress.PagesRead,
|
||||
BookScrollId = psvcm.progress.BookScrollId,
|
||||
ProgressCreated = psvcm.progress.Created,
|
||||
ProgressLastModified = psvcm.progress.LastModified
|
||||
}).ToListAsync();
|
||||
|
||||
|
||||
// Write the mapped data to a CSV file
|
||||
await using var writer = new StreamWriter(Path.Join(directoryService.ConfigDirectory, "progress_export.csv"));
|
||||
await using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
||||
await csv.WriteRecordsAsync(data);
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateProgressExport migration - Completed. This is not an error");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// On new installs, the db isn't setup yet, so this has nothing to do
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateProgressExport",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.0 released with a bug around LowestSeriesPath. This resets it for all users.
|
||||
/// </summary>
|
||||
public static class MigrateLowestSeriesFolderPath
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateLowestSeriesFolderPath"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateLowestSeriesFolderPath migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var series = await dataContext.Series.Where(s => !string.IsNullOrEmpty(s.LowestFolderPath)).ToListAsync();
|
||||
foreach (var s in series)
|
||||
{
|
||||
s.LowestFolderPath = string.Empty;
|
||||
unitOfWork.SeriesRepository.Update(s);
|
||||
}
|
||||
|
||||
// Save changes after processing all series
|
||||
if (dataContext.ChangeTracker.HasChanges())
|
||||
{
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateLowestSeriesFolderPath",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
logger.LogCritical(
|
||||
"Running MigrateLowestSeriesFolderPath migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
49
API/Data/ManualMigrations/v0.8.2/ManualMigrateSwitchToWal.cs
Normal file
49
API/Data/ManualMigrations/v0.8.2/ManualMigrateSwitchToWal.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.2 switches Default Kavita installs to WAL
|
||||
/// </summary>
|
||||
public static class ManualMigrateSwitchToWal
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateSwitchToWal"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateSwitchToWal migration - Please be patient, this may take some time. This is not an error");
|
||||
try
|
||||
{
|
||||
var connection = context.Database.GetDbConnection();
|
||||
await connection.OpenAsync();
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = "PRAGMA journal_mode=WAL;";
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error setting WAL");
|
||||
/* Swallow */
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateSwitchToWal",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateSwitchToWal migration - Completed. This is not an error");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.2 introduced Theme repo viewer, this adds Description to existing SiteTheme defaults
|
||||
/// </summary>
|
||||
public static class ManualMigrateThemeDescription
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateThemeDescription"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateThemeDescription migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var theme = await context.SiteTheme.FirstOrDefaultAsync(t => t.Name == "Dark");
|
||||
if (theme != null)
|
||||
{
|
||||
theme.Description = Seed.DefaultThemes.First().Description;
|
||||
}
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateThemeDescription",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateThemeDescription migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.History;
|
||||
using API.Services;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.2 I started collecting information on when the user first installed Kavita as a nice to have info for the user
|
||||
/// </summary>
|
||||
public static class MigrateInitialInstallData
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, ILogger<Program> logger, IDirectoryService directoryService)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateInitialInstallData"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateInitialInstallData migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var settings = await dataContext.ServerSetting.ToListAsync();
|
||||
|
||||
// Get the Install Date as Date the DB was written
|
||||
var dbFile = Path.Join(directoryService.ConfigDirectory, "kavita.db");
|
||||
if (!string.IsNullOrEmpty(dbFile) && directoryService.FileSystem.File.Exists(dbFile))
|
||||
{
|
||||
var fi = directoryService.FileSystem.FileInfo.New(dbFile);
|
||||
var setting = settings.First(s => s.Key == ServerSettingKey.FirstInstallDate);
|
||||
setting.Value = fi.CreationTimeUtc.ToString(CultureInfo.InvariantCulture);
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateInitialInstallData",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateInitialInstallData migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Some linux-based users are having non-rooted LowestFolderPaths. This will attempt to fix it or null them.
|
||||
/// Fixed in v0.8.2
|
||||
/// </summary>
|
||||
public static class MigrateSeriesLowestFolderPath
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, ILogger<Program> logger, IDirectoryService directoryService)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateSeriesLowestFolderPath"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running MigrateSeriesLowestFolderPath migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var seriesWithFolderPath =
|
||||
await dataContext.Series.Where(s => !string.IsNullOrEmpty(s.LowestFolderPath))
|
||||
.Include(s => s.Library)
|
||||
.ThenInclude(l => l.Folders)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var series in seriesWithFolderPath)
|
||||
{
|
||||
var isValidPath = series.Library.Folders
|
||||
.Any(folder => Parser.NormalizePath(series.LowestFolderPath!).StartsWith(Parser.NormalizePath(folder.Path), StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (isValidPath) continue;
|
||||
series.LowestFolderPath = null;
|
||||
dataContext.Entry(series).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateSeriesLowestFolderPath",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running MigrateSeriesLowestFolderPath migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.History;
|
||||
using Flurl.Util;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// At some point, encoding settings wrote bad data to the backend, maybe in v0.8.0. This just fixes any bad data.
|
||||
/// </summary>
|
||||
public static class ManualMigrateEncodeSettings
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateEncodeSettings"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateEncodeSettings migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
|
||||
var encodeAs = await context.ServerSetting.FirstAsync(s => s.Key == ServerSettingKey.EncodeMediaAs);
|
||||
var coverSize = await context.ServerSetting.FirstAsync(s => s.Key == ServerSettingKey.CoverImageSize);
|
||||
|
||||
var encodeMap = new Dictionary<string, string>
|
||||
{
|
||||
{ EncodeFormat.WEBP.ToString(), ((int)EncodeFormat.WEBP).ToString() },
|
||||
{ EncodeFormat.PNG.ToString(), ((int)EncodeFormat.PNG).ToString() },
|
||||
{ EncodeFormat.AVIF.ToString(), ((int)EncodeFormat.AVIF).ToString() }
|
||||
};
|
||||
|
||||
if (encodeMap.TryGetValue(encodeAs.Value, out var encodedValue))
|
||||
{
|
||||
encodeAs.Value = encodedValue;
|
||||
context.ServerSetting.Update(encodeAs);
|
||||
}
|
||||
|
||||
if (coverSize.Value == "0")
|
||||
{
|
||||
coverSize.Value = ((int)CoverImageSize.Default).ToString();
|
||||
context.ServerSetting.Update(coverSize);
|
||||
}
|
||||
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateEncodeSettings",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateEncodeSettings migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// Due to a bug in the initial merge of People/Scanner rework, people got messed up bad. This migration will clear out the table only for nightly users: 0.8.3.15/0.8.3.16
|
||||
/// </summary>
|
||||
public static class ManualMigrateRemovePeople
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateRemovePeople"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var version = BuildInfo.Version.ToString();
|
||||
if (version != "0.8.3.15" && version != "0.8.3.16")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateRemovePeople migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
context.Person.RemoveRange(context.Person);
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateRemovePeople",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateRemovePeople migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.History;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// When I removed Scrobble support for Book libraries, I forgot to turn the setting off for said libraries.
|
||||
/// </summary>
|
||||
public static class ManualMigrateUnscrobbleBookLibraries
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateUnscrobbleBookLibraries"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateUnscrobbleBookLibraries migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var libs = await context.Library.Where(l => l.Type == LibraryType.Book).ToListAsync();
|
||||
foreach (var lib in libs)
|
||||
{
|
||||
lib.AllowScrobbling = false;
|
||||
context.Entry(lib).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateUnscrobbleBookLibraries",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateUnscrobbleBookLibraries migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.3 still had a bug around LowestSeriesPath. This resets it for all users.
|
||||
/// </summary>
|
||||
public static class MigrateLowestSeriesFolderPath2
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IUnitOfWork unitOfWork, ILogger<Program> logger)
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateLowestSeriesFolderPath2"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateLowestSeriesFolderPath2 migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var series = await dataContext.Series.Where(s => !string.IsNullOrEmpty(s.LowestFolderPath)).ToListAsync();
|
||||
foreach (var s in series)
|
||||
{
|
||||
s.LowestFolderPath = string.Empty;
|
||||
unitOfWork.SeriesRepository.Update(s);
|
||||
}
|
||||
|
||||
// Save changes after processing all series
|
||||
if (dataContext.ChangeTracker.HasChanges())
|
||||
{
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateLowestSeriesFolderPath2",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
logger.LogCritical(
|
||||
"Running MigrateLowestSeriesFolderPath2 migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Entities.Metadata;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.5 - Migrating Kavita+ BlacklistedSeries table to Series entity to streamline implementation and generate a "Needs Manual Match" entry for the Series
|
||||
/// </summary>
|
||||
public static class ManualMigrateBlacklistTableToSeries
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateBlacklistTableToSeries"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateBlacklistTableToSeries migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Get all series in the Blacklist table and set their IsBlacklist = true
|
||||
var blacklistedSeries = await context.SeriesBlacklist
|
||||
.Include(s => s.Series.ExternalSeriesMetadata)
|
||||
.Select(s => s.Series)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var series in blacklistedSeries)
|
||||
{
|
||||
series.IsBlacklisted = true;
|
||||
series.ExternalSeriesMetadata ??= new ExternalSeriesMetadata() { SeriesId = series.Id };
|
||||
|
||||
if (series.ExternalSeriesMetadata.AniListId > 0)
|
||||
{
|
||||
series.IsBlacklisted = false;
|
||||
logger.LogInformation("{SeriesName} was in Blacklist table, but has valid AniList Id, not blacklisting", series.Name);
|
||||
}
|
||||
|
||||
context.Series.Entry(series).State = EntityState.Modified;
|
||||
}
|
||||
// Remove everything in SeriesBlacklist (it will be removed in another migration)
|
||||
context.SeriesBlacklist.RemoveRange(context.SeriesBlacklist);
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateBlacklistTableToSeries",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateBlacklistTableToSeries migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.History;
|
||||
using API.Entities.Metadata;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.5 - Migrating Kavita+ Series that are Blacklisted but have valid ExternalSeries row
|
||||
/// </summary>
|
||||
public static class ManualMigrateInvalidBlacklistSeries
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateInvalidBlacklistSeries"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateInvalidBlacklistSeries migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Get all series in the Blacklist table and set their IsBlacklist = true
|
||||
var blacklistedSeries = await context.Series
|
||||
.Include(s => s.ExternalSeriesMetadata)
|
||||
.Where(s => s.IsBlacklisted && s.ExternalSeriesMetadata.ValidUntilUtc > DateTime.MinValue)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var series in blacklistedSeries)
|
||||
{
|
||||
series.IsBlacklisted = false;
|
||||
context.Series.Entry(series).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateInvalidBlacklistSeries",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateInvalidBlacklistSeries migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.KavitaPlus.Manage;
|
||||
using API.Entities.History;
|
||||
using API.Entities.Metadata;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.5 - After user testing, the needs manual match has some edge cases from migrations and for best user experience,
|
||||
/// should be reset to allow the upgraded system to process better.
|
||||
/// </summary>
|
||||
public static class ManualMigrateNeedsManualMatch
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateNeedsManualMatch"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateNeedsManualMatch migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Get all series in the Blacklist table and set their IsBlacklist = true
|
||||
var series = await context.Series
|
||||
.FilterMatchState(MatchStateOption.Error)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var seriesEntry in series)
|
||||
{
|
||||
seriesEntry.IsBlacklisted = false;
|
||||
context.Series.Update(seriesEntry);
|
||||
}
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateNeedsManualMatch",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateNeedsManualMatch migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.History;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.5 - There seems to be some scrobble events that are pre-scrobble error table that can be processed over and over.
|
||||
/// This will take the given year and minus 1 from it and clear everything from that and anything that is errored.
|
||||
/// </summary>
|
||||
public static class ManualMigrateScrobbleErrors
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateScrobbleErrors"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateScrobbleErrors migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Get all series in the Blacklist table and set their IsBlacklist = true
|
||||
var events = await context.ScrobbleEvent
|
||||
.Where(se => se.LastModifiedUtc <= DateTime.UtcNow.AddYears(-1) || se.IsErrored)
|
||||
.ToListAsync();
|
||||
|
||||
context.ScrobbleEvent.RemoveRange(events);
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
logger.LogInformation("Removed {Count} old scrobble events", events.Count);
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateScrobbleErrors",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateScrobbleErrors migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
80
API/Data/ManualMigrations/v0.8.5/MigrateProgressExport.cs
Normal file
80
API/Data/ManualMigrations/v0.8.5/MigrateProgressExport.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Entities.History;
|
||||
using API.Services;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration.Attributes;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.5 - Progress is extracted and saved in a csv since PDF parser has massive changes
|
||||
/// </summary>
|
||||
public static class MigrateProgressExportForV085
|
||||
{
|
||||
public static async Task Migrate(DataContext dataContext, IDirectoryService directoryService, ILogger<Program> logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await dataContext.ManualMigrationHistory.AnyAsync(m => m.Name == "MigrateProgressExportForV085"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateProgressExportForV085 migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var data = await dataContext.AppUserProgresses
|
||||
.Join(dataContext.Series, progress => progress.SeriesId, series => series.Id, (progress, series) => new { progress, series })
|
||||
.Join(dataContext.Volume, ps => ps.progress.VolumeId, volume => volume.Id, (ps, volume) => new { ps.progress, ps.series, volume })
|
||||
.Join(dataContext.Chapter, psv => psv.progress.ChapterId, chapter => chapter.Id, (psv, chapter) => new { psv.progress, psv.series, psv.volume, chapter })
|
||||
.Join(dataContext.MangaFile, psvc => psvc.chapter.Id, mangaFile => mangaFile.ChapterId, (psvc, mangaFile) => new { psvc.progress, psvc.series, psvc.volume, psvc.chapter, mangaFile })
|
||||
.Join(dataContext.AppUser, psvcm => psvcm.progress.AppUserId, appUser => appUser.Id, (psvcm, appUser) => new
|
||||
{
|
||||
LibraryId = psvcm.series.LibraryId,
|
||||
LibraryName = psvcm.series.Library.Name,
|
||||
SeriesName = psvcm.series.Name,
|
||||
VolumeRange = psvcm.volume.MinNumber + "-" + psvcm.volume.MaxNumber,
|
||||
VolumeLookupName = psvcm.volume.Name,
|
||||
ChapterRange = psvcm.chapter.Range,
|
||||
MangaFileName = psvcm.mangaFile.FileName,
|
||||
MangaFilePath = psvcm.mangaFile.FilePath,
|
||||
AppUserName = appUser.UserName,
|
||||
AppUserId = appUser.Id,
|
||||
PagesRead = psvcm.progress.PagesRead,
|
||||
BookScrollId = psvcm.progress.BookScrollId,
|
||||
ProgressCreated = psvcm.progress.Created,
|
||||
ProgressLastModified = psvcm.progress.LastModified
|
||||
}).ToListAsync();
|
||||
|
||||
|
||||
// Write the mapped data to a CSV file
|
||||
await using var writer = new StreamWriter(Path.Join(directoryService.ConfigDirectory, "progress_export-v0.8.5.csv"));
|
||||
await using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
||||
await csv.WriteRecordsAsync(data);
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateProgressExportForV085 migration - Completed. This is not an error");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// On new installs, the db isn't setup yet, so this has nothing to do
|
||||
}
|
||||
|
||||
dataContext.ManualMigrationHistory.Add(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "MigrateProgressExportForV085",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await dataContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.6 - Manually check when a user triggers scrobble event generation
|
||||
/// </summary>
|
||||
public static class ManualMigrateScrobbleEventGen
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateScrobbleEventGen"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateScrobbleEventGen migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
var users = await context.Users
|
||||
.Where(u => u.AniListAccessToken != null)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
if (await context.ScrobbleEvent.AnyAsync(se => se.AppUserId == user.Id))
|
||||
{
|
||||
user.HasRunScrobbleEventGeneration = true;
|
||||
user.ScrobbleEventGenerationRan = DateTime.UtcNow;
|
||||
context.AppUser.Update(user);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateScrobbleEventGen",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateScrobbleEventGen migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.History;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// v0.8.6 - Change to not scrobble specials as they will never process, this migration removes all existing scrobble events
|
||||
/// </summary>
|
||||
public static class ManualMigrateScrobbleSpecials
|
||||
{
|
||||
public static async Task Migrate(DataContext context, ILogger<Program> logger)
|
||||
{
|
||||
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateScrobbleSpecials"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical("Running ManualMigrateScrobbleSpecials migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
// Get all series in the Blacklist table and set their IsBlacklist = true
|
||||
var events = await context.ScrobbleEvent
|
||||
.Where(se => se.VolumeNumber == Parser.SpecialVolumeNumber)
|
||||
.ToListAsync();
|
||||
|
||||
context.ScrobbleEvent.RemoveRange(events);
|
||||
|
||||
if (context.ChangeTracker.HasChanges())
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
logger.LogInformation("Removed {Count} scrobble events that were specials", events.Count);
|
||||
}
|
||||
|
||||
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
|
||||
{
|
||||
Name = "ManualMigrateScrobbleSpecials",
|
||||
ProductVersion = BuildInfo.Version.ToString(),
|
||||
RanAt = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
logger.LogCritical("Running ManualMigrateScrobbleSpecials migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +127,11 @@ public class ComicInfo
|
|||
public string CoverArtist { get; set; } = string.Empty;
|
||||
public string Editor { get; set; } = string.Empty;
|
||||
public string Publisher { get; set; } = string.Empty;
|
||||
public string Imprint { get; set; } = string.Empty;
|
||||
public string Characters { get; set; } = string.Empty;
|
||||
public string Teams { get; set; } = string.Empty;
|
||||
public string Locations { get; set; } = string.Empty;
|
||||
|
||||
|
||||
public static AgeRating ConvertAgeRatingToEnum(string value)
|
||||
{
|
||||
|
|
@ -151,9 +155,12 @@ public class ComicInfo
|
|||
info.Letterer = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Letterer);
|
||||
info.Penciller = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Penciller);
|
||||
info.Publisher = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Publisher);
|
||||
info.Imprint = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Imprint);
|
||||
info.Characters = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Characters);
|
||||
info.Translator = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Translator);
|
||||
info.CoverArtist = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.CoverArtist);
|
||||
info.Teams = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Teams);
|
||||
info.Locations = Services.Tasks.Scanner.Parser.Parser.CleanAuthor(info.Locations);
|
||||
|
||||
// We need to convert GTIN to ISBN
|
||||
if (!string.IsNullOrEmpty(info.GTIN))
|
||||
|
|
@ -174,7 +181,12 @@ public class ComicInfo
|
|||
|
||||
if (!string.IsNullOrEmpty(info.Number))
|
||||
{
|
||||
info.Number = info.Number.Replace(",", "."); // Corrective measure for non English OSes
|
||||
info.Number = info.Number.Trim().Replace(",", "."); // Corrective measure for non English OSes
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(info.Volume))
|
||||
{
|
||||
info.Volume = info.Volume.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2877
API/Data/Migrations/20240214232436_ChapterNumber.Designer.cs
generated
Normal file
2877
API/Data/Migrations/20240214232436_ChapterNumber.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
40
API/Data/Migrations/20240214232436_ChapterNumber.cs
Normal file
40
API/Data/Migrations/20240214232436_ChapterNumber.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ChapterNumber : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<float>(
|
||||
name: "MaxNumber",
|
||||
table: "Chapter",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
defaultValue: 0f);
|
||||
|
||||
migrationBuilder.AddColumn<float>(
|
||||
name: "MinNumber",
|
||||
table: "Chapter",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
defaultValue: 0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MaxNumber",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MinNumber",
|
||||
table: "Chapter");
|
||||
}
|
||||
}
|
||||
}
|
||||
2880
API/Data/Migrations/20240216000223_MangaFileNameTemp.Designer.cs
generated
Normal file
2880
API/Data/Migrations/20240216000223_MangaFileNameTemp.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
28
API/Data/Migrations/20240216000223_MangaFileNameTemp.cs
Normal file
28
API/Data/Migrations/20240216000223_MangaFileNameTemp.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MangaFileNameTemp : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "FileName",
|
||||
table: "MangaFile",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "FileName",
|
||||
table: "MangaFile");
|
||||
}
|
||||
}
|
||||
}
|
||||
2883
API/Data/Migrations/20240222125420_ChapterIssueSort.Designer.cs
generated
Normal file
2883
API/Data/Migrations/20240222125420_ChapterIssueSort.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
29
API/Data/Migrations/20240222125420_ChapterIssueSort.cs
Normal file
29
API/Data/Migrations/20240222125420_ChapterIssueSort.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ChapterIssueSort : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<float>(
|
||||
name: "SortOrder",
|
||||
table: "Chapter",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
defaultValue: 0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SortOrder",
|
||||
table: "Chapter");
|
||||
}
|
||||
}
|
||||
}
|
||||
2886
API/Data/Migrations/20240225235816_VolumeLookupName.Designer.cs
generated
Normal file
2886
API/Data/Migrations/20240225235816_VolumeLookupName.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
28
API/Data/Migrations/20240225235816_VolumeLookupName.cs
Normal file
28
API/Data/Migrations/20240225235816_VolumeLookupName.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class VolumeLookupName : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LookupName",
|
||||
table: "Volume",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LookupName",
|
||||
table: "Volume");
|
||||
}
|
||||
}
|
||||
}
|
||||
2889
API/Data/Migrations/20240309140117_SeriesImprints.Designer.cs
generated
Normal file
2889
API/Data/Migrations/20240309140117_SeriesImprints.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
29
API/Data/Migrations/20240309140117_SeriesImprints.cs
Normal file
29
API/Data/Migrations/20240309140117_SeriesImprints.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SeriesImprints : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ImprintLocked",
|
||||
table: "SeriesMetadata",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ImprintLocked",
|
||||
table: "SeriesMetadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
2892
API/Data/Migrations/20240313112552_SeriesLowestFolderPath.Designer.cs
generated
Normal file
2892
API/Data/Migrations/20240313112552_SeriesLowestFolderPath.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
28
API/Data/Migrations/20240313112552_SeriesLowestFolderPath.cs
Normal file
28
API/Data/Migrations/20240313112552_SeriesLowestFolderPath.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SeriesLowestFolderPath : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LowestFolderPath",
|
||||
table: "Series",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LowestFolderPath",
|
||||
table: "Series");
|
||||
}
|
||||
}
|
||||
}
|
||||
2898
API/Data/Migrations/20240314194402_TeamsAndLocations.Designer.cs
generated
Normal file
2898
API/Data/Migrations/20240314194402_TeamsAndLocations.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
40
API/Data/Migrations/20240314194402_TeamsAndLocations.cs
Normal file
40
API/Data/Migrations/20240314194402_TeamsAndLocations.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class TeamsAndLocations : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LocationLocked",
|
||||
table: "SeriesMetadata",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "TeamLocked",
|
||||
table: "SeriesMetadata",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LocationLocked",
|
||||
table: "SeriesMetadata");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TeamLocked",
|
||||
table: "SeriesMetadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
2904
API/Data/Migrations/20240321173812_UserMalToken.Designer.cs
generated
Normal file
2904
API/Data/Migrations/20240321173812_UserMalToken.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
38
API/Data/Migrations/20240321173812_UserMalToken.cs
Normal file
38
API/Data/Migrations/20240321173812_UserMalToken.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UserMalToken : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "MalAccessToken",
|
||||
table: "AspNetUsers",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "MalUserName",
|
||||
table: "AspNetUsers",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MalAccessToken",
|
||||
table: "AspNetUsers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MalUserName",
|
||||
table: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
||||
2916
API/Data/Migrations/20240328130057_PdfSettings.Designer.cs
generated
Normal file
2916
API/Data/Migrations/20240328130057_PdfSettings.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
62
API/Data/Migrations/20240328130057_PdfSettings.cs
Normal file
62
API/Data/Migrations/20240328130057_PdfSettings.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PdfSettings : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PdfLayoutMode",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PdfScrollMode",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PdfSpreadMode",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PdfTheme",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PdfLayoutMode",
|
||||
table: "AppUserPreferences");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PdfScrollMode",
|
||||
table: "AppUserPreferences");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PdfSpreadMode",
|
||||
table: "AppUserPreferences");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PdfTheme",
|
||||
table: "AppUserPreferences");
|
||||
}
|
||||
}
|
||||
}
|
||||
3019
API/Data/Migrations/20240331172900_UserBasedCollections.Designer.cs
generated
Normal file
3019
API/Data/Migrations/20240331172900_UserBasedCollections.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
92
API/Data/Migrations/20240331172900_UserBasedCollections.cs
Normal file
92
API/Data/Migrations/20240331172900_UserBasedCollections.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UserBasedCollections : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AppUserCollection",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Title = table.Column<string>(type: "TEXT", nullable: true),
|
||||
NormalizedTitle = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Summary = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Promoted = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
CoverImage = table.Column<string>(type: "TEXT", nullable: true),
|
||||
CoverImageLocked = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
AgeRating = table.Column<int>(type: "INTEGER", nullable: false, defaultValue: 0),
|
||||
Created = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
LastModified = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
LastModifiedUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
LastSyncUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
Source = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SourceUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||
AppUserId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AppUserCollection", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserCollection_AspNetUsers_AppUserId",
|
||||
column: x => x.AppUserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AppUserCollectionSeries",
|
||||
columns: table => new
|
||||
{
|
||||
CollectionsId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ItemsId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AppUserCollectionSeries", x => new { x.CollectionsId, x.ItemsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserCollectionSeries_AppUserCollection_CollectionsId",
|
||||
column: x => x.CollectionsId,
|
||||
principalTable: "AppUserCollection",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserCollectionSeries_Series_ItemsId",
|
||||
column: x => x.ItemsId,
|
||||
principalTable: "Series",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AppUserCollection_AppUserId",
|
||||
table: "AppUserCollection",
|
||||
column: "AppUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AppUserCollectionSeries_ItemsId",
|
||||
table: "AppUserCollectionSeries",
|
||||
column: "ItemsId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AppUserCollectionSeries");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AppUserCollection");
|
||||
}
|
||||
}
|
||||
}
|
||||
3019
API/Data/Migrations/20240418163829_ChapterSortOrderLock.Designer.cs
generated
Normal file
3019
API/Data/Migrations/20240418163829_ChapterSortOrderLock.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
40
API/Data/Migrations/20240418163829_ChapterSortOrderLock.cs
Normal file
40
API/Data/Migrations/20240418163829_ChapterSortOrderLock.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ChapterSortOrderLock : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PdfLayoutMode",
|
||||
table: "AppUserPreferences");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "SortOrderLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SortOrderLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PdfLayoutMode",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
3025
API/Data/Migrations/20240503120147_SmartCollectionFields.Designer.cs
generated
Normal file
3025
API/Data/Migrations/20240503120147_SmartCollectionFields.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
39
API/Data/Migrations/20240503120147_SmartCollectionFields.cs
Normal file
39
API/Data/Migrations/20240503120147_SmartCollectionFields.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SmartCollectionFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "MissingSeriesFromSource",
|
||||
table: "AppUserCollection",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "TotalSourceCount",
|
||||
table: "AppUserCollection",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MissingSeriesFromSource",
|
||||
table: "AppUserCollection");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TotalSourceCount",
|
||||
table: "AppUserCollection");
|
||||
}
|
||||
}
|
||||
}
|
||||
3043
API/Data/Migrations/20240510134030_SiteThemeFields.Designer.cs
generated
Normal file
3043
API/Data/Migrations/20240510134030_SiteThemeFields.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
78
API/Data/Migrations/20240510134030_SiteThemeFields.cs
Normal file
78
API/Data/Migrations/20240510134030_SiteThemeFields.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SiteThemeFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Author",
|
||||
table: "SiteTheme",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CompatibleVersion",
|
||||
table: "SiteTheme",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Description",
|
||||
table: "SiteTheme",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "GitHubPath",
|
||||
table: "SiteTheme",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PreviewUrls",
|
||||
table: "SiteTheme",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ShaHash",
|
||||
table: "SiteTheme",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Author",
|
||||
table: "SiteTheme");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CompatibleVersion",
|
||||
table: "SiteTheme");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Description",
|
||||
table: "SiteTheme");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "GitHubPath",
|
||||
table: "SiteTheme");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PreviewUrls",
|
||||
table: "SiteTheme");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ShaHash",
|
||||
table: "SiteTheme");
|
||||
}
|
||||
}
|
||||
}
|
||||
3064
API/Data/Migrations/20240704144224_PersonFields.Designer.cs
generated
Normal file
3064
API/Data/Migrations/20240704144224_PersonFields.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
91
API/Data/Migrations/20240704144224_PersonFields.cs
Normal file
91
API/Data/Migrations/20240704144224_PersonFields.cs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PersonFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AniListId",
|
||||
table: "Person",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Asin",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CoverImage",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "CoverImageLocked",
|
||||
table: "Person",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Description",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "HardcoverId",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "MalId",
|
||||
table: "Person",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AniListId",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Asin",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CoverImage",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CoverImageLocked",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Description",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "HardcoverId",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MalId",
|
||||
table: "Person");
|
||||
}
|
||||
}
|
||||
}
|
||||
3079
API/Data/Migrations/20240808100353_CoverPrimaryColors.Designer.cs
generated
Normal file
3079
API/Data/Migrations/20240808100353_CoverPrimaryColors.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
138
API/Data/Migrations/20240808100353_CoverPrimaryColors.cs
Normal file
138
API/Data/Migrations/20240808100353_CoverPrimaryColors.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CoverPrimaryColors : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PrimaryColor",
|
||||
table: "Volume",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryColor",
|
||||
table: "Volume",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PrimaryColor",
|
||||
table: "Series",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryColor",
|
||||
table: "Series",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PrimaryColor",
|
||||
table: "ReadingList",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryColor",
|
||||
table: "ReadingList",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PrimaryColor",
|
||||
table: "Library",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryColor",
|
||||
table: "Library",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PrimaryColor",
|
||||
table: "Chapter",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryColor",
|
||||
table: "Chapter",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PrimaryColor",
|
||||
table: "AppUserCollection",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryColor",
|
||||
table: "AppUserCollection",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PrimaryColor",
|
||||
table: "Volume");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryColor",
|
||||
table: "Volume");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PrimaryColor",
|
||||
table: "Series");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryColor",
|
||||
table: "Series");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PrimaryColor",
|
||||
table: "ReadingList");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryColor",
|
||||
table: "ReadingList");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PrimaryColor",
|
||||
table: "Library");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryColor",
|
||||
table: "Library");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PrimaryColor",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryColor",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PrimaryColor",
|
||||
table: "AppUserCollection");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryColor",
|
||||
table: "AppUserCollection");
|
||||
}
|
||||
}
|
||||
}
|
||||
3142
API/Data/Migrations/20240811154857_ChapterMetadataLocks.Designer.cs
generated
Normal file
3142
API/Data/Migrations/20240811154857_ChapterMetadataLocks.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
249
API/Data/Migrations/20240811154857_ChapterMetadataLocks.cs
Normal file
249
API/Data/Migrations/20240811154857_ChapterMetadataLocks.cs
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ChapterMetadataLocks : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AgeRatingLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "CharacterLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ColoristLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "CoverArtistLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "EditorLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "GenresLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ISBNLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ImprintLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "InkerLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LanguageLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LettererLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LocationLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "PencillerLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "PublisherLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ReleaseDateLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "SummaryLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "TagsLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "TeamLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "TitleNameLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "TranslatorLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "WriterLocked",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AgeRatingLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CharacterLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ColoristLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CoverArtistLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EditorLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "GenresLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ISBNLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ImprintLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InkerLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LanguageLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LettererLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LocationLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PencillerLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PublisherLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ReleaseDateLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SummaryLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TagsLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TeamLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TitleNameLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TranslatorLocked",
|
||||
table: "Chapter");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WriterLocked",
|
||||
table: "Chapter");
|
||||
}
|
||||
}
|
||||
}
|
||||
3145
API/Data/Migrations/20240813194728_VolumeCoverLocked.Designer.cs
generated
Normal file
3145
API/Data/Migrations/20240813194728_VolumeCoverLocked.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
29
API/Data/Migrations/20240813194728_VolumeCoverLocked.cs
Normal file
29
API/Data/Migrations/20240813194728_VolumeCoverLocked.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class VolumeCoverLocked : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "CoverImageLocked",
|
||||
table: "Volume",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CoverImageLocked",
|
||||
table: "Volume");
|
||||
}
|
||||
}
|
||||
}
|
||||
3145
API/Data/Migrations/20240917180034_AvgReadingTimeFloat.Designer.cs
generated
Normal file
3145
API/Data/Migrations/20240917180034_AvgReadingTimeFloat.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
66
API/Data/Migrations/20240917180034_AvgReadingTimeFloat.cs
Normal file
66
API/Data/Migrations/20240917180034_AvgReadingTimeFloat.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AvgReadingTimeFloat : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<float>(
|
||||
name: "AvgHoursToRead",
|
||||
table: "Volume",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER");
|
||||
|
||||
migrationBuilder.AlterColumn<float>(
|
||||
name: "AvgHoursToRead",
|
||||
table: "Series",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER");
|
||||
|
||||
migrationBuilder.AlterColumn<float>(
|
||||
name: "AvgHoursToRead",
|
||||
table: "Chapter",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "AvgHoursToRead",
|
||||
table: "Volume",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
oldClrType: typeof(float),
|
||||
oldType: "REAL");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "AvgHoursToRead",
|
||||
table: "Series",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
oldClrType: typeof(float),
|
||||
oldType: "REAL");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "AvgHoursToRead",
|
||||
table: "Chapter",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
oldClrType: typeof(float),
|
||||
oldType: "REAL");
|
||||
}
|
||||
}
|
||||
}
|
||||
3170
API/Data/Migrations/20241011143144_PeopleOverhaulPart1.Designer.cs
generated
Normal file
3170
API/Data/Migrations/20241011143144_PeopleOverhaulPart1.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
159
API/Data/Migrations/20241011143144_PeopleOverhaulPart1.cs
Normal file
159
API/Data/Migrations/20241011143144_PeopleOverhaulPart1.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PeopleOverhaulPart1 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ChapterPerson");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PersonSeriesMetadata");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Role",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ChapterPeople",
|
||||
columns: table => new
|
||||
{
|
||||
ChapterId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
PersonId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Role = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ChapterPeople", x => new { x.ChapterId, x.PersonId, x.Role });
|
||||
table.ForeignKey(
|
||||
name: "FK_ChapterPeople_Chapter_ChapterId",
|
||||
column: x => x.ChapterId,
|
||||
principalTable: "Chapter",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ChapterPeople_Person_PersonId",
|
||||
column: x => x.PersonId,
|
||||
principalTable: "Person",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SeriesMetadataPeople",
|
||||
columns: table => new
|
||||
{
|
||||
SeriesMetadataId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
PersonId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Role = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SeriesMetadataPeople", x => new { x.SeriesMetadataId, x.PersonId, x.Role });
|
||||
table.ForeignKey(
|
||||
name: "FK_SeriesMetadataPeople_Person_PersonId",
|
||||
column: x => x.PersonId,
|
||||
principalTable: "Person",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_SeriesMetadataPeople_SeriesMetadata_SeriesMetadataId",
|
||||
column: x => x.SeriesMetadataId,
|
||||
principalTable: "SeriesMetadata",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ChapterPeople_PersonId",
|
||||
table: "ChapterPeople",
|
||||
column: "PersonId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SeriesMetadataPeople_PersonId",
|
||||
table: "SeriesMetadataPeople",
|
||||
column: "PersonId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ChapterPeople");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "SeriesMetadataPeople");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Role",
|
||||
table: "Person",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ChapterPerson",
|
||||
columns: table => new
|
||||
{
|
||||
ChapterMetadatasId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
PeopleId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ChapterPerson", x => new { x.ChapterMetadatasId, x.PeopleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ChapterPerson_Chapter_ChapterMetadatasId",
|
||||
column: x => x.ChapterMetadatasId,
|
||||
principalTable: "Chapter",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ChapterPerson_Person_PeopleId",
|
||||
column: x => x.PeopleId,
|
||||
principalTable: "Person",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PersonSeriesMetadata",
|
||||
columns: table => new
|
||||
{
|
||||
PeopleId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SeriesMetadatasId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PersonSeriesMetadata", x => new { x.PeopleId, x.SeriesMetadatasId });
|
||||
table.ForeignKey(
|
||||
name: "FK_PersonSeriesMetadata_Person_PeopleId",
|
||||
column: x => x.PeopleId,
|
||||
principalTable: "Person",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_PersonSeriesMetadata_SeriesMetadata_SeriesMetadatasId",
|
||||
column: x => x.SeriesMetadatasId,
|
||||
principalTable: "SeriesMetadata",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ChapterPerson_PeopleId",
|
||||
table: "ChapterPerson",
|
||||
column: "PeopleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersonSeriesMetadata_SeriesMetadatasId",
|
||||
table: "PersonSeriesMetadata",
|
||||
column: "SeriesMetadatasId");
|
||||
}
|
||||
}
|
||||
}
|
||||
3182
API/Data/Migrations/20241011152321_PeopleOverhaulPart2.Designer.cs
generated
Normal file
3182
API/Data/Migrations/20241011152321_PeopleOverhaulPart2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
59
API/Data/Migrations/20241011152321_PeopleOverhaulPart2.cs
Normal file
59
API/Data/Migrations/20241011152321_PeopleOverhaulPart2.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PeopleOverhaulPart2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CoverImage",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "CoverImageLocked",
|
||||
table: "Person",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PrimaryColor",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryColor",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CoverImage",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CoverImageLocked",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PrimaryColor",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryColor",
|
||||
table: "Person");
|
||||
}
|
||||
}
|
||||
}
|
||||
3197
API/Data/Migrations/20241011172428_PeopleOverhaulPart3.Designer.cs
generated
Normal file
3197
API/Data/Migrations/20241011172428_PeopleOverhaulPart3.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
70
API/Data/Migrations/20241011172428_PeopleOverhaulPart3.cs
Normal file
70
API/Data/Migrations/20241011172428_PeopleOverhaulPart3.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PeopleOverhaulPart3 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AniListId",
|
||||
table: "Person",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Asin",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Description",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "HardcoverId",
|
||||
table: "Person",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "MalId",
|
||||
table: "Person",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AniListId",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Asin",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Description",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "HardcoverId",
|
||||
table: "Person");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MalId",
|
||||
table: "Person");
|
||||
}
|
||||
}
|
||||
}
|
||||
3203
API/Data/Migrations/20250105180131_SeriesDontMatchAndBlacklist.Designer.cs
generated
Normal file
3203
API/Data/Migrations/20250105180131_SeriesDontMatchAndBlacklist.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,40 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SeriesDontMatchAndBlacklist : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "DontMatch",
|
||||
table: "Series",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsBlacklisted",
|
||||
table: "Series",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DontMatch",
|
||||
table: "Series");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsBlacklisted",
|
||||
table: "Series");
|
||||
}
|
||||
}
|
||||
}
|
||||
3265
API/Data/Migrations/20250109173537_EmailHistory.Designer.cs
generated
Normal file
3265
API/Data/Migrations/20250109173537_EmailHistory.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
62
API/Data/Migrations/20250109173537_EmailHistory.cs
Normal file
62
API/Data/Migrations/20250109173537_EmailHistory.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class EmailHistory : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EmailHistory",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Sent = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
SendDate = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
EmailTemplate = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Subject = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Body = table.Column<string>(type: "TEXT", nullable: true),
|
||||
DeliveryStatus = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ErrorMessage = table.Column<string>(type: "TEXT", nullable: true),
|
||||
AppUserId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Created = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
LastModified = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
LastModifiedUtc = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EmailHistory", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_EmailHistory_AspNetUsers_AppUserId",
|
||||
column: x => x.AppUserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EmailHistory_AppUserId",
|
||||
table: "EmailHistory",
|
||||
column: "AppUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EmailHistory_Sent_AppUserId_EmailTemplate_SendDate",
|
||||
table: "EmailHistory",
|
||||
columns: new[] { "Sent", "AppUserId", "EmailTemplate", "SendDate" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EmailHistory");
|
||||
}
|
||||
}
|
||||
}
|
||||
3382
API/Data/Migrations/20250202163454_KavitaPlusUserAndMetadataSettings.Designer.cs
generated
Normal file
3382
API/Data/Migrations/20250202163454_KavitaPlusUserAndMetadataSettings.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,112 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class KavitaPlusUserAndMetadataSettings : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowMetadataMatching",
|
||||
table: "Library",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AniListScrobblingEnabled",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "WantToReadSync",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "MetadataSettings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Enabled = table.Column<bool>(type: "INTEGER", nullable: false, defaultValue: true),
|
||||
EnableSummary = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
EnablePublicationStatus = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
EnableRelationships = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
EnablePeople = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
EnableStartDate = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
EnableLocalizedName = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
EnableGenres = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
EnableTags = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
FirstLastPeopleNaming = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
AgeRatingMappings = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Blacklist = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Whitelist = table.Column<string>(type: "TEXT", nullable: true),
|
||||
PersonRoles = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MetadataSettings", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "MetadataFieldMapping",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
SourceType = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
DestinationType = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SourceValue = table.Column<string>(type: "TEXT", nullable: true),
|
||||
DestinationValue = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ExcludeFromSource = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
MetadataSettingsId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MetadataFieldMapping", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_MetadataFieldMapping_MetadataSettings_MetadataSettingsId",
|
||||
column: x => x.MetadataSettingsId,
|
||||
principalTable: "MetadataSettings",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MetadataFieldMapping_MetadataSettingsId",
|
||||
table: "MetadataFieldMapping",
|
||||
column: "MetadataSettingsId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "MetadataFieldMapping");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "MetadataSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowMetadataMatching",
|
||||
table: "Library");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AniListScrobblingEnabled",
|
||||
table: "AppUserPreferences");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WantToReadSync",
|
||||
table: "AppUserPreferences");
|
||||
}
|
||||
}
|
||||
}
|
||||
3398
API/Data/Migrations/20250208200843_MoreMetadtaSettings.Designer.cs
generated
Normal file
3398
API/Data/Migrations/20250208200843_MoreMetadtaSettings.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
61
API/Data/Migrations/20250208200843_MoreMetadtaSettings.cs
Normal file
61
API/Data/Migrations/20250208200843_MoreMetadtaSettings.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MoreMetadtaSettings : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "KavitaPlusConnection",
|
||||
table: "SeriesMetadataPeople",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "OrderWeight",
|
||||
table: "SeriesMetadataPeople",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "EnableCoverImage",
|
||||
table: "MetadataSettings",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Overrides",
|
||||
table: "MetadataSettings",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "KavitaPlusConnection",
|
||||
table: "SeriesMetadataPeople");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OrderWeight",
|
||||
table: "SeriesMetadataPeople");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EnableCoverImage",
|
||||
table: "MetadataSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Overrides",
|
||||
table: "MetadataSettings");
|
||||
}
|
||||
}
|
||||
}
|
||||
3403
API/Data/Migrations/20250328125012_AutomaticWebtoonReaderMode.Designer.cs
generated
Normal file
3403
API/Data/Migrations/20250328125012_AutomaticWebtoonReaderMode.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutomaticWebtoonReaderMode : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowAutomaticWebtoonReaderDetection",
|
||||
table: "AppUserPreferences",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowAutomaticWebtoonReaderDetection",
|
||||
table: "AppUserPreferences");
|
||||
}
|
||||
}
|
||||
}
|
||||
3409
API/Data/Migrations/20250408222330_ScrobbleGenerationDbCapture.Designer.cs
generated
Normal file
3409
API/Data/Migrations/20250408222330_ScrobbleGenerationDbCapture.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ScrobbleGenerationDbCapture : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "HasRunScrobbleEventGeneration",
|
||||
table: "AspNetUsers",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "ScrobbleEventGenerationRan",
|
||||
table: "AspNetUsers",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "HasRunScrobbleEventGeneration",
|
||||
table: "AspNetUsers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ScrobbleEventGenerationRan",
|
||||
table: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
||||
3433
API/Data/Migrations/20250415194829_KavitaPlusCBR.Designer.cs
generated
Normal file
3433
API/Data/Migrations/20250415194829_KavitaPlusCBR.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
106
API/Data/Migrations/20250415194829_KavitaPlusCBR.cs
Normal file
106
API/Data/Migrations/20250415194829_KavitaPlusCBR.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class KavitaPlusCBR : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "EnableChapterCoverImage",
|
||||
table: "MetadataSettings",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "EnableChapterPublisher",
|
||||
table: "MetadataSettings",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "EnableChapterReleaseDate",
|
||||
table: "MetadataSettings",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "EnableChapterSummary",
|
||||
table: "MetadataSettings",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "EnableChapterTitle",
|
||||
table: "MetadataSettings",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "CbrId",
|
||||
table: "ExternalSeriesMetadata",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "KavitaPlusConnection",
|
||||
table: "ChapterPeople",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "OrderWeight",
|
||||
table: "ChapterPeople",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EnableChapterCoverImage",
|
||||
table: "MetadataSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EnableChapterPublisher",
|
||||
table: "MetadataSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EnableChapterReleaseDate",
|
||||
table: "MetadataSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EnableChapterSummary",
|
||||
table: "MetadataSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EnableChapterTitle",
|
||||
table: "MetadataSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CbrId",
|
||||
table: "ExternalSeriesMetadata");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "KavitaPlusConnection",
|
||||
table: "ChapterPeople");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OrderWeight",
|
||||
table: "ChapterPeople");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,8 +5,10 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using API.Data.ManualMigrations;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Progress;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
|
|
@ -14,9 +16,11 @@ using Microsoft.EntityFrameworkCore;
|
|||
|
||||
namespace API.Data.Repositories;
|
||||
#nullable enable
|
||||
|
||||
public interface IAppUserProgressRepository
|
||||
{
|
||||
void Update(AppUserProgress userProgress);
|
||||
void Remove(AppUserProgress userProgress);
|
||||
Task<int> CleanupAbandonedChapters();
|
||||
Task<bool> UserHasProgress(LibraryType libraryType, int userId);
|
||||
Task<AppUserProgress?> GetUserProgressAsync(int chapterId, int userId);
|
||||
|
|
@ -36,8 +40,9 @@ public interface IAppUserProgressRepository
|
|||
Task<DateTime?> GetLatestProgressForSeries(int seriesId, int userId);
|
||||
Task<DateTime?> GetFirstProgressForSeries(int seriesId, int userId);
|
||||
Task UpdateAllProgressThatAreMoreThanChapterPages();
|
||||
Task<IList<FullProgressDto>> GetUserProgressForChapter(int chapterId, int userId = 0);
|
||||
}
|
||||
#nullable disable
|
||||
|
||||
public class AppUserProgressRepository : IAppUserProgressRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
|
|
@ -54,6 +59,11 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
|||
_context.Entry(userProgress).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Remove(AppUserProgress userProgress)
|
||||
{
|
||||
_context.Remove(userProgress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will remove any entries that have chapterIds that no longer exists. This will execute the save as well.
|
||||
/// </summary>
|
||||
|
|
@ -167,9 +177,10 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
|||
(appUserProgresses, chapter) => new {appUserProgresses, chapter})
|
||||
.Where(p => p.appUserProgresses.SeriesId == seriesId && p.appUserProgresses.AppUserId == userId &&
|
||||
p.appUserProgresses.PagesRead >= p.chapter.Pages)
|
||||
.Select(p => p.chapter.Range)
|
||||
.Where(p => p.chapter.MaxNumber != Parser.SpecialVolumeNumber)
|
||||
.Select(p => p.chapter.MaxNumber)
|
||||
.ToListAsync();
|
||||
return list.Count == 0 ? 0 : list.DefaultIfEmpty().Where(d => d != null).Max(d => (int) Math.Floor(Parser.MaxNumberFromRange(d)));
|
||||
return list.Count == 0 ? 0 : (int) list.DefaultIfEmpty().Max(d => d);
|
||||
}
|
||||
|
||||
public async Task<float> GetHighestFullyReadVolumeForSeries(int seriesId, int userId)
|
||||
|
|
@ -179,8 +190,10 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
|||
(appUserProgresses, chapter) => new {appUserProgresses, chapter})
|
||||
.Where(p => p.appUserProgresses.SeriesId == seriesId && p.appUserProgresses.AppUserId == userId &&
|
||||
p.appUserProgresses.PagesRead >= p.chapter.Pages)
|
||||
.Where(p => p.chapter.MaxNumber != Parser.SpecialVolumeNumber)
|
||||
.Select(p => p.chapter.Volume.MaxNumber)
|
||||
.ToListAsync();
|
||||
|
||||
return list.Count == 0 ? 0 : list.DefaultIfEmpty().Max();
|
||||
}
|
||||
|
||||
|
|
@ -231,6 +244,33 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
|||
await _context.Database.ExecuteSqlRawAsync(batchSql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="chapterId"></param>
|
||||
/// <param name="userId">If 0, will pull all records</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IList<FullProgressDto>> GetUserProgressForChapter(int chapterId, int userId = 0)
|
||||
{
|
||||
return await _context.AppUserProgresses
|
||||
.WhereIf(userId > 0, p => p.AppUserId == userId)
|
||||
.Where(p => p.ChapterId == chapterId)
|
||||
.Include(p => p.AppUser)
|
||||
.Select(p => new FullProgressDto()
|
||||
{
|
||||
AppUserId = p.AppUserId,
|
||||
ChapterId = p.ChapterId,
|
||||
PagesRead = p.PagesRead,
|
||||
Id = p.Id,
|
||||
Created = p.Created,
|
||||
CreatedUtc = p.CreatedUtc,
|
||||
LastModified = p.LastModified,
|
||||
LastModifiedUtc = p.LastModifiedUtc,
|
||||
UserName = p.AppUser.UserName
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
public async Task<AppUserProgress?> GetUserProgressAsync(int chapterId, int userId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,11 +22,16 @@ public enum ChapterIncludes
|
|||
None = 1,
|
||||
Volumes = 2,
|
||||
Files = 4,
|
||||
People = 8,
|
||||
Genres = 16,
|
||||
Tags = 32
|
||||
}
|
||||
|
||||
public interface IChapterRepository
|
||||
{
|
||||
void Update(Chapter chapter);
|
||||
void Remove(Chapter chapter);
|
||||
void Remove(IList<Chapter> chapters);
|
||||
Task<IEnumerable<Chapter>> GetChaptersByIdsAsync(IList<int> chapterIds, ChapterIncludes includes = ChapterIncludes.None);
|
||||
Task<IChapterInfoDto?> GetChapterInfoDtoAsync(int chapterId);
|
||||
Task<int> GetChapterTotalPagesAsync(int chapterId);
|
||||
|
|
@ -34,7 +39,7 @@ public interface IChapterRepository
|
|||
Task<ChapterDto?> GetChapterDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<ChapterMetadataDto?> GetChapterMetadataDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<IList<MangaFile>> GetFilesForChapterAsync(int chapterId);
|
||||
Task<IList<Chapter>> GetChaptersAsync(int volumeId);
|
||||
Task<IList<Chapter>> GetChaptersAsync(int volumeId, ChapterIncludes includes = ChapterIncludes.None);
|
||||
Task<IList<MangaFile>> GetFilesForChaptersAsync(IReadOnlyList<int> chapterIds);
|
||||
Task<string?> GetChapterCoverImageAsync(int chapterId);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
|
|
@ -42,6 +47,7 @@ public interface IChapterRepository
|
|||
Task<IEnumerable<string>> GetCoverImagesForLockedChaptersAsync();
|
||||
Task<ChapterDto> AddChapterModifiers(int userId, ChapterDto chapter);
|
||||
IEnumerable<Chapter> GetChaptersForSeries(int seriesId);
|
||||
Task<IList<Chapter>> GetAllChaptersForSeries(int seriesId);
|
||||
}
|
||||
public class ChapterRepository : IChapterRepository
|
||||
{
|
||||
|
|
@ -59,6 +65,16 @@ public class ChapterRepository : IChapterRepository
|
|||
_context.Entry(chapter).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Remove(Chapter chapter)
|
||||
{
|
||||
_context.Chapter.Remove(chapter);
|
||||
}
|
||||
|
||||
public void Remove(IList<Chapter> chapters)
|
||||
{
|
||||
_context.Chapter.RemoveRange(chapters);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Chapter>> GetChaptersByIdsAsync(IList<int> chapterIds, ChapterIncludes includes = ChapterIncludes.None)
|
||||
{
|
||||
return await _context.Chapter
|
||||
|
|
@ -78,7 +94,7 @@ public class ChapterRepository : IChapterRepository
|
|||
.Where(c => c.Id == chapterId)
|
||||
.Join(_context.Volume, c => c.VolumeId, v => v.Id, (chapter, volume) => new
|
||||
{
|
||||
ChapterNumber = chapter.Range,
|
||||
ChapterNumber = chapter.MinNumber,
|
||||
VolumeNumber = volume.Name,
|
||||
VolumeId = volume.Id,
|
||||
chapter.IsSpecial,
|
||||
|
|
@ -102,8 +118,8 @@ public class ChapterRepository : IChapterRepository
|
|||
})
|
||||
.Select(data => new ChapterInfoDto()
|
||||
{
|
||||
ChapterNumber = data.ChapterNumber,
|
||||
VolumeNumber = data.VolumeNumber + string.Empty,
|
||||
ChapterNumber = data.ChapterNumber + string.Empty, // TODO: Fix this
|
||||
VolumeNumber = data.VolumeNumber + string.Empty, // TODO: Fix this
|
||||
VolumeId = data.VolumeId,
|
||||
IsSpecial = data.IsSpecial,
|
||||
SeriesId = data.SeriesId,
|
||||
|
|
@ -175,6 +191,7 @@ public class ChapterRepository : IChapterRepository
|
|||
{
|
||||
return await _context.Chapter
|
||||
.Includes(includes)
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.FirstOrDefaultAsync(c => c.Id == chapterId);
|
||||
}
|
||||
|
||||
|
|
@ -183,10 +200,12 @@ public class ChapterRepository : IChapterRepository
|
|||
/// </summary>
|
||||
/// <param name="volumeId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IList<Chapter>> GetChaptersAsync(int volumeId)
|
||||
public async Task<IList<Chapter>> GetChaptersAsync(int volumeId, ChapterIncludes includes = ChapterIncludes.None)
|
||||
{
|
||||
return await _context.Chapter
|
||||
.Where(c => c.VolumeId == volumeId)
|
||||
.Includes(includes)
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
|
@ -267,11 +286,28 @@ public class ChapterRepository : IChapterRepository
|
|||
return chapter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Includes Volumes
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Chapter> GetChaptersForSeries(int seriesId)
|
||||
{
|
||||
return _context.Chapter
|
||||
.Where(c => c.Volume.SeriesId == seriesId)
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.Include(c => c.Volume)
|
||||
.AsEnumerable();
|
||||
}
|
||||
|
||||
public async Task<IList<Chapter>> GetAllChaptersForSeries(int seriesId)
|
||||
{
|
||||
return await _context.Chapter
|
||||
.Where(c => c.Volume.SeriesId == seriesId)
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.Include(c => c.Volume)
|
||||
.Include(c => c.People)
|
||||
.ThenInclude(cp => cp.Person)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,44 +3,64 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data.Misc;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Collection;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using API.Extensions.QueryExtensions.Filtering;
|
||||
using API.Services.Plus;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
|
||||
#nullable enable
|
||||
|
||||
[Flags]
|
||||
public enum CollectionTagIncludes
|
||||
{
|
||||
None = 1,
|
||||
SeriesMetadata = 2,
|
||||
SeriesMetadataWithSeries = 4
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CollectionIncludes
|
||||
{
|
||||
None = 1,
|
||||
Series = 2,
|
||||
}
|
||||
|
||||
public interface ICollectionTagRepository
|
||||
{
|
||||
void Add(CollectionTag tag);
|
||||
void Remove(CollectionTag tag);
|
||||
Task<IEnumerable<CollectionTagDto>> GetAllTagDtosAsync();
|
||||
Task<IEnumerable<CollectionTagDto>> SearchTagDtosAsync(string searchQuery, int userId);
|
||||
void Remove(AppUserCollection tag);
|
||||
Task<string?> GetCoverImageAsync(int collectionTagId);
|
||||
Task<IEnumerable<CollectionTagDto>> GetAllPromotedTagDtosAsync(int userId);
|
||||
Task<CollectionTag?> GetTagAsync(int tagId, CollectionTagIncludes includes = CollectionTagIncludes.None);
|
||||
void Update(CollectionTag tag);
|
||||
Task<int> RemoveTagsWithoutSeries();
|
||||
Task<IEnumerable<CollectionTag>> GetAllTagsAsync(CollectionTagIncludes includes = CollectionTagIncludes.None);
|
||||
Task<AppUserCollection?> GetCollectionAsync(int tagId, CollectionIncludes includes = CollectionIncludes.None);
|
||||
void Update(AppUserCollection tag);
|
||||
Task<int> RemoveCollectionsWithoutSeries();
|
||||
|
||||
Task<IEnumerable<AppUserCollection>> GetAllCollectionsAsync(CollectionIncludes includes = CollectionIncludes.None);
|
||||
/// <summary>
|
||||
/// Returns all of the user's collections with the option of other user's promoted
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="includePromoted"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<AppUserCollectionDto>> GetCollectionDtosAsync(int userId, bool includePromoted = false);
|
||||
Task<IEnumerable<AppUserCollectionDto>> GetCollectionDtosBySeriesAsync(int userId, int seriesId, bool includePromoted = false);
|
||||
|
||||
Task<IEnumerable<CollectionTag>> GetAllTagsByNamesAsync(IEnumerable<string> normalizedTitles,
|
||||
CollectionTagIncludes includes = CollectionTagIncludes.None);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<bool> TagExists(string title);
|
||||
Task<IList<CollectionTag>> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat);
|
||||
Task<bool> CollectionExists(string title, int userId);
|
||||
Task<IList<AppUserCollection>> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat);
|
||||
Task<IList<string>> GetRandomCoverImagesAsync(int collectionId);
|
||||
Task<IList<AppUserCollection>> GetCollectionsForUserAsync(int userId, CollectionIncludes includes = CollectionIncludes.None);
|
||||
Task UpdateCollectionAgeRating(AppUserCollection tag);
|
||||
Task<IEnumerable<AppUserCollection>> GetCollectionsByIds(IEnumerable<int> tags, CollectionIncludes includes = CollectionIncludes.None);
|
||||
Task<IList<AppUserCollection>> GetAllCollectionsForSyncing(DateTime expirationTime);
|
||||
}
|
||||
|
||||
public class CollectionTagRepository : ICollectionTagRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
|
|
@ -52,17 +72,12 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public void Add(CollectionTag tag)
|
||||
public void Remove(AppUserCollection tag)
|
||||
{
|
||||
_context.CollectionTag.Add(tag);
|
||||
_context.AppUserCollection.Remove(tag);
|
||||
}
|
||||
|
||||
public void Remove(CollectionTag tag)
|
||||
{
|
||||
_context.CollectionTag.Remove(tag);
|
||||
}
|
||||
|
||||
public void Update(CollectionTag tag)
|
||||
public void Update(AppUserCollection tag)
|
||||
{
|
||||
_context.Entry(tag).State = EntityState.Modified;
|
||||
}
|
||||
|
|
@ -70,38 +85,53 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
/// <summary>
|
||||
/// Removes any collection tags without any series
|
||||
/// </summary>
|
||||
public async Task<int> RemoveTagsWithoutSeries()
|
||||
public async Task<int> RemoveCollectionsWithoutSeries()
|
||||
{
|
||||
var tagsToDelete = await _context.CollectionTag
|
||||
.Include(c => c.SeriesMetadatas)
|
||||
.Where(c => c.SeriesMetadatas.Count == 0)
|
||||
var tagsToDelete = await _context.AppUserCollection
|
||||
.Include(c => c.Items)
|
||||
.Where(c => c.Items.Count == 0)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync();
|
||||
|
||||
_context.RemoveRange(tagsToDelete);
|
||||
|
||||
return await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CollectionTag>> GetAllTagsAsync(CollectionTagIncludes includes = CollectionTagIncludes.None)
|
||||
public async Task<IEnumerable<AppUserCollection>> GetAllCollectionsAsync(CollectionIncludes includes = CollectionIncludes.None)
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
return await _context.AppUserCollection
|
||||
.OrderBy(c => c.NormalizedTitle)
|
||||
.Includes(includes)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CollectionTag>> GetAllTagsByNamesAsync(IEnumerable<string> normalizedTitles, CollectionTagIncludes includes = CollectionTagIncludes.None)
|
||||
public async Task<IEnumerable<AppUserCollectionDto>> GetCollectionDtosAsync(int userId, bool includePromoted = false)
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
.Where(c => normalizedTitles.Contains(c.NormalizedTitle))
|
||||
.OrderBy(c => c.NormalizedTitle)
|
||||
.Includes(includes)
|
||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
return await _context.AppUserCollection
|
||||
.Where(uc => uc.AppUserId == userId || (includePromoted && uc.Promoted))
|
||||
.WhereIf(ageRating.AgeRating != AgeRating.NotApplicable, uc => uc.AgeRating <= ageRating.AgeRating)
|
||||
.OrderBy(uc => uc.Title)
|
||||
.ProjectTo<AppUserCollectionDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<AppUserCollectionDto>> GetCollectionDtosBySeriesAsync(int userId, int seriesId, bool includePromoted = false)
|
||||
{
|
||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
return await _context.AppUserCollection
|
||||
.Where(uc => uc.AppUserId == userId || (includePromoted && uc.Promoted))
|
||||
.Where(uc => uc.Items.Any(s => s.Id == seriesId))
|
||||
.WhereIf(ageRating.AgeRating != AgeRating.NotApplicable, uc => uc.AgeRating <= ageRating.AgeRating)
|
||||
.OrderBy(uc => uc.Title)
|
||||
.ProjectTo<AppUserCollectionDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<string?> GetCoverImageAsync(int collectionTagId)
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
return await _context.AppUserCollection
|
||||
.Where(c => c.Id == collectionTagId)
|
||||
.Select(c => c.CoverImage)
|
||||
.SingleOrDefaultAsync();
|
||||
|
|
@ -109,23 +139,30 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
|
||||
public async Task<IList<string>> GetAllCoverImagesAsync()
|
||||
{
|
||||
return (await _context.CollectionTag
|
||||
return await _context.AppUserCollection
|
||||
.Select(t => t.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.ToListAsync())!;
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> TagExists(string title)
|
||||
/// <summary>
|
||||
/// If any tag exists for that given user's collections
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> CollectionExists(string title, int userId)
|
||||
{
|
||||
var normalized = title.ToNormalized();
|
||||
return await _context.CollectionTag
|
||||
return await _context.AppUserCollection
|
||||
.Where(uc => uc.AppUserId == userId)
|
||||
.AnyAsync(x => x.NormalizedTitle != null && x.NormalizedTitle.Equals(normalized));
|
||||
}
|
||||
|
||||
public async Task<IList<CollectionTag>> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat)
|
||||
public async Task<IList<AppUserCollection>> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat)
|
||||
{
|
||||
var extension = encodeFormat.GetExtension();
|
||||
return await _context.CollectionTag
|
||||
return await _context.AppUserCollection
|
||||
.Where(c => !string.IsNullOrEmpty(c.CoverImage) && !c.CoverImage.EndsWith(extension))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
|
@ -133,44 +170,61 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
public async Task<IList<string>> GetRandomCoverImagesAsync(int collectionId)
|
||||
{
|
||||
var random = new Random();
|
||||
var data = await _context.CollectionTag
|
||||
var data = await _context.AppUserCollection
|
||||
.Where(t => t.Id == collectionId)
|
||||
.SelectMany(t => t.SeriesMetadatas)
|
||||
.Select(sm => sm.Series.CoverImage)
|
||||
.SelectMany(uc => uc.Items.Select(series => series.CoverImage))
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.ToListAsync();
|
||||
|
||||
return data
|
||||
.OrderBy(_ => random.Next())
|
||||
.Take(4)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CollectionTagDto>> GetAllTagDtosAsync()
|
||||
public async Task<IList<AppUserCollection>> GetCollectionsForUserAsync(int userId, CollectionIncludes includes = CollectionIncludes.None)
|
||||
{
|
||||
|
||||
return await _context.CollectionTag
|
||||
.OrderBy(c => c.NormalizedTitle)
|
||||
.AsNoTracking()
|
||||
.ProjectTo<CollectionTagDto>(_mapper.ConfigurationProvider)
|
||||
return await _context.AppUserCollection
|
||||
.Where(c => c.AppUserId == userId)
|
||||
.Includes(includes)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CollectionTagDto>> GetAllPromotedTagDtosAsync(int userId)
|
||||
public async Task UpdateCollectionAgeRating(AppUserCollection tag)
|
||||
{
|
||||
var userRating = await GetUserAgeRestriction(userId);
|
||||
return await _context.CollectionTag
|
||||
.Where(c => c.Promoted)
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.OrderBy(c => c.NormalizedTitle)
|
||||
.AsNoTracking()
|
||||
.ProjectTo<CollectionTagDto>(_mapper.ConfigurationProvider)
|
||||
var maxAgeRating = await _context.AppUserCollection
|
||||
.Where(t => t.Id == tag.Id)
|
||||
.SelectMany(uc => uc.Items.Select(s => s.Metadata))
|
||||
.Select(sm => sm.AgeRating)
|
||||
.ToListAsync();
|
||||
|
||||
|
||||
tag.AgeRating = maxAgeRating.Count != 0 ? maxAgeRating.Max() : AgeRating.Unknown;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<AppUserCollection>> GetCollectionsByIds(IEnumerable<int> tags, CollectionIncludes includes = CollectionIncludes.None)
|
||||
{
|
||||
return await _context.AppUserCollection
|
||||
.Where(c => tags.Contains(c.Id))
|
||||
.Includes(includes)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task<CollectionTag?> GetTagAsync(int tagId, CollectionTagIncludes includes = CollectionTagIncludes.None)
|
||||
public async Task<IList<AppUserCollection>> GetAllCollectionsForSyncing(DateTime expirationTime)
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
return await _context.AppUserCollection
|
||||
.Where(c => c.Source == ScrobbleProvider.Mal)
|
||||
.Where(c => c.LastSyncUtc <= expirationTime)
|
||||
.Include(c => c.Items)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserCollection?> GetCollectionAsync(int tagId, CollectionIncludes includes = CollectionIncludes.None)
|
||||
{
|
||||
return await _context.AppUserCollection
|
||||
.Where(c => c.Id == tagId)
|
||||
.Includes(includes)
|
||||
.AsSplitQuery()
|
||||
|
|
@ -190,16 +244,12 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
.SingleAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CollectionTagDto>> SearchTagDtosAsync(string searchQuery, int userId)
|
||||
public async Task<IEnumerable<AppUserCollectionDto>> SearchTagDtosAsync(string searchQuery, int userId)
|
||||
{
|
||||
var userRating = await GetUserAgeRestriction(userId);
|
||||
return await _context.CollectionTag
|
||||
.Where(s => EF.Functions.Like(s.Title!, $"%{searchQuery}%")
|
||||
|| EF.Functions.Like(s.NormalizedTitle!, $"%{searchQuery}%"))
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.OrderBy(s => s.NormalizedTitle)
|
||||
.AsNoTracking()
|
||||
.ProjectTo<CollectionTagDto>(_mapper.ConfigurationProvider)
|
||||
return await _context.AppUserCollection
|
||||
.Search(searchQuery, userId, userRating)
|
||||
.ProjectTo<AppUserCollectionDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
86
API/Data/Repositories/CoverDbRepository.cs
Normal file
86
API/Data/Repositories/CoverDbRepository.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using API.DTOs.CoverDb;
|
||||
using API.Entities;
|
||||
using API.Entities.Person;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// This is a manual repository, not a DB repo
|
||||
/// </summary>
|
||||
public class CoverDbRepository
|
||||
{
|
||||
private readonly List<CoverDbAuthor> _authors;
|
||||
|
||||
public CoverDbRepository(string filePath)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
// Read and deserialize YAML file
|
||||
var yamlContent = File.ReadAllText(filePath);
|
||||
var peopleData = deserializer.Deserialize<CoverDbPeople>(yamlContent);
|
||||
_authors = peopleData.People;
|
||||
}
|
||||
|
||||
public CoverDbAuthor? FindAuthorByNameOrAlias(string name)
|
||||
{
|
||||
return _authors.Find(author =>
|
||||
author.Name.Equals(name, StringComparison.OrdinalIgnoreCase) ||
|
||||
author.Aliases.Contains(name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public CoverDbAuthor? FindBestAuthorMatch(Person person)
|
||||
{
|
||||
var aniListId = person.AniListId > 0 ? $"{person.AniListId}" : string.Empty;
|
||||
var highestScore = 0;
|
||||
CoverDbAuthor? bestMatch = null;
|
||||
|
||||
foreach (var author in _authors)
|
||||
{
|
||||
var score = 0;
|
||||
|
||||
// Check metadata IDs and add points if they match
|
||||
if (!string.IsNullOrEmpty(author.Ids.AmazonId) && author.Ids.AmazonId == person.Asin)
|
||||
{
|
||||
score += 10;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(author.Ids.AnilistId) && author.Ids.AnilistId == aniListId)
|
||||
{
|
||||
score += 10;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(author.Ids.HardcoverId) && author.Ids.HardcoverId == person.HardcoverId)
|
||||
{
|
||||
score += 10;
|
||||
}
|
||||
|
||||
// Check for exact name match
|
||||
if (author.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
score += 7;
|
||||
}
|
||||
|
||||
// Check for alias match
|
||||
if (author.Aliases.Contains(person.Name, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
score += 5;
|
||||
}
|
||||
|
||||
// Update the best match if current score is higher
|
||||
if (score <= highestScore) continue;
|
||||
|
||||
highestScore = score;
|
||||
bestMatch = author;
|
||||
}
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
}
|
||||
37
API/Data/Repositories/EmailHistoryRepository.cs
Normal file
37
API/Data/Repositories/EmailHistoryRepository.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.Email;
|
||||
using API.Entities;
|
||||
using API.Helpers;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
|
||||
public interface IEmailHistoryRepository
|
||||
{
|
||||
Task<IList<EmailHistoryDto>> GetEmailDtos(UserParams userParams);
|
||||
}
|
||||
|
||||
public class EmailHistoryRepository : IEmailHistoryRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public EmailHistoryRepository(DataContext context, IMapper mapper)
|
||||
{
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
|
||||
public async Task<IList<EmailHistoryDto>> GetEmailDtos(UserParams userParams)
|
||||
{
|
||||
return await _context.EmailHistory
|
||||
.OrderByDescending(h => h.SendDate)
|
||||
.ProjectTo<EmailHistoryDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.DTOs;
|
||||
using API.DTOs.KavitaPlus.Manage;
|
||||
using API.DTOs.Recommendation;
|
||||
using API.DTOs.Scrobbling;
|
||||
using API.DTOs.SeriesDetail;
|
||||
|
|
@ -31,13 +32,12 @@ public interface IExternalSeriesMetadataRepository
|
|||
void Remove(IEnumerable<ExternalRecommendation>? recommendations);
|
||||
void Remove(ExternalSeriesMetadata metadata);
|
||||
Task<ExternalSeriesMetadata?> GetExternalSeriesMetadata(int seriesId);
|
||||
Task<bool> ExternalSeriesMetadataNeedsRefresh(int seriesId);
|
||||
Task<SeriesDetailPlusDto> GetSeriesDetailPlusDto(int seriesId);
|
||||
Task<bool> NeedsDataRefresh(int seriesId);
|
||||
Task<SeriesDetailPlusDto?> GetSeriesDetailPlusDto(int seriesId);
|
||||
Task LinkRecommendationsToSeries(Series series);
|
||||
Task<bool> IsBlacklistedSeries(int seriesId);
|
||||
Task CreateBlacklistedSeries(int seriesId, bool saveChanges = true);
|
||||
Task RemoveFromBlacklist(int seriesId);
|
||||
Task<IList<int>> GetAllSeriesIdsWithoutMetadata(int limit);
|
||||
Task<IList<int>> GetSeriesThatNeedExternalMetadata(int limit, bool includeStaleData = false);
|
||||
Task<IList<ManageMatchSeriesDto>> GetAllSeries(ManageMatchFilterDto filter);
|
||||
}
|
||||
|
||||
public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepository
|
||||
|
|
@ -106,7 +106,7 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
|
|||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> ExternalSeriesMetadataNeedsRefresh(int seriesId)
|
||||
public async Task<bool> NeedsDataRefresh(int seriesId)
|
||||
{
|
||||
var row = await _context.ExternalSeriesMetadata
|
||||
.Where(s => s.SeriesId == seriesId)
|
||||
|
|
@ -114,7 +114,7 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
|
|||
return row == null || row.ValidUntilUtc <= DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public async Task<SeriesDetailPlusDto> GetSeriesDetailPlusDto(int seriesId)
|
||||
public async Task<SeriesDetailPlusDto?> GetSeriesDetailPlusDto(int seriesId)
|
||||
{
|
||||
var seriesDetailDto = await _context.ExternalSeriesMetadata
|
||||
.Where(m => m.SeriesId == seriesId)
|
||||
|
|
@ -157,8 +157,8 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
|
|||
.OrderByDescending(r => r.Score);
|
||||
}
|
||||
|
||||
IEnumerable<RatingDto> ratings = new List<RatingDto>();
|
||||
if (seriesDetailDto.ExternalRatings != null && seriesDetailDto.ExternalRatings.Any())
|
||||
IEnumerable<RatingDto> ratings = [];
|
||||
if (seriesDetailDto.ExternalRatings != null && seriesDetailDto.ExternalRatings.Count != 0)
|
||||
{
|
||||
ratings = seriesDetailDto.ExternalRatings
|
||||
.Select(r => _mapper.Map<RatingDto>(r));
|
||||
|
|
@ -191,6 +191,7 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
|
|||
.Where(r => EF.Functions.Like(r.Name, series.Name) ||
|
||||
EF.Functions.Like(r.Name, series.LocalizedName))
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var rec in recMatches)
|
||||
{
|
||||
rec.SeriesId = series.Id;
|
||||
|
|
@ -201,55 +202,38 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
|
|||
|
||||
public Task<bool> IsBlacklistedSeries(int seriesId)
|
||||
{
|
||||
return _context.SeriesBlacklist.AnyAsync(s => s.SeriesId == seriesId);
|
||||
return _context.Series
|
||||
.Where(s => s.Id == seriesId)
|
||||
.Select(s => s.IsBlacklisted)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance against SeriesId and Saves to the DB
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
public async Task CreateBlacklistedSeries(int seriesId, bool saveChanges = true)
|
||||
{
|
||||
if (seriesId <= 0 || await _context.SeriesBlacklist.AnyAsync(s => s.SeriesId == seriesId)) return;
|
||||
|
||||
await _context.SeriesBlacklist.AddAsync(new SeriesBlacklist()
|
||||
{
|
||||
SeriesId = seriesId
|
||||
});
|
||||
if (saveChanges)
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the Series from Blacklist and Saves to the DB
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
public async Task RemoveFromBlacklist(int seriesId)
|
||||
{
|
||||
var seriesBlacklist = await _context.SeriesBlacklist.FirstOrDefaultAsync(sb => sb.SeriesId == seriesId);
|
||||
|
||||
if (seriesBlacklist != null)
|
||||
{
|
||||
// Remove the SeriesBlacklist entity from the context
|
||||
_context.SeriesBlacklist.Remove(seriesBlacklist);
|
||||
|
||||
// Save the changes to the database
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<int>> GetAllSeriesIdsWithoutMetadata(int limit)
|
||||
public async Task<IList<int>> GetSeriesThatNeedExternalMetadata(int limit, bool includeStaleData = false)
|
||||
{
|
||||
return await _context.Series
|
||||
.Where(s => !ExternalMetadataService.NonEligibleLibraryTypes.Contains(s.Library.Type))
|
||||
.Where(s => s.ExternalSeriesMetadata == null || s.ExternalSeriesMetadata.ValidUntilUtc < DateTime.UtcNow)
|
||||
.Where(s => s.Library.AllowMetadataMatching)
|
||||
.WhereIf(includeStaleData, s => s.ExternalSeriesMetadata == null || s.ExternalSeriesMetadata.ValidUntilUtc < DateTime.UtcNow)
|
||||
.Where(s => s.ExternalSeriesMetadata == null || s.ExternalSeriesMetadata.AniListId == 0)
|
||||
.Where(s => !s.IsBlacklisted && !s.DontMatch)
|
||||
.OrderByDescending(s => s.Library.Type)
|
||||
.ThenBy(s => s.NormalizedName)
|
||||
.Select(s => s.Id)
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<ManageMatchSeriesDto>> GetAllSeries(ManageMatchFilterDto filter)
|
||||
{
|
||||
return await _context.Series
|
||||
.Include(s => s.Library)
|
||||
.Include(s => s.ExternalSeriesMetadata)
|
||||
.Where(s => !ExternalMetadataService.NonEligibleLibraryTypes.Contains(s.Library.Type))
|
||||
.Where(s => s.Library.AllowMetadataMatching)
|
||||
.FilterMatchState(filter.MatchStateOption)
|
||||
.OrderBy(s => s.NormalizedName)
|
||||
.ProjectTo<ManageMatchSeriesDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue