516 lines
16 KiB
Go
516 lines
16 KiB
Go
package simulated_platform
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"seanime/internal/api/anilist"
|
|
"time"
|
|
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
// CollectionWrapper provides an ambivalent interface for anime and manga collections
|
|
type CollectionWrapper struct {
|
|
platform *SimulatedPlatform
|
|
isAnime bool
|
|
}
|
|
|
|
func (sp *SimulatedPlatform) GetAnimeCollectionWrapper() *CollectionWrapper {
|
|
return &CollectionWrapper{platform: sp, isAnime: true}
|
|
}
|
|
|
|
func (sp *SimulatedPlatform) GetMangaCollectionWrapper() *CollectionWrapper {
|
|
return &CollectionWrapper{platform: sp, isAnime: false}
|
|
}
|
|
|
|
// AddEntry adds a new entry to the collection
|
|
func (cw *CollectionWrapper) AddEntry(mediaId int, status anilist.MediaListStatus) error {
|
|
if cw.isAnime {
|
|
return cw.addAnimeEntry(mediaId, status)
|
|
}
|
|
return cw.addMangaEntry(mediaId, status)
|
|
}
|
|
|
|
// UpdateEntry updates an existing entry in the collection
|
|
func (cw *CollectionWrapper) UpdateEntry(mediaId int, status *anilist.MediaListStatus, scoreRaw *int, progress *int, startedAt *anilist.FuzzyDateInput, completedAt *anilist.FuzzyDateInput) error {
|
|
if cw.isAnime {
|
|
return cw.updateAnimeEntry(mediaId, status, scoreRaw, progress, startedAt, completedAt)
|
|
}
|
|
return cw.updateMangaEntry(mediaId, status, scoreRaw, progress, startedAt, completedAt)
|
|
}
|
|
|
|
// UpdateEntryProgress updates the progress of an entry
|
|
func (cw *CollectionWrapper) UpdateEntryProgress(mediaId int, progress int, totalCount *int) error {
|
|
status := anilist.MediaListStatusCurrent
|
|
if totalCount != nil && progress >= *totalCount {
|
|
status = anilist.MediaListStatusCompleted
|
|
}
|
|
|
|
return cw.UpdateEntry(mediaId, &status, nil, &progress, nil, nil)
|
|
}
|
|
|
|
// DeleteEntry removes an entry from the collection
|
|
func (cw *CollectionWrapper) DeleteEntry(mediaId int, isEntryId ...bool) error {
|
|
if cw.isAnime {
|
|
return cw.deleteAnimeEntry(mediaId, isEntryId...)
|
|
}
|
|
return cw.deleteMangaEntry(mediaId, isEntryId...)
|
|
}
|
|
|
|
// FindEntry finds an entry by media ID
|
|
func (cw *CollectionWrapper) FindEntry(mediaId int, isEntryId ...bool) (interface{}, error) {
|
|
if cw.isAnime {
|
|
return cw.findAnimeEntry(mediaId, isEntryId...)
|
|
}
|
|
return cw.findMangaEntry(mediaId, isEntryId...)
|
|
}
|
|
|
|
// UpdateMediaData updates the media data for an entry
|
|
func (cw *CollectionWrapper) UpdateMediaData(mediaId int, mediaData interface{}) error {
|
|
if cw.isAnime {
|
|
if baseAnime, ok := mediaData.(*anilist.BaseAnime); ok {
|
|
return cw.updateAnimeMediaData(mediaId, baseAnime)
|
|
}
|
|
return errors.New("invalid anime data type")
|
|
}
|
|
|
|
if baseManga, ok := mediaData.(*anilist.BaseManga); ok {
|
|
return cw.updateMangaMediaData(mediaId, baseManga)
|
|
}
|
|
return errors.New("invalid manga data type")
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Anime Collection Helper Methods
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
func (cw *CollectionWrapper) addAnimeEntry(mediaId int, status anilist.MediaListStatus) error {
|
|
collection, err := cw.platform.getOrCreateAnimeCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check if entry already exists
|
|
if _, err := cw.findAnimeEntry(mediaId); err == nil {
|
|
return errors.New("entry already exists")
|
|
}
|
|
|
|
// Fetch media data
|
|
mediaResp, err := cw.platform.client.BaseAnimeByID(context.Background(), &mediaId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Find or create the appropriate list
|
|
var targetList *anilist.AnimeCollection_MediaListCollection_Lists
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
if list.GetStatus() != nil && *list.GetStatus() == status {
|
|
targetList = list
|
|
break
|
|
}
|
|
}
|
|
|
|
if targetList == nil {
|
|
// Create new list
|
|
targetList = &anilist.AnimeCollection_MediaListCollection_Lists{
|
|
Status: &status,
|
|
Name: lo.ToPtr(string(status)),
|
|
IsCustomList: lo.ToPtr(false),
|
|
Entries: []*anilist.AnimeCollection_MediaListCollection_Lists_Entries{},
|
|
}
|
|
collection.GetMediaListCollection().Lists = append(collection.GetMediaListCollection().Lists, targetList)
|
|
}
|
|
|
|
// Create new entry
|
|
newEntry := &anilist.AnimeCollection_MediaListCollection_Lists_Entries{
|
|
ID: int(time.Now().UnixNano()), // Generate unique ID
|
|
Status: &status,
|
|
Progress: lo.ToPtr(0),
|
|
Media: mediaResp.GetMedia(),
|
|
Score: lo.ToPtr(0.0),
|
|
Notes: nil,
|
|
Repeat: lo.ToPtr(0),
|
|
Private: lo.ToPtr(false),
|
|
StartedAt: &anilist.AnimeCollection_MediaListCollection_Lists_Entries_StartedAt{},
|
|
CompletedAt: &anilist.AnimeCollection_MediaListCollection_Lists_Entries_CompletedAt{},
|
|
}
|
|
|
|
targetList.Entries = append(targetList.Entries, newEntry)
|
|
|
|
// Save collection
|
|
cw.platform.localManager.SaveSimulatedAnimeCollection(collection)
|
|
return nil
|
|
}
|
|
|
|
func (cw *CollectionWrapper) updateAnimeEntry(mediaId int, status *anilist.MediaListStatus, scoreRaw *int, progress *int, startedAt *anilist.FuzzyDateInput, completedAt *anilist.FuzzyDateInput) error {
|
|
collection, err := cw.platform.getOrCreateAnimeCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var foundEntry *anilist.AnimeCollection_MediaListCollection_Lists_Entries
|
|
var sourceList *anilist.AnimeCollection_MediaListCollection_Lists
|
|
var entryIndex int
|
|
|
|
// Find the entry
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for i, entry := range list.GetEntries() {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
foundEntry = entry
|
|
sourceList = list
|
|
entryIndex = i
|
|
break
|
|
}
|
|
}
|
|
if foundEntry != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
if foundEntry == nil || sourceList == nil {
|
|
return ErrMediaNotFound
|
|
}
|
|
|
|
// Update entry fields
|
|
if progress != nil {
|
|
foundEntry.Progress = progress
|
|
}
|
|
if scoreRaw != nil {
|
|
foundEntry.Score = lo.ToPtr(float64(*scoreRaw))
|
|
}
|
|
if startedAt != nil {
|
|
foundEntry.StartedAt = &anilist.AnimeCollection_MediaListCollection_Lists_Entries_StartedAt{
|
|
Year: startedAt.Year,
|
|
Month: startedAt.Month,
|
|
Day: startedAt.Day,
|
|
}
|
|
}
|
|
if completedAt != nil {
|
|
foundEntry.CompletedAt = &anilist.AnimeCollection_MediaListCollection_Lists_Entries_CompletedAt{
|
|
Year: completedAt.Year,
|
|
Month: completedAt.Month,
|
|
Day: completedAt.Day,
|
|
}
|
|
}
|
|
|
|
// If status changed, move entry to different list
|
|
if status != nil && foundEntry.GetStatus() != nil && *status != *foundEntry.GetStatus() {
|
|
foundEntry.Status = status
|
|
|
|
// Remove from current list
|
|
sourceList.Entries = append(sourceList.Entries[:entryIndex], sourceList.Entries[entryIndex+1:]...)
|
|
|
|
// Find or create target list
|
|
var targetList *anilist.AnimeCollection_MediaListCollection_Lists
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
if list.GetStatus() != nil && *list.GetStatus() == *status {
|
|
targetList = list
|
|
break
|
|
}
|
|
}
|
|
|
|
if targetList == nil {
|
|
targetList = &anilist.AnimeCollection_MediaListCollection_Lists{
|
|
Status: status,
|
|
Name: lo.ToPtr(string(*status)),
|
|
IsCustomList: lo.ToPtr(false),
|
|
Entries: []*anilist.AnimeCollection_MediaListCollection_Lists_Entries{},
|
|
}
|
|
collection.GetMediaListCollection().Lists = append(collection.GetMediaListCollection().Lists, targetList)
|
|
}
|
|
|
|
targetList.Entries = append(targetList.Entries, foundEntry)
|
|
}
|
|
|
|
cw.platform.localManager.SaveSimulatedAnimeCollection(collection)
|
|
return nil
|
|
}
|
|
|
|
func (cw *CollectionWrapper) deleteAnimeEntry(mediaId int, isEntryId ...bool) error {
|
|
collection, err := cw.platform.getOrCreateAnimeCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Find and remove entry
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for i, entry := range list.GetEntries() {
|
|
if len(isEntryId) > 0 && isEntryId[0] {
|
|
// If isEntryId is true, we assume mediaId is actually the entry ID
|
|
if entry.GetID() == mediaId {
|
|
list.Entries = append(list.Entries[:i], list.Entries[i+1:]...)
|
|
cw.platform.localManager.SaveSimulatedAnimeCollection(collection)
|
|
return nil
|
|
}
|
|
} else {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
list.Entries = append(list.Entries[:i], list.Entries[i+1:]...)
|
|
cw.platform.localManager.SaveSimulatedAnimeCollection(collection)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return ErrMediaNotFound
|
|
}
|
|
|
|
func (cw *CollectionWrapper) findAnimeEntry(mediaId int, isEntryId ...bool) (*anilist.AnimeCollection_MediaListCollection_Lists_Entries, error) {
|
|
collection, err := cw.platform.getOrCreateAnimeCollection()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for _, entry := range list.GetEntries() {
|
|
if len(isEntryId) > 0 && isEntryId[0] {
|
|
if entry.GetID() == mediaId {
|
|
return entry, nil
|
|
}
|
|
} else {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
return entry, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, ErrMediaNotFound
|
|
}
|
|
|
|
func (cw *CollectionWrapper) updateAnimeMediaData(mediaId int, mediaData *anilist.BaseAnime) error {
|
|
collection, err := cw.platform.getOrCreateAnimeCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for _, entry := range list.GetEntries() {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
entry.Media = mediaData
|
|
cw.platform.localManager.SaveSimulatedAnimeCollection(collection)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return ErrMediaNotFound
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Manga Collection Helper Methods
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
func (cw *CollectionWrapper) addMangaEntry(mediaId int, status anilist.MediaListStatus) error {
|
|
collection, err := cw.platform.getOrCreateMangaCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check if entry already exists
|
|
if _, err := cw.findMangaEntry(mediaId); err == nil {
|
|
return errors.New("entry already exists")
|
|
}
|
|
|
|
// Fetch media data
|
|
mediaResp, err := cw.platform.client.BaseMangaByID(context.Background(), &mediaId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Find or create the appropriate list
|
|
var targetList *anilist.MangaCollection_MediaListCollection_Lists
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
if list.GetStatus() != nil && *list.GetStatus() == status {
|
|
targetList = list
|
|
break
|
|
}
|
|
}
|
|
|
|
if targetList == nil {
|
|
// Create new list
|
|
targetList = &anilist.MangaCollection_MediaListCollection_Lists{
|
|
Status: &status,
|
|
Name: lo.ToPtr(string(status)),
|
|
IsCustomList: lo.ToPtr(false),
|
|
Entries: []*anilist.MangaCollection_MediaListCollection_Lists_Entries{},
|
|
}
|
|
collection.GetMediaListCollection().Lists = append(collection.GetMediaListCollection().Lists, targetList)
|
|
}
|
|
|
|
// Create new entry
|
|
newEntry := &anilist.MangaCollection_MediaListCollection_Lists_Entries{
|
|
ID: int(time.Now().UnixNano()),
|
|
Status: &status,
|
|
Progress: lo.ToPtr(0),
|
|
Media: mediaResp.GetMedia(),
|
|
Score: lo.ToPtr(0.0),
|
|
Notes: nil,
|
|
Repeat: lo.ToPtr(0),
|
|
Private: lo.ToPtr(false),
|
|
StartedAt: &anilist.MangaCollection_MediaListCollection_Lists_Entries_StartedAt{},
|
|
CompletedAt: &anilist.MangaCollection_MediaListCollection_Lists_Entries_CompletedAt{},
|
|
}
|
|
|
|
targetList.Entries = append(targetList.Entries, newEntry)
|
|
|
|
// Save collection
|
|
cw.platform.localManager.SaveSimulatedMangaCollection(collection)
|
|
return nil
|
|
}
|
|
|
|
func (cw *CollectionWrapper) updateMangaEntry(mediaId int, status *anilist.MediaListStatus, scoreRaw *int, progress *int, startedAt *anilist.FuzzyDateInput, completedAt *anilist.FuzzyDateInput) error {
|
|
collection, err := cw.platform.getOrCreateMangaCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var foundEntry *anilist.MangaCollection_MediaListCollection_Lists_Entries
|
|
var sourceList *anilist.MangaCollection_MediaListCollection_Lists
|
|
var entryIndex int
|
|
|
|
// Find the entry
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for i, entry := range list.GetEntries() {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
foundEntry = entry
|
|
sourceList = list
|
|
entryIndex = i
|
|
break
|
|
}
|
|
}
|
|
if foundEntry != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
if foundEntry == nil || sourceList == nil {
|
|
return ErrMediaNotFound
|
|
}
|
|
|
|
// Update entry fields
|
|
if progress != nil {
|
|
foundEntry.Progress = progress
|
|
}
|
|
if scoreRaw != nil {
|
|
foundEntry.Score = lo.ToPtr(float64(*scoreRaw))
|
|
}
|
|
if startedAt != nil {
|
|
foundEntry.StartedAt = &anilist.MangaCollection_MediaListCollection_Lists_Entries_StartedAt{
|
|
Year: startedAt.Year,
|
|
Month: startedAt.Month,
|
|
Day: startedAt.Day,
|
|
}
|
|
}
|
|
if completedAt != nil {
|
|
foundEntry.CompletedAt = &anilist.MangaCollection_MediaListCollection_Lists_Entries_CompletedAt{
|
|
Year: completedAt.Year,
|
|
Month: completedAt.Month,
|
|
Day: completedAt.Day,
|
|
}
|
|
}
|
|
|
|
// If status changed, move entry to different list
|
|
if status != nil && foundEntry.GetStatus() != nil && *status != *foundEntry.GetStatus() {
|
|
foundEntry.Status = status
|
|
|
|
// Remove from current list
|
|
sourceList.Entries = append(sourceList.Entries[:entryIndex], sourceList.Entries[entryIndex+1:]...)
|
|
|
|
// Find or create target list
|
|
var targetList *anilist.MangaCollection_MediaListCollection_Lists
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
if list.GetStatus() != nil && *list.GetStatus() == *status {
|
|
targetList = list
|
|
break
|
|
}
|
|
}
|
|
|
|
if targetList == nil {
|
|
targetList = &anilist.MangaCollection_MediaListCollection_Lists{
|
|
Status: status,
|
|
Name: lo.ToPtr(string(*status)),
|
|
IsCustomList: lo.ToPtr(false),
|
|
Entries: []*anilist.MangaCollection_MediaListCollection_Lists_Entries{},
|
|
}
|
|
collection.GetMediaListCollection().Lists = append(collection.GetMediaListCollection().Lists, targetList)
|
|
}
|
|
|
|
targetList.Entries = append(targetList.Entries, foundEntry)
|
|
}
|
|
|
|
cw.platform.localManager.SaveSimulatedMangaCollection(collection)
|
|
return nil
|
|
}
|
|
|
|
func (cw *CollectionWrapper) deleteMangaEntry(mediaId int, isEntryId ...bool) error {
|
|
collection, err := cw.platform.getOrCreateMangaCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Find and remove entry
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for i, entry := range list.GetEntries() {
|
|
if len(isEntryId) > 0 && isEntryId[0] {
|
|
if entry.GetID() == mediaId {
|
|
list.Entries = append(list.Entries[:i], list.Entries[i+1:]...)
|
|
cw.platform.localManager.SaveSimulatedMangaCollection(collection)
|
|
return nil
|
|
}
|
|
} else {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
list.Entries = append(list.Entries[:i], list.Entries[i+1:]...)
|
|
cw.platform.localManager.SaveSimulatedMangaCollection(collection)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ErrMediaNotFound
|
|
}
|
|
|
|
func (cw *CollectionWrapper) findMangaEntry(mediaId int, isEntryId ...bool) (*anilist.MangaCollection_MediaListCollection_Lists_Entries, error) {
|
|
collection, err := cw.platform.getOrCreateMangaCollection()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for _, entry := range list.GetEntries() {
|
|
if len(isEntryId) > 0 && isEntryId[0] {
|
|
if entry.GetID() == mediaId {
|
|
return entry, nil
|
|
}
|
|
} else {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
return entry, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, ErrMediaNotFound
|
|
}
|
|
|
|
func (cw *CollectionWrapper) updateMangaMediaData(mediaId int, mediaData *anilist.BaseManga) error {
|
|
collection, err := cw.platform.getOrCreateMangaCollection()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, list := range collection.GetMediaListCollection().GetLists() {
|
|
for _, entry := range list.GetEntries() {
|
|
if entry.GetMedia().GetID() == mediaId {
|
|
entry.Media = mediaData
|
|
cw.platform.localManager.SaveSimulatedMangaCollection(collection)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return ErrMediaNotFound
|
|
}
|