Added the rest of the fields, no migration yet, needs unit tests.
This commit is contained in:
parent
7c692a1b46
commit
c4419dcf28
11 changed files with 324 additions and 41 deletions
|
|
@ -43,6 +43,29 @@ public class MetadataSettingsDto
|
|||
/// </summary>
|
||||
public bool EnableCoverImage { get; set; }
|
||||
|
||||
#region Chapter Metadata
|
||||
/// <summary>
|
||||
/// Allow Summary to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterSummary { get; set; }
|
||||
/// <summary>
|
||||
/// Allow Release Date to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterReleaseDate { get; set; }
|
||||
/// <summary>
|
||||
/// Allow Title to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterTitle { get; set; }
|
||||
/// <summary>
|
||||
/// Allow Publisher to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterPublisher { get; set; }
|
||||
/// <summary>
|
||||
/// Allow setting the cover image for the Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterCoverImage { get; set; }
|
||||
#endregion
|
||||
|
||||
// Need to handle the Genre/tags stuff
|
||||
public bool EnableGenres { get; set; } = true;
|
||||
public bool EnableTags { get; set; } = true;
|
||||
|
|
|
|||
|
|
@ -312,6 +312,11 @@ public static class Seed
|
|||
EnableLocalizedName = false,
|
||||
FirstLastPeopleNaming = true,
|
||||
EnableCoverImage = true,
|
||||
EnableChapterTitle = false,
|
||||
EnableChapterSummary = true,
|
||||
EnableChapterPublisher = true,
|
||||
EnableChapterCoverImage = false,
|
||||
EnableChapterReleaseDate = true,
|
||||
PersonRoles = [PersonRole.Writer, PersonRole.CoverArtist, PersonRole.Character]
|
||||
};
|
||||
await context.MetadataSettings.AddAsync(existing);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
/// </summary>
|
||||
public enum MetadataSettingField
|
||||
{
|
||||
#region Series Metadata
|
||||
Summary = 1,
|
||||
PublicationStatus = 2,
|
||||
StartDate = 3,
|
||||
|
|
@ -13,5 +14,18 @@ public enum MetadataSettingField
|
|||
LocalizedName = 6,
|
||||
Covers = 7,
|
||||
AgeRating = 8,
|
||||
People = 9
|
||||
People = 9,
|
||||
#endregion
|
||||
|
||||
#region Chapter Metadata
|
||||
|
||||
ChapterTitle = 10,
|
||||
ChapterSummary = 11,
|
||||
ChapterReleaseDate = 12,
|
||||
ChapterPublisher = 13,
|
||||
ChapterCovers = 14,
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ public class MetadataSettings
|
|||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
#region Series Metadata
|
||||
|
||||
/// <summary>
|
||||
/// Allow the Summary to be written
|
||||
/// </summary>
|
||||
|
|
@ -42,6 +44,30 @@ public class MetadataSettings
|
|||
/// Allow setting the cover image
|
||||
/// </summary>
|
||||
public bool EnableCoverImage { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Chapter Metadata
|
||||
/// <summary>
|
||||
/// Allow Summary to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterSummary { get; set; }
|
||||
/// <summary>
|
||||
/// Allow Release Date to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterReleaseDate { get; set; }
|
||||
/// <summary>
|
||||
/// Allow Title to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterTitle { get; set; }
|
||||
/// <summary>
|
||||
/// Allow Publisher to be set within Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterPublisher { get; set; }
|
||||
/// <summary>
|
||||
/// Allow setting the cover image for the Chapter/Issue
|
||||
/// </summary>
|
||||
public bool EnableChapterCoverImage { get; set; }
|
||||
#endregion
|
||||
|
||||
// Need to handle the Genre/tags stuff
|
||||
public bool EnableGenres { get; set; } = true;
|
||||
|
|
|
|||
|
|
@ -10,5 +10,14 @@ public class ChapterPeople
|
|||
public int PersonId { get; set; }
|
||||
public virtual Person Person { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source of this connection. If not Kavita, this implies Metadata Download linked this and it can be removed between matches
|
||||
/// </summary>
|
||||
public bool KavitaPlusConnection { get; set; }
|
||||
/// <summary>
|
||||
/// A weight that allows lower numbers to sort first
|
||||
/// </summary>
|
||||
public int OrderWeight { get; set; }
|
||||
|
||||
public required PersonRole Role { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1075,51 +1075,171 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
_logger.LogDebug("Updating {ChapterNumber} with metadata", chapter.Range);
|
||||
|
||||
// Write the metadata
|
||||
if (!string.IsNullOrEmpty(potentialMatch.Title) && !potentialMatch.Title.Contains(series.Name))
|
||||
{
|
||||
chapter.Title = potentialMatch.Title;
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
madeModification = true;
|
||||
}
|
||||
madeModification = UpdateChapterTitle(chapter, settings, potentialMatch.Title, series.Name) || madeModification;
|
||||
madeModification = UpdateChapterSummary(chapter, settings, potentialMatch.Summary) || madeModification;
|
||||
madeModification = UpdateChapterReleaseDate(chapter, settings, potentialMatch.ReleaseDate) || madeModification;
|
||||
madeModification = await UpdateChapterPublisher(chapter, settings, potentialMatch.Publisher) || madeModification;
|
||||
|
||||
if (!chapter.SummaryLocked && string.IsNullOrEmpty(chapter.Summary) & !string.IsNullOrEmpty(potentialMatch.Summary))
|
||||
{
|
||||
chapter.Summary = potentialMatch.Summary;
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
madeModification = true;
|
||||
}
|
||||
madeModification = await UpdateChapterPeople(chapter, settings, PersonRole.CoverArtist, potentialMatch.Artists) || madeModification;
|
||||
madeModification = await UpdateChapterPeople(chapter, settings, PersonRole.Writer, potentialMatch.Writers) || madeModification;
|
||||
|
||||
// ReleaseDate
|
||||
// Cover Image
|
||||
madeModification = await UpdateChapterCoverImage(chapter, settings, potentialMatch.CoverImageUrl) || madeModification;
|
||||
|
||||
|
||||
// Update People (Writer/Artist/Publisher)
|
||||
if (!chapter.PublisherLocked)
|
||||
{
|
||||
// Update publisher
|
||||
}
|
||||
|
||||
//var artists = potentialMatch.Artists.Select(p => new PersonDto())
|
||||
|
||||
// await SeriesService.HandlePeopleUpdateAsync(series.Metadata, artists, PersonRole.CoverArtist, _unitOfWork);
|
||||
//
|
||||
// foreach (var person in series.Metadata.People.Where(p => p.Role == PersonRole.CoverArtist))
|
||||
// {
|
||||
// var meta = upstreamArtists.FirstOrDefault(c => c.Name == person.Person.Name);
|
||||
// person.OrderWeight = 0;
|
||||
// if (meta != null)
|
||||
// {
|
||||
// person.KavitaPlusConnection = true;
|
||||
// }
|
||||
// }
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
await _unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
|
||||
return madeModification;
|
||||
}
|
||||
|
||||
_unitOfWork.SeriesRepository.Update(series);
|
||||
|
||||
private static bool UpdateChapterSummary(Chapter chapter, MetadataSettingsDto settings, string? summary)
|
||||
{
|
||||
if (!settings.EnableChapterSummary) return false;
|
||||
|
||||
if (string.IsNullOrEmpty(summary)) return false;
|
||||
|
||||
if (chapter.SummaryLocked && !settings.HasOverride(MetadataSettingField.ChapterSummary))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(summary) && !settings.HasOverride(MetadataSettingField.ChapterSummary))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
chapter.Summary = StringHelper.RemoveSourceInDescription(StringHelper.SquashBreaklines(summary));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool UpdateChapterTitle(Chapter chapter, MetadataSettingsDto settings, string? title, string seriesName)
|
||||
{
|
||||
if (!settings.EnableChapterTitle) return false;
|
||||
|
||||
if (string.IsNullOrEmpty(title)) return false;
|
||||
|
||||
if (chapter.TitleNameLocked && !settings.HasOverride(MetadataSettingField.ChapterTitle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!title.Contains(seriesName) && !settings.HasOverride(MetadataSettingField.ChapterTitle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
chapter.TitleName = title;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool UpdateChapterReleaseDate(Chapter chapter, MetadataSettingsDto settings, DateTime? releaseDate)
|
||||
{
|
||||
if (!settings.EnableChapterReleaseDate) return false;
|
||||
|
||||
if (releaseDate == null || releaseDate == DateTime.MinValue) return false;
|
||||
|
||||
if (chapter.ReleaseDateLocked && !settings.HasOverride(MetadataSettingField.ChapterReleaseDate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!settings.HasOverride(MetadataSettingField.ChapterReleaseDate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
chapter.ReleaseDate = releaseDate.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateChapterPublisher(Chapter chapter, MetadataSettingsDto settings, string? publisher)
|
||||
{
|
||||
if (!settings.EnableChapterPublisher) return false;
|
||||
|
||||
if (string.IsNullOrEmpty(publisher)) return false;
|
||||
|
||||
if (chapter.PublisherLocked && !settings.HasOverride(MetadataSettingField.ChapterPublisher))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(publisher) && !settings.HasOverride(MetadataSettingField.ChapterPublisher))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await UpdateChapterPeople(chapter, settings, PersonRole.Publisher, [publisher]);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateChapterCoverImage(Chapter chapter, MetadataSettingsDto settings, string? coverUrl)
|
||||
{
|
||||
if (!settings.EnableChapterCoverImage) return false;
|
||||
|
||||
if (string.IsNullOrEmpty(coverUrl)) return false;
|
||||
|
||||
if (chapter.CoverImageLocked && !settings.HasOverride(MetadataSettingField.ChapterCovers))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(coverUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await DownloadChapterCovers(chapter, coverUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateChapterPeople(Chapter chapter, MetadataSettingsDto settings, PersonRole role, IList<string>? staff)
|
||||
{
|
||||
if (!settings.EnablePeople) return false;
|
||||
|
||||
if (staff?.Count == 0) return false;
|
||||
|
||||
if (chapter.CoverArtistLocked && !settings.HasOverride(MetadataSettingField.People))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!settings.IsPersonAllowed(role))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
chapter.People ??= [];
|
||||
var artists = staff
|
||||
.Select(w => new PersonDto()
|
||||
{
|
||||
Name = w,
|
||||
})
|
||||
.Concat(chapter.People
|
||||
.Where(p => p.Role == role)
|
||||
.Where(p => !p.KavitaPlusConnection)
|
||||
.Select(p => _mapper.Map<PersonDto>(p.Person))
|
||||
)
|
||||
.DistinctBy(p => Parser.Normalize(p.Name))
|
||||
.ToList();
|
||||
|
||||
await PersonHelper.UpdateChapterPeopleAsync(chapter, staff, role, _unitOfWork);
|
||||
|
||||
foreach (var person in chapter.People.Where(p => p.Role == role))
|
||||
{
|
||||
var meta = artists.FirstOrDefault(c => c.Name == person.Person.Name);
|
||||
person.OrderWeight = 0;
|
||||
|
||||
if (meta != null)
|
||||
{
|
||||
person.KavitaPlusConnection = true;
|
||||
}
|
||||
}
|
||||
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
return madeModification;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateCoverImage(Series series, MetadataSettingsDto settings, ExternalSeriesDetailDto externalMetadata)
|
||||
|
|
@ -1244,6 +1364,18 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
}
|
||||
}
|
||||
|
||||
private async Task DownloadChapterCovers(Chapter chapter, string coverUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _coverDbService.SetChapterCoverByUrl(chapter, coverUrl, false, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an exception downloading cover image for Chapter {ChapterName} ({SeriesId})", chapter.Range, chapter.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadAndSetPersonCovers(List<SeriesStaffDto> people)
|
||||
{
|
||||
foreach (var staff in people)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public interface ICoverDbService
|
|||
Task<string?> DownloadPersonImageAsync(Person person, EncodeFormat encodeFormat, string url);
|
||||
Task SetPersonCoverByUrl(Person person, string url, bool fromBase64 = true, bool checkNoImagePlaceholder = false);
|
||||
Task SetSeriesCoverByUrl(Series series, string url, bool fromBase64 = true, bool chooseBetterImage = false);
|
||||
Task SetChapterCoverByUrl(Chapter chapter, string url, bool fromBase64 = true, bool chooseBetterImage = false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -580,6 +581,51 @@ public class CoverDbService : ICoverDbService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task SetChapterCoverByUrl(Chapter chapter, string url, bool fromBase64 = true, bool chooseBetterImage = false)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
var filePath = await CreateThumbnail(url, $"{ImageService.GetChapterFormat(chapter.Id, chapter.VolumeId)}", fromBase64);
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
// Additional check to see if downloaded image is similar and we have a higher resolution
|
||||
if (chooseBetterImage && !string.IsNullOrEmpty(chapter.CoverImage))
|
||||
{
|
||||
try
|
||||
{
|
||||
var betterImage = Path.Join(_directoryService.CoverImageDirectory, chapter.CoverImage)
|
||||
.GetBetterImage(Path.Join(_directoryService.CoverImageDirectory, filePath))!;
|
||||
filePath = Path.GetFileName(betterImage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an issue trying to choose a better cover image for Chapter: {FileName} ({ChapterId})", chapter.Range, chapter.Id);
|
||||
}
|
||||
}
|
||||
|
||||
chapter.CoverImage = filePath;
|
||||
chapter.CoverImageLocked = true;
|
||||
_imageService.UpdateColorScape(chapter);
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
chapter.CoverImage = null;
|
||||
chapter.CoverImageLocked = false;
|
||||
_imageService.UpdateColorScape(chapter);
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
}
|
||||
|
||||
if (_unitOfWork.HasChanges())
|
||||
{
|
||||
await _unitOfWork.CommitAsync();
|
||||
await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
|
||||
MessageFactory.CoverUpdateEvent(chapter.Id, MessageFactoryEntityTypes.Chapter), false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> CreateThumbnail(string url, string filename, bool fromBase64 = true)
|
||||
{
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {MetadataSettingField} from "../admin/_models/metadata-setting-field";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
|
|
@ -10,9 +10,19 @@ export class MetadataSettingFiledPipe implements PipeTransform {
|
|||
|
||||
transform(value: MetadataSettingField): string {
|
||||
switch (value) {
|
||||
case MetadataSettingField.ChapterTitle:
|
||||
return translate('metadata-setting-field-pipe.title');
|
||||
case MetadataSettingField.ChapterSummary:
|
||||
return translate('metadata-setting-field-pipe.summary');
|
||||
case MetadataSettingField.ChapterReleaseDate:
|
||||
return translate('metadata-setting-field-pipe.release-date');
|
||||
case MetadataSettingField.ChapterPublisher:
|
||||
return translate('metadata-setting-field-pipe.publisher');
|
||||
case MetadataSettingField.ChapterCovers:
|
||||
return translate('metadata-setting-field-pipe.covers');
|
||||
case MetadataSettingField.AgeRating:
|
||||
return translate('metadata-setting-field-pipe.age-rating');
|
||||
case MetadataSettingField.People:
|
||||
case MetadataSettingField.People:
|
||||
return translate('metadata-setting-field-pipe.people');
|
||||
case MetadataSettingField.Covers:
|
||||
return translate('metadata-setting-field-pipe.covers');
|
||||
|
|
|
|||
|
|
@ -7,7 +7,14 @@ export enum MetadataSettingField {
|
|||
LocalizedName = 6,
|
||||
Covers = 7,
|
||||
AgeRating = 8,
|
||||
People = 9
|
||||
People = 9,
|
||||
|
||||
// Chapter fields
|
||||
ChapterTitle = 10,
|
||||
ChapterSummary = 11,
|
||||
ChapterReleaseDate = 12,
|
||||
ChapterPublisher = 13,
|
||||
ChapterCovers = 14,
|
||||
}
|
||||
|
||||
export const allMetadataSettingField = Object.keys(MetadataSettingField)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ export interface MetadataSettings {
|
|||
enableStartDate: boolean;
|
||||
enableCoverImage: boolean;
|
||||
enableLocalizedName: boolean;
|
||||
|
||||
enableChapterSummary: boolean;
|
||||
enableChapterReleaseDate: boolean;
|
||||
enableChapterTitle: boolean;
|
||||
enableChapterPublisher: boolean;
|
||||
enableChapterCoverImage: boolean;
|
||||
|
||||
|
||||
enableGenres: boolean;
|
||||
enableTags: boolean;
|
||||
firstLastPeopleNaming: boolean;
|
||||
|
|
|
|||
|
|
@ -2685,7 +2685,10 @@
|
|||
"start-date": "{{manage-metadata-settings.enable-start-date-label}}",
|
||||
"genres": "{{metadata-fields.genres-title}}",
|
||||
"tags": "{{metadata-fields.tags-title}}",
|
||||
"localized-name": "{{edit-series-modal.localized-name-label}}"
|
||||
"localized-name": "{{edit-series-modal.localized-name-label}}",
|
||||
"release-date": "Release Date",
|
||||
"publisher": "{{person-role-pipe.publisher}}",
|
||||
"title": "Title"
|
||||
},
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue