Fixed up updating issue data

This commit is contained in:
Joseph Milazzo 2025-04-15 15:46:10 -05:00
parent 31819d7723
commit 3f1212a84f
12 changed files with 3705 additions and 27 deletions

File diff suppressed because it is too large Load diff

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

View file

@ -15,7 +15,7 @@ namespace API.Data.Migrations
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); modelBuilder.HasAnnotation("ProductVersion", "9.0.4");
modelBuilder.Entity("API.Entities.AppRole", b => modelBuilder.Entity("API.Entities.AppRole", b =>
{ {
@ -1429,6 +1429,9 @@ namespace API.Data.Migrations
b.Property<int>("AverageExternalRating") b.Property<int>("AverageExternalRating")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("CbrId")
.HasColumnType("INTEGER");
b.Property<string>("GoogleBooksId") b.Property<string>("GoogleBooksId")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -1645,6 +1648,21 @@ namespace API.Data.Migrations
b.Property<string>("Blacklist") b.Property<string>("Blacklist")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool>("EnableChapterCoverImage")
.HasColumnType("INTEGER");
b.Property<bool>("EnableChapterPublisher")
.HasColumnType("INTEGER");
b.Property<bool>("EnableChapterReleaseDate")
.HasColumnType("INTEGER");
b.Property<bool>("EnableChapterSummary")
.HasColumnType("INTEGER");
b.Property<bool>("EnableChapterTitle")
.HasColumnType("INTEGER");
b.Property<bool>("EnableCoverImage") b.Property<bool>("EnableCoverImage")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("INTEGER") .HasColumnType("INTEGER")
@ -1707,6 +1725,12 @@ namespace API.Data.Migrations
b.Property<int>("Role") b.Property<int>("Role")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool>("KavitaPlusConnection")
.HasColumnType("INTEGER");
b.Property<int>("OrderWeight")
.HasColumnType("INTEGER");
b.HasKey("ChapterId", "PersonId", "Role"); b.HasKey("ChapterId", "PersonId", "Role");
b.HasIndex("PersonId"); b.HasIndex("PersonId");

View file

@ -306,6 +306,8 @@ public class ChapterRepository : IChapterRepository
.Where(c => c.Volume.SeriesId == seriesId) .Where(c => c.Volume.SeriesId == seriesId)
.OrderBy(c => c.SortOrder) .OrderBy(c => c.SortOrder)
.Include(c => c.Volume) .Include(c => c.Volume)
.Include(c => c.People)
.ThenInclude(cp => cp.Person)
.ToListAsync(); .ToListAsync();
} }
} }

View file

@ -234,4 +234,25 @@ public class Chapter : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
PrimaryColor = string.Empty; PrimaryColor = string.Empty;
SecondaryColor = string.Empty; SecondaryColor = string.Empty;
} }
public bool IsPersonRoleLocked(PersonRole role)
{
return role switch
{
PersonRole.Character => CharacterLocked,
PersonRole.Writer => WriterLocked,
PersonRole.Penciller => PencillerLocked,
PersonRole.Inker => InkerLocked,
PersonRole.Colorist => ColoristLocked,
PersonRole.Letterer => LettererLocked,
PersonRole.CoverArtist => CoverArtistLocked,
PersonRole.Editor => EditorLocked,
PersonRole.Publisher => PublisherLocked,
PersonRole.Translator => TranslatorLocked,
PersonRole.Imprint => ImprintLocked,
PersonRole.Team => TeamLocked,
PersonRole.Location => LocationLocked,
_ => throw new ArgumentOutOfRangeException(nameof(role), role, null)
};
}
} }

View file

@ -1199,18 +1199,18 @@ public class ExternalMetadataService : IExternalMetadataService
if (staff?.Count == 0) return false; if (staff?.Count == 0) return false;
if (chapter.CoverArtistLocked && !settings.HasOverride(MetadataSettingField.People)) if (chapter.IsPersonRoleLocked(role) && !settings.HasOverride(MetadataSettingField.People))
{ {
return false; return false;
} }
if (!settings.IsPersonAllowed(role)) if (!settings.IsPersonAllowed(role) && role != PersonRole.Publisher)
{ {
return false; return false;
} }
chapter.People ??= []; chapter.People ??= [];
var artists = staff var people = staff!
.Select(w => new PersonDto() .Select(w => new PersonDto()
{ {
Name = w, Name = w,
@ -1227,7 +1227,7 @@ public class ExternalMetadataService : IExternalMetadataService
foreach (var person in chapter.People.Where(p => p.Role == role)) foreach (var person in chapter.People.Where(p => p.Role == role))
{ {
var meta = artists.FirstOrDefault(c => c.Name == person.Person.Name); var meta = people.FirstOrDefault(c => c.Name == person.Person.Name);
person.OrderWeight = 0; person.OrderWeight = 0;
if (meta != null) if (meta != null)

View file

@ -65,6 +65,12 @@ public class SettingsService : ISettingsService
existingMetadataSetting.FirstLastPeopleNaming = dto.FirstLastPeopleNaming; existingMetadataSetting.FirstLastPeopleNaming = dto.FirstLastPeopleNaming;
existingMetadataSetting.EnableCoverImage = dto.EnableCoverImage; existingMetadataSetting.EnableCoverImage = dto.EnableCoverImage;
existingMetadataSetting.EnableChapterPublisher = dto.EnableChapterPublisher;
existingMetadataSetting.EnableChapterSummary = dto.EnableChapterSummary;
existingMetadataSetting.EnableChapterTitle = dto.EnableChapterTitle;
existingMetadataSetting.EnableChapterReleaseDate = dto.EnableChapterReleaseDate;
existingMetadataSetting.EnableChapterCoverImage = dto.EnableChapterCoverImage;
existingMetadataSetting.AgeRatingMappings = dto.AgeRatingMappings ?? []; existingMetadataSetting.AgeRatingMappings = dto.AgeRatingMappings ?? [];
existingMetadataSetting.Blacklist = (dto.Blacklist ?? []).Where(s => !string.IsNullOrWhiteSpace(s)).DistinctBy(d => d.ToNormalized()).ToList() ?? []; existingMetadataSetting.Blacklist = (dto.Blacklist ?? []).Where(s => !string.IsNullOrWhiteSpace(s)).DistinctBy(d => d.ToNormalized()).ToList() ?? [];

View file

@ -977,26 +977,26 @@ public class ProcessSeries : IProcessSeries
chapter.ReleaseDate = new DateTime(comicInfo.Year, month, day); chapter.ReleaseDate = new DateTime(comicInfo.Year, month, day);
} }
if (!chapter.ColoristLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Colorist))
{ {
var people = TagHelper.GetTagValues(comicInfo.Colorist); var people = TagHelper.GetTagValues(comicInfo.Colorist);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Colorist); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Colorist);
} }
if (!chapter.CharacterLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Character))
{ {
var people = TagHelper.GetTagValues(comicInfo.Characters); var people = TagHelper.GetTagValues(comicInfo.Characters);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Character); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Character);
} }
if (!chapter.TranslatorLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Translator))
{ {
var people = TagHelper.GetTagValues(comicInfo.Translator); var people = TagHelper.GetTagValues(comicInfo.Translator);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Translator); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Translator);
} }
if (!chapter.WriterLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Writer))
{ {
var personSw = Stopwatch.StartNew(); var personSw = Stopwatch.StartNew();
var people = TagHelper.GetTagValues(comicInfo.Writer); var people = TagHelper.GetTagValues(comicInfo.Writer);
@ -1004,55 +1004,55 @@ public class ProcessSeries : IProcessSeries
_logger.LogTrace("[TIME] Kavita took {Time} ms to process writer on Chapter: {File} for {Count} people", personSw.ElapsedMilliseconds, chapter.Files.First().FileName, people.Count); _logger.LogTrace("[TIME] Kavita took {Time} ms to process writer on Chapter: {File} for {Count} people", personSw.ElapsedMilliseconds, chapter.Files.First().FileName, people.Count);
} }
if (!chapter.EditorLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Editor))
{ {
var people = TagHelper.GetTagValues(comicInfo.Editor); var people = TagHelper.GetTagValues(comicInfo.Editor);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Editor); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Editor);
} }
if (!chapter.InkerLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Inker))
{ {
var people = TagHelper.GetTagValues(comicInfo.Inker); var people = TagHelper.GetTagValues(comicInfo.Inker);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Inker); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Inker);
} }
if (!chapter.LettererLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Letterer))
{ {
var people = TagHelper.GetTagValues(comicInfo.Letterer); var people = TagHelper.GetTagValues(comicInfo.Letterer);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Letterer); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Letterer);
} }
if (!chapter.PencillerLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Penciller))
{ {
var people = TagHelper.GetTagValues(comicInfo.Penciller); var people = TagHelper.GetTagValues(comicInfo.Penciller);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Penciller); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Penciller);
} }
if (!chapter.CoverArtistLocked) if (!chapter.IsPersonRoleLocked(PersonRole.CoverArtist))
{ {
var people = TagHelper.GetTagValues(comicInfo.CoverArtist); var people = TagHelper.GetTagValues(comicInfo.CoverArtist);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.CoverArtist); await UpdateChapterPeopleAsync(chapter, people, PersonRole.CoverArtist);
} }
if (!chapter.PublisherLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Publisher))
{ {
var people = TagHelper.GetTagValues(comicInfo.Publisher); var people = TagHelper.GetTagValues(comicInfo.Publisher);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Publisher); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Publisher);
} }
if (!chapter.ImprintLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Imprint))
{ {
var people = TagHelper.GetTagValues(comicInfo.Imprint); var people = TagHelper.GetTagValues(comicInfo.Imprint);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Imprint); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Imprint);
} }
if (!chapter.TeamLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Team))
{ {
var people = TagHelper.GetTagValues(comicInfo.Teams); var people = TagHelper.GetTagValues(comicInfo.Teams);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Team); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Team);
} }
if (!chapter.LocationLocked) if (!chapter.IsPersonRoleLocked(PersonRole.Location))
{ {
var people = TagHelper.GetTagValues(comicInfo.Locations); var people = TagHelper.GetTagValues(comicInfo.Locations);
await UpdateChapterPeopleAsync(chapter, people, PersonRole.Location); await UpdateChapterPeopleAsync(chapter, people, PersonRole.Location);

View file

@ -11,15 +11,15 @@ export class MetadataSettingFiledPipe implements PipeTransform {
transform(value: MetadataSettingField): string { transform(value: MetadataSettingField): string {
switch (value) { switch (value) {
case MetadataSettingField.ChapterTitle: case MetadataSettingField.ChapterTitle:
return translate('metadata-setting-field-pipe.title'); return translate('metadata-setting-field-pipe.chapter-title');
case MetadataSettingField.ChapterSummary: case MetadataSettingField.ChapterSummary:
return translate('metadata-setting-field-pipe.summary'); return translate('metadata-setting-field-pipe.chapter-summary');
case MetadataSettingField.ChapterReleaseDate: case MetadataSettingField.ChapterReleaseDate:
return translate('metadata-setting-field-pipe.release-date'); return translate('metadata-setting-field-pipe.chapter-release-date');
case MetadataSettingField.ChapterPublisher: case MetadataSettingField.ChapterPublisher:
return translate('metadata-setting-field-pipe.publisher'); return translate('metadata-setting-field-pipe.chapter-publisher');
case MetadataSettingField.ChapterCovers: case MetadataSettingField.ChapterCovers:
return translate('metadata-setting-field-pipe.covers'); return translate('metadata-setting-field-pipe.chapter-covers');
case MetadataSettingField.AgeRating: case MetadataSettingField.AgeRating:
return translate('metadata-setting-field-pipe.age-rating'); return translate('metadata-setting-field-pipe.age-rating');
case MetadataSettingField.People: case MetadataSettingField.People:

View file

@ -89,6 +89,70 @@
} }
</div> </div>
<div class="setting-section-break"></div>
<!-- Chapter-based fields -->
<h5>{{t('chapter-header')}}</h5>
<div class="row g-0 mt-4 mb-4">
@if(settingsForm.get('enableChapterTitle'); as formControl) {
<app-setting-switch [title]="t('enable-chapter-title-label')" [subtitle]="t('enable-chapter-title-tooltip')">
<ng-template #switch>
<div class="form-check form-switch float-end">
<input id="enable-chapter-title" type="checkbox" class="form-check-input" formControlName="enableChapterTitle">
</div>
</ng-template>
</app-setting-switch>
}
</div>
<div class="row g-0 mt-4 mb-4">
@if(settingsForm.get('enableChapterSummary'); as formControl) {
<app-setting-switch [title]="t('enable-chapter-summary-label')" [subtitle]="t('enable-chapter-summary-tooltip')">
<ng-template #switch>
<div class="form-check form-switch float-end">
<input id="enable-chapter-summary" type="checkbox" class="form-check-input" formControlName="enableChapterSummary">
</div>
</ng-template>
</app-setting-switch>
}
</div>
<div class="row g-0 mt-4 mb-4">
@if(settingsForm.get('enableChapterReleaseDate'); as formControl) {
<app-setting-switch [title]="t('enable-chapter-release-date-label')" [subtitle]="t('enable-chapter-release-date-tooltip')">
<ng-template #switch>
<div class="form-check form-switch float-end">
<input id="enable-chapter-release-date" type="checkbox" class="form-check-input" formControlName="enableChapterReleaseDate">
</div>
</ng-template>
</app-setting-switch>
}
</div>
<div class="row g-0 mt-4 mb-4">
@if(settingsForm.get('enableChapterPublisher'); as formControl) {
<app-setting-switch [title]="t('enable-chapter-publisher-label')" [subtitle]="t('enable-chapter-publisher-tooltip')">
<ng-template #switch>
<div class="form-check form-switch float-end">
<input id="enable-chapter-publisher" type="checkbox" class="form-check-input" formControlName="enableChapterPublisher">
</div>
</ng-template>
</app-setting-switch>
}
</div>
<div class="row g-0 mt-4 mb-4">
@if(settingsForm.get('enableChapterCoverImage'); as formControl) {
<app-setting-switch [title]="t('enable-chapter-cover-label')" [subtitle]="t('enable-chapter-cover-tooltip')">
<ng-template #switch>
<div class="form-check form-switch float-end">
<input id="enable-chapter-cover" type="checkbox" class="form-check-input" formControlName="enableChapterCoverImage">
</div>
</ng-template>
</app-setting-switch>
}
</div>
@if(settingsForm.get('enablePeople'); as formControl) { @if(settingsForm.get('enablePeople'); as formControl) {
<div class="setting-section-break"></div> <div class="setting-section-break"></div>
@ -133,6 +197,7 @@
<div class="setting-section-break"></div> <div class="setting-section-break"></div>

View file

@ -79,6 +79,13 @@ export class ManageMetadataSettingsComponent implements OnInit {
this.settingsForm.addControl('enableStartDate', new FormControl(settings.enableStartDate, [])); this.settingsForm.addControl('enableStartDate', new FormControl(settings.enableStartDate, []));
this.settingsForm.addControl('enableCoverImage', new FormControl(settings.enableCoverImage, [])); this.settingsForm.addControl('enableCoverImage', new FormControl(settings.enableCoverImage, []));
this.settingsForm.addControl('enableChapterTitle', new FormControl(settings.enableChapterTitle, []));
this.settingsForm.addControl('enableChapterSummary', new FormControl(settings.enableChapterSummary, []));
this.settingsForm.addControl('enableChapterReleaseDate', new FormControl(settings.enableChapterReleaseDate, []));
this.settingsForm.addControl('enableChapterPublisher', new FormControl(settings.enableChapterPublisher, []));
this.settingsForm.addControl('enableChapterCoverImage', new FormControl(settings.enableChapterCoverImage, []));
this.settingsForm.addControl('blacklist', new FormControl((settings.blacklist || '').join(','), [])); this.settingsForm.addControl('blacklist', new FormControl((settings.blacklist || '').join(','), []));
this.settingsForm.addControl('whitelist', new FormControl((settings.whitelist || '').join(','), [])); this.settingsForm.addControl('whitelist', new FormControl((settings.whitelist || '').join(','), []));
this.settingsForm.addControl('firstLastPeopleNaming', new FormControl((settings.firstLastPeopleNaming), [])); this.settingsForm.addControl('firstLastPeopleNaming', new FormControl((settings.firstLastPeopleNaming), []));

View file

@ -806,6 +806,17 @@
"enable-cover-image-tooltip": "Allow Kavita to write the cover image for the Series", "enable-cover-image-tooltip": "Allow Kavita to write the cover image for the Series",
"enable-start-date-label": "Start Date", "enable-start-date-label": "Start Date",
"enable-start-date-tooltip": "Allow Start Date of Series to be written to the Series", "enable-start-date-tooltip": "Allow Start Date of Series to be written to the Series",
"enable-chapter-title-label": "Title",
"enable-chapter-title-tooltip": "Allow Title of Chapter/Issue to be written",
"enable-chapter-summary-label": "{{manage-metadata-settings.summary-label}}",
"enable-chapter-summary-tooltip": "{{manage-metadata-settings.summary-tooltip}}",
"enable-chapter-release-date-label": "Release Date",
"enable-chapter-release-date-tooltip": "Allow Release Date of Chapter/Issue to be written",
"enable-chapter-publisher-label": "Publisher",
"enable-chapter-publisher-tooltip": "Allow Publisher of Chapter/Issue to be written",
"enable-chapter-cover-label": "Chapter Cover",
"enable-chapter-cover-tooltip": "Allow Cover of Chapter/Issue to be set",
"enable-genres-label": "Genres", "enable-genres-label": "Genres",
"enable-genres-tooltip": "Allow Series Genres to be written.", "enable-genres-tooltip": "Allow Series Genres to be written.",
"enable-tags-label": "Tags", "enable-tags-label": "Tags",
@ -827,7 +838,8 @@
"first-last-name-tooltip": "Ensure People's names are written First then Last", "first-last-name-tooltip": "Ensure People's names are written First then Last",
"person-roles-label": "Roles", "person-roles-label": "Roles",
"overrides-label": "Overrides", "overrides-label": "Overrides",
"overrides-description": "Allow Kavita to write over locked fields." "overrides-description": "Allow Kavita to write over locked fields.",
"chapter-header": "Chapter Fields"
}, },
"book-line-overlay": { "book-line-overlay": {
@ -2686,9 +2698,11 @@
"genres": "{{metadata-fields.genres-title}}", "genres": "{{metadata-fields.genres-title}}",
"tags": "{{metadata-fields.tags-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", "chapter-release-date": "Release Date (Chapter)",
"publisher": "{{person-role-pipe.publisher}}", "chapter-summary": "Summary (Chapter)",
"title": "Title" "chapter-covers": "Covers (Chapter)",
"chapter-publisher": "{{person-role-pipe.publisher}} (Chapter)",
"chapter-title": "Title (Chapter)"
}, },