package main import ( "encoding/json" "errors" "fmt" "sort" "strings" "time" "github.com/gookit/color" "github.com/xujiajun/nutsdb" ) func JikanConvert(jik *SeasonAnimeJikan) Anime { res := Anime{ Anime: jik.MalID, Title: jik.Title, TitleEn: jik.TitleEnglish, TitleJp: jik.TitleJapanese, ImageMediumURL: jik.Images.Jpg.ImageURL, ImageLargeURL: jik.Images.Jpg.LargeImageURL, ImageThumbURL: jik.Images.Jpg.SmallImageURL, Type: jik.Type, Status: jik.Status, Episodes: jik.Episodes, Synopsis: jik.Synopsis, StartDate: jik.Aired.From, EndDate: jik.Aired.To, Year: jik.Year, Season: jik.Season, Score: jik.Score, ScoredBy: jik.ScoredBy, Rank: jik.Rank, Popularity: jik.Popularity, Members: jik.Members, Source: jik.Source, Weekday: jik.Broadcast.Day, TrailerURL: jik.Trailer.URL, TrailerEmbedURL: jik.Trailer.EmbedURL, } for _, g := range jik.Genres { res.Genres = append(res.Genres, AnimeGenre{ ID: g.MalID, Name: g.Name, }) } for _, s := range jik.Studios { res.Studios = append(res.Studios, AnimeStudio{ ID: s.MalID, Name: s.Name, }) } res.URL = fmt.Sprintf("https://myanimelist.net/anime/%d", res.Anime) // disable autoplay hack res.TrailerEmbedURL = strings.Replace(res.TrailerEmbedURL, "autoplay=1", "autoplay=0", -1) return res } func MalConvert(mal *AnimeDetailMal) Anime { res := Anime{ Anime: mal.ID, Title: mal.Title, TitleEn: mal.AlternativeTitles.En, TitleJp: mal.AlternativeTitles.Ja, ImageMediumURL: mal.MainPicture.Medium, ImageLargeURL: mal.MainPicture.Large, ImageThumbURL: "", Type: mal.MediaType, Status: mal.Status, Episodes: mal.NumEpisodes, Synopsis: mal.Synopsis, Year: mal.StartSeason.Year, Season: mal.StartSeason.Season, Score: mal.Mean, ScoredBy: mal.NumScoringUsers, Rank: mal.Rank, Popularity: mal.Popularity, Members: mal.NumListUsers, Source: mal.Source, Weekday: mal.Broadcast.DayOfTheWeek, TrailerURL: "", } for _, g := range mal.Genres { res.Genres = append(res.Genres, AnimeGenre{ ID: g.ID, Name: g.Name, }) } for _, s := range mal.Studios { res.Studios = append(res.Studios, AnimeStudio{ ID: s.ID, Name: s.Name, }) } res.StartDate, _ = time.Parse("2006-01-02", mal.StartDate) res.EndDate, _ = time.Parse("2006-01-02", mal.EndDate) res.URL = fmt.Sprintf("https://myanimelist.net/anime/%d", res.Anime) return res } func UserConvert(user *UserJikan) User { return User{ MalID: user.Data.MalID, Username: user.Data.Username, URL: user.Data.URL, ImageURL: user.Data.Images.Jpg.ImageURL, LastOnline: user.Data.LastOnline, Gender: user.Data.Gender, Birthday: user.Data.Birthday, Location: user.Data.Location, Joined: user.Data.Joined, } } func GetSeasonCache(key string) ([]Anime, error) { data, err := seasoncache.Get(key) if err != nil { return nil, err } var seasonData []Anime err = json.Unmarshal(data, &seasonData) return seasonData, err } func SearchSeasons(animeId int64) (*Anime, error) { season, err := GetSeasonCache(GetCurrentSeasonString()) if err != nil { return nil, err } for _, a := range season { if a.Anime == animeId { return &a, err } } season, err = GetSeasonCache(GetNextSeasonString()) if err != nil { return nil, err } for _, a := range season { if a.Anime == animeId { return &a, err } } season, err = GetSeasonCache(GetNextNextSeasonString()) if err != nil { return nil, err } for _, a := range season { if a.Anime == animeId { return &a, err } } season, err = GetSeasonCache(GetLastSeasonString()) if err != nil { return nil, err } for _, a := range season { if a.Anime == animeId { return &a, err } } return nil, errors.New("anime not found") } func SearchAnime(animeId int64) (*Anime, error) { // search season first anime, err := SearchSeasons(animeId) if err != nil { // get from MAL anime, _, err = GetAnimeDetailData(animeId) if err != nil { return nil, err } } return anime, err } func SearchAppointments(animeId int64) ([]Appointment, error) { appointments, err := ReadAppointments() if err != nil { if strings.Contains(err.Error(), "not found") || err == nutsdb.ErrBucketEmpty { return make([]Appointment, 0), nil } else { return appointments, err } } result := make([]Appointment, 0) for _, a := range appointments { if animeId == a.Anime { result = append(result, a) } } return result, nil } func FetchProgress(animeId int64, username string) (int, time.Time, int, string, error) { // check watching first newProgress, updated, score, err := FetchProgressOnState(animeId, username, malApiStatusW) if err != nil { return newProgress, updated, score, "", err } if newProgress != -1 { return newProgress, updated, score, malApiStatusW, err } // check completed newProgress, updated, score, err = FetchProgressOnState(animeId, username, malApiStatusC) if err != nil { return newProgress, updated, score, "", err } if newProgress != -1 { return newProgress, updated, score, malApiStatusC, err } // check on hold newProgress, updated, score, err = FetchProgressOnState(animeId, username, malApiStatusH) if err != nil { return newProgress, updated, score, "", err } if newProgress != -1 { return newProgress, updated, score, malApiStatusH, err } // check dropped newProgress, updated, score, err = FetchProgressOnState(animeId, username, malApiStatusD) if err != nil { return newProgress, updated, score, "", err } if newProgress != -1 { return newProgress, updated, score, malApiStatusD, err } // has no progress or PTW return 0, updated, 0, "", nil } func FetchProgressOnState(animeId int64, username, malStatus string) (int, time.Time, int, error) { list, _, err := GetUserAnimeListData(username, malStatus) if err != nil { return 0, time.Time{}, 0, err } for _, a := range list.Data { // check if found if a.Node.ID == animeId { return a.ListStatus.NumEpisodesWatched, a.ListStatus.UpdatedAt, a.ListStatus.Score, nil } } // no progess found return -1, time.Now(), 0, nil } func AddToChat(old, new, user string) string { buf := NewRingBuf(chatLength) buf.Write([]byte(old)) buf.Write([]byte(fmt.Sprintf("[%s][%s]: %s\n", time.Now().Format("02.01.|15:04:05"), user, new))) return buf.String() } func GetAnimeWatchFromDb(animeId int64) (*AnimeUser, error) { dbAnime, err := ReadAnimeUsers() if err != nil { return nil, err } for _, a := range dbAnime { if a.Anime == animeId { return &a, err } } return nil, errors.New("anime not found") } func CheckAnimeExistInDb(animeId int64) (bool, error) { dbAnime, err := ReadAnimeUsers() if err != nil { return false, err } for _, a := range dbAnime { if a.Anime == animeId { return true, err } } return false, err } func CheckAnimeExistInDbAndUserWatches(animeId, userId int64) (bool, error) { dbAnime, err := ReadAnimeUsers() if err != nil { return false, err } for _, a := range dbAnime { if a.Anime == animeId { for _, u := range a.Users { if u.MalID == userId { return true, err } } return false, err } } return false, err } func BuildMovieCharts() ([]MovieChart, error) { key := "charts" var charts []MovieChart data, err := mmCache.Get(key) if err == nil { err = json.Unmarshal(data, &charts) if err == nil { return charts, err } } movieList, err := MmReadCharts() if err != nil { return nil, err } users, err := ReadRegisteredUsers() if err != nil { return nil, err } charts = make([]MovieChart, 0) for _, m := range movieList { c := MovieChart{ MmId: m.Id, Anime: m.Anime, Title: m.Title, } scoreSum := 0 for _, u := range users { progress, _, score, err := FetchProgressOnState(c.Anime, u.Username, malApiStatusC) if err != nil { color.Errorln(err.Error()) continue } if progress == -1 || score == 0 { // user has no progress/score continue } scoreSum += score c.UserCount++ } if c.UserCount > 0 { c.AvgScore = float64(scoreSum) / float64(c.UserCount) } charts = append(charts, c) } sort.SliceStable(charts, func(i, j int) bool { return charts[i].AvgScore > charts[j].AvgScore }) bytes, err := json.Marshal(charts) if err == nil { mmCache.Set(key, bytes) } return charts, nil }