108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
|
package arn
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
currentlyAiringBonus = 5.0
|
||
|
longSummaryBonus = 0.1
|
||
|
popularityThreshold = 5
|
||
|
popularityPenalty = 8.0
|
||
|
watchingPopularityWeight = 0.07
|
||
|
completedPopularityWeight = watchingPopularityWeight
|
||
|
plannedPopularityWeight = watchingPopularityWeight * (2.0 / 3.0)
|
||
|
droppedPopularityWeight = -plannedPopularityWeight
|
||
|
visualsWeight = 0.0075
|
||
|
storyWeight = 0.0075
|
||
|
soundtrackWeight = 0.0075
|
||
|
movieBonus = 0.28
|
||
|
agePenalty = 11.0
|
||
|
ageThreshold = 6 * 30 * 24 * time.Hour
|
||
|
)
|
||
|
|
||
|
// SortAnimeByPopularity sorts the given slice of anime by popularity.
|
||
|
func SortAnimeByPopularity(animes []*Anime) {
|
||
|
sort.Slice(animes, func(i, j int) bool {
|
||
|
aPopularity := animes[i].Popularity.Total()
|
||
|
bPopularity := animes[j].Popularity.Total()
|
||
|
|
||
|
if aPopularity == bPopularity {
|
||
|
return animes[i].Title.Canonical < animes[j].Title.Canonical
|
||
|
}
|
||
|
|
||
|
return aPopularity > bPopularity
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// SortAnimeByQuality sorts the given slice of anime by quality.
|
||
|
func SortAnimeByQuality(animes []*Anime) {
|
||
|
SortAnimeByQualityDetailed(animes, "")
|
||
|
}
|
||
|
|
||
|
// SortAnimeByQualityDetailed sorts the given slice of anime by quality.
|
||
|
func SortAnimeByQualityDetailed(animes []*Anime, filterStatus string) {
|
||
|
sort.Slice(animes, func(i, j int) bool {
|
||
|
a := animes[i]
|
||
|
b := animes[j]
|
||
|
|
||
|
scoreA := a.Score()
|
||
|
scoreB := b.Score()
|
||
|
|
||
|
// If we show currently running shows, rank shows that started a long time ago a bit lower
|
||
|
if filterStatus == "current" {
|
||
|
if a.StartDate != "" && time.Since(a.StartDateTime()) > ageThreshold {
|
||
|
scoreA -= agePenalty
|
||
|
}
|
||
|
|
||
|
if b.StartDate != "" && time.Since(b.StartDateTime()) > ageThreshold {
|
||
|
scoreB -= agePenalty
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if scoreA == scoreB {
|
||
|
return a.Title.Canonical < b.Title.Canonical
|
||
|
}
|
||
|
|
||
|
return scoreA > scoreB
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Score returns the score used for the anime ranking.
|
||
|
func (anime *Anime) Score() float64 {
|
||
|
score := anime.Rating.Overall
|
||
|
score += anime.Rating.Story * storyWeight
|
||
|
score += anime.Rating.Visuals * visualsWeight
|
||
|
score += anime.Rating.Soundtrack * soundtrackWeight
|
||
|
|
||
|
score += float64(anime.Popularity.Watching) * watchingPopularityWeight
|
||
|
score += float64(anime.Popularity.Planned) * plannedPopularityWeight
|
||
|
score += float64(anime.Popularity.Completed) * completedPopularityWeight
|
||
|
score += float64(anime.Popularity.Dropped) * droppedPopularityWeight
|
||
|
|
||
|
if anime.Status == "current" {
|
||
|
score += currentlyAiringBonus
|
||
|
}
|
||
|
|
||
|
if anime.Type == "movie" {
|
||
|
score += movieBonus
|
||
|
}
|
||
|
|
||
|
if anime.Popularity.Total() < popularityThreshold {
|
||
|
score -= popularityPenalty
|
||
|
}
|
||
|
|
||
|
if len(anime.Summary) >= 140 {
|
||
|
score += longSummaryBonus
|
||
|
}
|
||
|
|
||
|
return score
|
||
|
}
|
||
|
|
||
|
// ScoreHumanReadable returns the score used for the anime ranking in human readable format.
|
||
|
func (anime *Anime) ScoreHumanReadable() string {
|
||
|
return fmt.Sprintf("%.1f", anime.Score())
|
||
|
}
|