crunchy-cli/utils/walk.go
2022-01-17 15:53:28 +01:00

210 lines
5.3 KiB
Go

package utils
import (
"fmt"
"github.com/ByteDream/crunchyroll-go"
)
// EpisodeWalker is an easy-to-use struct which walks down given urls
// and triggers functions where you can perform further actions with
// the delivered objects
type EpisodeWalker struct {
// The Crunchyroll instance to perform all actions on
Crunchyroll *crunchyroll.Crunchyroll
// If CheckDuplicates is true, duplicated urls, seasons and episodes
// are filtered out and the values given in OnSeason for example is
// always unique
CheckDuplicates bool
// OnUrl gets called when an url is parsed.
// The error is generally only nil when the url is invalid
OnUrl func(url string, err error) error
// OnSeason gets called when a season was parsed
OnSeason func(season *crunchyroll.Season, err error) error
// OnEpisode gets called when a season was parsed
OnEpisode func(episode *crunchyroll.Episode, err error) error
}
// WalkURLs walks through all urls.
// Urls to seasons and episodes are support, normal as well as beta urls
func (ew EpisodeWalker) WalkURLs(urls []string) error {
var episodeIds, seasonIds, seriesNames []string
var episodeNames map[string][]string
for _, url := range urls {
if episodeId, ok := crunchyroll.ParseBetaEpisodeURL(url); ok && !(ew.CheckDuplicates && sliceContains(episodeIds, episodeId)) {
episodeIds = append(episodeIds, episodeId)
} else if seasonId, ok := crunchyroll.ParseBetaSeriesURL(url); ok && !(ew.CheckDuplicates && sliceContains(seasonIds, seasonId)) {
seasonIds = append(seasonIds, seasonId)
} else if seriesName, title, _, _, ok := crunchyroll.ParseEpisodeURL(url); ok {
if eps, ok := episodeNames[seriesName]; ok {
if !ew.CheckDuplicates || !sliceContains(eps, title) {
eps = append(eps, title)
}
} else {
episodeNames[seriesName] = []string{title}
}
} else if seriesName, ok := crunchyroll.ParseVideoURL(url); ok && !(ew.CheckDuplicates && sliceContains(seriesNames, seriesName)) {
seriesNames = append(seriesNames, seriesName)
} else {
err := fmt.Errorf("invalid url %s", url)
if ew.OnUrl != nil {
if err = ew.OnUrl(url, err); err != nil {
return err
}
continue
} else {
return err
}
}
if ew.OnUrl != nil {
if err := ew.OnUrl(url, nil); err != nil {
return err
}
}
}
for _, name := range seriesNames {
video, err := ew.Crunchyroll.FindVideoByName(name)
if err != nil {
return err
}
// in all cases i've ever tested video was a series - even
// if it was listed as a movie on the crunchyroll website.
// i just hope no error occurs here :)
seasons, err := video.(*crunchyroll.Series).Seasons()
if err != nil {
return err
}
for _, season := range seasons {
if ew.CheckDuplicates {
if sliceContains(seasonIds, season.ID) {
continue
}
seasonIds = append(seasonIds, season.ID)
}
if ew.OnSeason != nil {
if err := ew.OnSeason(season, nil); err != nil {
return err
}
}
}
}
if err := ew.walkEpisodeIds(episodeIds); err != nil {
return err
} else if err := ew.walkSeasonIds(seasonIds); err != nil {
return err
} else if err := ew.walkEpisodeNames(episodeNames); err != nil {
return err
}
return nil
}
func (ew EpisodeWalker) walkEpisodeIds(episodeIds []string) error {
var episodeIdsCheck []string
for _, id := range episodeIds {
if ew.CheckDuplicates {
if sliceContains(episodeIdsCheck, id) {
continue
}
episodeIdsCheck = append(episodeIdsCheck, id)
}
episode, err := crunchyroll.EpisodeFromID(ew.Crunchyroll, id)
if ew.OnEpisode != nil {
if err = ew.OnEpisode(episode, err); err != nil {
return err
}
}
}
return nil
}
func (ew EpisodeWalker) walkSeasonIds(seasonIds []string) error {
var episodeIdsCheck []string
for _, id := range seasonIds {
season, err := crunchyroll.SeasonFromID(ew.Crunchyroll, id)
if ew.OnSeason != nil {
if err = ew.OnSeason(season, err); err != nil {
return err
}
} else if err != nil {
return err
}
eps, err := season.Episodes()
if err != nil {
return err
}
for _, ep := range eps {
if ew.CheckDuplicates {
if sliceContains(episodeIdsCheck, ep.ID) {
continue
}
episodeIdsCheck = append(episodeIdsCheck, ep.ID)
}
if ew.OnEpisode != nil {
if err = ew.OnEpisode(ep, nil); err != nil {
return err
}
}
}
}
return nil
}
func (ew EpisodeWalker) walkEpisodeNames(episodeNames map[string][]string) error {
var episodeIdsCheck []string
for seriesName, episodeName := range episodeNames {
video, err := ew.Crunchyroll.FindVideoByName(seriesName)
if err != nil {
return err
}
series := video.(*crunchyroll.Series)
seasons, err := series.Seasons()
if err != nil {
return err
}
for _, season := range seasons {
eps, err := season.Episodes()
if err != nil {
return err
}
for _, ep := range eps {
for _, name := range episodeName {
if ep.SlugTitle == name {
if ew.OnEpisode != nil {
if ew.CheckDuplicates {
if sliceContains(episodeIdsCheck, ep.ID) {
continue
}
episodeIdsCheck = append(episodeIdsCheck, ep.ID)
}
if err = ew.OnEpisode(ep, nil); err != nil {
return err
}
}
break
}
}
}
}
}
return nil
}
func sliceContains(slice []string, elem string) bool {
for _, s := range slice {
if elem == s {
return true
}
}
return false
}