mirror of
https://github.com/ultrasn0w/huso.git
synced 2025-12-13 08:49:53 +01:00
358 lines
8.5 KiB
Go
358 lines
8.5 KiB
Go
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
|
|
}
|