Reading List Enhancement (#3005)

This commit is contained in:
Joe Milazzo 2024-06-14 16:51:55 -05:00 committed by GitHub
parent 6218f84861
commit 3936308f8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 122 additions and 93 deletions

View File

@ -190,10 +190,11 @@ public class OpdsController : BaseApiController
{ {
Text = stream.Name Text = stream.Name
}, },
Links = new List<FeedLink>() Links =
{ [
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, $"{prefix}{apiKey}/smart-filter/{stream.SmartFilterId}/"), CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation,
} $"{prefix}{apiKey}/smart-filters/{stream.SmartFilterId}/")
]
}); });
break; break;
} }
@ -306,7 +307,7 @@ public class OpdsController : BaseApiController
/// Returns the Series matching this smart filter. If FromDashboard, will only return 20 records. /// Returns the Series matching this smart filter. If FromDashboard, will only return 20 records.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet("{apiKey}/smart-filter/{filterId}")] [HttpGet("{apiKey}/smart-filters/{filterId}")]
[Produces("application/xml")] [Produces("application/xml")]
public async Task<IActionResult> GetSmartFilter(string apiKey, int filterId, [FromQuery] int pageNumber = 0) public async Task<IActionResult> GetSmartFilter(string apiKey, int filterId, [FromQuery] int pageNumber = 0)
{ {
@ -318,8 +319,8 @@ public class OpdsController : BaseApiController
var filter = await _unitOfWork.AppUserSmartFilterRepository.GetById(filterId); var filter = await _unitOfWork.AppUserSmartFilterRepository.GetById(filterId);
if (filter == null) return BadRequest(_localizationService.Translate(userId, "smart-filter-doesnt-exist")); if (filter == null) return BadRequest(_localizationService.Translate(userId, "smart-filter-doesnt-exist"));
var feed = CreateFeed(await _localizationService.Translate(userId, "smartFilter-" + filter.Id), $"{prefix}{apiKey}/smart-filter/{filter.Id}/", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "smartFilters-" + filter.Id), $"{apiKey}/smart-filters/{filter.Id}/", apiKey, prefix);
SetFeedId(feed, "smartFilter-" + filter.Id); SetFeedId(feed, "smartFilters-" + filter.Id);
var decodedFilter = SmartFilterHelper.Decode(filter.Filter); var decodedFilter = SmartFilterHelper.Decode(filter.Filter);
var series = await _unitOfWork.SeriesRepository.GetSeriesDtoForLibraryIdV2Async(userId, GetUserParams(pageNumber), var series = await _unitOfWork.SeriesRepository.GetSeriesDtoForLibraryIdV2Async(userId, GetUserParams(pageNumber),
@ -331,7 +332,7 @@ public class OpdsController : BaseApiController
feed.Entries.Add(CreateSeries(seriesDto, seriesMetadatas.First(s => s.SeriesId == seriesDto.Id), apiKey, prefix, baseUrl)); feed.Entries.Add(CreateSeries(seriesDto, seriesMetadatas.First(s => s.SeriesId == seriesDto.Id), apiKey, prefix, baseUrl));
} }
AddPagination(feed, series, $"{prefix}{apiKey}/smart-filter/{filterId}/"); AddPagination(feed, series, $"{prefix}{apiKey}/smart-filters/{filterId}/");
return CreateXmlResult(SerializeXml(feed)); return CreateXmlResult(SerializeXml(feed));
} }
@ -345,18 +346,20 @@ public class OpdsController : BaseApiController
var (_, prefix) = await GetPrefix(); var (_, prefix) = await GetPrefix();
var filters = _unitOfWork.AppUserSmartFilterRepository.GetAllDtosByUserId(userId); var filters = _unitOfWork.AppUserSmartFilterRepository.GetAllDtosByUserId(userId);
var feed = CreateFeed(await _localizationService.Translate(userId, "smartFilters"), $"{prefix}{apiKey}/smart-filters", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "smartFilters"), $"{apiKey}/smart-filters", apiKey, prefix);
SetFeedId(feed, "smartFilters"); SetFeedId(feed, "smartFilters");
foreach (var filter in filters) foreach (var filter in filters)
{ {
feed.Entries.Add(new FeedEntry() feed.Entries.Add(new FeedEntry()
{ {
Id = filter.Id.ToString(), Id = filter.Id.ToString(),
Title = filter.Name, Title = filter.Name,
Links = new List<FeedLink>() Links =
{ [
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, $"{prefix}{apiKey}/smart-filter/{filter.Id}") CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation,
} $"{prefix}{apiKey}/smart-filters/{filter.Id}")
]
}); });
} }
@ -374,7 +377,7 @@ public class OpdsController : BaseApiController
var (_, prefix) = await GetPrefix(); var (_, prefix) = await GetPrefix();
var externalSources = await _unitOfWork.AppUserExternalSourceRepository.GetExternalSources(userId); var externalSources = await _unitOfWork.AppUserExternalSourceRepository.GetExternalSources(userId);
var feed = CreateFeed(await _localizationService.Translate(userId, "external-sources"), $"{prefix}{apiKey}/external-sources", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "external-sources"), $"{apiKey}/external-sources", apiKey, prefix);
SetFeedId(feed, "externalSources"); SetFeedId(feed, "externalSources");
foreach (var externalSource in externalSources) foreach (var externalSource in externalSources)
{ {
@ -404,7 +407,7 @@ public class OpdsController : BaseApiController
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds) if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
return BadRequest(await _localizationService.Translate(userId, "opds-disabled")); return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
var (baseUrl, prefix) = await GetPrefix(); var (baseUrl, prefix) = await GetPrefix();
var feed = CreateFeed(await _localizationService.Translate(userId, "libraries"), $"{prefix}{apiKey}/libraries", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "libraries"), $"{apiKey}/libraries", apiKey, prefix);
SetFeedId(feed, "libraries"); SetFeedId(feed, "libraries");
// Ensure libraries follow SideNav order // Ensure libraries follow SideNav order
@ -415,12 +418,15 @@ public class OpdsController : BaseApiController
{ {
Id = library!.Id.ToString(), Id = library!.Id.ToString(),
Title = library.Name!, Title = library.Name!,
Links = new List<FeedLink>() Links =
{ [
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, $"{prefix}{apiKey}/libraries/{library.Id}"), CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation,
CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"{baseUrl}api/image/library-cover?libraryId={library.Id}&apiKey={apiKey}"), $"{prefix}{apiKey}/libraries/{library.Id}"),
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image, $"{baseUrl}api/image/library-cover?libraryId={library.Id}&apiKey={apiKey}") CreateLink(FeedLinkRelation.Image, FeedLinkType.Image,
} $"{baseUrl}api/image/library-cover?libraryId={library.Id}&apiKey={apiKey}"),
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image,
$"{baseUrl}api/image/library-cover?libraryId={library.Id}&apiKey={apiKey}")
]
}); });
} }
@ -462,7 +468,7 @@ public class OpdsController : BaseApiController
var tags = await _unitOfWork.CollectionTagRepository.GetCollectionDtosAsync(user.Id, true); var tags = await _unitOfWork.CollectionTagRepository.GetCollectionDtosAsync(user.Id, true);
var (baseUrl, prefix) = await GetPrefix(); var (baseUrl, prefix) = await GetPrefix();
var feed = CreateFeed(await _localizationService.Translate(userId, "collections"), $"{prefix}{apiKey}/collections", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "collections"), $"{apiKey}/collections", apiKey, prefix);
SetFeedId(feed, "collections"); SetFeedId(feed, "collections");
feed.Entries.AddRange(tags.Select(tag => new FeedEntry() feed.Entries.AddRange(tags.Select(tag => new FeedEntry()
@ -505,7 +511,7 @@ public class OpdsController : BaseApiController
var series = await _unitOfWork.SeriesRepository.GetSeriesDtoForCollectionAsync(collectionId, userId, GetUserParams(pageNumber)); var series = await _unitOfWork.SeriesRepository.GetSeriesDtoForCollectionAsync(collectionId, userId, GetUserParams(pageNumber));
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(series.Select(s => s.Id)); var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(series.Select(s => s.Id));
var feed = CreateFeed(tag.Title + " Collection", $"{prefix}{apiKey}/collections/{collectionId}", apiKey, prefix); var feed = CreateFeed(tag.Title + " Collection", $"{apiKey}/collections/{collectionId}", apiKey, prefix);
SetFeedId(feed, $"collections-{collectionId}"); SetFeedId(feed, $"collections-{collectionId}");
AddPagination(feed, series, $"{prefix}{apiKey}/collections/{collectionId}"); AddPagination(feed, series, $"{prefix}{apiKey}/collections/{collectionId}");
@ -531,7 +537,7 @@ public class OpdsController : BaseApiController
true, GetUserParams(pageNumber), false); true, GetUserParams(pageNumber), false);
var feed = CreateFeed("All Reading Lists", $"{prefix}{apiKey}/reading-list", apiKey, prefix); var feed = CreateFeed("All Reading Lists", $"{apiKey}/reading-list", apiKey, prefix);
SetFeedId(feed, "reading-list"); SetFeedId(feed, "reading-list");
foreach (var readingListDto in readingLists) foreach (var readingListDto in readingLists)
{ {
@ -579,7 +585,7 @@ public class OpdsController : BaseApiController
return BadRequest(await _localizationService.Translate(userId, "reading-list-restricted")); return BadRequest(await _localizationService.Translate(userId, "reading-list-restricted"));
} }
var feed = CreateFeed(readingList.Title + " " + await _localizationService.Translate(userId, "reading-list"), $"{prefix}{apiKey}/reading-list/{readingListId}", apiKey, prefix); var feed = CreateFeed(readingList.Title + " " + await _localizationService.Translate(userId, "reading-list"), $"{apiKey}/reading-list/{readingListId}", apiKey, prefix);
SetFeedId(feed, $"reading-list-{readingListId}"); SetFeedId(feed, $"reading-list-{readingListId}");
var items = (await _unitOfWork.ReadingListRepository.GetReadingListItemDtosByIdAsync(readingListId, userId)).ToList(); var items = (await _unitOfWork.ReadingListRepository.GetReadingListItemDtosByIdAsync(readingListId, userId)).ToList();
@ -587,7 +593,7 @@ public class OpdsController : BaseApiController
{ {
feed.Entries.Add( feed.Entries.Add(
CreateChapter(apiKey, $"{item.Order} - {item.SeriesName}: {item.Title}", CreateChapter(apiKey, $"{item.Order} - {item.SeriesName}: {item.Title}",
string.Empty, item.ChapterId, item.VolumeId, item.SeriesId, prefix, baseUrl)); item.Summary ?? string.Empty, item.ChapterId, item.VolumeId, item.SeriesId, prefix, baseUrl));
} }
return CreateXmlResult(SerializeXml(feed)); return CreateXmlResult(SerializeXml(feed));
} }
@ -644,7 +650,7 @@ public class OpdsController : BaseApiController
var recentlyAdded = await _unitOfWork.SeriesRepository.GetRecentlyAddedV2(userId, GetUserParams(pageNumber), _filterV2Dto); var recentlyAdded = await _unitOfWork.SeriesRepository.GetRecentlyAddedV2(userId, GetUserParams(pageNumber), _filterV2Dto);
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(recentlyAdded.Select(s => s.Id)); var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(recentlyAdded.Select(s => s.Id));
var feed = CreateFeed(await _localizationService.Translate(userId, "recently-added"), $"{prefix}{apiKey}/recently-added", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "recently-added"), $"{apiKey}/recently-added", apiKey, prefix);
SetFeedId(feed, "recently-added"); SetFeedId(feed, "recently-added");
AddPagination(feed, recentlyAdded, $"{prefix}{apiKey}/recently-added"); AddPagination(feed, recentlyAdded, $"{prefix}{apiKey}/recently-added");
@ -668,7 +674,7 @@ public class OpdsController : BaseApiController
var seriesDtos = await _unitOfWork.SeriesRepository.GetMoreIn(userId, 0, genreId, GetUserParams(pageNumber)); var seriesDtos = await _unitOfWork.SeriesRepository.GetMoreIn(userId, 0, genreId, GetUserParams(pageNumber));
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(seriesDtos.Select(s => s.Id)); var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(seriesDtos.Select(s => s.Id));
var feed = CreateFeed(await _localizationService.Translate(userId, "more-in-genre", genre.Title), $"{prefix}{apiKey}/more-in-genre", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "more-in-genre", genre.Title), $"{apiKey}/more-in-genre", apiKey, prefix);
SetFeedId(feed, "more-in-genre"); SetFeedId(feed, "more-in-genre");
AddPagination(feed, seriesDtos, $"{prefix}{apiKey}/more-in-genre"); AddPagination(feed, seriesDtos, $"{prefix}{apiKey}/more-in-genre");
@ -691,9 +697,8 @@ public class OpdsController : BaseApiController
var seriesDtos = (await _unitOfWork.SeriesRepository.GetRecentlyUpdatedSeries(userId, PageSize)).ToList(); var seriesDtos = (await _unitOfWork.SeriesRepository.GetRecentlyUpdatedSeries(userId, PageSize)).ToList();
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(seriesDtos.Select(s => s.SeriesId)); var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(seriesDtos.Select(s => s.SeriesId));
var feed = CreateFeed(await _localizationService.Translate(userId, "recently-updated"), $"{prefix}{apiKey}/recently-updated", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "recently-updated"), $"{apiKey}/recently-updated", apiKey, prefix);
SetFeedId(feed, "recently-updated"); SetFeedId(feed, "recently-updated");
//AddPagination(feed, seriesDtos, $"{prefix}{apiKey}/recently-updated");
foreach (var groupedSeries in seriesDtos) foreach (var groupedSeries in seriesDtos)
{ {
@ -727,7 +732,7 @@ public class OpdsController : BaseApiController
Response.AddPaginationHeader(pagedList.CurrentPage, pagedList.PageSize, pagedList.TotalCount, pagedList.TotalPages); Response.AddPaginationHeader(pagedList.CurrentPage, pagedList.PageSize, pagedList.TotalCount, pagedList.TotalPages);
var feed = CreateFeed(await _localizationService.Translate(userId, "on-deck"), $"{prefix}{apiKey}/on-deck", apiKey, prefix); var feed = CreateFeed(await _localizationService.Translate(userId, "on-deck"), $"{apiKey}/on-deck", apiKey, prefix);
SetFeedId(feed, "on-deck"); SetFeedId(feed, "on-deck");
AddPagination(feed, pagedList, $"{prefix}{apiKey}/on-deck"); AddPagination(feed, pagedList, $"{prefix}{apiKey}/on-deck");
@ -762,7 +767,7 @@ public class OpdsController : BaseApiController
var series = await _unitOfWork.SeriesRepository.SearchSeries(userId, isAdmin, libraries.Select(l => l.Id).ToArray(), query); var series = await _unitOfWork.SeriesRepository.SearchSeries(userId, isAdmin, libraries.Select(l => l.Id).ToArray(), query);
var feed = CreateFeed(query, $"{prefix}{apiKey}/series?query=" + query, apiKey, prefix); var feed = CreateFeed(query, $"{apiKey}/series?query=" + query, apiKey, prefix);
SetFeedId(feed, "search-series"); SetFeedId(feed, "search-series");
foreach (var seriesDto in series.Series) foreach (var seriesDto in series.Series)
{ {
@ -846,7 +851,7 @@ public class OpdsController : BaseApiController
var (baseUrl, prefix) = await GetPrefix(); var (baseUrl, prefix) = await GetPrefix();
var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId); var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId);
var feed = CreateFeed(series!.Name + " - Storyline", $"{prefix}{apiKey}/series/{series.Id}", apiKey, prefix); var feed = CreateFeed(series!.Name + " - Storyline", $"{apiKey}/series/{series.Id}", apiKey, prefix);
SetFeedId(feed, $"series-{series.Id}"); SetFeedId(feed, $"series-{series.Id}");
feed.Links.Add(CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"{baseUrl}api/image/series-cover?seriesId={seriesId}&apiKey={apiKey}")); feed.Links.Add(CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"{baseUrl}api/image/series-cover?seriesId={seriesId}&apiKey={apiKey}"));
@ -914,11 +919,11 @@ public class OpdsController : BaseApiController
(await _unitOfWork.ChapterRepository.GetChaptersAsync(volumeId)) (await _unitOfWork.ChapterRepository.GetChaptersAsync(volumeId))
.OrderBy(x => x.MinNumber, _chapterSortComparerDefaultLast); .OrderBy(x => x.MinNumber, _chapterSortComparerDefaultLast);
var feed = CreateFeed(series.Name + " - Volume " + volume!.Name + $" - {_seriesService.FormatChapterName(userId, libraryType)}s ", var feed = CreateFeed(series.Name + " - Volume " + volume!.Name + $" - {_seriesService.FormatChapterName(userId, libraryType)}s ",
$"{prefix}{apiKey}/series/{seriesId}/volume/{volumeId}", apiKey, prefix); $"{apiKey}/series/{seriesId}/volume/{volumeId}", apiKey, prefix);
SetFeedId(feed, $"series-{series.Id}-volume-{volume.Id}-{_seriesService.FormatChapterName(userId, libraryType)}s"); SetFeedId(feed, $"series-{series.Id}-volume-{volume.Id}-{_seriesService.FormatChapterName(userId, libraryType)}s");
foreach (var chapter in chapters) foreach (var chapter in chapters)
{ {
//var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapter.Id);
var chapterDto = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id, ChapterIncludes.Files | ChapterIncludes.People); var chapterDto = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id, ChapterIncludes.Files | ChapterIncludes.People);
foreach (var mangaFile in chapterDto.Files) foreach (var mangaFile in chapterDto.Files)
{ {
@ -947,7 +952,7 @@ public class OpdsController : BaseApiController
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(volumeId); var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(volumeId);
var feed = CreateFeed(series.Name + " - Volume " + volume!.Name + $" - {_seriesService.FormatChapterName(userId, libraryType)}s", var feed = CreateFeed(series.Name + " - Volume " + volume!.Name + $" - {_seriesService.FormatChapterName(userId, libraryType)}s",
$"{prefix}{apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}", apiKey, prefix); $"{apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}", apiKey, prefix);
SetFeedId(feed, $"series-{series.Id}-volume-{volumeId}-{_seriesService.FormatChapterName(userId, libraryType)}-{chapterId}-files"); SetFeedId(feed, $"series-{series.Id}-volume-{volumeId}-{_seriesService.FormatChapterName(userId, libraryType)}-{chapterId}-files");
foreach (var mangaFile in chapter.Files) foreach (var mangaFile in chapter.Files)
@ -1074,16 +1079,19 @@ public class OpdsController : BaseApiController
Id = searchResultDto.SeriesId.ToString(), Id = searchResultDto.SeriesId.ToString(),
Title = $"{searchResultDto.Name}", Title = $"{searchResultDto.Name}",
Summary = $"Format: {searchResultDto.Format}", Summary = $"Format: {searchResultDto.Format}",
Links = new List<FeedLink>() Links =
{ [
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, $"{prefix}{apiKey}/series/{searchResultDto.SeriesId}"), CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation,
CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"{baseUrl}api/image/series-cover?seriesId={searchResultDto.SeriesId}&apiKey={apiKey}"), $"{prefix}{apiKey}/series/{searchResultDto.SeriesId}"),
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image, $"{baseUrl}api/image/series-cover?seriesId={searchResultDto.SeriesId}&apiKey={apiKey}") CreateLink(FeedLinkRelation.Image, FeedLinkType.Image,
} $"{baseUrl}api/image/series-cover?seriesId={searchResultDto.SeriesId}&apiKey={apiKey}"),
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image,
$"{baseUrl}api/image/series-cover?seriesId={searchResultDto.SeriesId}&apiKey={apiKey}")
]
}; };
} }
private static FeedEntry CreateChapter(string apiKey, string title, string summary, int chapterId, int volumeId, int seriesId, string prefix, string baseUrl) private static FeedEntry CreateChapter(string apiKey, string title, string? summary, int chapterId, int volumeId, int seriesId, string prefix, string baseUrl)
{ {
return new FeedEntry() return new FeedEntry()
{ {
@ -1091,15 +1099,15 @@ public class OpdsController : BaseApiController
Title = title, Title = title,
Summary = summary ?? string.Empty, Summary = summary ?? string.Empty,
Links = new List<FeedLink>() Links =
{ [
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation,
$"{prefix}{apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}"), $"{prefix}{apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}"),
CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, CreateLink(FeedLinkRelation.Image, FeedLinkType.Image,
$"{baseUrl}api/image/chapter-cover?chapterId={chapterId}&apiKey={apiKey}"), $"{baseUrl}api/image/chapter-cover?chapterId={chapterId}&apiKey={apiKey}"),
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image, CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image,
$"{baseUrl}api/image/chapter-cover?chapterId={chapterId}&apiKey={apiKey}") $"{baseUrl}api/image/chapter-cover?chapterId={chapterId}&apiKey={apiKey}")
} ]
}; };
} }

View File

@ -39,4 +39,8 @@ public class ReadingListItemDto
/// </summary> /// </summary>
/// <remarks>This is only used for CDisplayEx</remarks> /// <remarks>This is only used for CDisplayEx</remarks>
public long FileSize { get; set; } public long FileSize { get; set; }
/// <summary>
/// The chapter summary
/// </summary>
public string? Summary { get; set; }
} }

View File

@ -206,13 +206,7 @@ public class ReadingListRepository : IReadingListRepository
public async Task<IEnumerable<ReadingListItemDto>> GetReadingListItemDtosByIdAsync(int readingListId, int userId) public async Task<IEnumerable<ReadingListItemDto>> GetReadingListItemDtosByIdAsync(int readingListId, int userId)
{ {
var userLibraries = _context.Library var userLibraries = _context.Library.GetUserLibraries(userId);
.Include(l => l.AppUsers)
.Where(library => library.AppUsers.Any(user => user.Id == userId))
.AsSplitQuery()
.AsNoTracking()
.Select(library => library.Id)
.ToList();
var items = await _context.ReadingListItem var items = await _context.ReadingListItem
.Where(s => s.ReadingListId == readingListId) .Where(s => s.ReadingListId == readingListId)
@ -223,7 +217,8 @@ public class ReadingListRepository : IReadingListRepository
chapter.ReleaseDate, chapter.ReleaseDate,
ReadingListItem = data, ReadingListItem = data,
ChapterTitleName = chapter.TitleName, ChapterTitleName = chapter.TitleName,
FileSize = chapter.Files.Sum(f => f.Bytes) FileSize = chapter.Files.Sum(f => f.Bytes),
chapter.Summary,
}) })
.Join(_context.Volume, s => s.ReadingListItem.VolumeId, volume => volume.Id, (data, volume) => new .Join(_context.Volume, s => s.ReadingListItem.VolumeId, volume => volume.Id, (data, volume) => new
@ -234,6 +229,7 @@ public class ReadingListRepository : IReadingListRepository
data.ReleaseDate, data.ReleaseDate,
data.ChapterTitleName, data.ChapterTitleName,
data.FileSize, data.FileSize,
data.Summary,
VolumeId = volume.Id, VolumeId = volume.Id,
VolumeNumber = volume.Name, VolumeNumber = volume.Name,
}) })
@ -251,6 +247,7 @@ public class ReadingListRepository : IReadingListRepository
data.ReleaseDate, data.ReleaseDate,
data.ChapterTitleName, data.ChapterTitleName,
data.FileSize, data.FileSize,
data.Summary,
LibraryName = _context.Library.Where(l => l.Id == s.LibraryId).Select(l => l.Name).Single(), LibraryName = _context.Library.Where(l => l.Id == s.LibraryId).Select(l => l.Name).Single(),
LibraryType = _context.Library.Where(l => l.Id == s.LibraryId).Select(l => l.Type).Single() LibraryType = _context.Library.Where(l => l.Id == s.LibraryId).Select(l => l.Type).Single()
}) })
@ -272,7 +269,8 @@ public class ReadingListRepository : IReadingListRepository
LibraryType = data.LibraryType, LibraryType = data.LibraryType,
ChapterTitleName = data.ChapterTitleName, ChapterTitleName = data.ChapterTitleName,
LibraryName = data.LibraryName, LibraryName = data.LibraryName,
FileSize = data.FileSize FileSize = data.FileSize,
Summary = data.Summary
}) })
.Where(o => userLibraries.Contains(o.LibraryId)) .Where(o => userLibraries.Contains(o.LibraryId))
.OrderBy(rli => rli.Order) .OrderBy(rli => rli.Order)

View File

@ -579,45 +579,61 @@ public class ProcessSeries : IProcessSeries
} }
private static void DeterminePublicationStatus(Series series, List<Chapter> chapters) private void DeterminePublicationStatus(Series series, List<Chapter> chapters)
{ {
// Count (aka expected total number of chapters or volumes from metadata) across all chapters try
series.Metadata.TotalCount = chapters.Max(chapter => chapter.TotalCount); {
// The actual number of count's defined across all chapter's metadata // Count (aka expected total number of chapters or volumes from metadata) across all chapters
series.Metadata.MaxCount = chapters.Max(chapter => chapter.Count); series.Metadata.TotalCount = chapters.Max(chapter => chapter.TotalCount);
// The actual number of count's defined across all chapter's metadata
series.Metadata.MaxCount = chapters.Max(chapter => chapter.Count);
var maxVolume = (int) series.Volumes.Where(v => v.MaxNumber.IsNot(Parser.Parser.SpecialVolumeNumber)).Max(v => v.MaxNumber); var nonSpecialVolumes = series.Volumes.Where(v => v.MaxNumber.IsNot(Parser.Parser.SpecialVolumeNumber));
var maxChapter = (int) chapters.Max(c => c.MaxNumber);
// Single books usually don't have a number in their Range (filename) var maxVolume = (int) (nonSpecialVolumes.Any() ? nonSpecialVolumes.Max(v => v.MaxNumber) : 0);
if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1) var maxChapter = (int) chapters.Max(c => c.MaxNumber);
{
series.Metadata.MaxCount = 1;
} else if (series.Metadata.TotalCount <= 1 && chapters.Count == 1 && chapters[0].IsSpecial)
{
// If a series has a TotalCount of 1 (or no total count) and there is only a Special, mark it as Complete
series.Metadata.MaxCount = series.Metadata.TotalCount;
} else if ((maxChapter == Parser.Parser.DefaultChapterNumber || maxChapter > series.Metadata.TotalCount) && maxVolume <= series.Metadata.TotalCount)
{
series.Metadata.MaxCount = maxVolume;
} else if (maxVolume == series.Metadata.TotalCount)
{
series.Metadata.MaxCount = maxVolume;
} else
{
series.Metadata.MaxCount = maxChapter;
}
if (!series.Metadata.PublicationStatusLocked) // Single books usually don't have a number in their Range (filename)
{ if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1)
series.Metadata.PublicationStatus = PublicationStatus.OnGoing;
if (series.Metadata.MaxCount == series.Metadata.TotalCount && series.Metadata.TotalCount > 0)
{ {
series.Metadata.PublicationStatus = PublicationStatus.Completed; series.Metadata.MaxCount = 1;
} else if (series.Metadata.TotalCount > 0 && series.Metadata.MaxCount > 0)
{
series.Metadata.PublicationStatus = PublicationStatus.Ended;
} }
else if (series.Metadata.TotalCount <= 1 && chapters.Count == 1 && chapters[0].IsSpecial)
{
// If a series has a TotalCount of 1 (or no total count) and there is only a Special, mark it as Complete
series.Metadata.MaxCount = series.Metadata.TotalCount;
}
else if ((maxChapter == Parser.Parser.DefaultChapterNumber || maxChapter > series.Metadata.TotalCount) &&
maxVolume <= series.Metadata.TotalCount)
{
series.Metadata.MaxCount = maxVolume;
}
else if (maxVolume == series.Metadata.TotalCount)
{
series.Metadata.MaxCount = maxVolume;
}
else
{
series.Metadata.MaxCount = maxChapter;
}
if (!series.Metadata.PublicationStatusLocked)
{
series.Metadata.PublicationStatus = PublicationStatus.OnGoing;
if (series.Metadata.MaxCount == series.Metadata.TotalCount && series.Metadata.TotalCount > 0)
{
series.Metadata.PublicationStatus = PublicationStatus.Completed;
}
else if (series.Metadata.TotalCount > 0 && series.Metadata.MaxCount > 0)
{
series.Metadata.PublicationStatus = PublicationStatus.Ended;
}
}
}
catch (Exception ex)
{
_logger.LogCritical(ex, "There was an issue determining Publication Status");
series.Metadata.PublicationStatus = PublicationStatus.OnGoing;
} }
} }

View File

@ -2,7 +2,7 @@
"TokenKey": "super secret unguessable key that is longer because we require it", "TokenKey": "super secret unguessable key that is longer because we require it",
"Port": 5000, "Port": 5000,
"IpAddresses": "0.0.0.0,::", "IpAddresses": "0.0.0.0,::",
"BaseUrl": "/", "BaseUrl": "/tes/",
"Cache": 75, "Cache": 75,
"AllowIFraming": false "AllowIFraming": false
} }

View File

@ -17,6 +17,7 @@ export interface ReadingListItem {
title: string; title: string;
libraryType: LibraryType; libraryType: LibraryType;
libraryName: string; libraryName: string;
summary?: string;
} }
export interface ReadingList { export interface ReadingList {

View File

@ -32,12 +32,13 @@
</div> </div>
</h5> </h5>
<div class="ps-1 d-none d-md-inline-block"> <div class="ps-1 d-none d-md-inline-block mb-1">
<app-series-format [format]="item.seriesFormat"></app-series-format> <app-series-format [format]="item.seriesFormat"></app-series-format>
<a href="/library/{{item.libraryId}}/series/{{item.seriesId}}">{{item.seriesName}}</a> <a href="/library/{{item.libraryId}}/series/{{item.seriesId}}">{{item.seriesName}}</a>
</div> </div>
<!-- TODO: Let's add summary here--> <app-read-more [text]="item.summary || ''"></app-read-more>
@if (item.releaseDate !== '0001-01-01T00:00:00') { @if (item.releaseDate !== '0001-01-01T00:00:00') {
<div class="ps-1 mt-2"> <div class="ps-1 mt-2">

View File

@ -10,6 +10,7 @@ import { DatePipe } from '@angular/common';
import { ImageComponent } from '../../../shared/image/image.component'; import { ImageComponent } from '../../../shared/image/image.component';
import {TranslocoDirective} from "@ngneat/transloco"; import {TranslocoDirective} from "@ngneat/transloco";
import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component"; import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component";
import {ReadMoreComponent} from "../../../shared/read-more/read-more.component";
@Component({ @Component({
selector: 'app-reading-list-item', selector: 'app-reading-list-item',
@ -17,7 +18,7 @@ import {SeriesFormatComponent} from "../../../shared/series-format/series-format
styleUrls: ['./reading-list-item.component.scss'], styleUrls: ['./reading-list-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true, standalone: true,
imports: [ImageComponent, NgbProgressbar, DatePipe, MangaFormatPipe, MangaFormatIconPipe, TranslocoDirective, SeriesFormatComponent] imports: [ImageComponent, NgbProgressbar, DatePipe, MangaFormatPipe, MangaFormatIconPipe, TranslocoDirective, SeriesFormatComponent, ReadMoreComponent]
}) })
export class ReadingListItemComponent { export class ReadingListItemComponent {