diff options
| author | Bobby <[email protected]> | 2026-02-24 14:02:37 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-02-24 14:02:37 +0530 |
| commit | de0c8e5b052342bc7aaa3e8e3795ce432208af61 (patch) | |
| tree | 4ee288d86fc17c4e28a101a35cbdf9b2c1597042 /repositories | |
| parent | 45e858c3062eacb2f9d51b65d5680eb211ce5482 (diff) | |
| download | metachan-de0c8e5b052342bc7aaa3e8e3795ce432208af61.tar.xz metachan-de0c8e5b052342bc7aaa3e8e3795ce432208af61.zip | |
Refactor entities and repositories: add AnimeCharacter and CharacterVoiceActor types, update character handling in anime, and enhance producer enrichment logic
Diffstat (limited to 'repositories')
| -rw-r--r-- | repositories/anime.go | 100 | ||||
| -rw-r--r-- | repositories/meta.go | 24 | ||||
| -rw-r--r-- | repositories/producer.go | 56 |
3 files changed, 176 insertions, 4 deletions
diff --git a/repositories/anime.go b/repositories/anime.go index 8870f35..b20a256 100644 --- a/repositories/anime.go +++ b/repositories/anime.go @@ -51,8 +51,6 @@ func GetAnime[T idType](maptype enums.MappingType, id T) (entities.Anime, error) Preload("Episodes.StreamInfo"). Preload("Episodes.StreamInfo.SubSources"). Preload("Episodes.StreamInfo.DubSources"). - Preload("Characters"). - Preload("Characters.VoiceActors"). Preload("Schedule"). Preload("Seasons"). Preload("Seasons.Title"). @@ -69,9 +67,82 @@ func GetAnime[T idType](maptype enums.MappingType, id T) (entities.Anime, error) return entities.Anime{}, errors.New("anime not found") } + loadAnimeCharacters(&anime) + return anime, nil } +func loadAnimeCharacters(anime *entities.Anime) { + var rows []struct { + CharacterID uint + Role string + } + DB.Table("anime_characters"). + Select("character_id, role"). + Where("anime_id = ?", anime.ID). + Scan(&rows) + + for _, row := range rows { + var char entities.Character + if err := DB.First(&char, row.CharacterID).Error; err != nil { + continue + } + DB.Preload("VoiceActor"). + Where("character_id = ?", char.ID). + Find(&char.VoiceActors) + char.Role = row.Role + anime.Characters = append(anime.Characters, char) + } +} + +func SaveAnimeCharacters(animeID uint, characters []entities.Character) error { + for i := range characters { + char := &characters[i] + + DB.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "mal_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"url", "image_url", "name"}), + }).Create(char) + if char.ID == 0 { + DB.Where("mal_id = ?", char.MALID).First(char) + } + + DB.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "anime_id"}, {Name: "character_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"role"}), + }).Create(&entities.AnimeCharacter{ + AnimeID: animeID, + CharacterID: char.ID, + Role: char.Role, + }) + + for _, cva := range char.VoiceActors { + va := cva.VoiceActor + if va == nil { + continue + } + + DB.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "mal_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"url", "image", "name"}), + }).Create(va) + if va.ID == 0 { + DB.Where("mal_id = ?", va.MALID).First(va) + } + + DB.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "character_id"}, {Name: "voice_actor_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"language"}), + }).Create(&entities.CharacterVoiceActor{ + CharacterID: char.ID, + VoiceActorID: va.ID, + Language: cva.Language, + }) + } + } + return nil +} + func CreateOrUpdateAnime(anime *entities.Anime) error { if anime == nil { return fmt.Errorf("anime is nil") @@ -85,7 +156,7 @@ func CreateOrUpdateAnime(anime *entities.Anime) error { result = DB.Session(&gorm.Session{FullSaveAssociations: true}).Clauses(clause.OnConflict{ UpdateAll: true, - }).Save(anime) + }).Omit("Characters", "Episodes").Save(anime) if result.Error != nil { return fmt.Errorf("failed to save anime: %w", result.Error) @@ -95,6 +166,29 @@ func CreateOrUpdateAnime(anime *entities.Anime) error { return nil } +func SaveAnimeEpisodes(animeID uint, episodes []entities.Episode) error { + for i := range episodes { + ep := &episodes[i] + ep.AnimeID = animeID + + var existing entities.Episode + if DB.Where("episode_id = ?", ep.EpisodeID).First(&existing).Error == nil { + ep.ID = existing.ID + ep.TitleID = existing.TitleID + DB.Model(ep).Omit("SkipTimes", "StreamInfo", "Title").Updates(ep) + if ep.Title != nil && existing.TitleID != 0 { + ep.Title.ID = existing.TitleID + DB.Save(ep.Title) + } + } else { + DB.Session(&gorm.Session{FullSaveAssociations: true}). + Omit("SkipTimes", "StreamInfo"). + Create(ep) + } + } + return nil +} + func SaveEpisodeSkipTimes(episodeID string, skipTimes []entities.EpisodeSkipTime) error { if len(skipTimes) == 0 { return nil diff --git a/repositories/meta.go b/repositories/meta.go index 34487a4..817a843 100644 --- a/repositories/meta.go +++ b/repositories/meta.go @@ -72,6 +72,30 @@ func CreateOrUpdateSimpleTitle(title *entities.SimpleTitle) (uint, error) { return title.ID, nil } +func GetAllImagesMapped() (map[string]uint, error) { + var images []entities.SimpleImage + if err := DB.Select("id, image_url").Find(&images).Error; err != nil { + return nil, err + } + m := make(map[string]uint, len(images)) + for _, img := range images { + m[img.ImageURL] = img.ID + } + return m, nil +} + +func GetAllTitlesMapped() (map[string]uint, error) { + var titles []entities.SimpleTitle + if err := DB.Select("id, type, title").Find(&titles).Error; err != nil { + return nil, err + } + m := make(map[string]uint, len(titles)) + for _, t := range titles { + m[t.Type+":"+t.Title] = t.ID + } + return m, nil +} + func CreateOrUpdateExternalURL(url *entities.ExternalURL) (uint, error) { result := DB.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "name"}, {Name: "url"}}, diff --git a/repositories/producer.go b/repositories/producer.go index 53deefe..afe9108 100644 --- a/repositories/producer.go +++ b/repositories/producer.go @@ -4,26 +4,80 @@ import ( "errors" "metachan/entities" "metachan/utils/logger" + "time" "gorm.io/gorm/clause" ) func CreateOrUpdateProducer(producer *entities.Producer) error { + for i := range producer.Titles { + t := &producer.Titles[i] + DB.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "type"}, {Name: "title"}}, + DoNothing: true, + }).Create(t) + if t.ID == 0 { + DB.Where("type = ? AND title = ?", t.Type, t.Title).First(t) + } + } + result := DB.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "mal_id"}}, DoUpdates: clause.AssignmentColumns([]string{ "url", "favorites", "count", "established", "about", "image_id", }), - }).Create(producer) + }).Omit("Titles").Create(producer) if result.Error != nil { logger.Errorf("Producer", "Failed to create or update producer: %v", result.Error) return errors.New("failed to create or update producer") } + if len(producer.Titles) > 0 { + if err := DB.Model(producer).Association("Titles").Replace(producer.Titles); err != nil { + logger.Errorf("Producer", "Failed to associate titles for producer %d: %v", producer.MALID, err) + } + } + return nil } +func GetAllProducers() ([]entities.Producer, error) { + var producers []entities.Producer + if err := DB.Select("id, mal_id, enriched_at").Find(&producers).Error; err != nil { + return nil, err + } + return producers, nil +} + +func GetProducerExternalURLCount(producer *entities.Producer) int64 { + return DB.Model(producer).Association("ExternalURLs").Count() +} + +func ReplaceProducerExternalURLs(producer *entities.Producer, urls []entities.ExternalURL) error { + return DB.Model(producer).Association("ExternalURLs").Replace(urls) +} + +func UpdateProducerDetails(id uint, url, established, about string, favorites, count int, imageURL string) error { + now := time.Now() + updates := map[string]interface{}{ + "url": url, + "favorites": favorites, + "count": count, + "established": established, + "about": about, + "enriched_at": now, + } + if imageURL != "" { + img := entities.SimpleImage{ImageURL: imageURL} + imgID, err := CreateOrUpdateSimpleImage(&img) + if err == nil && imgID != 0 { + updates["image_id"] = imgID + } + } + return DB.Model(&entities.Producer{}).Where("id = ?", id).Updates(updates).Error +} + func BatchCreateProducers(producers []entities.Producer) error { if len(producers) == 0 { return nil |
