mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Polish 5 (#3579)
This commit is contained in:
parent
78a98d0d18
commit
5af851af08
8
.github/workflows/openapi-gen.yml
vendored
8
.github/workflows/openapi-gen.yml
vendored
@ -21,6 +21,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.REPO_GHA_PAT }}
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
@ -59,7 +60,12 @@ jobs:
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
|
||||
# Pull latest changes with rebase to avoid merge commits
|
||||
git pull --rebase origin develop
|
||||
|
||||
# Commit and push
|
||||
git commit -m "Update OpenAPI documentation" openapi.json
|
||||
git push
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPO_GHA_PAT }}
|
||||
GITHUB_TOKEN: ${{ secrets.REPO_GHA_PAT }}
|
@ -298,38 +298,38 @@ public class ScannerServiceTests : AbstractDbTest
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ScanLibrary_PublishersInheritFromChapters()
|
||||
{
|
||||
const string testcase = "Flat Special - Manga.json";
|
||||
|
||||
var infos = new Dictionary<string, ComicInfo>();
|
||||
infos.Add("Uzaki-chan Wants to Hang Out! v01 (2019) (Digital) (danke-Empire).cbz", new ComicInfo()
|
||||
[Fact]
|
||||
public async Task ScanLibrary_PublishersInheritFromChapters()
|
||||
{
|
||||
Publisher = "Correct Publisher"
|
||||
});
|
||||
infos.Add("Uzaki-chan Wants to Hang Out! - 2022 New Years Special SP01.cbz", new ComicInfo()
|
||||
{
|
||||
Publisher = "Special Publisher"
|
||||
});
|
||||
infos.Add("Uzaki-chan Wants to Hang Out! - Ch. 103 - Kouhai and Control.cbz", new ComicInfo()
|
||||
{
|
||||
Publisher = "Chapter Publisher"
|
||||
});
|
||||
const string testcase = "Flat Special - Manga.json";
|
||||
|
||||
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
|
||||
var infos = new Dictionary<string, ComicInfo>();
|
||||
infos.Add("Uzaki-chan Wants to Hang Out! v01 (2019) (Digital) (danke-Empire).cbz", new ComicInfo()
|
||||
{
|
||||
Publisher = "Correct Publisher"
|
||||
});
|
||||
infos.Add("Uzaki-chan Wants to Hang Out! - 2022 New Years Special SP01.cbz", new ComicInfo()
|
||||
{
|
||||
Publisher = "Special Publisher"
|
||||
});
|
||||
infos.Add("Uzaki-chan Wants to Hang Out! - Ch. 103 - Kouhai and Control.cbz", new ComicInfo()
|
||||
{
|
||||
Publisher = "Chapter Publisher"
|
||||
});
|
||||
|
||||
var library = await _scannerHelper.GenerateScannerData(testcase, infos);
|
||||
|
||||
|
||||
var scanner = _scannerHelper.CreateServices();
|
||||
await scanner.ScanLibrary(library.Id);
|
||||
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
|
||||
var scanner = _scannerHelper.CreateServices();
|
||||
await scanner.ScanLibrary(library.Id);
|
||||
var postLib = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(library.Id, LibraryIncludes.Series);
|
||||
|
||||
Assert.NotNull(postLib);
|
||||
Assert.Single(postLib.Series);
|
||||
var publishers = postLib.Series.First().Metadata.People
|
||||
.Where(p => p.Role == PersonRole.Publisher);
|
||||
Assert.Equal(3, publishers.Count());
|
||||
}
|
||||
Assert.NotNull(postLib);
|
||||
Assert.Single(postLib.Series);
|
||||
var publishers = postLib.Series.First().Metadata.People
|
||||
.Where(p => p.Role == PersonRole.Publisher);
|
||||
Assert.Equal(3, publishers.Count());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
@ -12,11 +12,6 @@
|
||||
<LangVersion>latestmajor</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Moved to GA -->
|
||||
<!-- <Target Name="PostBuild" AfterTargets="Build" Condition=" '$(Configuration)' == 'Debug' ">-->
|
||||
<!-- <Delete Files="../openapi.json" />-->
|
||||
<!-- <Exec Command="swagger tofile --output ../openapi.json bin/$(Configuration)/$(TargetFramework)/$(AssemblyName).dll v1" />-->
|
||||
<!-- </Target>-->
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
|
@ -7,6 +7,7 @@ using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using API.Services.Plus;
|
||||
using EasyCaching.Core;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -21,7 +22,8 @@ public class LicenseController(
|
||||
ILogger<LicenseController> logger,
|
||||
ILicenseService licenseService,
|
||||
ILocalizationService localizationService,
|
||||
ITaskScheduler taskScheduler)
|
||||
ITaskScheduler taskScheduler,
|
||||
IEasyCachingProviderFactory cachingProviderFactory)
|
||||
: BaseApiController
|
||||
{
|
||||
/// <summary>
|
||||
@ -32,8 +34,13 @@ public class LicenseController(
|
||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.LicenseCache)]
|
||||
public async Task<ActionResult<bool>> HasValidLicense(bool forceCheck = false)
|
||||
{
|
||||
|
||||
var result = await licenseService.HasActiveLicense(forceCheck);
|
||||
if (result)
|
||||
|
||||
var licenseInfoProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.License);
|
||||
var cacheValue = await licenseInfoProvider.GetAsync<bool>(LicenseService.CacheKey);
|
||||
|
||||
if (result && !cacheValue.IsNull && !cacheValue.Value)
|
||||
{
|
||||
await taskScheduler.ScheduleKavitaPlusTasks();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Interfaces;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -101,4 +102,24 @@ public class SeriesMetadata : IHasConcurrencyToken
|
||||
{
|
||||
RowVersion++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Any People in this Role present
|
||||
/// </summary>
|
||||
/// <param name="role"></param>
|
||||
/// <returns></returns>
|
||||
public bool AnyOfRole(PersonRole role)
|
||||
{
|
||||
return People.Any(p => p.Role == role);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Are all instances of the role from Kavita+
|
||||
/// </summary>
|
||||
/// <param name="role"></param>
|
||||
/// <returns></returns>
|
||||
public bool AllKavitaPlus(PersonRole role)
|
||||
{
|
||||
return People.Where(p => p.Role == role).All(p => p.KavitaPlusConnection);
|
||||
}
|
||||
}
|
||||
|
@ -295,7 +295,16 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||
if (data == null) return _defaultReturn;
|
||||
|
||||
// Get from Kavita+ API the Full Series metadata with rec/rev and cache to ExternalMetadata tables
|
||||
return await FetchExternalMetadataForSeries(seriesId, libraryType, data);
|
||||
try
|
||||
{
|
||||
return await FetchExternalMetadataForSeries(seriesId, libraryType, data);
|
||||
}
|
||||
catch (KavitaException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Rate limit hit fetching metadata");
|
||||
// This can happen when we hit rate limit
|
||||
return _defaultReturn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -314,38 +323,49 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||
_unitOfWork.SeriesRepository.Update(series);
|
||||
|
||||
// Refetch metadata with a Direct lookup
|
||||
var metadata = await FetchExternalMetadataForSeries(seriesId, series.Library.Type, new PlusSeriesRequestDto()
|
||||
try
|
||||
{
|
||||
AniListId = anilistId,
|
||||
MalId = malId,
|
||||
SeriesName = series.Name // Required field, not used since AniList/Mal Id are passed
|
||||
});
|
||||
var metadata = await FetchExternalMetadataForSeries(seriesId, series.Library.Type,
|
||||
new PlusSeriesRequestDto()
|
||||
{
|
||||
AniListId = anilistId,
|
||||
MalId = malId,
|
||||
SeriesName = series.Name // Required field, not used since AniList/Mal Id are passed
|
||||
});
|
||||
|
||||
if (metadata.Series == null)
|
||||
{
|
||||
_logger.LogError("Unable to Match {SeriesName} with Kavita+ Series AniList Id: {AniListId}", series.Name, anilistId);
|
||||
return;
|
||||
if (metadata.Series == null)
|
||||
{
|
||||
_logger.LogError("Unable to Match {SeriesName} with Kavita+ Series AniList Id: {AniListId}",
|
||||
series.Name, anilistId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all scrobble events and rewrite them to be the correct
|
||||
var events = await _unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
|
||||
_unitOfWork.ScrobbleRepository.Remove(events);
|
||||
|
||||
// Find all scrobble errors and remove them
|
||||
var errors = await _unitOfWork.ScrobbleRepository.GetAllScrobbleErrorsForSeries(seriesId);
|
||||
_unitOfWork.ScrobbleRepository.Remove(errors);
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
// Regenerate all events for the series for all users
|
||||
BackgroundJob.Enqueue(() => _scrobblingService.CreateEventsFromExistingHistoryForSeries(seriesId));
|
||||
|
||||
// Name can be null on Series even with a direct match
|
||||
_logger.LogInformation("Matched {SeriesName} with Kavita+ Series {MatchSeriesName}", series.Name,
|
||||
metadata.Series.Name);
|
||||
}
|
||||
catch (KavitaException ex)
|
||||
{
|
||||
// We can't rethrow because Fix match is done in a background thread and Hangfire will requeue multiple times
|
||||
_logger.LogInformation(ex, "Rate limit hit for matching {SeriesName} with Kavita+", series.Name);
|
||||
}
|
||||
|
||||
// Find all scrobble events and rewrite them to be the correct
|
||||
var events = await _unitOfWork.ScrobbleRepository.GetAllEventsForSeries(seriesId);
|
||||
_unitOfWork.ScrobbleRepository.Remove(events);
|
||||
|
||||
// Find all scrobble errors and remove them
|
||||
var errors = await _unitOfWork.ScrobbleRepository.GetAllScrobbleErrorsForSeries(seriesId);
|
||||
_unitOfWork.ScrobbleRepository.Remove(errors);
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
// Regenerate all events for the series for all users
|
||||
BackgroundJob.Enqueue(() => _scrobblingService.CreateEventsFromExistingHistoryForSeries(seriesId));
|
||||
|
||||
// Name can be null on Series even with a direct match
|
||||
_logger.LogInformation("Matched {SeriesName} with Kavita+ Series {MatchSeriesName}", series.Name, metadata.Series.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a series to Dont Match and removes all previously cached
|
||||
/// Sets a series to Don't Match and removes all previously cached
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
public async Task UpdateSeriesDontMatch(int seriesId, bool dontMatch)
|
||||
@ -383,7 +403,10 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||
{
|
||||
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Library);
|
||||
if (series == null) return _defaultReturn;
|
||||
if (series == null)
|
||||
{
|
||||
return _defaultReturn;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@ -417,7 +440,7 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||
|
||||
|
||||
// Recommendations
|
||||
externalSeriesMetadata.ExternalRecommendations ??= new List<ExternalRecommendation>();
|
||||
externalSeriesMetadata.ExternalRecommendations ??= [];
|
||||
var recs = await ProcessRecommendations(libraryType, result.Recommendations, externalSeriesMetadata);
|
||||
|
||||
var extRatings = externalSeriesMetadata.ExternalRatings
|
||||
@ -437,11 +460,19 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||
{
|
||||
externalSeriesMetadata.Series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
|
||||
|
||||
madeMetadataModification = await WriteExternalMetadataToSeries(result.Series, seriesId);
|
||||
if (madeMetadataModification)
|
||||
try
|
||||
{
|
||||
_unitOfWork.SeriesRepository.Update(series);
|
||||
madeMetadataModification = await WriteExternalMetadataToSeries(result.Series, seriesId);
|
||||
if (madeMetadataModification)
|
||||
{
|
||||
_unitOfWork.SeriesRepository.Update(series);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an exception when trying to write Series metadata from Kavita+");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// WriteExternalMetadataToSeries will commit but not always
|
||||
@ -466,13 +497,27 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||
}
|
||||
catch (FlurlHttpException ex)
|
||||
{
|
||||
var errorMessage = await ex.GetResponseStringAsync();
|
||||
// Trim quotes if the response is a JSON string
|
||||
errorMessage = errorMessage.Trim('"');
|
||||
|
||||
if (ex.StatusCode == 500)
|
||||
{
|
||||
return _defaultReturn;
|
||||
}
|
||||
|
||||
if (ex.StatusCode == 400 && errorMessage.Contains("Too many Requests"))
|
||||
{
|
||||
throw new KavitaException("Too many requests, slow down");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.Message.Contains("Too Many Requests"))
|
||||
{
|
||||
throw new KavitaException("Too many requests, slow down");
|
||||
}
|
||||
|
||||
_logger.LogError(ex, "Unable to fetch external series metadata from Kavita+");
|
||||
}
|
||||
|
||||
@ -1079,10 +1124,18 @@ public class ExternalMetadataService : IExternalMetadataService
|
||||
var aniListId = ScrobblingService.ExtractId<int?>(staff.Url, ScrobblingService.AniListStaffWebsite);
|
||||
if (aniListId is null or <= 0) continue;
|
||||
var person = await _unitOfWork.PersonRepository.GetPersonByAniListId(aniListId.Value);
|
||||
if (person != null && !string.IsNullOrEmpty(staff.ImageUrl) && string.IsNullOrEmpty(person.CoverImage))
|
||||
if (person == null || string.IsNullOrEmpty(staff.ImageUrl) ||
|
||||
!string.IsNullOrEmpty(person.CoverImage) || staff.ImageUrl.EndsWith("default.jpg")) continue;
|
||||
|
||||
try
|
||||
{
|
||||
await _coverDbService.SetPersonCoverByUrl(person, staff.ImageUrl, false, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an exception saving cover image for Person {PersonName} ({PersonId})", person.Name, person.Id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,10 @@ public class LicenseService(
|
||||
{
|
||||
private readonly TimeSpan _licenseCacheTimeout = TimeSpan.FromHours(8);
|
||||
public const string Cron = "0 */9 * * *";
|
||||
private const string CacheKey = "license";
|
||||
/// <summary>
|
||||
/// Cache key for if license is valid or not
|
||||
/// </summary>
|
||||
public const string CacheKey = "license";
|
||||
private const string LicenseInfoCacheKey = "license-info";
|
||||
|
||||
|
||||
|
@ -19,7 +19,6 @@ using API.SignalR;
|
||||
using Flurl.Http;
|
||||
using Hangfire;
|
||||
using Kavita.Common;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Kavita.Common.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -77,7 +76,7 @@ public class ScrobblingService : IScrobblingService
|
||||
public const string AniListCharacterWebsite = "https://anilist.co/character/";
|
||||
|
||||
|
||||
private static readonly IDictionary<string, int> WeblinkExtractionMap = new Dictionary<string, int>()
|
||||
private static readonly Dictionary<string, int> WeblinkExtractionMap = new Dictionary<string, int>()
|
||||
{
|
||||
{AniListWeblinkWebsite, 0},
|
||||
{MalWeblinkWebsite, 0},
|
||||
@ -89,18 +88,14 @@ public class ScrobblingService : IScrobblingService
|
||||
|
||||
private const int ScrobbleSleepTime = 1000; // We can likely tie this to AniList's 90 rate / min ((60 * 1000) / 90)
|
||||
|
||||
private static readonly IList<ScrobbleProvider> BookProviders = new List<ScrobbleProvider>()
|
||||
{
|
||||
};
|
||||
private static readonly IList<ScrobbleProvider> LightNovelProviders = new List<ScrobbleProvider>()
|
||||
{
|
||||
private static readonly IList<ScrobbleProvider> BookProviders = [];
|
||||
private static readonly IList<ScrobbleProvider> LightNovelProviders =
|
||||
[
|
||||
ScrobbleProvider.AniList
|
||||
};
|
||||
private static readonly IList<ScrobbleProvider> ComicProviders = new List<ScrobbleProvider>();
|
||||
private static readonly IList<ScrobbleProvider> MangaProviders = new List<ScrobbleProvider>()
|
||||
{
|
||||
ScrobbleProvider.AniList
|
||||
};
|
||||
];
|
||||
private static readonly IList<ScrobbleProvider> ComicProviders = [];
|
||||
private static readonly IList<ScrobbleProvider> MangaProviders = (List<ScrobbleProvider>)
|
||||
[ScrobbleProvider.AniList];
|
||||
|
||||
|
||||
private const string UnknownSeriesErrorMessage = "Series cannot be matched for Scrobbling";
|
||||
@ -532,11 +527,10 @@ public class ScrobblingService : IScrobblingService
|
||||
{
|
||||
// Create a new ExternalMetadata entry to indicate that this is not matchable
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(evt.SeriesId, SeriesIncludes.ExternalMetadata);
|
||||
if (series.ExternalSeriesMetadata == null)
|
||||
{
|
||||
series.ExternalSeriesMetadata = new ExternalSeriesMetadata() {SeriesId = evt.SeriesId};
|
||||
}
|
||||
series!.IsBlacklisted = true;
|
||||
if (series == null) return 0;
|
||||
|
||||
series.ExternalSeriesMetadata ??= new ExternalSeriesMetadata() {SeriesId = evt.SeriesId};
|
||||
series.IsBlacklisted = true;
|
||||
_unitOfWork.SeriesRepository.Update(series);
|
||||
|
||||
_unitOfWork.ScrobbleRepository.Attach(new ScrobbleError()
|
||||
@ -824,6 +818,7 @@ public class ScrobblingService : IScrobblingService
|
||||
readEvt.AppUser.Id);
|
||||
_unitOfWork.ScrobbleRepository.Update(readEvt);
|
||||
}
|
||||
|
||||
progressCounter = await ProcessEvents(readEvents, userRateLimits, usersToScrobble.Count, progressCounter, totalProgress, async evt => new ScrobbleDto()
|
||||
{
|
||||
Format = evt.Format,
|
||||
@ -888,9 +883,9 @@ public class ScrobblingService : IScrobblingService
|
||||
await _unitOfWork.CommitAsync();
|
||||
}
|
||||
}
|
||||
catch (FlurlHttpException)
|
||||
catch (FlurlHttpException ex)
|
||||
{
|
||||
_logger.LogError("Kavita+ API or a Scrobble service may be experiencing an outage. Stopping sending data");
|
||||
_logger.LogError(ex, "Kavita+ API or a Scrobble service may be experiencing an outage. Stopping sending data");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -986,7 +981,7 @@ public class ScrobblingService : IScrobblingService
|
||||
{
|
||||
if (ex.Message.Contains("Access token is invalid"))
|
||||
{
|
||||
_logger.LogCritical("Access Token for UserId: {UserId} needs to be regenerated/renewed to continue scrobbling", evt.AppUser.Id);
|
||||
_logger.LogCritical(ex, "Access Token for UserId: {UserId} needs to be regenerated/renewed to continue scrobbling", evt.AppUser.Id);
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = AccessTokenErrorMessage;
|
||||
_unitOfWork.ScrobbleRepository.Update(evt);
|
||||
@ -1106,7 +1101,7 @@ public class ScrobblingService : IScrobblingService
|
||||
return null; // Unsupported website
|
||||
}
|
||||
|
||||
if (id == null)
|
||||
if (Equals(id, default(T)))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(id), "ID cannot be null.");
|
||||
}
|
||||
@ -1140,7 +1135,7 @@ public class ScrobblingService : IScrobblingService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogInformation("User {UserName} had an issue figuring out rate: {Message}", user.UserName, ex.Message);
|
||||
_logger.LogInformation(ex, "User {UserName} had an issue figuring out rate: {Message}", user.UserName, ex.Message);
|
||||
userRateLimits.Add(user.Id, 0);
|
||||
}
|
||||
|
||||
|
@ -331,84 +331,123 @@ public class ProcessSeries : IProcessSeries
|
||||
}
|
||||
|
||||
#region PeopleAndTagsAndGenres
|
||||
if (!series.Metadata.WriterLocked)
|
||||
{
|
||||
var personSw = Stopwatch.StartNew();
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Writer)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Writer);
|
||||
_logger.LogTrace("[TIME] Kavita took {Time} ms to process writer on Series: {File} for {Count} people", personSw.ElapsedMilliseconds, series.Name, chapterPeople.Count);
|
||||
}
|
||||
if (!series.Metadata.WriterLocked)
|
||||
{
|
||||
var personSw = Stopwatch.StartNew();
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Writer)).ToList();
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Writer))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Writer);
|
||||
}
|
||||
_logger.LogTrace("[TIME] Kavita took {Time} ms to process writer on Series: {File} for {Count} people", personSw.ElapsedMilliseconds, series.Name, chapterPeople.Count);
|
||||
}
|
||||
|
||||
if (!series.Metadata.ColoristLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Colorist)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Colorist);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Colorist))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Colorist);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.PublisherLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Publisher)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Publisher);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Publisher))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Publisher);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.CoverArtistLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.CoverArtist)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.CoverArtist);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.CoverArtist))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.CoverArtist);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.CharacterLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Character)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Character);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Character))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Character);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.EditorLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Editor)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Editor);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Editor))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Editor);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.InkerLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Inker)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Inker);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Inker))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Inker);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.ImprintLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Imprint)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Imprint);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Imprint))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Imprint);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.TeamLocked)
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Team)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Team);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Team))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Team);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.LocationLocked)
|
||||
if (!series.Metadata.LocationLocked && !series.Metadata.AllKavitaPlus(PersonRole.Location))
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Location)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Location);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Location))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Location);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.LettererLocked)
|
||||
if (!series.Metadata.LettererLocked && !series.Metadata.AllKavitaPlus(PersonRole.Letterer))
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Letterer)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Letterer);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Location))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Letterer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.PencillerLocked)
|
||||
if (!series.Metadata.PencillerLocked && !series.Metadata.AllKavitaPlus(PersonRole.Penciller))
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Penciller)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Penciller);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Penciller))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Penciller);
|
||||
}
|
||||
}
|
||||
|
||||
if (!series.Metadata.TranslatorLocked)
|
||||
if (!series.Metadata.TranslatorLocked && !series.Metadata.AllKavitaPlus(PersonRole.Translator))
|
||||
{
|
||||
var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Translator)).ToList();
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Translator);
|
||||
if (ShouldUpdatePeopleForRole(series, chapterPeople, PersonRole.Translator))
|
||||
{
|
||||
await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Translator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -428,6 +467,34 @@ public class ProcessSeries : IProcessSeries
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that we don't overwrite Person metadata when all metadata is coming from Kavita+ metadata match functionality
|
||||
/// </summary>
|
||||
/// <param name="series"></param>
|
||||
/// <param name="chapterPeople"></param>
|
||||
/// <param name="role"></param>
|
||||
/// <returns></returns>
|
||||
private static bool ShouldUpdatePeopleForRole(Series series, List<ChapterPeople> chapterPeople, PersonRole role)
|
||||
{
|
||||
if (chapterPeople.Count == 0) return false;
|
||||
|
||||
// If metadata already has this role, but all entries are from KavitaPlus, we should retain them
|
||||
if (series.Metadata.AnyOfRole(role))
|
||||
{
|
||||
var existingPeople = series.Metadata.People.Where(p => p.Role == role);
|
||||
|
||||
// If all existing people are KavitaPlus but new chapter people exist, we should still update
|
||||
if (existingPeople.All(p => p.KavitaPlusConnection))
|
||||
{
|
||||
return false; // Ensure we don't remove KavitaPlus people
|
||||
}
|
||||
|
||||
return true; // Default case: metadata exists, and it's okay to update
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task UpdateCollectionTags(Series series, Chapter firstChapter)
|
||||
{
|
||||
// Get the default admin to associate these tags to
|
||||
@ -553,8 +620,8 @@ public class ProcessSeries : IProcessSeries
|
||||
.Where(v => v.MaxNumber.IsNot(Parser.Parser.SpecialVolumeNumber))
|
||||
.ToList();
|
||||
|
||||
var maxVolume = (int) (nonSpecialVolumes.Any() ? nonSpecialVolumes.Max(v => v.MaxNumber) : 0);
|
||||
var maxChapter = (int) chapters.Max(c => c.MaxNumber);
|
||||
var maxVolume = (int)(nonSpecialVolumes.Any() ? nonSpecialVolumes.Max(v => v.MaxNumber) : 0);
|
||||
var maxChapter = (int)chapters.Max(c => c.MaxNumber);
|
||||
|
||||
// Single books usually don't have a number in their Range (filename)
|
||||
if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user