canape/backend/shows/handlers.go
Grégoire Delattre fa7bdc2a65
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build is passing
Fix panic while refreshing episode
2021-08-30 10:34:23 -10:00

433 lines
11 KiB
Go

package shows
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"net/http"
"git.quimbo.fr/odwrtw/canape/backend/auth"
"git.quimbo.fr/odwrtw/canape/backend/models"
"git.quimbo.fr/odwrtw/canape/backend/subtitles"
"git.quimbo.fr/odwrtw/canape/backend/web"
"github.com/gorilla/mux"
polochon "github.com/odwrtw/polochon/lib"
"github.com/odwrtw/polochon/lib/papi"
)
// ErrPolochonUnavailable is an error returned if the polochon server is not available
var ErrPolochonUnavailable = fmt.Errorf("Invalid polochon address")
// GetDetailsHandler handles details of a show
func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
id := vars["id"]
user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient(env.Database)
if err != nil {
return env.RenderError(w, err)
}
pShow, err := client.GetShow(id)
if err != nil && err != papi.ErrResourceNotFound {
env.Log.Println("Got error getting show ", err)
}
wShow, err := models.IsShowWishlisted(env.Database, user.ID, id)
if err != nil && err != papi.ErrResourceNotFound {
env.Log.Println("Got error getting wishlisted show ", err)
}
s := NewWithClient(id, client, pShow, wShow)
// First try from the db
first := []polochon.Detailer{env.Backend.Detailer}
// Then try from the polochon detailers
detailers := env.Config.Show.Detailers
err = s.GetAndFetch(env, first, detailers)
if err != nil {
env.Log.Error(err)
return err
}
env.Log.Debug("getting episodes torrents")
for _, e := range s.Show.Episodes {
// Get torrents from the db
backend := []polochon.Torrenter{env.Backend.Torrenter}
err := GetTorrents(env, e, backend)
if err != nil {
env.Log.Error(err)
}
}
return env.RenderJSON(w, s)
}
// RefreshShowHandler refreshes details of a show + torrents
func RefreshShowHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
id := vars["id"]
user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient(env.Database)
if err != nil {
return env.RenderError(w, err)
}
pShow, err := client.GetShow(id)
if err != nil && err != papi.ErrResourceNotFound {
env.Log.Println("Got error getting show ", err)
}
wShow, err := models.IsShowWishlisted(env.Database, user.ID, id)
if err != nil && err != papi.ErrResourceNotFound {
env.Log.Println("Got error getting wishlisted show ", err)
}
s := NewWithClient(id, client, pShow, wShow)
// Refresh the polochon detailers
detailers := env.Config.Show.Detailers
err = s.Refresh(env, detailers)
if err != nil {
env.Log.Error(err)
return err
}
env.Log.Debug("getting episodes torrents")
for _, e := range s.Episodes {
// Get torrents from the db
err := RefreshTorrents(env, e, env.Config.Show.Torrenters)
if err != nil {
env.Log.Error(err)
}
}
// Get everything from DB again
detailer := []polochon.Detailer{env.Backend.Detailer}
err = s.GetDetails(env, detailer)
if err != nil {
return env.RenderError(w, err)
}
return env.RenderJSON(w, s)
}
// SearchShow will search a show
func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
search := vars["search"]
user := auth.GetCurrentUser(r, env.Log)
var shows []*polochon.Show
searchers := env.Config.Show.Searchers
// Iterate on all the searchers to search for the show
for _, searcher := range searchers {
log := env.Log.WithField("searcher", searcher.Name())
result, err := searcher.SearchShow(search, log)
if err != nil {
log.Errorf("error while searching show : %s", err)
continue
}
log.Debug("found show")
// Add the results to the list of results
shows = append(shows, result...)
}
env.Log.Debugf("got %d shows doing search %q", len(shows), search)
client, err := user.NewPapiClient(env.Database)
if err != nil {
return env.RenderError(w, err)
}
// Get the polochon's shows
pShows, err := client.GetShows()
if err != nil {
return env.RenderError(w, err)
}
// Get the user's wishlisted shows
wShows, err := models.GetShowWishlist(env.Database, user.ID)
if err != nil {
return env.RenderError(w, err)
}
showList := []*Show{}
// Now iterate over all the shows to get details
for _, s := range shows {
pShow, _ := pShows.Has(s.ImdbID)
wShow, _ := wShows.IsShowInWishlist(s.ImdbID)
show := NewWithClient(s.ImdbID, client, pShow, wShow)
// First try from the db
first := []polochon.Detailer{env.Backend.Detailer}
// Then try from the polochon detailers
detailers := env.Config.Show.Detailers
err := show.GetAndFetch(env, first, detailers)
if err != nil {
env.Log.Error(err)
}
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()))
}
}
user := auth.GetCurrentUser(r, env.Log)
if err := models.AddShowToWishlist(env.Database, user.ID, id, 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"]
user := auth.GetCurrentUser(r, env.Log)
if err := models.DeleteShowFromWishlist(env.Database, user.ID, id); 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 {
user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient(env.Database)
if err != nil {
return env.RenderError(w, err)
}
// Get the polochon's shows
pShows, err := client.GetShows()
if err != nil {
return env.RenderError(w, err)
}
wShows, err := models.GetShowWishlist(env.Database, user.ID)
if err != nil {
return env.RenderError(w, err)
}
showList := []*Show{}
for _, wishedShow := range wShows.List() {
pShow, _ := pShows.Has(wishedShow.ImdbID)
show := NewWithClient(wishedShow.ImdbID, client, pShow, wishedShow)
// First check in the DB
before := []polochon.Detailer{env.Backend.Detailer}
// Then with the default detailers
after := env.Config.Show.Detailers
err := show.GetAndFetch(env, before, after)
if err != nil {
env.Log.Errorf("error while getting show details : %s", err)
continue
}
showList = append(showList, show)
}
return env.RenderJSON(w, showList)
}
// PolochonShowsHandler will returns shows from Polochon
func PolochonShowsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
user := auth.GetCurrentUser(r, env.Log)
// Get the polochon's shows
shows, err := getPolochonShows(env, user)
if err != nil {
return env.RenderError(w, err)
}
// Get details in DB for each shows
// Fetch the details if not found
for _, s := range shows {
// First try from the db
first := []polochon.Detailer{env.Backend.Detailer}
// Then try from the polochon detailer
detailers := env.Config.Show.Detailers
err := s.GetAndFetch(env, first, detailers)
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 {
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"])
user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient(env.Database)
if err != nil {
return env.RenderError(w, err)
}
pShow, err := client.GetShow(id)
if err != nil && err != papi.ErrResourceNotFound {
env.Log.Warnf("Error getting show %q", err)
}
if pShow == nil {
pShow = &papi.Show{Show: &polochon.Show{ImdbID: id}}
}
s := &Show{
Show: pShow.Show,
client: client,
pShow: pShow,
}
e := NewEpisode(s, season, episode)
// Refresh the episode
err = e.Refresh(env, env.Config.Show.Detailers)
if err != nil {
env.Log.Error(err)
return env.RenderError(w, err)
}
// Refresh the torrents
err = e.RefreshTorrents(env, env.Config.Show.Torrenters)
if err != nil {
env.Log.Error(err)
}
// Get everything from DB again
detailer := []polochon.Detailer{env.Backend.Detailer}
err = e.GetEpisodeDetails(env, detailer)
if err != nil {
env.Log.Error(err)
return env.RenderError(w, err)
}
return env.RenderJSON(w, e)
}
// RefreshEpisodeSubtitlesHandler refreshes details for an episode
func RefreshEpisodeSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
id := vars["id"]
lang := polochon.Language(vars["lang"])
// 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"])
// Get the user
user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client
client, err := user.NewPapiClient(env.Database)
if err != nil {
return env.RenderError(w, err)
}
e := &papi.Episode{
ShowEpisode: &polochon.ShowEpisode{
ShowImdbID: id,
Season: season,
Episode: episode,
},
}
sub, err := client.UpdateSubtitle(e, lang)
if err != nil {
return env.RenderError(w, err)
}
// TODO: handle this with a better error
if sub == nil {
return env.RenderJSON(w, nil)
}
url, err := client.DownloadURL(sub)
if err != nil {
return env.RenderError(w, err)
}
s := &subtitles.Subtitle{
Subtitle: sub.Subtitle,
URL: url,
VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, lang),
}
return env.RenderJSON(w, s)
}
// DownloadVVTSubtitle returns a vvt subtitle for the movie
func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
id := vars["id"]
lang := polochon.Language(vars["lang"])
season, _ := strconv.Atoi(vars["season"])
episode, _ := strconv.Atoi(vars["episode"])
// Get the user
user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client
client, err := user.NewPapiClient(env.Database)
if err != nil {
return env.RenderError(w, err)
}
s := &papi.Subtitle{
Subtitle: &polochon.Subtitle{
Video: &papi.Episode{
ShowEpisode: &polochon.ShowEpisode{
ShowImdbID: id,
Season: season,
Episode: episode,
},
},
Lang: lang,
},
}
url, err := client.DownloadURLWithToken(s)
if err != nil {
return env.RenderError(w, err)
}
return subtitles.ConvertSubtitle(url, w)
}