node build fixed
This commit is contained in:
327
seanime-2.9.10/internal/local/diff.go
Normal file
327
seanime-2.9.10/internal/local/diff.go
Normal 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()),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user