package shows import ( "encoding/json" "errors" "fmt" "net" "net/http" "net/url" "strconv" "github.com/gorilla/mux" customError "github.com/odwrtw/errors" "github.com/odwrtw/papi" polochon "github.com/odwrtw/polochon/lib" "github.com/odwrtw/polochon/modules/pam" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web" ) // ErrPolochonUnavailable is an error returned if the polochon server is not available var ErrPolochonUnavailable = fmt.Errorf("Invalid polochon address") // GetDetailsHandler retrieves details of a show func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { return DetailsHandler(env, w, r, false) } // RefreahDetailsHandler refresh details of a show func RefreshDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { return DetailsHandler(env, w, r, true) } // DetailsHandler handles details of an episode func DetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request, force bool) error { vars := mux.Vars(r) id := vars["id"] v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) if !ok { return env.RenderError(w, errors.New("invalid user type")) } s := New(id) if err := s.GetDetails(env, user, force); err != nil { return env.RenderError(w, err) } if err := s.GetEpisodes(env, force); err != nil { return env.RenderError(w, err) } // Get the show from the polochon of the user pShow, err := getPolochonShow(user, s.ImdbID) if err != nil { env.Log.Warnf("error while getting polochon show %s : %s", s.ImdbID, err) } // For each of the user's polochon episodes, add a direct link to it for _, pEpisode := range pShow.Episodes { for _, e := range s.Episodes { if e.Season != pEpisode.Season || e.Episode != pEpisode.Episode { continue } e.PolochonURL = pEpisode.PolochonURL } } return env.RenderJSON(w, s) } // SearchShow will search a show func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error { var data struct { Key string `json:"key"` } if err := json.NewDecoder(r.Body).Decode(&data); err != nil { return env.RenderError(w, errors.New("failed to get the search key")) } if data.Key == "" { return env.RenderError(w, errors.New("no given key")) } v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) if !ok { return env.RenderError(w, errors.New("invalid user type")) } var shows []*polochon.Show searchers := env.Config.ShowSearchers for _, searcher := range searchers { result, err := searcher.SearchShow(data.Key, env.Log) if err != nil { env.Log.Errorf("error while searching show : %s", err) continue } shows = append(shows, result...) } env.Log.Debugf("got %d shows doing search %q", len(shows), data.Key) showList := []*Show{} for _, s := range shows { show := New(s.ImdbID) err := show.GetDetails(env, user, false) if err != nil { env.Log.Errorf("error while getting show details : %s", err) continue } showList = append(showList, show) } return env.RenderJSON(w, showList) } // AddToWishlist adds a show to the user's wishlist func AddToWishlist(env *web.Env, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) id := vars["id"] var data struct { Season int `json:"season"` Episode int `json:"episode"` } if r.ContentLength > 0 { if err := json.NewDecoder(r.Body).Decode(&data); err != nil { return env.RenderError(w, errors.New("failed to get POST data season and episode "+err.Error())) } } v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) if !ok { return env.RenderError(w, errors.New("invalid user type")) } s := New(id) if err := s.AddToWishlist(env, user, data.Season, data.Episode); err != nil { env.Log.Warnf("Error while adding to db : %s", err) return env.RenderError(w, err) } return env.RenderOK(w, "Show added to wishlist") } // DeleteFromWishlist deletes a show from the user's wishlist func DeleteFromWishlist(env *web.Env, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) id := vars["id"] v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) if !ok { return env.RenderError(w, errors.New("invalid user type")) } s := New(id) if err := s.DeleteFromWishlist(env, user); err != nil { env.Log.Warnf("Error while deleting to db : %s", err) return env.RenderError(w, err) } return env.RenderOK(w, "Show deleted from wishlist") } // GetWishlistHandler returns the tracked shows of a user func GetWishlistHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) if !ok { return env.RenderError(w, errors.New("invalid user type")) } shows, err := GetWishlist(env, user) if err != nil { return env.RenderError(w, err) } return env.RenderJSON(w, shows) } // getPolochonShows returns all the Shows from the polochon of a user func getPolochonShows(user *users.User) ([]*Show, error) { shows := []*Show{} var polochonConfig config.UserPolochon err := user.GetConfig("polochon", &polochonConfig) if err != nil { return shows, err } client, err := papi.New(polochonConfig.URL) if err != nil { return shows, err } if polochonConfig.Token != "" { client.SetToken(polochonConfig.Token) } pshows, err := client.GetShows() if err != nil { // Catch network error for accessing specified polochon address if uerr, ok := err.(*url.Error); ok { if nerr, ok := uerr.Err.(*net.OpError); ok { if nerr.Op == "dial" { return shows, ErrPolochonUnavailable } } } return shows, err } for _, pshow := range pshows { show := New(pshow.ImdbID) for _, season := range pshow.Seasons { for _, episode := range season.Episodes { e := NewEpisode() e.Season = episode.Season e.Episode = episode.Episode e.ShowImdbID = episode.ShowImdbID e.PolochonURL, _ = client.DownloadURL( &papi.Episode{ ShowImdbID: show.ImdbID, Episode: e.Episode, Season: e.Season, }, ) show.Episodes = append(show.Episodes, e) } } shows = append(shows, show) } return shows, nil } // getPolochonShow returns a Show with its epidodes from the polochon of a user func getPolochonShow(user *users.User, imdbID string) (Show, error) { shows, err := getPolochonShows(user) if err != nil { return Show{}, err } for _, s := range shows { if s.ImdbID == imdbID { return *s, nil } } return Show{}, nil } // FromPolochon will returns shows from Polochon func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error { v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) if !ok { return env.RenderError(w, errors.New("invalid user type")) } shows, err := getPolochonShows(user) if err != nil { return env.RenderError(w, err) } var polochonConfig config.UserPolochon err = user.GetConfig("polochon", &polochonConfig) if err != nil { return env.RenderError(w, err) } detailer, err := pam.New(&pam.Params{ Endpoint: polochonConfig.URL, Token: polochonConfig.Token, }) for _, s := range shows { s.Detailers = []polochon.Detailer{detailer} err := s.GetDetails(env, user, false) if err != nil { env.Log.Error(err) } } return env.RenderJSON(w, shows) } // RefreshEpisodeHandler refresh details of an episode func RefreshEpisodeHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { return EpisodeDetailsHandler(env, w, r, true) } // EpisodeDetailsHandler handles details of a show func EpisodeDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request, force bool) error { vars := mux.Vars(r) id := vars["id"] // No need to check errors here as the router is making sure that season // and episode are numbers season, _ := strconv.Atoi(vars["season"]) episode, _ := strconv.Atoi(vars["episode"]) v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) if !ok { return env.RenderError(w, errors.New("invalid user type")) } s := New(id) e, err := s.GetEpisodeDetails(env, season, episode, force) if err != nil { return env.RenderError(w, err) } err = e.GetTorrents(env, force) if err != nil && customError.IsFatal(err) { return env.RenderError(w, err) } // Get the show from the polochon of the user pShow, err := getPolochonShow(user, id) if err != nil { env.Log.Warnf("error while getting polochon episode %s S%02dE%02d : %s", id, season, episode, err) } // Find if the user has a the episode in its polochon to add the // DownloadURL for _, pEpisode := range pShow.Episodes { if e.Season == pEpisode.Season && e.Episode == pEpisode.Episode { e.PolochonURL = pEpisode.PolochonURL break } } return env.RenderJSON(w, e) }