mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Tech Debt + Series Sort bugfix (#1192)
* Code cleanup. When copying files, if the target file already exists, append (1), (2), etc onto the file (this is enhancing existing implementation to allow multiple numbers) * Added a ton of null checks to UpdateSeriesMetadata and made the code work on the rare case (not really possible) that SeriesMetadata doesn't exist. * Updated Genre code to use strings to ensure a better, more fault tolerant update experience. * More cleanup on the codebase * Fixed a bug where Series SortName was getting emptied on file scan * Fixed a bad copy * Fixed unit tests
This commit is contained in:
parent
a00e8f121f
commit
19678383b2
@ -210,7 +210,6 @@ namespace API.Tests.Parser
|
|||||||
[InlineData("._Love Hina/Love Hina/", true)]
|
[InlineData("._Love Hina/Love Hina/", true)]
|
||||||
[InlineData("@Recently-Snapshot/Love Hina/", true)]
|
[InlineData("@Recently-Snapshot/Love Hina/", true)]
|
||||||
[InlineData("@recycle/Love Hina/", true)]
|
[InlineData("@recycle/Love Hina/", true)]
|
||||||
[InlineData("@recycle/Love Hina/", true)]
|
|
||||||
[InlineData("E:/Test/__MACOSX/Love Hina/", true)]
|
[InlineData("E:/Test/__MACOSX/Love Hina/", true)]
|
||||||
public void HasBlacklistedFolderInPathTest(string inputPath, bool expected)
|
public void HasBlacklistedFolderInPathTest(string inputPath, bool expected)
|
||||||
{
|
{
|
||||||
|
@ -557,6 +557,24 @@ namespace API.Tests.Services
|
|||||||
Assert.Equal(2, ds.GetFiles("/manga/output/").Count());
|
Assert.Equal(2, ds.GetFiles("/manga/output/").Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CopyFilesToDirectory_ShouldAppendWhenTargetFileExists()
|
||||||
|
{
|
||||||
|
const string testDirectory = "/manga/";
|
||||||
|
var fileSystem = new MockFileSystem();
|
||||||
|
fileSystem.AddFile($"{testDirectory}file.zip", new MockFileData(""));
|
||||||
|
fileSystem.AddFile($"/manga/output/file (1).zip", new MockFileData(""));
|
||||||
|
fileSystem.AddFile($"/manga/output/file (2).zip", new MockFileData(""));
|
||||||
|
|
||||||
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
|
||||||
|
ds.CopyFilesToDirectory(new []{$"{testDirectory}file.zip"}, "/manga/output/");
|
||||||
|
ds.CopyFilesToDirectory(new []{$"{testDirectory}file.zip"}, "/manga/output/");
|
||||||
|
var outputFiles = ds.GetFiles("/manga/output/").Select(API.Parser.Parser.NormalizePath).ToList();
|
||||||
|
Assert.Equal(4, outputFiles.Count()); // we have 2 already there and 2 copies
|
||||||
|
// For some reason, this has C:/ on directory even though everything is emulated
|
||||||
|
Assert.True(outputFiles.Contains(API.Parser.Parser.NormalizePath("/manga/output/file (3).zip")) || outputFiles.Contains(API.Parser.Parser.NormalizePath("C:/manga/output/file (3).zip")));
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ListDirectory
|
#region ListDirectory
|
||||||
|
@ -6,8 +6,11 @@ using System.Threading.Tasks;
|
|||||||
using API.Data;
|
using API.Data;
|
||||||
using API.Data.Repositories;
|
using API.Data.Repositories;
|
||||||
using API.DTOs;
|
using API.DTOs;
|
||||||
|
using API.DTOs.CollectionTags;
|
||||||
|
using API.DTOs.Metadata;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
using API.Extensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.SignalR;
|
using API.SignalR;
|
||||||
@ -99,6 +102,9 @@ public class SeriesServiceTests
|
|||||||
{
|
{
|
||||||
_context.Series.RemoveRange(_context.Series.ToList());
|
_context.Series.RemoveRange(_context.Series.ToList());
|
||||||
_context.AppUserRating.RemoveRange(_context.AppUserRating.ToList());
|
_context.AppUserRating.RemoveRange(_context.AppUserRating.ToList());
|
||||||
|
_context.Genre.RemoveRange(_context.Genre.ToList());
|
||||||
|
_context.CollectionTag.RemoveRange(_context.CollectionTag.ToList());
|
||||||
|
_context.Person.RemoveRange(_context.Person.ToList());
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
@ -569,4 +575,174 @@ public class SeriesServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region UpdateSeriesMetadata
|
||||||
|
|
||||||
|
private void SetupUpdateSeriesMetadataDb()
|
||||||
|
{
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Book,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateSeriesMetadata_ShouldCreateEmptyMetadata_IfDoesntExist()
|
||||||
|
{
|
||||||
|
await ResetDb();
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Book,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var success = await _seriesService.UpdateSeriesMetadata(new UpdateSeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesMetadata = new SeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesId = 1,
|
||||||
|
Genres = new List<GenreTagDto>() {new GenreTagDto() {Id = 0, Title = "New Genre"}}
|
||||||
|
},
|
||||||
|
CollectionTags = new List<CollectionTagDto>()
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.True(success);
|
||||||
|
|
||||||
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series.Metadata);
|
||||||
|
Assert.True(series.Metadata.Genres.Select(g => g.Title).Contains("New Genre".SentenceCase()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateSeriesMetadata_ShouldCreateNewTags_IfNoneExist()
|
||||||
|
{
|
||||||
|
await ResetDb();
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Book,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var success = await _seriesService.UpdateSeriesMetadata(new UpdateSeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesMetadata = new SeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesId = 1,
|
||||||
|
Genres = new List<GenreTagDto>() {new GenreTagDto() {Id = 0, Title = "New Genre"}},
|
||||||
|
Tags = new List<TagDto>() {new TagDto() {Id = 0, Title = "New Tag"}},
|
||||||
|
Characters = new List<PersonDto>() {new PersonDto() {Id = 0, Name = "Joe Shmo", Role = PersonRole.Character}},
|
||||||
|
Colorists = new List<PersonDto>() {new PersonDto() {Id = 0, Name = "Joe Shmo", Role = PersonRole.Colorist}},
|
||||||
|
Pencillers = new List<PersonDto>() {new PersonDto() {Id = 0, Name = "Joe Shmo 2", Role = PersonRole.Penciller}},
|
||||||
|
},
|
||||||
|
CollectionTags = new List<CollectionTagDto>()
|
||||||
|
{
|
||||||
|
new CollectionTagDto() {Id = 0, Promoted = false, Summary = string.Empty, CoverImageLocked = false, Title = "New Collection"}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.True(success);
|
||||||
|
|
||||||
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series.Metadata);
|
||||||
|
Assert.True(series.Metadata.Genres.Select(g => g.Title).Contains("New Genre".SentenceCase()));
|
||||||
|
Assert.True(series.Metadata.People.All(g => g.Name is "Joe Shmo" or "Joe Shmo 2"));
|
||||||
|
Assert.True(series.Metadata.Tags.Select(g => g.Title).Contains("New Tag".SentenceCase()));
|
||||||
|
Assert.True(series.Metadata.CollectionTags.Select(g => g.Title).Contains("New Collection"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateSeriesMetadata_ShouldRemoveExistingTags()
|
||||||
|
{
|
||||||
|
await ResetDb();
|
||||||
|
var s = new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library()
|
||||||
|
{
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Book,
|
||||||
|
},
|
||||||
|
Metadata = DbFactory.SeriesMetadata(new List<CollectionTag>())
|
||||||
|
};
|
||||||
|
var g = DbFactory.Genre("Existing Genre", false);
|
||||||
|
s.Metadata.Genres = new List<Genre>() {g};
|
||||||
|
_context.Series.Add(s);
|
||||||
|
|
||||||
|
_context.Genre.Add(g);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var success = await _seriesService.UpdateSeriesMetadata(new UpdateSeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesMetadata = new SeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesId = 1,
|
||||||
|
Genres = new List<GenreTagDto>() {new () {Id = 0, Title = "New Genre"}},
|
||||||
|
},
|
||||||
|
CollectionTags = new List<CollectionTagDto>()
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.True(success);
|
||||||
|
|
||||||
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series.Metadata);
|
||||||
|
Assert.True(series.Metadata.Genres.Select(g => g.Title).All(g => g == "New Genre".SentenceCase()));
|
||||||
|
Assert.False(series.Metadata.GenresLocked); // GenreLocked is false unless the UI Explicitly says it should be locked
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateSeriesMetadata_ShouldLockIfTold()
|
||||||
|
{
|
||||||
|
await ResetDb();
|
||||||
|
var s = new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library()
|
||||||
|
{
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Book,
|
||||||
|
},
|
||||||
|
Metadata = DbFactory.SeriesMetadata(new List<CollectionTag>())
|
||||||
|
};
|
||||||
|
var g = DbFactory.Genre("Existing Genre", false);
|
||||||
|
s.Metadata.Genres = new List<Genre>() {g};
|
||||||
|
s.Metadata.GenresLocked = true;
|
||||||
|
_context.Series.Add(s);
|
||||||
|
|
||||||
|
_context.Genre.Add(g);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var success = await _seriesService.UpdateSeriesMetadata(new UpdateSeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesMetadata = new SeriesMetadataDto()
|
||||||
|
{
|
||||||
|
SeriesId = 1,
|
||||||
|
Genres = new List<GenreTagDto>() {new () {Id = 1, Title = "Existing Genre"}},
|
||||||
|
GenresLocked = true
|
||||||
|
},
|
||||||
|
CollectionTags = new List<CollectionTagDto>()
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.True(success);
|
||||||
|
|
||||||
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series.Metadata);
|
||||||
|
Assert.True(series.Metadata.Genres.Select(g => g.Title).All(g => g == "Existing Genre".SentenceCase()));
|
||||||
|
Assert.True(series.Metadata.GenresLocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -26,20 +26,18 @@ namespace API.Controllers
|
|||||||
private readonly IDirectoryService _directoryService;
|
private readonly IDirectoryService _directoryService;
|
||||||
private readonly IDownloadService _downloadService;
|
private readonly IDownloadService _downloadService;
|
||||||
private readonly IEventHub _eventHub;
|
private readonly IEventHub _eventHub;
|
||||||
private readonly UserManager<AppUser> _userManager;
|
|
||||||
private readonly ILogger<DownloadController> _logger;
|
private readonly ILogger<DownloadController> _logger;
|
||||||
private readonly IBookmarkService _bookmarkService;
|
private readonly IBookmarkService _bookmarkService;
|
||||||
private const string DefaultContentType = "application/octet-stream";
|
private const string DefaultContentType = "application/octet-stream";
|
||||||
|
|
||||||
public DownloadController(IUnitOfWork unitOfWork, IArchiveService archiveService, IDirectoryService directoryService,
|
public DownloadController(IUnitOfWork unitOfWork, IArchiveService archiveService, IDirectoryService directoryService,
|
||||||
IDownloadService downloadService, IEventHub eventHub, UserManager<AppUser> userManager, ILogger<DownloadController> logger, IBookmarkService bookmarkService)
|
IDownloadService downloadService, IEventHub eventHub, ILogger<DownloadController> logger, IBookmarkService bookmarkService)
|
||||||
{
|
{
|
||||||
_unitOfWork = unitOfWork;
|
_unitOfWork = unitOfWork;
|
||||||
_archiveService = archiveService;
|
_archiveService = archiveService;
|
||||||
_directoryService = directoryService;
|
_directoryService = directoryService;
|
||||||
_downloadService = downloadService;
|
_downloadService = downloadService;
|
||||||
_eventHub = eventHub;
|
_eventHub = eventHub;
|
||||||
_userManager = userManager;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_bookmarkService = bookmarkService;
|
_bookmarkService = bookmarkService;
|
||||||
}
|
}
|
||||||
|
@ -782,6 +782,7 @@ public class OpdsController : BaseApiController
|
|||||||
{
|
{
|
||||||
CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"),
|
CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"),
|
||||||
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"),
|
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"),
|
||||||
|
// We can't not include acc link in the feed, panels doesn't work with just page streaming option. We have to block download directly
|
||||||
accLink,
|
accLink,
|
||||||
CreatePageStreamLink(seriesId, volumeId, chapterId, mangaFile, apiKey)
|
CreatePageStreamLink(seriesId, volumeId, chapterId, mangaFile, apiKey)
|
||||||
},
|
},
|
||||||
@ -792,14 +793,6 @@ public class OpdsController : BaseApiController
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// We can't not show acc link in the feed, panels wont work like that. We have to block download directly
|
|
||||||
// var user = await _unitOfWork.UserRepository.GetUserByIdAsync(await GetUser(apiKey));
|
|
||||||
// if (await _downloadService.HasDownloadPermission(user))
|
|
||||||
// {
|
|
||||||
// entry.Links.Add(accLink);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using API.DTOs.CollectionTags;
|
using API.DTOs.CollectionTags;
|
||||||
using API.DTOs.Metadata;
|
using API.DTOs.Metadata;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
@ -8,7 +9,7 @@ namespace API.DTOs
|
|||||||
public class SeriesMetadataDto
|
public class SeriesMetadataDto
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Summary { get; set; }
|
public string Summary { get; set; } = string.Empty;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Collections the Series belongs to
|
/// Collections the Series belongs to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -18,7 +18,7 @@ namespace API.Data
|
|||||||
{
|
{
|
||||||
public static Series Series(string name)
|
public static Series Series(string name)
|
||||||
{
|
{
|
||||||
return new ()
|
return new Series
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
OriginalName = name,
|
OriginalName = name,
|
||||||
|
@ -378,7 +378,7 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns Volumes, Metadata, and Collection Tags
|
/// Returns Volumes, Metadata (Incl Genres and People), and Collection Tags
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="seriesId"></param>
|
/// <param name="seriesId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
@ -60,6 +60,12 @@ namespace API.Parser
|
|||||||
private static readonly Regex NormalizeRegex = new Regex(@"[^\p{L}0-9\+]",
|
private static readonly Regex NormalizeRegex = new Regex(@"[^\p{L}0-9\+]",
|
||||||
MatchOptions, RegexTimeout);
|
MatchOptions, RegexTimeout);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recognizes the Special token only
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex SpecialTokenRegex = new Regex(@"SP\d+",
|
||||||
|
MatchOptions, RegexTimeout);
|
||||||
|
|
||||||
|
|
||||||
private static readonly Regex[] MangaVolumeRegex = new[]
|
private static readonly Regex[] MangaVolumeRegex = new[]
|
||||||
{
|
{
|
||||||
@ -976,9 +982,8 @@ namespace API.Parser
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string CleanSpecialTitle(string name)
|
public static string CleanSpecialTitle(string name)
|
||||||
{
|
{
|
||||||
// TODO: Optimize this code & Test
|
|
||||||
if (string.IsNullOrEmpty(name)) return name;
|
if (string.IsNullOrEmpty(name)) return name;
|
||||||
var cleaned = new Regex(@"SP\d+").Replace(name.Replace('_', ' '), string.Empty).Trim();
|
var cleaned = SpecialTokenRegex.Replace(name.Replace('_', ' '), string.Empty).Trim();
|
||||||
var lastIndex = cleaned.LastIndexOf('.');
|
var lastIndex = cleaned.LastIndexOf('.');
|
||||||
if (lastIndex > 0)
|
if (lastIndex > 0)
|
||||||
{
|
{
|
||||||
|
@ -71,6 +71,8 @@ namespace API.Services
|
|||||||
private static readonly Regex ExcludeDirectories = new Regex(
|
private static readonly Regex ExcludeDirectories = new Regex(
|
||||||
@"@eaDir|\.DS_Store|\.qpkg",
|
@"@eaDir|\.DS_Store|\.qpkg",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
private static readonly Regex FileCopyAppend = new Regex(@"\(\d+\)",
|
||||||
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
public static readonly string BackupDirectory = Path.Join(Directory.GetCurrentDirectory(), "config", "backups");
|
public static readonly string BackupDirectory = Path.Join(Directory.GetCurrentDirectory(), "config", "backups");
|
||||||
|
|
||||||
public DirectoryService(ILogger<DirectoryService> logger, IFileSystem fileSystem)
|
public DirectoryService(ILogger<DirectoryService> logger, IFileSystem fileSystem)
|
||||||
@ -370,24 +372,11 @@ namespace API.Services
|
|||||||
foreach (var file in filePaths)
|
foreach (var file in filePaths)
|
||||||
{
|
{
|
||||||
currentFile = file;
|
currentFile = file;
|
||||||
|
|
||||||
var fileInfo = FileSystem.FileInfo.FromFileName(file);
|
var fileInfo = FileSystem.FileInfo.FromFileName(file);
|
||||||
if (fileInfo.Exists)
|
var targetFile = FileSystem.FileInfo.FromFileName(RenameFileForCopy(file, directoryPath, prepend));
|
||||||
{
|
|
||||||
// TODO: I need to handle if file already exists and allow either an overwrite or prepend (2) to it
|
fileInfo.CopyTo(FileSystem.Path.Join(directoryPath, targetFile.Name));
|
||||||
try
|
|
||||||
{
|
|
||||||
fileInfo.CopyTo(FileSystem.Path.Join(directoryPath, prepend + fileInfo.Name));
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "File copy, dest already exists. Appending (2)");
|
|
||||||
fileInfo.CopyTo(FileSystem.Path.Join(directoryPath, prepend + FileSystem.Path.GetFileNameWithoutExtension(fileInfo.Name) + " (2)" + FileSystem.Path.GetExtension(fileInfo.Name)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Tried to copy {File} but it doesn't exist", file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -399,6 +388,42 @@ namespace API.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the combined filepath given a prepend (optional), output directory path, and a full input file path.
|
||||||
|
/// If the output file already exists, will append (1), (2), etc until it can be written out
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileToCopy"></param>
|
||||||
|
/// <param name="directoryPath"></param>
|
||||||
|
/// <param name="prepend"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string RenameFileForCopy(string fileToCopy, string directoryPath, string prepend = "")
|
||||||
|
{
|
||||||
|
var fileInfo = FileSystem.FileInfo.FromFileName(fileToCopy);
|
||||||
|
var filename = prepend + fileInfo.Name;
|
||||||
|
|
||||||
|
var targetFile = FileSystem.FileInfo.FromFileName(FileSystem.Path.Join(directoryPath, filename));
|
||||||
|
if (!targetFile.Exists)
|
||||||
|
{
|
||||||
|
return targetFile.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
var noExtension = FileSystem.Path.GetFileNameWithoutExtension(fileInfo.Name);
|
||||||
|
if (FileCopyAppend.IsMatch(noExtension))
|
||||||
|
{
|
||||||
|
var match = FileCopyAppend.Match(noExtension).Value;
|
||||||
|
var matchNumber = match.Replace("(", string.Empty).Replace(")", string.Empty);
|
||||||
|
noExtension = noExtension.Replace(match, $"({int.Parse(matchNumber) + 1})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
noExtension += " (1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
var newFilename = prepend + noExtension +
|
||||||
|
FileSystem.Path.GetExtension(fileInfo.Name);
|
||||||
|
return RenameFileForCopy(FileSystem.Path.Join(directoryPath, newFilename), directoryPath, prepend);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lists all directories in a root path. Will exclude Hidden or System directories.
|
/// Lists all directories in a root path. Will exclude Hidden or System directories.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -52,103 +52,99 @@ public class SeriesService : ISeriesService
|
|||||||
var allPeople = (await _unitOfWork.PersonRepository.GetAllPeople()).ToList();
|
var allPeople = (await _unitOfWork.PersonRepository.GetAllPeople()).ToList();
|
||||||
var allTags = (await _unitOfWork.TagRepository.GetAllTagsAsync()).ToList();
|
var allTags = (await _unitOfWork.TagRepository.GetAllTagsAsync()).ToList();
|
||||||
|
|
||||||
if (series.Metadata == null)
|
series.Metadata ??= DbFactory.SeriesMetadata(updateSeriesMetadataDto.CollectionTags
|
||||||
|
.Select(dto => DbFactory.CollectionTag(dto.Id, dto.Title, dto.Summary, dto.Promoted)).ToList());
|
||||||
|
|
||||||
|
if (series.Metadata.AgeRating != updateSeriesMetadataDto.SeriesMetadata.AgeRating)
|
||||||
{
|
{
|
||||||
series.Metadata = DbFactory.SeriesMetadata(updateSeriesMetadataDto.CollectionTags
|
series.Metadata.AgeRating = updateSeriesMetadataDto.SeriesMetadata.AgeRating;
|
||||||
.Select(dto => DbFactory.CollectionTag(dto.Id, dto.Title, dto.Summary, dto.Promoted)).ToList());
|
series.Metadata.AgeRatingLocked = true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (series.Metadata.PublicationStatus != updateSeriesMetadataDto.SeriesMetadata.PublicationStatus)
|
||||||
{
|
{
|
||||||
if (series.Metadata.AgeRating != updateSeriesMetadataDto.SeriesMetadata.AgeRating)
|
series.Metadata.PublicationStatus = updateSeriesMetadataDto.SeriesMetadata.PublicationStatus;
|
||||||
{
|
series.Metadata.PublicationStatusLocked = true;
|
||||||
series.Metadata.AgeRating = updateSeriesMetadataDto.SeriesMetadata.AgeRating;
|
|
||||||
series.Metadata.AgeRatingLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (series.Metadata.PublicationStatus != updateSeriesMetadataDto.SeriesMetadata.PublicationStatus)
|
|
||||||
{
|
|
||||||
series.Metadata.PublicationStatus = updateSeriesMetadataDto.SeriesMetadata.PublicationStatus;
|
|
||||||
series.Metadata.PublicationStatusLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (series.Metadata.Summary != updateSeriesMetadataDto.SeriesMetadata.Summary.Trim())
|
|
||||||
{
|
|
||||||
series.Metadata.Summary = updateSeriesMetadataDto.SeriesMetadata?.Summary.Trim();
|
|
||||||
series.Metadata.SummaryLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (series.Metadata.Language != updateSeriesMetadataDto.SeriesMetadata.Language)
|
|
||||||
{
|
|
||||||
series.Metadata.Language = updateSeriesMetadataDto.SeriesMetadata?.Language;
|
|
||||||
series.Metadata.LanguageLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
series.Metadata.CollectionTags ??= new List<CollectionTag>();
|
|
||||||
UpdateRelatedList(updateSeriesMetadataDto.CollectionTags, series, allCollectionTags, (tag) =>
|
|
||||||
{
|
|
||||||
series.Metadata.CollectionTags.Add(tag);
|
|
||||||
});
|
|
||||||
|
|
||||||
series.Metadata.Genres ??= new List<Genre>();
|
|
||||||
UpdateGenreList(updateSeriesMetadataDto.SeriesMetadata.Genres, series, allGenres, (genre) =>
|
|
||||||
{
|
|
||||||
series.Metadata.Genres.Add(genre);
|
|
||||||
}, () => series.Metadata.GenresLocked = true);
|
|
||||||
|
|
||||||
series.Metadata.Tags ??= new List<Tag>();
|
|
||||||
UpdateTagList(updateSeriesMetadataDto.SeriesMetadata.Tags, series, allTags, (tag) =>
|
|
||||||
{
|
|
||||||
series.Metadata.Tags.Add(tag);
|
|
||||||
}, () => series.Metadata.TagsLocked = true);
|
|
||||||
|
|
||||||
void HandleAddPerson(Person person)
|
|
||||||
{
|
|
||||||
PersonHelper.AddPersonIfNotExists(series.Metadata.People, person);
|
|
||||||
allPeople.Add(person);
|
|
||||||
}
|
|
||||||
|
|
||||||
series.Metadata.People ??= new List<Person>();
|
|
||||||
UpdatePeopleList(PersonRole.Writer, updateSeriesMetadataDto.SeriesMetadata.Writers, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.WriterLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Character, updateSeriesMetadataDto.SeriesMetadata.Characters, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.CharacterLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Colorist, updateSeriesMetadataDto.SeriesMetadata.Colorists, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.ColoristLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Editor, updateSeriesMetadataDto.SeriesMetadata.Editors, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.EditorLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Inker, updateSeriesMetadataDto.SeriesMetadata.Inkers, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.InkerLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Letterer, updateSeriesMetadataDto.SeriesMetadata.Letterers, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.LettererLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Penciller, updateSeriesMetadataDto.SeriesMetadata.Pencillers, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.PencillerLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Publisher, updateSeriesMetadataDto.SeriesMetadata.Publishers, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.PublisherLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.Translator, updateSeriesMetadataDto.SeriesMetadata.Translators, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.TranslatorLocked = true);
|
|
||||||
UpdatePeopleList(PersonRole.CoverArtist, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, series, allPeople,
|
|
||||||
HandleAddPerson, () => series.Metadata.CoverArtistLocked = true);
|
|
||||||
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked) series.Metadata.AgeRatingLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.PublicationStatusLocked) series.Metadata.PublicationStatusLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.LanguageLocked) series.Metadata.LanguageLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.GenresLocked) series.Metadata.GenresLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.TagsLocked) series.Metadata.TagsLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.CharacterLocked) series.Metadata.CharacterLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.ColoristLocked) series.Metadata.ColoristLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.EditorLocked) series.Metadata.EditorLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.InkerLocked) series.Metadata.InkerLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.LettererLocked) series.Metadata.LettererLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.PencillerLocked) series.Metadata.PencillerLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.PublisherLocked) series.Metadata.PublisherLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked) series.Metadata.TranslatorLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked) series.Metadata.CoverArtistLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.WriterLocked) series.Metadata.WriterLocked = false;
|
|
||||||
if (!updateSeriesMetadataDto.SeriesMetadata.SummaryLocked) series.Metadata.SummaryLocked = false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (series.Metadata.Summary != updateSeriesMetadataDto.SeriesMetadata.Summary.Trim())
|
||||||
|
{
|
||||||
|
series.Metadata.Summary = updateSeriesMetadataDto.SeriesMetadata?.Summary.Trim();
|
||||||
|
series.Metadata.SummaryLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (series.Metadata.Language != updateSeriesMetadataDto.SeriesMetadata.Language)
|
||||||
|
{
|
||||||
|
series.Metadata.Language = updateSeriesMetadataDto.SeriesMetadata?.Language;
|
||||||
|
series.Metadata.LanguageLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
series.Metadata.CollectionTags ??= new List<CollectionTag>();
|
||||||
|
UpdateRelatedList(updateSeriesMetadataDto.CollectionTags, series, allCollectionTags, (tag) =>
|
||||||
|
{
|
||||||
|
series.Metadata.CollectionTags.Add(tag);
|
||||||
|
});
|
||||||
|
|
||||||
|
series.Metadata.Genres ??= new List<Genre>();
|
||||||
|
UpdateGenreList(updateSeriesMetadataDto.SeriesMetadata.Genres, series, allGenres, (genre) =>
|
||||||
|
{
|
||||||
|
series.Metadata.Genres.Add(genre);
|
||||||
|
}, () => series.Metadata.GenresLocked = true);
|
||||||
|
|
||||||
|
series.Metadata.Tags ??= new List<Tag>();
|
||||||
|
UpdateTagList(updateSeriesMetadataDto.SeriesMetadata.Tags, series, allTags, (tag) =>
|
||||||
|
{
|
||||||
|
series.Metadata.Tags.Add(tag);
|
||||||
|
}, () => series.Metadata.TagsLocked = true);
|
||||||
|
|
||||||
|
void HandleAddPerson(Person person)
|
||||||
|
{
|
||||||
|
PersonHelper.AddPersonIfNotExists(series.Metadata.People, person);
|
||||||
|
allPeople.Add(person);
|
||||||
|
}
|
||||||
|
|
||||||
|
series.Metadata.People ??= new List<Person>();
|
||||||
|
UpdatePeopleList(PersonRole.Writer, updateSeriesMetadataDto.SeriesMetadata.Writers, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.WriterLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Character, updateSeriesMetadataDto.SeriesMetadata.Characters, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.CharacterLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Colorist, updateSeriesMetadataDto.SeriesMetadata.Colorists, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.ColoristLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Editor, updateSeriesMetadataDto.SeriesMetadata.Editors, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.EditorLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Inker, updateSeriesMetadataDto.SeriesMetadata.Inkers, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.InkerLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Letterer, updateSeriesMetadataDto.SeriesMetadata.Letterers, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.LettererLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Penciller, updateSeriesMetadataDto.SeriesMetadata.Pencillers, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.PencillerLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Publisher, updateSeriesMetadataDto.SeriesMetadata.Publishers, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.PublisherLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.Translator, updateSeriesMetadataDto.SeriesMetadata.Translators, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.TranslatorLocked = true);
|
||||||
|
UpdatePeopleList(PersonRole.CoverArtist, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, series, allPeople,
|
||||||
|
HandleAddPerson, () => series.Metadata.CoverArtistLocked = true);
|
||||||
|
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked) series.Metadata.AgeRatingLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.PublicationStatusLocked) series.Metadata.PublicationStatusLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.LanguageLocked) series.Metadata.LanguageLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.GenresLocked) series.Metadata.GenresLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.TagsLocked) series.Metadata.TagsLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.CharacterLocked) series.Metadata.CharacterLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.ColoristLocked) series.Metadata.ColoristLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.EditorLocked) series.Metadata.EditorLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.InkerLocked) series.Metadata.InkerLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.LettererLocked) series.Metadata.LettererLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.PencillerLocked) series.Metadata.PencillerLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.PublisherLocked) series.Metadata.PublisherLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked) series.Metadata.TranslatorLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked) series.Metadata.CoverArtistLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.WriterLocked) series.Metadata.WriterLocked = false;
|
||||||
|
if (!updateSeriesMetadataDto.SeriesMetadata.SummaryLocked) series.Metadata.SummaryLocked = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!_unitOfWork.HasChanges())
|
if (!_unitOfWork.HasChanges())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -184,6 +180,7 @@ public class SeriesService : ISeriesService
|
|||||||
private static void UpdateRelatedList(ICollection<CollectionTagDto> tags, Series series, IReadOnlyCollection<CollectionTag> allTags,
|
private static void UpdateRelatedList(ICollection<CollectionTagDto> tags, Series series, IReadOnlyCollection<CollectionTag> allTags,
|
||||||
Action<CollectionTag> handleAdd)
|
Action<CollectionTag> handleAdd)
|
||||||
{
|
{
|
||||||
|
if (tags == null) return;
|
||||||
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
||||||
var existingTags = series.Metadata.CollectionTags.ToList();
|
var existingTags = series.Metadata.CollectionTags.ToList();
|
||||||
foreach (var existing in existingTags)
|
foreach (var existing in existingTags)
|
||||||
@ -216,11 +213,13 @@ public class SeriesService : ISeriesService
|
|||||||
|
|
||||||
private static void UpdateGenreList(ICollection<GenreTagDto> tags, Series series, IReadOnlyCollection<Genre> allTags, Action<Genre> handleAdd, Action onModified)
|
private static void UpdateGenreList(ICollection<GenreTagDto> tags, Series series, IReadOnlyCollection<Genre> allTags, Action<Genre> handleAdd, Action onModified)
|
||||||
{
|
{
|
||||||
|
if (tags == null) return;
|
||||||
var isModified = false;
|
var isModified = false;
|
||||||
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
||||||
var existingTags = series.Metadata.Genres.ToList();
|
var existingTags = series.Metadata.Genres.ToList();
|
||||||
foreach (var existing in existingTags)
|
foreach (var existing in existingTags)
|
||||||
{
|
{
|
||||||
|
// NOTE: Why don't I use a NormalizedName here (outside of memory pressure from string creation)?
|
||||||
if (tags.SingleOrDefault(t => t.Id == existing.Id) == null)
|
if (tags.SingleOrDefault(t => t.Id == existing.Id) == null)
|
||||||
{
|
{
|
||||||
// Remove tag
|
// Remove tag
|
||||||
@ -232,10 +231,12 @@ public class SeriesService : ISeriesService
|
|||||||
// At this point, all tags that aren't in dto have been removed.
|
// At this point, all tags that aren't in dto have been removed.
|
||||||
foreach (var tagTitle in tags.Select(t => t.Title))
|
foreach (var tagTitle in tags.Select(t => t.Title))
|
||||||
{
|
{
|
||||||
var existingTag = allTags.SingleOrDefault(t => t.Title == tagTitle);
|
// This should be normalized name
|
||||||
|
var normalizedTitle = Parser.Parser.Normalize(tagTitle);
|
||||||
|
var existingTag = allTags.SingleOrDefault(t => t.NormalizedTitle == normalizedTitle);
|
||||||
if (existingTag != null)
|
if (existingTag != null)
|
||||||
{
|
{
|
||||||
if (series.Metadata.Genres.All(t => t.Title != tagTitle))
|
if (series.Metadata.Genres.All(t => t.NormalizedTitle != normalizedTitle))
|
||||||
{
|
{
|
||||||
handleAdd(existingTag);
|
handleAdd(existingTag);
|
||||||
isModified = true;
|
isModified = true;
|
||||||
@ -257,6 +258,8 @@ public class SeriesService : ISeriesService
|
|||||||
|
|
||||||
private static void UpdateTagList(ICollection<TagDto> tags, Series series, IReadOnlyCollection<Tag> allTags, Action<Tag> handleAdd, Action onModified)
|
private static void UpdateTagList(ICollection<TagDto> tags, Series series, IReadOnlyCollection<Tag> allTags, Action<Tag> handleAdd, Action onModified)
|
||||||
{
|
{
|
||||||
|
if (tags == null) return;
|
||||||
|
|
||||||
var isModified = false;
|
var isModified = false;
|
||||||
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
||||||
var existingTags = series.Metadata.Tags.ToList();
|
var existingTags = series.Metadata.Tags.ToList();
|
||||||
@ -300,6 +303,7 @@ public class SeriesService : ISeriesService
|
|||||||
private static void UpdatePeopleList(PersonRole role, ICollection<PersonDto> tags, Series series, IReadOnlyCollection<Person> allTags,
|
private static void UpdatePeopleList(PersonRole role, ICollection<PersonDto> tags, Series series, IReadOnlyCollection<Person> allTags,
|
||||||
Action<Person> handleAdd, Action onModified)
|
Action<Person> handleAdd, Action onModified)
|
||||||
{
|
{
|
||||||
|
if (tags == null) return;
|
||||||
var isModified = false;
|
var isModified = false;
|
||||||
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
// I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different
|
||||||
var existingTags = series.Metadata.People.Where(p => p.Role == role).ToList();
|
var existingTags = series.Metadata.People.Where(p => p.Role == role).ToList();
|
||||||
|
@ -523,7 +523,14 @@ public class ScannerService : IScannerService
|
|||||||
series.Format = parsedInfos[0].Format;
|
series.Format = parsedInfos[0].Format;
|
||||||
}
|
}
|
||||||
series.OriginalName ??= parsedInfos[0].Series;
|
series.OriginalName ??= parsedInfos[0].Series;
|
||||||
if (!series.SortNameLocked) series.SortName = parsedInfos[0].SeriesSort;
|
if (!series.SortNameLocked)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(parsedInfos[0].SeriesSort))
|
||||||
|
{
|
||||||
|
series.SortName = parsedInfos[0].SeriesSort;
|
||||||
|
}
|
||||||
|
series.SortName = series.Name;
|
||||||
|
}
|
||||||
|
|
||||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.LibraryScanProgressEvent(library.Name, ProgressEventType.Ended, series.Name));
|
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.LibraryScanProgressEvent(library.Name, ProgressEventType.Ended, series.Name));
|
||||||
|
|
||||||
|
@ -54,18 +54,16 @@ public class VersionUpdaterService : IVersionUpdaterService
|
|||||||
{
|
{
|
||||||
private readonly ILogger<VersionUpdaterService> _logger;
|
private readonly ILogger<VersionUpdaterService> _logger;
|
||||||
private readonly IEventHub _eventHub;
|
private readonly IEventHub _eventHub;
|
||||||
private readonly IPresenceTracker _tracker;
|
|
||||||
private readonly Markdown _markdown = new MarkdownDeep.Markdown();
|
private readonly Markdown _markdown = new MarkdownDeep.Markdown();
|
||||||
#pragma warning disable S1075
|
#pragma warning disable S1075
|
||||||
private const string GithubLatestReleasesUrl = "https://api.github.com/repos/Kareadita/Kavita/releases/latest";
|
private const string GithubLatestReleasesUrl = "https://api.github.com/repos/Kareadita/Kavita/releases/latest";
|
||||||
private const string GithubAllReleasesUrl = "https://api.github.com/repos/Kareadita/Kavita/releases";
|
private const string GithubAllReleasesUrl = "https://api.github.com/repos/Kareadita/Kavita/releases";
|
||||||
#pragma warning restore S1075
|
#pragma warning restore S1075
|
||||||
|
|
||||||
public VersionUpdaterService(ILogger<VersionUpdaterService> logger, IEventHub eventHub, IPresenceTracker tracker)
|
public VersionUpdaterService(ILogger<VersionUpdaterService> logger, IEventHub eventHub)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_eventHub = eventHub;
|
_eventHub = eventHub;
|
||||||
_tracker = tracker;
|
|
||||||
|
|
||||||
FlurlHttp.ConfigureClient(GithubLatestReleasesUrl, cli =>
|
FlurlHttp.ConfigureClient(GithubLatestReleasesUrl, cli =>
|
||||||
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
|
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
|
||||||
|
@ -36,9 +36,10 @@ public class EventHub : IEventHub
|
|||||||
if (onlyAdmins)
|
if (onlyAdmins)
|
||||||
{
|
{
|
||||||
var admins = await _presenceTracker.GetOnlineAdmins();
|
var admins = await _presenceTracker.GetOnlineAdmins();
|
||||||
_messageHub.Clients.Users(admins);
|
users = _messageHub.Clients.Users(admins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await users.SendAsync(method, message);
|
await users.SendAsync(method, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,7 +311,6 @@ namespace API.SignalR
|
|||||||
{
|
{
|
||||||
Name = CoverUpdate,
|
Name = CoverUpdate,
|
||||||
Title = "Updating Cover",
|
Title = "Updating Cover",
|
||||||
//SubTitle = series.Name, // TODO: Refactor this
|
|
||||||
Progress = ProgressType.None,
|
Progress = ProgressType.None,
|
||||||
Body = new
|
Body = new
|
||||||
{
|
{
|
||||||
|
@ -34,6 +34,6 @@ namespace API.SignalR
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// When event took place
|
/// When event took place
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime EventTime = DateTime.Now;
|
public readonly DateTime EventTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
<h4>
|
<h4>
|
||||||
<span id="member-name--{{idx}}">{{invite.username | titlecase}} </span>
|
<span id="member-name--{{idx}}">{{invite.username | titlecase}} </span>
|
||||||
<div class="float-end">
|
<div class="float-end">
|
||||||
<button class="btn btn-danger me-2" (click)="deleteUser(invite)">Cancel</button>
|
<button class="btn btn-danger btn-sm me-2" (click)="deleteUser(invite)">Cancel</button>
|
||||||
<button class="btn btn-secondary me-2" (click)="resendEmail(invite)">Resend</button>
|
<button class="btn btn-secondary btn-sm" (click)="resendEmail(invite)">Resend</button>
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user