mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Reading List Enhancement (#3005)
This commit is contained in:
parent
6218f84861
commit
3936308f8c
@ -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}")
|
||||||
}
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
@ -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 {
|
||||||
|
@ -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">
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user