Recommendations split into TV and movies
This commit is contained in:
parent
04ecbd737a
commit
6d3199a754
@ -12,6 +12,7 @@ const popular-company-height = 246px
|
|||||||
padding 0.5rem 0.75rem
|
padding 0.5rem 0.75rem
|
||||||
overflow hidden
|
overflow hidden
|
||||||
ui-element
|
ui-element
|
||||||
|
box-shadow shadow-light
|
||||||
|
|
||||||
.popular-company-header
|
.popular-company-header
|
||||||
margin-bottom 0.5rem
|
margin-bottom 0.5rem
|
||||||
|
@ -11,8 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxRecommendations = 20
|
maxRecommendations = 10
|
||||||
worstGenreCount = 5
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Anime shows a list of recommended anime.
|
// Anime shows a list of recommended anime.
|
||||||
@ -26,51 +25,28 @@ func Anime(ctx *aero.Context) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
animeList := viewUser.AnimeList()
|
animeList := viewUser.AnimeList()
|
||||||
genreItems := animeList.Genres()
|
|
||||||
genreAffinity := map[string]float64{}
|
|
||||||
worstGenres := []string{}
|
|
||||||
|
|
||||||
for genre, animeListItems := range genreItems {
|
|
||||||
affinity := 0.0
|
|
||||||
|
|
||||||
for _, item := range animeListItems {
|
|
||||||
// if item.Status == arn.AnimeListStatusDropped {
|
|
||||||
// affinity -= 5.0
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
|
|
||||||
if item.Rating.Overall != 0 {
|
|
||||||
affinity += item.Rating.Overall
|
|
||||||
} else {
|
|
||||||
affinity += 5.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
genreAffinity[genre] = affinity
|
|
||||||
worstGenres = append(worstGenres, genre)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(worstGenres, func(i, j int) bool {
|
|
||||||
return genreAffinity[worstGenres[i]] < genreAffinity[worstGenres[j]]
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(worstGenres) > worstGenreCount {
|
|
||||||
worstGenres = worstGenres[:worstGenreCount]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all anime
|
// Get all anime
|
||||||
recommendations := arn.AllAnime()
|
var tv []*arn.Anime
|
||||||
|
var movies []*arn.Anime
|
||||||
|
allAnime := arn.AllAnime()
|
||||||
|
|
||||||
// Affinity maps an anime ID to a number that indicates how likely a user is going to enjoy that anime.
|
// Affinity maps an anime ID to a number that indicates how likely a user is going to enjoy that anime.
|
||||||
affinity := map[string]float64{}
|
affinity := map[string]float64{}
|
||||||
|
|
||||||
// Calculate affinity for each anime
|
// Calculate affinity for each anime
|
||||||
for _, anime := range recommendations {
|
for _, anime := range allAnime {
|
||||||
// Skip anime that are upcoming or tba
|
// Skip anime that are upcoming or tba
|
||||||
if anime.Status == "upcoming" || anime.Status == "tba" {
|
if anime.Status == "upcoming" || anime.Status == "tba" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if anime.Type == "tv" {
|
||||||
|
tv = append(tv, anime)
|
||||||
|
} else if anime.Type == "movie" {
|
||||||
|
movies = append(movies, anime)
|
||||||
|
}
|
||||||
|
|
||||||
// Skip anime from my list (except planned anime)
|
// Skip anime from my list (except planned anime)
|
||||||
existing := animeList.Find(anime.ID)
|
existing := animeList.Find(anime.ID)
|
||||||
|
|
||||||
@ -78,48 +54,47 @@ func Anime(ctx *aero.Context) string {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip anime that don't have one of the top genres for that user
|
animeAffinity := anime.Score()
|
||||||
worstGenreFound := false
|
|
||||||
|
|
||||||
for _, genre := range anime.Genres {
|
|
||||||
if arn.Contains(worstGenres, genre) {
|
|
||||||
worstGenreFound = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if worstGenreFound {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
animeAffinity := 0.0
|
|
||||||
|
|
||||||
// Planned anime go higher
|
// Planned anime go higher
|
||||||
if existing != nil && existing.Status == arn.AnimeListStatusPlanned {
|
if existing != nil && existing.Status == arn.AnimeListStatusPlanned {
|
||||||
animeAffinity += 75.0
|
animeAffinity += 15.0
|
||||||
}
|
}
|
||||||
|
|
||||||
animeAffinity += float64(anime.Popularity.Total())
|
|
||||||
animeAffinity += anime.Rating.Overall * 80
|
|
||||||
affinity[anime.ID] = animeAffinity
|
affinity[anime.ID] = animeAffinity
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort
|
// Sort
|
||||||
sort.Slice(recommendations, func(i, j int) bool {
|
sort.Slice(tv, func(i, j int) bool {
|
||||||
affinityA := affinity[recommendations[i].ID]
|
affinityA := affinity[tv[i].ID]
|
||||||
affinityB := affinity[recommendations[j].ID]
|
affinityB := affinity[tv[j].ID]
|
||||||
|
|
||||||
if affinityA == affinityB {
|
if affinityA == affinityB {
|
||||||
return recommendations[i].Title.Canonical < recommendations[j].Title.Canonical
|
return tv[i].Title.Canonical < tv[j].Title.Canonical
|
||||||
|
}
|
||||||
|
|
||||||
|
return affinityA > affinityB
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.Slice(movies, func(i, j int) bool {
|
||||||
|
affinityA := affinity[movies[i].ID]
|
||||||
|
affinityB := affinity[movies[j].ID]
|
||||||
|
|
||||||
|
if affinityA == affinityB {
|
||||||
|
return movies[i].Title.Canonical < movies[j].Title.Canonical
|
||||||
}
|
}
|
||||||
|
|
||||||
return affinityA > affinityB
|
return affinityA > affinityB
|
||||||
})
|
})
|
||||||
|
|
||||||
// Take the top 10
|
// Take the top 10
|
||||||
if len(recommendations) > maxRecommendations {
|
if len(tv) > maxRecommendations {
|
||||||
recommendations = recommendations[:maxRecommendations]
|
tv = tv[:maxRecommendations]
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.HTML(components.RecommendedAnime(recommendations, worstGenres, viewUser, user))
|
if len(movies) > maxRecommendations {
|
||||||
|
movies = movies[:maxRecommendations]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.HTML(components.RecommendedAnime(tv, movies, viewUser, user))
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
component RecommendedAnime(animes []*arn.Anime, worstGenres []string, viewUser *arn.User, user *arn.User)
|
component RecommendedAnime(animes []*arn.Anime, movies []*arn.Anime, viewUser *arn.User, user *arn.User)
|
||||||
h1(title="This is a machine-generated list of anime recommendations.")= "Recommended anime for " + viewUser.Nick
|
h1= "Recommendations for " + viewUser.Nick
|
||||||
|
|
||||||
|
h2.recommendations-category
|
||||||
|
Icon("tv")
|
||||||
|
span TV series
|
||||||
|
AnimeGrid(animes, user)
|
||||||
|
|
||||||
|
h2.recommendations-category
|
||||||
|
Icon("film")
|
||||||
|
span Movies
|
||||||
|
|
||||||
AnimeGrid(animes, user)
|
AnimeGrid(movies, user)
|
5
pages/recommended/anime.scarlet
Normal file
5
pages/recommended/anime.scarlet
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.recommendations-category
|
||||||
|
text-align left
|
||||||
|
font-size 1.8rem
|
||||||
|
border-bottom ui-border
|
||||||
|
padding-bottom 1rem
|
@ -163,7 +163,6 @@ export class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't ajaxify invalid links, links with a target or links that disable ajax specifically
|
// Don't ajaxify invalid links, links with a target or links that disable ajax specifically
|
||||||
console.log(link.hash)
|
|
||||||
if(link.href === "" || link.href.includes("#") || link.target.length > 0 || link.dataset.ajax === "false") {
|
if(link.href === "" || link.href.includes("#") || link.target.length > 0 || link.dataset.ajax === "false") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user