213 lines
6.8 KiB
Go
213 lines
6.8 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"seanime/internal/api/anilist"
|
|
"seanime/internal/database/db_bridge"
|
|
"seanime/internal/library/anime"
|
|
"seanime/internal/torrentstream"
|
|
"seanime/internal/util"
|
|
"seanime/internal/util/result"
|
|
"time"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// HandleGetLibraryCollection
|
|
//
|
|
// @summary returns the main local anime collection.
|
|
// @desc This creates a new LibraryCollection struct and returns it.
|
|
// @desc This is used to get the main anime collection of the user.
|
|
// @desc It uses the cached Anilist anime collection for the GET method.
|
|
// @desc It refreshes the AniList anime collection if the POST method is used.
|
|
// @route /api/v1/library/collection [GET,POST]
|
|
// @returns anime.LibraryCollection
|
|
func (h *Handler) HandleGetLibraryCollection(c echo.Context) error {
|
|
|
|
animeCollection, err := h.App.GetAnimeCollection(false)
|
|
if err != nil {
|
|
return h.RespondWithError(c, err)
|
|
}
|
|
|
|
if animeCollection == nil {
|
|
return h.RespondWithData(c, &anime.LibraryCollection{})
|
|
}
|
|
|
|
originalAnimeCollection := animeCollection
|
|
|
|
var lfs []*anime.LocalFile
|
|
nakamaLibrary, fromNakama := h.App.NakamaManager.GetHostAnimeLibrary()
|
|
if fromNakama {
|
|
// Save the original anime collection to restore it later
|
|
originalAnimeCollection = animeCollection.Copy()
|
|
lfs = nakamaLibrary.LocalFiles
|
|
// Merge missing media entries into the collection
|
|
currentMediaIds := make(map[int]struct{})
|
|
for _, list := range animeCollection.MediaListCollection.GetLists() {
|
|
for _, entry := range list.GetEntries() {
|
|
currentMediaIds[entry.GetMedia().GetID()] = struct{}{}
|
|
}
|
|
}
|
|
|
|
nakamaMediaIds := make(map[int]struct{})
|
|
for _, lf := range lfs {
|
|
if lf.MediaId > 0 {
|
|
nakamaMediaIds[lf.MediaId] = struct{}{}
|
|
}
|
|
}
|
|
|
|
missingMediaIds := make(map[int]struct{})
|
|
for _, lf := range lfs {
|
|
if lf.MediaId > 0 {
|
|
if _, ok := currentMediaIds[lf.MediaId]; !ok {
|
|
missingMediaIds[lf.MediaId] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, list := range nakamaLibrary.AnimeCollection.MediaListCollection.GetLists() {
|
|
for _, entry := range list.GetEntries() {
|
|
if _, ok := missingMediaIds[entry.GetMedia().GetID()]; ok {
|
|
// create a new entry with blank list data
|
|
newEntry := &anilist.AnimeListEntry{
|
|
ID: entry.GetID(),
|
|
Media: entry.GetMedia(),
|
|
Status: &[]anilist.MediaListStatus{anilist.MediaListStatusPlanning}[0],
|
|
}
|
|
animeCollection.MediaListCollection.AddEntryToList(newEntry, anilist.MediaListStatusPlanning)
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
lfs, _, err = db_bridge.GetLocalFiles(h.App.Database)
|
|
if err != nil {
|
|
return h.RespondWithError(c, err)
|
|
}
|
|
}
|
|
|
|
libraryCollection, err := anime.NewLibraryCollection(c.Request().Context(), &anime.NewLibraryCollectionOptions{
|
|
AnimeCollection: animeCollection,
|
|
Platform: h.App.AnilistPlatform,
|
|
LocalFiles: lfs,
|
|
MetadataProvider: h.App.MetadataProvider,
|
|
})
|
|
if err != nil {
|
|
return h.RespondWithError(c, err)
|
|
}
|
|
|
|
// Restore the original anime collection if it was modified
|
|
if fromNakama {
|
|
*animeCollection = *originalAnimeCollection
|
|
}
|
|
|
|
if !fromNakama {
|
|
if (h.App.SecondarySettings.Torrentstream != nil && h.App.SecondarySettings.Torrentstream.Enabled && h.App.SecondarySettings.Torrentstream.IncludeInLibrary) ||
|
|
(h.App.Settings.GetLibrary() != nil && h.App.Settings.GetLibrary().EnableOnlinestream && h.App.Settings.GetLibrary().IncludeOnlineStreamingInLibrary) ||
|
|
(h.App.SecondarySettings.Debrid != nil && h.App.SecondarySettings.Debrid.Enabled && h.App.SecondarySettings.Debrid.IncludeDebridStreamInLibrary) {
|
|
h.App.TorrentstreamRepository.HydrateStreamCollection(&torrentstream.HydrateStreamCollectionOptions{
|
|
AnimeCollection: animeCollection,
|
|
LibraryCollection: libraryCollection,
|
|
MetadataProvider: h.App.MetadataProvider,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Add and remove necessary metadata when hydrating from Nakama
|
|
if fromNakama {
|
|
for _, ep := range libraryCollection.ContinueWatchingList {
|
|
ep.IsNakamaEpisode = true
|
|
}
|
|
for _, list := range libraryCollection.Lists {
|
|
for _, entry := range list.Entries {
|
|
if entry.EntryLibraryData == nil {
|
|
continue
|
|
}
|
|
entry.NakamaEntryLibraryData = &anime.NakamaEntryLibraryData{
|
|
UnwatchedCount: entry.EntryLibraryData.UnwatchedCount,
|
|
MainFileCount: entry.EntryLibraryData.MainFileCount,
|
|
}
|
|
entry.EntryLibraryData = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hydrate total library size
|
|
if libraryCollection != nil && libraryCollection.Stats != nil {
|
|
libraryCollection.Stats.TotalSize = util.Bytes(h.App.TotalLibrarySize)
|
|
}
|
|
|
|
return h.RespondWithData(c, libraryCollection)
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
var animeScheduleCache = result.NewCache[int, []*anime.ScheduleItem]()
|
|
|
|
// HandleGetAnimeCollectionSchedule
|
|
//
|
|
// @summary returns anime collection schedule
|
|
// @desc This is used by the "Schedule" page to display the anime schedule.
|
|
// @route /api/v1/library/schedule [GET]
|
|
// @returns []anime.ScheduleItem
|
|
func (h *Handler) HandleGetAnimeCollectionSchedule(c echo.Context) error {
|
|
|
|
// Invalidate the cache when the Anilist collection is refreshed
|
|
h.App.AddOnRefreshAnilistCollectionFunc("HandleGetAnimeCollectionSchedule", func() {
|
|
animeScheduleCache.Clear()
|
|
})
|
|
|
|
if ret, ok := animeScheduleCache.Get(1); ok {
|
|
return h.RespondWithData(c, ret)
|
|
}
|
|
|
|
animeSchedule, err := h.App.AnilistPlatform.GetAnimeAiringSchedule(c.Request().Context())
|
|
if err != nil {
|
|
return h.RespondWithError(c, err)
|
|
}
|
|
|
|
animeCollection, err := h.App.GetAnimeCollection(false)
|
|
if err != nil {
|
|
return h.RespondWithError(c, err)
|
|
}
|
|
|
|
ret := anime.GetScheduleItems(animeSchedule, animeCollection)
|
|
|
|
animeScheduleCache.SetT(1, ret, 1*time.Hour)
|
|
|
|
return h.RespondWithData(c, ret)
|
|
}
|
|
|
|
// HandleAddUnknownMedia
|
|
//
|
|
// @summary adds the given media to the user's AniList planning collections
|
|
// @desc Since media not found in the user's AniList collection are not displayed in the library, this route is used to add them.
|
|
// @desc The response is ignored in the frontend, the client should just refetch the entire library collection.
|
|
// @route /api/v1/library/unknown-media [POST]
|
|
// @returns anilist.AnimeCollection
|
|
func (h *Handler) HandleAddUnknownMedia(c echo.Context) error {
|
|
|
|
type body struct {
|
|
MediaIds []int `json:"mediaIds"`
|
|
}
|
|
|
|
b := new(body)
|
|
if err := c.Bind(b); err != nil {
|
|
return h.RespondWithError(c, err)
|
|
}
|
|
|
|
// Add non-added media entries to AniList collection
|
|
if err := h.App.AnilistPlatform.AddMediaToCollection(c.Request().Context(), b.MediaIds); err != nil {
|
|
return h.RespondWithError(c, errors.New("error: Anilist responded with an error, this is most likely a rate limit issue"))
|
|
}
|
|
|
|
// Bypass the cache
|
|
animeCollection, err := h.App.GetAnimeCollection(true)
|
|
if err != nil {
|
|
return h.RespondWithError(c, errors.New("error: Anilist responded with an error, wait one minute before refreshing"))
|
|
}
|
|
|
|
return h.RespondWithData(c, animeCollection)
|
|
|
|
}
|