From d1235fea1af060a2127847b1d8f0993850e4e5bc Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 17 Jan 2022 15:53:28 +0100 Subject: [PATCH] Added walk utils api --- utils/walk.go | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 utils/walk.go diff --git a/utils/walk.go b/utils/walk.go new file mode 100644 index 0000000..4eb9ff3 --- /dev/null +++ b/utils/walk.go @@ -0,0 +1,210 @@ +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 +}