CBL Import Rework (#1862)
* Fixed a typo in a log * Invalid XML files now "validate" correctly by sending back a failure. * Cleaned up messaging on backend and frontend to provide some linking on series name when collision, handle corrupt xml files, etc. * When reading list conflict occurs, show the reading list name that's conflicting. Started refactoring the code to allow multiple files to be imported at once. * Started adding new CBL elements for some enhancements I have planned with maintainers. * Default to empty string for IpAddress to allow to fallback into existing experience * Tweaked the layout of reading list page (not complete), moved some not used much controls to page extras and reordered the buttons for reading list * Edit Reading Lists now allows selection of cover image from existing items * Fixed a bug where cover chooser base64 to image would fail to write webp files. * Refactored the validate step to now handle multiple files in one go. * Clean up code * Don't show CBL name if there were xml errors that prevented showing it * Don't allow user to go prev step after they perform the import. * Cleaned up the heading code for accordions * Fixed a bug with import keeping failed items * Sort the failures to the bottom of result windows * CBL import is pretty solid. Need one pass from Robbie on Reading List Page
This commit is contained in:
parent
c846b36047
commit
b55d9e3994
30 changed files with 609 additions and 249 deletions
|
@ -1,4 +1,6 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.ReadingLists.CBL;
|
||||
using API.Extensions;
|
||||
|
@ -32,10 +34,43 @@ public class CblController : BaseApiController
|
|||
public async Task<ActionResult<CblImportSummaryDto>> ValidateCbl([FromForm(Name = "cbl")] IFormFile file)
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
var cbl = await SaveAndLoadCblFile(userId, file);
|
||||
|
||||
var importSummary = await _readingListService.ValidateCblFile(userId, cbl);
|
||||
return Ok(importSummary);
|
||||
try
|
||||
{
|
||||
var cbl = await SaveAndLoadCblFile(file);
|
||||
var importSummary = await _readingListService.ValidateCblFile(userId, cbl);
|
||||
importSummary.FileName = file.FileName;
|
||||
return Ok(importSummary);
|
||||
}
|
||||
catch (ArgumentNullException)
|
||||
{
|
||||
return Ok(new CblImportSummaryDto()
|
||||
{
|
||||
FileName = file.FileName,
|
||||
Success = CblImportResult.Fail,
|
||||
Results = new List<CblBookResult>()
|
||||
{
|
||||
new CblBookResult()
|
||||
{
|
||||
Reason = CblImportReason.InvalidFile
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return Ok(new CblImportSummaryDto()
|
||||
{
|
||||
FileName = file.FileName,
|
||||
Success = CblImportResult.Fail,
|
||||
Results = new List<CblBookResult>()
|
||||
{
|
||||
new CblBookResult()
|
||||
{
|
||||
Reason = CblImportReason.InvalidFile
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -48,13 +83,47 @@ public class CblController : BaseApiController
|
|||
[HttpPost("import")]
|
||||
public async Task<ActionResult<CblImportSummaryDto>> ImportCbl([FromForm(Name = "cbl")] IFormFile file, [FromForm(Name = "dryRun")] bool dryRun = false)
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
var cbl = await SaveAndLoadCblFile(userId, file);
|
||||
try
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
var cbl = await SaveAndLoadCblFile(file);
|
||||
var importSummary = await _readingListService.CreateReadingListFromCbl(userId, cbl, dryRun);
|
||||
importSummary.FileName = file.FileName;
|
||||
return Ok(importSummary);
|
||||
} catch (ArgumentNullException)
|
||||
{
|
||||
return Ok(new CblImportSummaryDto()
|
||||
{
|
||||
FileName = file.FileName,
|
||||
Success = CblImportResult.Fail,
|
||||
Results = new List<CblBookResult>()
|
||||
{
|
||||
new CblBookResult()
|
||||
{
|
||||
Reason = CblImportReason.InvalidFile
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return Ok(new CblImportSummaryDto()
|
||||
{
|
||||
FileName = file.FileName,
|
||||
Success = CblImportResult.Fail,
|
||||
Results = new List<CblBookResult>()
|
||||
{
|
||||
new CblBookResult()
|
||||
{
|
||||
Reason = CblImportReason.InvalidFile
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(await _readingListService.CreateReadingListFromCbl(userId, cbl, dryRun));
|
||||
}
|
||||
|
||||
private async Task<CblReadingList> SaveAndLoadCblFile(int userId, IFormFile file)
|
||||
private async Task<CblReadingList> SaveAndLoadCblFile(IFormFile file)
|
||||
{
|
||||
var filename = Path.GetRandomFileName();
|
||||
var outputFile = Path.Join(_directoryService.TempDirectory, filename);
|
||||
|
|
|
@ -506,45 +506,6 @@ public class ReaderController : BaseApiController
|
|||
return Ok(await _unitOfWork.AppUserProgressRepository.HasAnyProgressOnSeriesAsync(seriesId, userId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks every chapter that is sorted below the passed number as Read. This will not mark any specials as read.
|
||||
/// </summary>
|
||||
/// <remarks>This is built for Tachiyomi and is not expected to be called by any other place</remarks>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Deprecated. Use 'Tachiyomi/mark-chapter-until-as-read'")]
|
||||
[HttpPost("mark-chapter-until-as-read")]
|
||||
public async Task<ActionResult<bool>> MarkChaptersUntilAsRead(int seriesId, float chapterNumber)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
user.Progresses ??= new List<AppUserProgress>();
|
||||
|
||||
// Tachiyomi sends chapter 0.0f when there's no chapters read.
|
||||
// Due to the encoding for volumes this marks all chapters in volume 0 (loose chapters) as read so we ignore it
|
||||
if (chapterNumber == 0.0f) return true;
|
||||
|
||||
if (chapterNumber < 1.0f)
|
||||
{
|
||||
// This is a hack to track volume number. We need to map it back by x100
|
||||
var volumeNumber = int.Parse($"{chapterNumber * 100f}");
|
||||
await _readerService.MarkVolumesUntilAsRead(user, seriesId, volumeNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _readerService.MarkChaptersUntilAsRead(user, seriesId, chapterNumber);
|
||||
}
|
||||
|
||||
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
|
||||
if (!_unitOfWork.HasChanges()) return Ok(true);
|
||||
if (await _unitOfWork.CommitAsync()) return Ok(true);
|
||||
|
||||
await _unitOfWork.RollbackAsync();
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of bookmarked pages for a given Chapter
|
||||
/// </summary>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.DTOs.ReadingLists;
|
||||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
|
@ -421,6 +423,18 @@ public class ReadingListController : BaseApiController
|
|||
return Ok("Nothing to do");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of characters associated with the reading list
|
||||
/// </summary>
|
||||
/// <param name="readingListId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("characters")]
|
||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.TenMinute)]
|
||||
public ActionResult<IEnumerable<PersonDto>> GetCharactersForList(int readingListId)
|
||||
{
|
||||
return Ok(_unitOfWork.ReadingListRepository.GetReadingListCharactersAsync(readingListId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -80,7 +80,7 @@ public class SettingsController : BaseApiController
|
|||
{
|
||||
_logger.LogInformation("{UserName} is resetting IP Addresses Setting", User.GetUsername());
|
||||
var ipAddresses = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.IpAddresses);
|
||||
ipAddresses.Value = Configuration.DefaultIPAddresses;
|
||||
ipAddresses.Value = Configuration.DefaultIpAddresses;
|
||||
_unitOfWork.SettingsRepository.Update(ipAddresses);
|
||||
|
||||
if (!await _unitOfWork.CommitAsync())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue