extract: add merge/validation functions
This commit is contained in:
133
internal/episodes/service/merge.go
Normal file
133
internal/episodes/service/merge.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"mal/integrations/jikan"
|
||||||
|
"mal/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type episodePartial struct {
|
||||||
|
title string
|
||||||
|
filler bool
|
||||||
|
recap bool
|
||||||
|
sub bool
|
||||||
|
dub bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func titleCandidates(anime domain.Anime) []string {
|
||||||
|
out := []string{anime.Title}
|
||||||
|
if anime.TitleEnglish != "" && anime.TitleEnglish != anime.Title {
|
||||||
|
out = append(out, anime.TitleEnglish)
|
||||||
|
}
|
||||||
|
if anime.TitleJapanese != "" {
|
||||||
|
out = append(out, anime.TitleJapanese)
|
||||||
|
}
|
||||||
|
for _, syn := range anime.TitleSynonyms {
|
||||||
|
if syn != "" && syn != anime.Title && syn != anime.TitleEnglish && syn != anime.TitleJapanese {
|
||||||
|
out = append(out, syn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCanonicalEpisodePayloadValid(payload domain.CanonicalEpisodeList, expectedCount int) bool {
|
||||||
|
if expectedCount <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(payload.Episodes) > expectedCount {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, episode := range payload.Episodes {
|
||||||
|
if episode.Number <= 0 || episode.Number > expectedCount {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeEpisodes(jikanEpisodes []jikan.Episode, availability domain.EpisodeAvailability, expectedCount int) []domain.CanonicalEpisode {
|
||||||
|
byNumber := map[int]episodePartial{}
|
||||||
|
|
||||||
|
for i, ep := range jikanEpisodes {
|
||||||
|
if exceedsExpectedCount(i+1, expectedCount) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
number, ok := jikanEpisodeNumber(ep, i)
|
||||||
|
if !ok || exceedsExpectedCount(number, expectedCount) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mergeEpisode(&byNumber, number, func(item *episodePartial) {
|
||||||
|
item.title = strings.TrimSpace(ep.Title)
|
||||||
|
item.filler = ep.Filler
|
||||||
|
item.recap = ep.Recap
|
||||||
|
})
|
||||||
|
}
|
||||||
|
mergeAvailability(&byNumber, availability.Sub, expectedCount, func(item *episodePartial) { item.sub = true })
|
||||||
|
mergeAvailability(&byNumber, availability.Dub, expectedCount, func(item *episodePartial) { item.dub = true })
|
||||||
|
|
||||||
|
numbers := make([]int, 0, len(byNumber))
|
||||||
|
for number := range byNumber {
|
||||||
|
numbers = append(numbers, number)
|
||||||
|
}
|
||||||
|
sort.Ints(numbers)
|
||||||
|
|
||||||
|
episodes := make([]domain.CanonicalEpisode, 0, len(numbers))
|
||||||
|
for _, number := range numbers {
|
||||||
|
item := byNumber[number]
|
||||||
|
title := item.title
|
||||||
|
if title == "" {
|
||||||
|
title = fmt.Sprintf("Episode %d", number)
|
||||||
|
}
|
||||||
|
episodes = append(episodes, domain.CanonicalEpisode{
|
||||||
|
Number: number,
|
||||||
|
Title: title,
|
||||||
|
HasSub: item.sub,
|
||||||
|
HasDub: item.dub,
|
||||||
|
SubOnly: item.sub && !item.dub,
|
||||||
|
Filler: item.filler,
|
||||||
|
Recap: item.recap,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return episodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeEpisode(byNumber *map[int]episodePartial, number int, update func(*episodePartial)) {
|
||||||
|
item := (*byNumber)[number]
|
||||||
|
update(&item)
|
||||||
|
(*byNumber)[number] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeAvailability(byNumber *map[int]episodePartial, numbers []int, expectedCount int, update func(*episodePartial)) {
|
||||||
|
for _, number := range numbers {
|
||||||
|
if number <= 0 || exceedsExpectedCount(number, expectedCount) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mergeEpisode(byNumber, number, update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jikanEpisodeNumber(ep jikan.Episode, index int) (int, bool) {
|
||||||
|
number, err := strconv.Atoi(strings.TrimSpace(ep.Episode))
|
||||||
|
if err == nil && number > 0 {
|
||||||
|
return number, true
|
||||||
|
}
|
||||||
|
if index < 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return index + 1, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func exceedsExpectedCount(number int, expectedCount int) bool {
|
||||||
|
return expectedCount > 0 && number > expectedCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncate(value string, maxLen int) string {
|
||||||
|
if len(value) <= maxLen {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return value[:maxLen]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user