#nullable enable using System; using System.Collections.Generic; using System.Threading.Tasks; using API.Data; using API.DTOs; using API.Services; using API.Services.Reading; using Kavita.Common; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace API.Controllers; public record BulkSetSeriesProfiles(List ProfileIds, List SeriesIds); [Route("api/reading-profile")] public class ReadingProfileController(ILogger logger, IUnitOfWork unitOfWork, IReadingProfileService readingProfileService, IClientInfoAccessor clientInfoAccessor): BaseApiController { /// /// Gets all non-implicit reading profiles for a user /// /// [HttpGet("all")] public async Task>> GetAllReadingProfiles() { return Ok(await unitOfWork.AppUserReadingProfileRepository.GetProfilesDtoForUser(UserId, true)); } /// /// Returns the ReadingProfile that should be applied to the given series, walks up the tree. /// Series -> Library -> Default /// /// /// /// /// Defaults to currently active device /// [HttpGet("{libraryId:int}/{seriesId:int}")] public async Task> GetProfileForSeries(int libraryId, int seriesId, [FromQuery] bool skipImplicit, [FromQuery] int? deviceId = null) { deviceId ??= clientInfoAccessor.CurrentDeviceId; return Ok(await readingProfileService.GetReadingProfileDtoForSeries(UserId, libraryId, seriesId, deviceId, skipImplicit)); } /// /// Returns all Reading Profiles bound to a series /// /// /// [HttpGet("series")] public async Task>> GetProfilesForSeries(int seriesId) { return Ok(await readingProfileService.GetReadingProfileDtosForSeries(UserId, seriesId)); } /// /// Returns all the Reading rofiles bound to the library /// /// /// [HttpGet("library")] public async Task>> GetProfilesForLibrary(int libraryId) { return Ok(await readingProfileService.GetReadingProfileDtosForLibrary(UserId, libraryId)); } /// /// Creates a new reading profile for the current user /// /// /// [HttpPost("create")] public async Task> CreateReadingProfile([FromBody] UserReadingProfileDto dto) { return Ok(await readingProfileService.CreateReadingProfile(UserId, dto)); } /// /// Promotes the implicit profile to a user profile. Removes the series from other profiles /// /// /// Defaults to currently active device /// [HttpPost("promote")] public async Task> PromoteImplicitReadingProfile([FromQuery] int profileId, [FromQuery] int? deviceId = null) { deviceId ??= clientInfoAccessor.CurrentDeviceId; return Ok(await readingProfileService.PromoteImplicitProfile(UserId, profileId, deviceId)); } /// /// Update the implicit reading profile for a series, creates one if none exists /// /// Any modification to the reader settings during reading will create an implicit profile. Use "update-parent" to save to the bound series profile. /// /// /// /// Defaults to currently active device /// [HttpPost("series")] public async Task> UpdateReadingProfileForSeries( [FromBody] UserReadingProfileDto dto, [FromQuery] int libraryId, [FromQuery] int seriesId, [FromQuery] int? deviceId = null) { deviceId ??= clientInfoAccessor.CurrentDeviceId; var updatedProfile = await readingProfileService.UpdateImplicitReadingProfile(UserId, libraryId, seriesId, dto, deviceId); return Ok(updatedProfile); } /// /// Updates the non-implicit reading profile for the given series, and removes implicit profiles /// /// /// /// /// Defaults to currently active device /// [HttpPost("update-parent")] public async Task> UpdateParentProfileForSeries( [FromBody] UserReadingProfileDto dto, [FromQuery] int libraryId, [FromQuery] int seriesId, [FromQuery] int? deviceId = null) { deviceId ??= clientInfoAccessor.CurrentDeviceId; var newParentProfile = await readingProfileService.UpdateParent(UserId, libraryId, seriesId, dto, deviceId); return Ok(newParentProfile); } /// /// Updates the given reading profile, must belong to the current user /// /// /// The updated reading profile /// /// This does not update connected series and libraries. /// [HttpPost] public async Task> UpdateReadingProfile(UserReadingProfileDto dto) { return Ok(await readingProfileService.UpdateReadingProfile(UserId, dto)); } /// /// Deletes the given profile, requires the profile to belong to the logged-in user /// /// /// /// /// [HttpDelete] public async Task DeleteReadingProfile([FromQuery] int profileId) { await readingProfileService.DeleteReadingProfile(UserId, profileId); return Ok(); } /// /// Sets the reading profile for a given series, removes the old one /// /// /// /// [HttpPost("series/{seriesId:int}")] public async Task SetSeriesProfiles(int seriesId, List profileIds) { await readingProfileService.SetSeriesProfiles(UserId, profileIds, seriesId); return Ok(); } /// /// Clears the reading profile for the given series for the currently logged-in user /// /// /// [HttpDelete("series/{seriesId:int}")] public async Task ClearSeriesProfile(int seriesId) { await readingProfileService.ClearSeriesProfile(UserId, seriesId); return Ok(); } /// /// Sets the reading profile for a given library, removes the old one /// /// /// /// [HttpPost("library/{libraryId:int}")] public async Task SetLibraryProfiles(int libraryId, List profileIds) { await readingProfileService.SetLibraryProfiles(UserId, profileIds, libraryId); return Ok(); } /// /// Clears the reading profile for the given library for the currently logged-in user /// /// /// [HttpDelete("library/{libraryId:int}")] public async Task ClearLibraryProfile(int libraryId) { await readingProfileService.ClearLibraryProfile(UserId, libraryId); return Ok(); } /// /// Assigns the reading profile to all passes series, and deletes their implicit profiles /// /// /// [HttpPost("bulk")] public async Task BulkAddReadingProfile(BulkSetSeriesProfiles body) { await readingProfileService.BulkSetSeriesProfiles(UserId, body.ProfileIds, body.SeriesIds); return Ok(); } /// /// Set the assigned devices for a reading profile /// /// /// /// [HttpPost("set-devices")] public async Task SetProfileDevices([FromQuery] int profileId, [FromBody] List deviceIds) { await readingProfileService.SetProfileDevices(UserId, profileId, deviceIds); return Ok(); } }