Files
seanime-docker/seanime-2.9.10/internal/library/anime/entry_simple.go
2025-09-20 14:08:38 +01:00

149 lines
4.2 KiB
Go

package anime
import (
"context"
"errors"
"seanime/internal/api/anilist"
"seanime/internal/platforms/platform"
"sort"
"github.com/sourcegraph/conc/pool"
)
type (
SimpleEntry struct {
MediaId int `json:"mediaId"`
Media *anilist.BaseAnime `json:"media"`
EntryListData *EntryListData `json:"listData"`
EntryLibraryData *EntryLibraryData `json:"libraryData"`
Episodes []*Episode `json:"episodes"`
NextEpisode *Episode `json:"nextEpisode"`
LocalFiles []*LocalFile `json:"localFiles"`
CurrentEpisodeCount int `json:"currentEpisodeCount"`
}
SimpleEntryListData struct {
Progress int `json:"progress,omitempty"`
Score float64 `json:"score,omitempty"`
Status *anilist.MediaListStatus `json:"status,omitempty"`
StartedAt string `json:"startedAt,omitempty"`
CompletedAt string `json:"completedAt,omitempty"`
}
NewSimpleAnimeEntryOptions struct {
MediaId int
LocalFiles []*LocalFile // All local files
AnimeCollection *anilist.AnimeCollection
Platform platform.Platform
}
)
func NewSimpleEntry(ctx context.Context, opts *NewSimpleAnimeEntryOptions) (*SimpleEntry, error) {
if opts.AnimeCollection == nil ||
opts.Platform == nil {
return nil, errors.New("missing arguments when creating simple media entry")
}
// Create new Entry
entry := new(SimpleEntry)
entry.MediaId = opts.MediaId
// +---------------------+
// | AniList entry |
// +---------------------+
// Get the Anilist List entry
anilistEntry, found := opts.AnimeCollection.GetListEntryFromAnimeId(opts.MediaId)
// Set the media
// If the Anilist List entry does not exist, fetch the media from AniList
if !found {
// If the Anilist entry does not exist, instantiate one with zero values
anilistEntry = &anilist.AnimeListEntry{}
// Fetch the media
fetchedMedia, err := opts.Platform.GetAnime(ctx, opts.MediaId) // DEVNOTE: Maybe cache it?
if err != nil {
return nil, err
}
entry.Media = fetchedMedia
} else {
entry.Media = anilistEntry.Media
}
entry.CurrentEpisodeCount = entry.Media.GetCurrentEpisodeCount()
// +---------------------+
// | Local files |
// +---------------------+
// Get the entry's local files
lfs := GetLocalFilesFromMediaId(opts.LocalFiles, opts.MediaId)
entry.LocalFiles = lfs // Returns empty slice if no local files are found
libraryData, _ := NewEntryLibraryData(&NewEntryLibraryDataOptions{
EntryLocalFiles: lfs,
MediaId: entry.Media.ID,
CurrentProgress: anilistEntry.GetProgressSafe(),
})
entry.EntryLibraryData = libraryData
// Instantiate EntryListData
// If the media exist in the user's anime list, add the details
if found {
entry.EntryListData = &EntryListData{
Progress: anilistEntry.GetProgressSafe(),
Score: anilistEntry.GetScoreSafe(),
Status: anilistEntry.Status,
Repeat: anilistEntry.GetRepeatSafe(),
StartedAt: anilist.ToEntryStartDate(anilistEntry.StartedAt),
CompletedAt: anilist.ToEntryCompletionDate(anilistEntry.CompletedAt),
}
}
// +---------------------+
// | Episodes |
// +---------------------+
// Create episode entities
entry.hydrateEntryEpisodeData()
return entry, nil
}
//----------------------------------------------------------------------------------------------------------------------
// hydrateEntryEpisodeData
// AniZipData, Media and LocalFiles should be defined
func (e *SimpleEntry) hydrateEntryEpisodeData() {
// +---------------------+
// | Episodes |
// +---------------------+
p := pool.NewWithResults[*Episode]()
for _, lf := range e.LocalFiles {
lf := lf
p.Go(func() *Episode {
return NewSimpleEpisode(&NewSimpleEpisodeOptions{
LocalFile: lf,
Media: e.Media,
IsDownloaded: true,
})
})
}
episodes := p.Wait()
// Sort by progress number
sort.Slice(episodes, func(i, j int) bool {
return episodes[i].EpisodeNumber < episodes[j].EpisodeNumber
})
e.Episodes = episodes
nextEp, found := e.FindNextEpisode()
if found {
e.NextEpisode = nextEp
}
}