diff --git a/API.Tests/Services/DirectoryServiceTests.cs b/API.Tests/Services/DirectoryServiceTests.cs index 94cc8fc06..9844e7766 100644 --- a/API.Tests/Services/DirectoryServiceTests.cs +++ b/API.Tests/Services/DirectoryServiceTests.cs @@ -742,6 +742,9 @@ public class DirectoryServiceTests [InlineData(new [] {"C:/Manga/"}, new [] {"C:/Manga/Love Hina/Vol. 01.cbz", "C:/Manga/Love Hina/Specials/Sp01.cbz"}, "C:/Manga/Love Hina")] + [InlineData(new [] {"/manga"}, + new [] {"/manga/Love Hina/Vol. 01.cbz", "/manga/Love Hina/Specials/Sp01.cbz"}, + "/manga/Love Hina")] public void FindLowestDirectoriesFromFilesTest(string[] rootDirectories, string[] files, string expectedDirectory) { var fileSystem = new MockFileSystem(); diff --git a/API/Data/ManualMigrations/MigrateSeriesLowestFolderPath.cs b/API/Data/ManualMigrations/MigrateSeriesLowestFolderPath.cs new file mode 100644 index 000000000..ca68392cd --- /dev/null +++ b/API/Data/ManualMigrations/MigrateSeriesLowestFolderPath.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using API.Entities; +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 + +/// +/// Some linux-based users are having non-rooted LowestFolderPaths. This will attempt to fix it or null them. +/// Fixed in v0.8.2 +/// +public static class MigrateSeriesLowestFolderPath +{ + public static async Task Migrate(DataContext dataContext, ILogger 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"); + } +} diff --git a/API/Data/ManualMigrations/MigrateSmartFilterEncoding.cs b/API/Data/ManualMigrations/MigrateSmartFilterEncoding.cs index 6f97dbaad..e684ef6a0 100644 --- a/API/Data/ManualMigrations/MigrateSmartFilterEncoding.cs +++ b/API/Data/ManualMigrations/MigrateSmartFilterEncoding.cs @@ -3,7 +3,9 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using API.DTOs.Filtering.v2; +using API.Entities; using API.Helpers; +using Kavita.Common.EnvironmentInfo; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -29,7 +31,7 @@ public static class MigrateSmartFilterEncoding 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; @@ -43,6 +45,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"); } diff --git a/API/Services/DirectoryService.cs b/API/Services/DirectoryService.cs index 3c7150883..5a38c5903 100644 --- a/API/Services/DirectoryService.cs +++ b/API/Services/DirectoryService.cs @@ -745,12 +745,12 @@ public class DirectoryService : IDirectoryService /// /// Recursively scans a folder and returns the max last write time on any folders and files /// - /// If the folder is empty, this will return MaxValue for a DateTime + /// If the folder is empty or non-existant, this will return MaxValue for a DateTime /// /// Max Last Write Time public DateTime GetLastWriteTime(string folderPath) { - if (!FileSystem.Directory.Exists(folderPath)) throw new IOException($"{folderPath} does not exist"); + if (!FileSystem.Directory.Exists(folderPath)) return DateTime.MaxValue; var fileEntries = FileSystem.Directory.GetFileSystemEntries(folderPath, "*.*", SearchOption.AllDirectories); if (fileEntries.Length == 0) return DateTime.MaxValue; return fileEntries.Max(path => FileSystem.File.GetLastWriteTime(path)); diff --git a/API/Services/Tasks/Scanner/ParseScannedFiles.cs b/API/Services/Tasks/Scanner/ParseScannedFiles.cs index 742b37410..6c4946307 100644 --- a/API/Services/Tasks/Scanner/ParseScannedFiles.cs +++ b/API/Services/Tasks/Scanner/ParseScannedFiles.cs @@ -143,7 +143,8 @@ public class ParseScannedFiles _logger.LogDebug("[ProcessFiles] Dirty check passed, series list: {@SeriesModified}", series2); foreach (var s in series2) { - _logger.LogDebug("[ProcessFiles] Last Scanned: {LastScanned} vs Directory Check: {DirectoryLastScanned}", s.LastScanned, _directoryService + _logger.LogDebug("[ProcessFiles] Last Scanned: {LastScanned} vs Directory Check: {DirectoryLastScanned}", + s.LastScanned, _directoryService .GetLastWriteTime(s.LowestFolderPath!) .Truncate(TimeSpan.TicksPerSecond)); } diff --git a/API/Startup.cs b/API/Startup.cs index c6b1e852d..30c663a4b 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -269,6 +269,7 @@ public class Startup // v0.8.2 await ManualMigrateThemeDescription.Migrate(dataContext, logger); await MigrateInitialInstallData.Migrate(dataContext, logger, directoryService); + await MigrateSeriesLowestFolderPath.Migrate(dataContext, logger, directoryService); // Update the version in the DB after all migrations are run var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);