aboutsummaryrefslogtreecommitdiff
path: root/repositories
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-02-24 14:02:37 +0530
committerBobby <[email protected]>2026-02-24 14:02:37 +0530
commitde0c8e5b052342bc7aaa3e8e3795ce432208af61 (patch)
tree4ee288d86fc17c4e28a101a35cbdf9b2c1597042 /repositories
parent45e858c3062eacb2f9d51b65d5680eb211ce5482 (diff)
downloadmetachan-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.go100
-rw-r--r--repositories/meta.go24
-rw-r--r--repositories/producer.go56
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