Angular 19 + Even more bugfixes (#3675)

This commit is contained in:
Joe Milazzo 2025-03-25 16:43:41 -05:00 committed by GitHub
parent 535165c445
commit cc3ae7f472
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
249 changed files with 4400 additions and 4315 deletions

View file

@ -235,131 +235,121 @@ public class ChapterController : BaseApiController
#region Genres
if (dto.Genres is {Count: > 0})
{
chapter.Genres ??= new List<Genre>();
await GenreHelper.UpdateChapterGenres(chapter, dto.Genres.Select(t => t.Title), _unitOfWork);
}
chapter.Genres ??= [];
await GenreHelper.UpdateChapterGenres(chapter, dto.Genres.Select(t => t.Title), _unitOfWork);
#endregion
#region Tags
if (dto.Tags is {Count: > 0})
{
chapter.Tags ??= new List<Tag>();
await TagHelper.UpdateChapterTags(chapter, dto.Tags.Select(t => t.Title), _unitOfWork);
}
chapter.Tags ??= [];
await TagHelper.UpdateChapterTags(chapter, dto.Tags.Select(t => t.Title), _unitOfWork);
#endregion
#region People
if (PersonHelper.HasAnyPeople(dto))
{
chapter.People ??= new List<ChapterPeople>();
chapter.People ??= [];
// Update writers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Writers.Select(p => p.Name).ToList(),
PersonRole.Writer,
_unitOfWork
);
// Update writers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Writers.Select(p => p.Name).ToList(),
PersonRole.Writer,
_unitOfWork
);
// Update characters
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Characters.Select(p => p.Name).ToList(),
PersonRole.Character,
_unitOfWork
);
// Update characters
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Characters.Select(p => p.Name).ToList(),
PersonRole.Character,
_unitOfWork
);
// Update pencillers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Pencillers.Select(p => p.Name).ToList(),
PersonRole.Penciller,
_unitOfWork
);
// Update pencillers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Pencillers.Select(p => p.Name).ToList(),
PersonRole.Penciller,
_unitOfWork
);
// Update inkers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Inkers.Select(p => p.Name).ToList(),
PersonRole.Inker,
_unitOfWork
);
// Update inkers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Inkers.Select(p => p.Name).ToList(),
PersonRole.Inker,
_unitOfWork
);
// Update colorists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Colorists.Select(p => p.Name).ToList(),
PersonRole.Colorist,
_unitOfWork
);
// Update colorists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Colorists.Select(p => p.Name).ToList(),
PersonRole.Colorist,
_unitOfWork
);
// Update letterers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Letterers.Select(p => p.Name).ToList(),
PersonRole.Letterer,
_unitOfWork
);
// Update letterers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Letterers.Select(p => p.Name).ToList(),
PersonRole.Letterer,
_unitOfWork
);
// Update cover artists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.CoverArtists.Select(p => p.Name).ToList(),
PersonRole.CoverArtist,
_unitOfWork
);
// Update cover artists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.CoverArtists.Select(p => p.Name).ToList(),
PersonRole.CoverArtist,
_unitOfWork
);
// Update editors
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Editors.Select(p => p.Name).ToList(),
PersonRole.Editor,
_unitOfWork
);
// Update editors
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Editors.Select(p => p.Name).ToList(),
PersonRole.Editor,
_unitOfWork
);
// Update publishers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Publishers.Select(p => p.Name).ToList(),
PersonRole.Publisher,
_unitOfWork
);
// Update publishers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Publishers.Select(p => p.Name).ToList(),
PersonRole.Publisher,
_unitOfWork
);
// Update translators
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Translators.Select(p => p.Name).ToList(),
PersonRole.Translator,
_unitOfWork
);
// Update translators
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Translators.Select(p => p.Name).ToList(),
PersonRole.Translator,
_unitOfWork
);
// Update imprints
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Imprints.Select(p => p.Name).ToList(),
PersonRole.Imprint,
_unitOfWork
);
// Update imprints
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Imprints.Select(p => p.Name).ToList(),
PersonRole.Imprint,
_unitOfWork
);
// Update teams
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Teams.Select(p => p.Name).ToList(),
PersonRole.Team,
_unitOfWork
);
// Update teams
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Teams.Select(p => p.Name).ToList(),
PersonRole.Team,
_unitOfWork
);
// Update locations
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Locations.Select(p => p.Name).ToList(),
PersonRole.Location,
_unitOfWork
);
}
// Update locations
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Locations.Select(p => p.Name).ToList(),
PersonRole.Location,
_unitOfWork
);
#endregion
#region Locks

View file

@ -34,29 +34,26 @@ public class SettingsController : BaseApiController
{
private readonly ILogger<SettingsController> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ITaskScheduler _taskScheduler;
private readonly IDirectoryService _directoryService;
private readonly IMapper _mapper;
private readonly IEmailService _emailService;
private readonly ILibraryWatcher _libraryWatcher;
private readonly ILocalizationService _localizationService;
private readonly ISettingsService _settingsService;
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler,
IDirectoryService directoryService, IMapper mapper, IEmailService emailService, ILibraryWatcher libraryWatcher,
ILocalizationService localizationService, ISettingsService settingsService)
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, IMapper mapper,
IEmailService emailService, ILocalizationService localizationService, ISettingsService settingsService)
{
_logger = logger;
_unitOfWork = unitOfWork;
_taskScheduler = taskScheduler;
_directoryService = directoryService;
_mapper = mapper;
_emailService = emailService;
_libraryWatcher = libraryWatcher;
_localizationService = localizationService;
_settingsService = settingsService;
}
/// <summary>
/// Returns the base url for this instance (if set)
/// </summary>
/// <returns></returns>
[HttpGet("base-url")]
public async Task<ActionResult<string>> GetBaseUrl()
{

View file

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers;
#nullable enable
public class VolumeController : BaseApiController
{
@ -23,13 +24,15 @@ public class VolumeController : BaseApiController
_eventHub = eventHub;
}
/// <summary>
/// Returns the appropriate Volume
/// </summary>
/// <param name="volumeId"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<VolumeDto>> GetVolume(int volumeId)
public async Task<ActionResult<VolumeDto?>> GetVolume(int volumeId)
{
var volume =
await _unitOfWork.VolumeRepository.GetVolumeDtoAsync(volumeId, User.GetUserId());
return Ok(volume);
return Ok(await _unitOfWork.VolumeRepository.GetVolumeDtoAsync(volumeId, User.GetUserId()));
}
[Authorize(Policy = "RequireAdminRole")]
@ -39,7 +42,7 @@ public class VolumeController : BaseApiController
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(volumeId,
VolumeIncludes.Chapters | VolumeIncludes.People | VolumeIncludes.Tags);
if (volume == null)
return BadRequest(_localizationService.Translate(User.GetUserId(), "chapter-doesnt-exist"));
return BadRequest(_localizationService.Translate(User.GetUserId(), "volume-doesnt-exist"));
_unitOfWork.VolumeRepository.Remove(volume);

View file

@ -1,4 +1,5 @@
using System;
using System.Text.Json.Serialization;
using API.Entities.Enums;
using API.Services;
@ -45,6 +46,7 @@ public class ServerSettingDto
/// <summary>
/// Represents a unique Id to this Kavita installation. Only used in Stats to identify unique installs.
/// </summary>
public string InstallId { get; set; } = default!;
/// <summary>
/// The format that should be used when saving media for Kavita

View file

@ -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");
}
}

View file

@ -193,6 +193,7 @@ public class AppUserProgressRepository : IAppUserProgressRepository
.Where(p => p.chapter.MaxNumber != Parser.SpecialVolumeNumber)
.Select(p => p.chapter.Volume.MaxNumber)
.ToListAsync();
return list.Count == 0 ? 0 : list.DefaultIfEmpty().Max();
}

View file

@ -9,6 +9,8 @@ public static class OrderableHelper
{
public static void ReorderItems(List<AppUserDashboardStream> items, int itemId, int toPosition)
{
if (toPosition < 0) throw new ArgumentException("toPosition cannot be less than 0");
var item = items.Find(r => r.Id == itemId);
if (item != null)
{
@ -24,6 +26,8 @@ public static class OrderableHelper
public static void ReorderItems(List<AppUserSideNavStream> items, int itemId, int toPosition)
{
if (toPosition < 0) throw new ArgumentException("toPosition cannot be less than 0");
var item = items.Find(r => r.Id == itemId);
if (item != null && toPosition < items.Count)
{
@ -48,10 +52,15 @@ public static class OrderableHelper
public static void ReorderItems(List<ReadingListItem> items, int readingListItemId, int toPosition)
{
if (toPosition < 0) throw new ArgumentException("toPosition cannot be less than 0");
var item = items.Find(r => r.Id == readingListItemId);
if (item != null)
{
items.Remove(item);
// Ensure toPosition is within the new list bounds
toPosition = Math.Min(toPosition, items.Count);
items.Insert(toPosition, item);
}

View file

@ -104,7 +104,7 @@ public static class TagHelper
public static void UpdateTagList(ICollection<TagDto>? existingDbTags, Series series, IReadOnlyCollection<Tag> newTags, Action<Tag> handleAdd, Action onModified)
{
UpdateTagList(existingDbTags.Select(t => t.Title).ToList(), series, newTags, handleAdd, onModified);
UpdateTagList((existingDbTags ?? []).Select(t => t.Title).ToList(), series, newTags, handleAdd, onModified);
}
public static void UpdateTagList(ICollection<string>? existingDbTags, Series series, IReadOnlyCollection<Tag> newTags, Action<Tag> handleAdd, Action onModified)
@ -155,5 +155,4 @@ public static class TagHelper
onModified();
}
}
}

View file

@ -407,6 +407,12 @@ public class ScrobblingService : IScrobblingService
Format = series.Library.Type.ConvertToPlusMediaFormat(series.Format),
};
if (evt.VolumeNumber is Parser.SpecialVolumeNumber)
{
// We don't process Specials because they will never match on AniList
return;
}
_unitOfWork.ScrobbleRepository.Attach(evt);
await _unitOfWork.CommitAsync();
_logger.LogDebug("Added Scrobbling Read update on {SeriesName} - Volume: {VolumeNumber} Chapter: {ChapterNumber} for User: {UserId}", series.Name, evt.VolumeNumber, evt.ChapterNumber, userId);

View file

@ -211,90 +211,87 @@ public class SeriesService : ISeriesService
}
}
// Update people and locks
if (updateSeriesMetadataDto.SeriesMetadata != null)
{
if (PersonHelper.HasAnyPeople(updateSeriesMetadataDto.SeriesMetadata))
series.Metadata.People ??= [];
// Writers
if (!series.Metadata.WriterLocked || !updateSeriesMetadataDto.SeriesMetadata.WriterLocked)
{
series.Metadata.People ??= [];
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Writers, PersonRole.Writer, _unitOfWork);
}
// Writers
if (!series.Metadata.WriterLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Writers, PersonRole.Writer, _unitOfWork);
}
// Cover Artists
if (!series.Metadata.CoverArtistLocked || !updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, PersonRole.CoverArtist, _unitOfWork);
}
// Cover Artists
if (!series.Metadata.CoverArtistLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, PersonRole.CoverArtist, _unitOfWork);
}
// Colorists
if (!series.Metadata.ColoristLocked || !updateSeriesMetadataDto.SeriesMetadata.ColoristLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Colorists, PersonRole.Colorist, _unitOfWork);
}
// Colorists
if (!series.Metadata.ColoristLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Colorists, PersonRole.Colorist, _unitOfWork);
}
// Editors
if (!series.Metadata.EditorLocked || !updateSeriesMetadataDto.SeriesMetadata.EditorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Editors, PersonRole.Editor, _unitOfWork);
}
// Editors
if (!series.Metadata.EditorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Editors, PersonRole.Editor, _unitOfWork);
}
// Inkers
if (!series.Metadata.InkerLocked || !updateSeriesMetadataDto.SeriesMetadata.InkerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Inkers, PersonRole.Inker, _unitOfWork);
}
// Inkers
if (!series.Metadata.InkerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Inkers, PersonRole.Inker, _unitOfWork);
}
// Letterers
if (!series.Metadata.LettererLocked || !updateSeriesMetadataDto.SeriesMetadata.LettererLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Letterers, PersonRole.Letterer, _unitOfWork);
}
// Letterers
if (!series.Metadata.LettererLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Letterers, PersonRole.Letterer, _unitOfWork);
}
// Pencillers
if (!series.Metadata.PencillerLocked || !updateSeriesMetadataDto.SeriesMetadata.PencillerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Pencillers, PersonRole.Penciller, _unitOfWork);
}
// Pencillers
if (!series.Metadata.PencillerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Pencillers, PersonRole.Penciller, _unitOfWork);
}
// Publishers
if (!series.Metadata.PublisherLocked || !updateSeriesMetadataDto.SeriesMetadata.PublisherLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Publishers, PersonRole.Publisher, _unitOfWork);
}
// Publishers
if (!series.Metadata.PublisherLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Publishers, PersonRole.Publisher, _unitOfWork);
}
// Imprints
if (!series.Metadata.ImprintLocked || !updateSeriesMetadataDto.SeriesMetadata.ImprintLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Imprints, PersonRole.Imprint, _unitOfWork);
}
// Imprints
if (!series.Metadata.ImprintLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Imprints, PersonRole.Imprint, _unitOfWork);
}
// Teams
if (!series.Metadata.TeamLocked || !updateSeriesMetadataDto.SeriesMetadata.TeamLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Teams, PersonRole.Team, _unitOfWork);
}
// Teams
if (!series.Metadata.TeamLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Teams, PersonRole.Team, _unitOfWork);
}
// Locations
if (!series.Metadata.LocationLocked || !updateSeriesMetadataDto.SeriesMetadata.LocationLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Locations, PersonRole.Location, _unitOfWork);
}
// Locations
if (!series.Metadata.LocationLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Locations, PersonRole.Location, _unitOfWork);
}
// Translators
if (!series.Metadata.TranslatorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Translators, PersonRole.Translator, _unitOfWork);
}
// Characters
if (!series.Metadata.CharacterLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Characters, PersonRole.Character, _unitOfWork);
}
// Translators
if (!series.Metadata.TranslatorLocked || !updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Translators, PersonRole.Translator, _unitOfWork);
}
// Characters
if (!series.Metadata.CharacterLocked || !updateSeriesMetadataDto.SeriesMetadata.CharacterLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Characters, PersonRole.Character, _unitOfWork);
}
series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked;

View file

@ -123,7 +123,10 @@ public class StreamService : IStreamService
AppUserIncludes.DashboardStreams);
var stream = user?.DashboardStreams.FirstOrDefault(d => d.Id == dto.Id);
if (stream == null)
{
throw new KavitaException(await _localizationService.Translate(userId, "dashboard-stream-doesnt-exist"));
}
if (stream.Order == dto.ToPosition) return;
var list = user!.DashboardStreams.OrderBy(s => s.Order).ToList();

View file

@ -547,6 +547,12 @@ public partial class VersionUpdaterService : IVersionUpdaterService
// Remove "Fixed:", "Added:" etc. if present
var cleanedItem = CleanSectionItem(trimmedLine);
// Some sections like API/Developer/Removed don't have the title repeated, so we need to check for an additional cleaning
if (cleanedItem.StartsWith("- "))
{
cleanedItem = trimmedLine.Substring(2);
}
// Only add non-empty items
if (!string.IsNullOrWhiteSpace(cleanedItem))
{

View file

@ -285,6 +285,9 @@ public class Startup
await ManualMigrateNeedsManualMatch.Migrate(dataContext, logger);
await MigrateProgressExportForV085.Migrate(dataContext, directoryService, logger);
// v0.8.6
await ManualMigrateScrobbleSpecials.Migrate(dataContext, logger);
// Update the version in the DB after all migrations are run
var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);
installVersion.Value = BuildInfo.Version.ToString();