node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1,327 @@
package local
import (
"fmt"
"seanime/internal/api/anilist"
hibikemanga "seanime/internal/extension/hibike/manga"
"seanime/internal/library/anime"
"seanime/internal/manga"
"slices"
"strings"
"github.com/rs/zerolog"
"github.com/samber/lo"
"github.com/samber/mo"
)
// DEVNOTE: Here we compare the media data from the current up-to-date collections with the local data.
// Outdated media are added to the Syncer to be updated.
// If the media doesn't have a snapshot -> a new snapshot is created.
// If the reference key is different -> the metadata is re-fetched and the snapshot is updated.
// If the list data is different -> the list data is updated.
const (
DiffTypeMissing DiffType = iota // We need to add a new snapshot
DiffTypeMetadata // We need to re-fetch the snapshot metadata (episode metadata / chapter containers), list data will be updated as well
DiffTypeListData // We need to update the list data
)
type (
Diff struct {
Logger *zerolog.Logger
}
DiffType int
)
//----------------------------------------------------------------------------------------------------------------------------------------------------
type GetAnimeDiffOptions struct {
Collection *anilist.AnimeCollection
LocalCollection mo.Option[*anilist.AnimeCollection]
LocalFiles []*anime.LocalFile
TrackedAnime map[int]*TrackedMedia
Snapshots map[int]*AnimeSnapshot
}
type AnimeDiffResult struct {
AnimeEntry *anilist.AnimeListEntry
AnimeSnapshot *AnimeSnapshot
DiffType DiffType
}
// GetAnimeDiffs returns the anime that have changed.
// The anime is considered changed if:
// - It doesn't have a snapshot
// - The reference key is different (e.g. the number of local files has changed), meaning we need to update the snapshot.
func (d *Diff) GetAnimeDiffs(opts GetAnimeDiffOptions) map[int]*AnimeDiffResult {
collection := opts.Collection
localCollection := opts.LocalCollection
trackedAnimeMap := opts.TrackedAnime
snapshotMap := opts.Snapshots
changedMap := make(map[int]*AnimeDiffResult)
if len(collection.MediaListCollection.Lists) == 0 || len(trackedAnimeMap) == 0 {
return changedMap
}
for _, _list := range collection.MediaListCollection.Lists {
if _list.GetStatus() == nil || _list.GetEntries() == nil {
continue
}
for _, _entry := range _list.GetEntries() {
// Check if the anime is tracked
_, isTracked := trackedAnimeMap[_entry.GetMedia().GetID()]
if !isTracked {
continue
}
if localCollection.IsAbsent() {
d.Logger.Trace().Msgf("local manager: Diff > Anime %d, local collection is missing", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &AnimeDiffResult{
AnimeEntry: _entry,
DiffType: DiffTypeMissing,
}
continue // Go to the next anime
}
// Check if the anime has a snapshot
snapshot, hasSnapshot := snapshotMap[_entry.GetMedia().GetID()]
if !hasSnapshot {
d.Logger.Trace().Msgf("local manager: Diff > Anime %d is missing a snapshot", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &AnimeDiffResult{
AnimeEntry: _entry,
DiffType: DiffTypeMissing,
}
continue // Go to the next anime
}
_lfs := lo.Filter(opts.LocalFiles, func(lf *anime.LocalFile, _ int) bool {
return lf.MediaId == _entry.GetMedia().GetID()
})
// Check if the anime has changed
_referenceKey := GetAnimeReferenceKey(_entry.Media, _lfs)
// Check if the reference key is different
if snapshotMap[_entry.GetMedia().GetID()].ReferenceKey != _referenceKey {
d.Logger.Trace().Str("localReferenceKey", snapshotMap[_entry.GetMedia().GetID()].ReferenceKey).Str("currentReferenceKey", _referenceKey).Msgf("local manager: Diff > Anime %d has an outdated snapshot", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &AnimeDiffResult{
AnimeEntry: _entry,
AnimeSnapshot: snapshot,
DiffType: DiffTypeMetadata,
}
continue // Go to the next anime
}
localEntry, found := localCollection.MustGet().GetListEntryFromAnimeId(_entry.GetMedia().GetID())
if !found {
d.Logger.Trace().Msgf("local manager: Diff > Anime %d is missing from the local collection", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &AnimeDiffResult{
AnimeEntry: _entry,
AnimeSnapshot: snapshot,
DiffType: DiffTypeMissing,
}
continue // Go to the next anime
}
// Check if the list data has changed
_listDataKey := GetAnimeListDataKey(_entry)
localListDataKey := GetAnimeListDataKey(localEntry)
if _listDataKey != localListDataKey {
d.Logger.Trace().Str("localListDataKey", localListDataKey).Str("currentListDataKey", _listDataKey).Msgf("local manager: Diff > Anime %d has changed list data", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &AnimeDiffResult{
AnimeEntry: _entry,
AnimeSnapshot: snapshot,
DiffType: DiffTypeListData,
}
continue // Go to the next anime
}
}
}
return changedMap
}
//----------------------------------------------------------------------------------------------------------------------------------------------------
type GetMangaDiffOptions struct {
Collection *anilist.MangaCollection
LocalCollection mo.Option[*anilist.MangaCollection]
DownloadedChapterContainers []*manga.ChapterContainer
TrackedManga map[int]*TrackedMedia
Snapshots map[int]*MangaSnapshot
}
type MangaDiffResult struct {
MangaEntry *anilist.MangaListEntry
MangaSnapshot *MangaSnapshot
DiffType DiffType
}
// GetMangaDiffs returns the manga that have changed.
func (d *Diff) GetMangaDiffs(opts GetMangaDiffOptions) map[int]*MangaDiffResult {
collection := opts.Collection
localCollection := opts.LocalCollection
trackedMangaMap := opts.TrackedManga
snapshotMap := opts.Snapshots
changedMap := make(map[int]*MangaDiffResult)
if len(collection.MediaListCollection.Lists) == 0 || len(trackedMangaMap) == 0 {
return changedMap
}
for _, _list := range collection.MediaListCollection.Lists {
if _list.GetStatus() == nil || _list.GetEntries() == nil {
continue
}
for _, _entry := range _list.GetEntries() {
// Check if the manga is tracked
_, isTracked := trackedMangaMap[_entry.GetMedia().GetID()]
if !isTracked {
continue
}
if localCollection.IsAbsent() {
d.Logger.Trace().Msgf("local manager: Diff > Manga %d, local collection is missing", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &MangaDiffResult{
MangaEntry: _entry,
DiffType: DiffTypeMissing,
}
continue // Go to the next manga
}
// Check if the manga has a snapshot
snapshot, hasSnapshot := snapshotMap[_entry.GetMedia().GetID()]
if !hasSnapshot {
d.Logger.Trace().Msgf("local manager: Diff > Manga %d is missing a snapshot", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &MangaDiffResult{
MangaEntry: _entry,
DiffType: DiffTypeMissing,
}
continue // Go to the next manga
}
// Check if the manga has changed
_referenceKey := GetMangaReferenceKey(_entry.Media, opts.DownloadedChapterContainers)
// Check if the reference key is different
if snapshotMap[_entry.GetMedia().GetID()].ReferenceKey != _referenceKey {
d.Logger.Trace().Str("localReferenceKey", snapshotMap[_entry.GetMedia().GetID()].ReferenceKey).Str("currentReferenceKey", _referenceKey).Msgf("local manager: Diff > Manga %d has an outdated snapshot", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &MangaDiffResult{
MangaEntry: _entry,
MangaSnapshot: snapshot,
DiffType: DiffTypeMetadata,
}
continue // Go to the next manga
}
localEntry, found := localCollection.MustGet().GetListEntryFromMangaId(_entry.GetMedia().GetID())
if !found {
d.Logger.Trace().Msgf("local manager: Diff > Manga %d is missing from the local collection", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &MangaDiffResult{
MangaEntry: _entry,
MangaSnapshot: snapshot,
DiffType: DiffTypeMissing,
}
continue // Go to the next manga
}
// Check if the list data has changed
_listDataKey := GetMangaListDataKey(_entry)
localListDataKey := GetMangaListDataKey(localEntry)
if _listDataKey != localListDataKey {
d.Logger.Trace().Str("localListDataKey", localListDataKey).Str("currentListDataKey", _listDataKey).Msgf("local manager: Diff > Manga %d has changed list data", _entry.GetMedia().GetID())
changedMap[_entry.GetMedia().GetID()] = &MangaDiffResult{
MangaEntry: _entry,
MangaSnapshot: snapshot,
DiffType: DiffTypeListData,
}
continue // Go to the next manga
}
}
}
return changedMap
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func GetAnimeReferenceKey(bAnime *anilist.BaseAnime, lfs []*anime.LocalFile) string {
// Reference key is used to compare the snapshot with the current data.
// If the reference key is different, the snapshot is outdated.
animeLfs := lo.Filter(lfs, func(lf *anime.LocalFile, _ int) bool {
return lf.MediaId == bAnime.ID
})
// Extract the paths and sort them to maintain a consistent order.
paths := lo.Map(animeLfs, func(lf *anime.LocalFile, _ int) string {
return lf.Path
})
slices.Sort(paths)
return fmt.Sprintf("%d-%s", bAnime.ID, strings.Join(paths, ","))
}
func GetMangaReferenceKey(bManga *anilist.BaseManga, dcc []*manga.ChapterContainer) string {
// Reference key is used to compare the snapshot with the current data.
// If the reference key is different, the snapshot is outdated.
mangaDcc := lo.Filter(dcc, func(dc *manga.ChapterContainer, _ int) bool {
return dc.MediaId == bManga.ID
})
slices.SortFunc(mangaDcc, func(i, j *manga.ChapterContainer) int {
return strings.Compare(i.Provider, j.Provider)
})
var k string
for _, dc := range mangaDcc {
l := dc.Provider + "-"
slices.SortFunc(dc.Chapters, func(i, j *hibikemanga.ChapterDetails) int {
return strings.Compare(i.ID, j.ID)
})
for _, c := range dc.Chapters {
l += c.ID + "-"
}
k += l
}
return fmt.Sprintf("%d-%s", bManga.ID, k)
}
func GetAnimeListDataKey(entry *anilist.AnimeListEntry) string {
return fmt.Sprintf("%s-%d-%f-%d-%v-%v-%v-%v-%v-%v",
MediaListStatusPointerValue(entry.GetStatus()),
IntPointerValue(entry.GetProgress()),
Float64PointerValue(entry.GetScore()),
IntPointerValue(entry.GetRepeat()),
IntPointerValue(entry.GetStartedAt().GetYear()),
IntPointerValue(entry.GetStartedAt().GetMonth()),
IntPointerValue(entry.GetStartedAt().GetDay()),
IntPointerValue(entry.GetCompletedAt().GetYear()),
IntPointerValue(entry.GetCompletedAt().GetMonth()),
IntPointerValue(entry.GetCompletedAt().GetDay()),
)
}
func GetMangaListDataKey(entry *anilist.MangaListEntry) string {
return fmt.Sprintf("%s-%d-%f-%d-%v-%v-%v-%v-%v-%v",
MediaListStatusPointerValue(entry.GetStatus()),
IntPointerValue(entry.GetProgress()),
Float64PointerValue(entry.GetScore()),
IntPointerValue(entry.GetRepeat()),
IntPointerValue(entry.GetStartedAt().GetYear()),
IntPointerValue(entry.GetStartedAt().GetMonth()),
IntPointerValue(entry.GetStartedAt().GetDay()),
IntPointerValue(entry.GetCompletedAt().GetYear()),
IntPointerValue(entry.GetCompletedAt().GetMonth()),
IntPointerValue(entry.GetCompletedAt().GetDay()),
)
}