Merge branch 'wishlist' into 'master'
Add Wishlist to the party See merge request !33
This commit is contained in:
commit
879bc2a412
@ -66,12 +66,12 @@ CREATE UNIQUE INDEX ON episodes (show_imdb_id, season, episode);
|
||||
CREATE TRIGGER update_episodes_updated_at BEFORE UPDATE ON episodes FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
CREATE TABLE shows_tracked (
|
||||
show_imdb_id text NOT NULL REFERENCES shows (imdb_id) ON DELETE CASCADE,
|
||||
imdb_id text NOT NULL REFERENCES shows (imdb_id) ON DELETE CASCADE,
|
||||
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
season smallint NOT NULL,
|
||||
episode smallint NOT NULL
|
||||
);
|
||||
CREATE INDEX ON shows_tracked (show_imdb_id, user_id);
|
||||
CREATE UNIQUE INDEX ON shows_tracked (imdb_id, user_id);
|
||||
CREATE INDEX ON shows_tracked (user_id);
|
||||
|
||||
CREATE TABLE movies (
|
||||
@ -92,3 +92,10 @@ CREATE TABLE movies (
|
||||
);
|
||||
CREATE INDEX ON movies (imdb_id);
|
||||
CREATE TRIGGER update_movies_updated_at BEFORE UPDATE ON movies FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
CREATE TABLE movies_tracked (
|
||||
imdb_id text NOT NULL REFERENCES movies (imdb_id) ON DELETE CASCADE,
|
||||
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE UNIQUE INDEX ON movies_tracked (imdb_id, user_id);
|
||||
CREATE INDEX ON movies_tracked (user_id);
|
||||
|
@ -108,7 +108,7 @@ func GetMovies(env *web.Env, user *users.User, source string, category string, f
|
||||
movieList := []*movies.Movie{}
|
||||
for _, id := range movieIds {
|
||||
movie := movies.New(id)
|
||||
err := movie.GetDetails(env, force)
|
||||
err := movie.GetDetails(env, user, force)
|
||||
if err != nil {
|
||||
env.Log.Errorf("error while getting movie details : %s", err)
|
||||
continue
|
||||
@ -131,7 +131,7 @@ func GetMovies(env *web.Env, user *users.User, source string, category string, f
|
||||
}
|
||||
|
||||
// GetShows get some shows
|
||||
func GetShows(env *web.Env, source string, category string, force bool) ([]*shows.Show, error) {
|
||||
func GetShows(env *web.Env, user *users.User, source string, category string, force bool) ([]*shows.Show, error) {
|
||||
showIds, err := GetMediaIDs(env, "show", source, category, force)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -140,7 +140,7 @@ func GetShows(env *web.Env, source string, category string, force bool) ([]*show
|
||||
showList := []*shows.Show{}
|
||||
for _, id := range showIds {
|
||||
show := shows.New(id)
|
||||
err := show.GetDetails(env, force)
|
||||
err := show.GetDetails(env, user, force)
|
||||
if err != nil {
|
||||
env.Log.Errorf("error while getting show details : %s", err)
|
||||
continue
|
||||
@ -208,8 +208,14 @@ func ExploreShows(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
category = "popular"
|
||||
}
|
||||
|
||||
v := auth.GetCurrentUser(r, env.Log)
|
||||
user, ok := v.(*users.User)
|
||||
if !ok {
|
||||
return env.RenderError(w, errors.New("invalid user"))
|
||||
}
|
||||
|
||||
// Get the medias without trying to refresh them
|
||||
shows, err := GetShows(env, source, category, false)
|
||||
shows, err := GetShows(env, user, source, category, false)
|
||||
if err != nil {
|
||||
return env.RenderError(w, err)
|
||||
}
|
||||
@ -244,7 +250,7 @@ func Refresh(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
v := auth.GetCurrentUser(r, env.Log)
|
||||
user, ok := v.(*users.User)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid user type")
|
||||
return env.RenderError(w, fmt.Errorf("invalid user type"))
|
||||
}
|
||||
|
||||
// We'll refresh the medias for each sources
|
||||
@ -273,11 +279,17 @@ func RefreshShows(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
category = "popular"
|
||||
}
|
||||
|
||||
v := auth.GetCurrentUser(r, env.Log)
|
||||
user, ok := v.(*users.User)
|
||||
if !ok {
|
||||
return env.RenderError(w, fmt.Errorf("invalid user type"))
|
||||
}
|
||||
|
||||
// We'll refresh the medias for each sources
|
||||
for _, source := range ShowMediaSources {
|
||||
env.Log.Debugf("refreshing %s", source)
|
||||
// GetMedias and refresh them
|
||||
_, err := GetShows(env, source, category, true)
|
||||
_, err := GetShows(env, user, source, category, true)
|
||||
if err != nil {
|
||||
return env.RenderError(w, err)
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
for _, m := range movies {
|
||||
m.Detailers = []polochon.Detailer{detailer}
|
||||
err := m.GetDetails(env, false)
|
||||
err := m.GetDetails(env, user, false)
|
||||
if err != nil {
|
||||
env.Log.Error(err)
|
||||
}
|
||||
@ -120,8 +120,14 @@ func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
v := auth.GetCurrentUser(r, env.Log)
|
||||
user, ok := v.(*users.User)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid user type")
|
||||
}
|
||||
|
||||
m := New(id)
|
||||
if err := m.GetDetails(env, true); err != nil {
|
||||
if err := m.GetDetails(env, user, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -170,7 +176,7 @@ func SearchMovie(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
movieList := []*Movie{}
|
||||
for _, m := range movies {
|
||||
movie := New(m.ImdbID)
|
||||
err := movie.GetDetails(env, false)
|
||||
err := movie.GetDetails(env, user, false)
|
||||
if err != nil {
|
||||
env.Log.Errorf("error while getting movie details : %s", err)
|
||||
continue
|
||||
@ -207,18 +213,18 @@ func DeleteHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
v := auth.GetCurrentUser(r, env.Log)
|
||||
user, ok := v.(*users.User)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid user type")
|
||||
return env.RenderError(w, errors.New("invalid user type"))
|
||||
}
|
||||
|
||||
var polochonConfig config.UserPolochon
|
||||
err := user.GetConfig("polochon", &polochonConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
return env.RenderError(w, err)
|
||||
}
|
||||
|
||||
client, err := papi.New(polochonConfig.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
return env.RenderError(w, err)
|
||||
}
|
||||
|
||||
if polochonConfig.Token != "" {
|
||||
@ -227,3 +233,57 @@ func DeleteHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
return client.Delete(&papi.Movie{ImdbID: id})
|
||||
}
|
||||
|
||||
// AddToWishlist adds a movie to the user's wishlist
|
||||
func AddToWishlist(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"))
|
||||
}
|
||||
|
||||
m := New(id)
|
||||
if err := m.AddToWishlist(env, user); err != nil {
|
||||
return env.RenderError(w, err)
|
||||
}
|
||||
|
||||
return env.RenderOK(w, "Movie added to wishlist")
|
||||
}
|
||||
|
||||
// DeleteFromWishlist deletes a movie 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"))
|
||||
}
|
||||
|
||||
m := New(id)
|
||||
if err := m.DeleteFromWishlist(env, user); err != nil {
|
||||
return env.RenderError(w, err)
|
||||
}
|
||||
|
||||
return env.RenderOK(w, "Movie deleted from wishlist")
|
||||
}
|
||||
|
||||
// GetWishlistHandler returns the wishlisted movies 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"))
|
||||
}
|
||||
|
||||
movies, err := GetWishlist(env, user)
|
||||
if err != nil {
|
||||
return env.RenderError(w, err)
|
||||
}
|
||||
|
||||
return env.RenderJSON(w, movies)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/torrents"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||
)
|
||||
|
||||
@ -32,12 +33,48 @@ const (
|
||||
RETURNING id;`
|
||||
|
||||
getMovieQueryByImdbID = `
|
||||
SELECT *
|
||||
FROM movies WHERE imdb_id=$1;`
|
||||
SELECT
|
||||
movies.id,
|
||||
movies.title,
|
||||
movies.imdb_id,
|
||||
movies.tmdb_id,
|
||||
movies.votes,
|
||||
movies.rating,
|
||||
movies.plot,
|
||||
movies.year,
|
||||
movies.original_title,
|
||||
movies.runtime,
|
||||
movies.genres,
|
||||
movies.sort_title,
|
||||
movies.tagline,
|
||||
movies.created_at,
|
||||
movies.updated_at,
|
||||
movies_tracked.user_id
|
||||
FROM movies LEFT JOIN movies_tracked
|
||||
ON movies.imdb_id=movies_tracked.imdb_id AND movies_tracked.user_id=$2
|
||||
WHERE movies.imdb_id=$1;`
|
||||
|
||||
getMovieQueryByID = `
|
||||
SELECT *
|
||||
FROM movies WHERE id=$1;`
|
||||
SELECT
|
||||
movies.id,
|
||||
movies.title,
|
||||
movies.imdb_id,
|
||||
movies.tmdb_id,
|
||||
movies.votes,
|
||||
movies.rating,
|
||||
movies.plot,
|
||||
movies.year,
|
||||
movies.original_title,
|
||||
movies.runtime,
|
||||
movies.genres,
|
||||
movies.sort_title,
|
||||
movies.tagline,
|
||||
movies.created_at,
|
||||
movies.updated_at,
|
||||
movies_tracked.user_id
|
||||
FROM movies LEFT JOIN movies_tracked
|
||||
ON movies.imdb_id=movies_tracked.imdb_id AND movies_tracked.user_id=$2
|
||||
WHERE movies.id=$1;`
|
||||
|
||||
deleteMovieQuery = `DELETE FROM movies WHERE id=$1;`
|
||||
)
|
||||
@ -47,6 +84,7 @@ type MovieDB struct {
|
||||
ID string `db:"id"`
|
||||
ImdbID string `db:"imdb_id"`
|
||||
TmdbID int `db:"tmdb_id"`
|
||||
UserID *string `db:"user_id"`
|
||||
Title string `db:"title"`
|
||||
OriginalTitle string `db:"original_title"`
|
||||
SortTitle string `db:"sort_title"`
|
||||
@ -104,12 +142,16 @@ func (m *Movie) FillFromDB(mDB *MovieDB) {
|
||||
m.Genres = mDB.Genres
|
||||
m.SortTitle = mDB.SortTitle
|
||||
m.Tagline = mDB.Tagline
|
||||
if mDB.UserID != nil {
|
||||
m.Wishlisted = true
|
||||
}
|
||||
}
|
||||
|
||||
// Movie represents a movie
|
||||
type Movie struct {
|
||||
sqly.BaseModel
|
||||
polochon.Movie
|
||||
Wishlisted bool `json:"wishlisted"`
|
||||
PolochonURL string `json:"polochon_url"`
|
||||
PosterURL string `json:"poster_url"`
|
||||
}
|
||||
@ -124,13 +166,13 @@ func New(imdbID string) *Movie {
|
||||
}
|
||||
|
||||
// Get returns show details in database from id or imdbid or an error
|
||||
func (m *Movie) Get(env *web.Env) error {
|
||||
func (m *Movie) Get(env *web.Env, user *users.User) error {
|
||||
var mDB MovieDB
|
||||
var err error
|
||||
if m.ID != "" {
|
||||
err = env.Database.QueryRowx(getMovieQueryByID, m.ID).StructScan(&mDB)
|
||||
err = env.Database.QueryRowx(getMovieQueryByID, m.ID, user.ID).StructScan(&mDB)
|
||||
} else if m.ImdbID != "" {
|
||||
err = env.Database.QueryRowx(getMovieQueryByImdbID, m.ImdbID).StructScan(&mDB)
|
||||
err = env.Database.QueryRowx(getMovieQueryByImdbID, m.ImdbID, user.ID).StructScan(&mDB)
|
||||
} else {
|
||||
err = fmt.Errorf("Can't get movie details, you have to specify an ID or ImdbID")
|
||||
}
|
||||
@ -151,7 +193,7 @@ func (m *Movie) Get(env *web.Env) error {
|
||||
//
|
||||
// If force is used, the detailer will be used even if the movie is found in
|
||||
// database
|
||||
func (m *Movie) GetDetails(env *web.Env, force bool) error {
|
||||
func (m *Movie) GetDetails(env *web.Env, user *users.User, force bool) error {
|
||||
log := env.Log.WithFields(logrus.Fields{
|
||||
"imdb_id": m.ImdbID,
|
||||
"function": "movies.GetDetails",
|
||||
@ -163,7 +205,7 @@ func (m *Movie) GetDetails(env *web.Env, force bool) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
err = m.Get(env)
|
||||
err = m.Get(env, user)
|
||||
switch err {
|
||||
case nil:
|
||||
log.Debug("movie found in database")
|
||||
|
83
src/internal/movies/wishlist.go
Normal file
83
src/internal/movies/wishlist.go
Normal file
@ -0,0 +1,83 @@
|
||||
package movies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||
)
|
||||
|
||||
const (
|
||||
upsertWishlistQuery = `
|
||||
INSERT INTO movies_tracked (imdb_id, user_id)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (imdb_id, user_id)
|
||||
DO UPDATE
|
||||
SET imdb_id=$1, user_id=$2;`
|
||||
|
||||
getWishlistQueryByUserID = `
|
||||
SELECT
|
||||
movies.id,
|
||||
movies.title,
|
||||
movies.imdb_id,
|
||||
movies.tmdb_id,
|
||||
movies.votes,
|
||||
movies.rating,
|
||||
movies.plot,
|
||||
movies.year,
|
||||
movies.original_title,
|
||||
movies.runtime,
|
||||
movies.genres,
|
||||
movies.sort_title,
|
||||
movies.tagline,
|
||||
movies.created_at,
|
||||
movies.updated_at,
|
||||
movies_tracked.user_id
|
||||
FROM movies INNER JOIN movies_tracked
|
||||
ON movies.imdb_id=movies_tracked.imdb_id AND movies_tracked.user_id=$1;`
|
||||
|
||||
deleteWishlistedQueryByID = `DELETE FROM movies_tracked WHERE imdb_id=$1 AND user_id=$2;`
|
||||
)
|
||||
|
||||
// AddToWishlist Adds a movie to a user's wishlist
|
||||
func (m *Movie) AddToWishlist(env *web.Env, user *users.User) error {
|
||||
_, err := env.Database.Exec(upsertWishlistQuery, m.ImdbID, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteFromWishlist deletes a movie from a user's wishlist
|
||||
func (m *Movie) DeleteFromWishlist(env *web.Env, user *users.User) error {
|
||||
r, err := env.Database.Exec(deleteWishlistedQueryByID, m.ImdbID, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, _ := r.RowsAffected()
|
||||
if count != 1 {
|
||||
return fmt.Errorf("Unexpected number of row deleted: %d", count)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetWishlist returns a list of movies wishlisted by user
|
||||
func GetWishlist(env *web.Env, user *users.User) ([]*Movie, error) {
|
||||
var moviesDB = []*MovieDB{}
|
||||
err := env.Database.Select(&moviesDB, getWishlistQueryByUserID, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var movies []*Movie
|
||||
for _, movieDB := range moviesDB {
|
||||
movie := New(movieDB.ImdbID)
|
||||
// Set the poster url
|
||||
movie.PosterURL = movie.GetPosterURL(env)
|
||||
|
||||
movie.FillFromDB(movieDB)
|
||||
movies = append(movies, movie)
|
||||
}
|
||||
|
||||
return movies, nil
|
||||
}
|
@ -7,20 +7,38 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
polochon "github.com/odwrtw/polochon/lib"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||
)
|
||||
|
||||
// GetDetailsHandler retrieves details for a movie
|
||||
// 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 a show
|
||||
func DetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request, force bool) error {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
s := New(id)
|
||||
if err := s.GetDetails(env, false); err != nil {
|
||||
return err
|
||||
v := auth.GetCurrentUser(r, env.Log)
|
||||
user, ok := v.(*users.User)
|
||||
if !ok {
|
||||
return env.RenderError(w, errors.New("invalid user type"))
|
||||
}
|
||||
if err := s.GetEpisodes(env); err != nil {
|
||||
return err
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return env.RenderJSON(w, s)
|
||||
@ -39,6 +57,12 @@ func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
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 {
|
||||
@ -54,7 +78,7 @@ func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
showList := []*Show{}
|
||||
for _, s := range shows {
|
||||
show := New(s.ImdbID)
|
||||
err := show.GetDetails(env, false)
|
||||
err := show.GetDetails(env, user, false)
|
||||
if err != nil {
|
||||
env.Log.Errorf("error while getting show details : %s", err)
|
||||
continue
|
||||
@ -64,3 +88,69 @@ func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -46,11 +46,12 @@ const (
|
||||
shows.tvdb_id,
|
||||
shows.year,
|
||||
shows.first_aired,
|
||||
shows.created_at,
|
||||
shows_tracked.season,
|
||||
shows_tracked.episode,
|
||||
shows.updated_at,
|
||||
COALESCE(shows_tracked.season,0),
|
||||
COALESCE(shows_tracked.episode,0) AS trackedepisode
|
||||
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.show_imdb_id AND shows_tracked.user_id=$2
|
||||
shows.created_at
|
||||
FROM shows LEFT JOIN shows_tracked
|
||||
ON shows.imdb_id=shows_tracked.imdb_id AND shows_tracked.user_id=$2
|
||||
WHERE shows.imdb_id=$1;`
|
||||
|
||||
getShowWithUserQueryByID = `
|
||||
@ -63,11 +64,11 @@ const (
|
||||
shows.tvdb_id,
|
||||
shows.year,
|
||||
shows.first_aired,
|
||||
shows.created_at,
|
||||
shows_tracked.season,
|
||||
shows_tracked.episode,
|
||||
shows.updated_at,
|
||||
COALESCE(shows_tracked.season,0),
|
||||
COALESCE(shows_tracked.episode,0)
|
||||
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.show_imdb_id AND shows_tracked.user_id=$2
|
||||
shows.created_at
|
||||
FROM shows LEFT JOIN shows_tracked ON shows.imdb_id=shows_tracked.imdb_id AND shows_tracked.user_id=$2
|
||||
WHERE shows.id=$1;`
|
||||
)
|
||||
|
||||
@ -81,8 +82,8 @@ type Show struct {
|
||||
sqly.BaseModel
|
||||
polochon.Show
|
||||
Episodes []*Episode `json:"episodes"`
|
||||
TrackedSeason int `json:"tracked_season"`
|
||||
TrackedEpisode int `json:"tracked_episode"`
|
||||
TrackedSeason *int `json:"tracked_season"`
|
||||
TrackedEpisode *int `json:"tracked_episode"`
|
||||
BannerURL string `json:"banner_url"`
|
||||
FanartURL string `json:"fanart_url"`
|
||||
PosterURL string `json:"poster_url"`
|
||||
@ -93,6 +94,8 @@ type ShowDB struct {
|
||||
ID string `db:"id"`
|
||||
ImdbID string `db:"imdb_id"`
|
||||
TvdbID int `db:"tvdb_id"`
|
||||
TrackedSeason *int `db:"season"`
|
||||
TrackedEpisode *int `db:"episode"`
|
||||
Title string `db:"title"`
|
||||
Rating float32 `db:"rating"`
|
||||
Plot string `db:"plot"`
|
||||
@ -131,6 +134,8 @@ func (s *Show) FillFromDB(sDB *ShowDB) {
|
||||
s.FirstAired = &sDB.FirstAired
|
||||
s.Created = sDB.Created
|
||||
s.Updated = sDB.Updated
|
||||
s.TrackedSeason = sDB.TrackedSeason
|
||||
s.TrackedEpisode = sDB.TrackedEpisode
|
||||
}
|
||||
|
||||
// New returns a new Show with a polochon ShowConfig
|
||||
@ -142,21 +147,20 @@ func New(imdbID string) *Show {
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns show details in database from id or imdbid or an error
|
||||
func (s *Show) Get(env *web.Env) error {
|
||||
var sDB ShowDB
|
||||
// Get returns a show with user info like tracked
|
||||
func (s *Show) Get(env *web.Env, user *users.User) error {
|
||||
var err error
|
||||
var sDB ShowDB
|
||||
if s.ID != "" {
|
||||
err = env.Database.QueryRowx(getShowQueryByID, s.ID).StructScan(&sDB)
|
||||
err = env.Database.QueryRowx(getShowWithUserQueryByID, s.ID, user.ID).StructScan(&sDB)
|
||||
} else if s.ImdbID != "" {
|
||||
err = env.Database.QueryRowx(getShowQueryByImdbID, s.ImdbID).StructScan(&sDB)
|
||||
err = env.Database.QueryRowx(getShowWithUserQueryByImdbID, s.ImdbID, user.ID).StructScan(&sDB)
|
||||
} else {
|
||||
err = fmt.Errorf("Can't get show details, you have to specify an ID or ImdbID")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the poster url
|
||||
s.PosterURL = s.GetPosterURL(env)
|
||||
|
||||
@ -164,29 +168,10 @@ func (s *Show) Get(env *web.Env) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAsUser returns a show with user info like tracked
|
||||
func (s *Show) GetAsUser(db *sqlx.DB, user *users.User) error {
|
||||
var err error
|
||||
if s.ID != "" {
|
||||
err = db.QueryRowx(getShowWithUserQueryByID, s.ID, user.ID).StructScan(s)
|
||||
} else if s.ImdbID != "" {
|
||||
err = db.QueryRowx(getShowWithUserQueryByImdbID, s.ImdbID, user.ID).StructScan(s)
|
||||
} else {
|
||||
err = fmt.Errorf("Can't get show details, you have to specify an ID or ImdbID")
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() == "sql: no rows in result set" {
|
||||
return ErrNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDetails retrieves details for the show, first try to
|
||||
// get info from db, if not exists, use polochon.Detailer
|
||||
// and save informations in the database for future use
|
||||
func (s *Show) GetDetails(env *web.Env, force bool) error {
|
||||
func (s *Show) GetDetails(env *web.Env, user *users.User, force bool) error {
|
||||
log := env.Log.WithFields(logrus.Fields{
|
||||
"imdb_id": s.ImdbID,
|
||||
"function": "shows.GetDetails",
|
||||
@ -198,7 +183,7 @@ func (s *Show) GetDetails(env *web.Env, force bool) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
err = s.Get(env)
|
||||
err = s.Get(env, user)
|
||||
switch err {
|
||||
case nil:
|
||||
log.Debug("show found in database")
|
||||
@ -282,44 +267,6 @@ func (s *Show) imgURL(env *web.Env, imgType string) string {
|
||||
return fmt.Sprintf("img/shows/%s-%s.jpg", s.ImdbID, imgType)
|
||||
}
|
||||
|
||||
// GetDetailsAsUser like GetDetails but with User context
|
||||
func (s *Show) GetDetailsAsUser(db *sqlx.DB, user *users.User, log *logrus.Entry) error {
|
||||
var err error
|
||||
err = s.GetAsUser(db, user)
|
||||
if err == nil {
|
||||
// found ok
|
||||
return nil
|
||||
}
|
||||
if err != ErrNotFound {
|
||||
// Unexpected error
|
||||
return err
|
||||
}
|
||||
err = s.Show.GetDetails(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Episodes = []*Episode{}
|
||||
for _, pe := range s.Show.Episodes {
|
||||
s.Episodes = append(s.Episodes, &Episode{ShowEpisode: *pe})
|
||||
}
|
||||
|
||||
err = s.Upsert(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsTracked returns true if the show is tracked use this function
|
||||
// after retrieve the show with GetAsUser or other *AsUser functions
|
||||
func (s *Show) IsTracked() bool {
|
||||
if s.TrackedSeason != 0 && s.TrackedEpisode != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Upsert a show in the database
|
||||
func (s *Show) Upsert(db *sqlx.DB) error {
|
||||
sDB := NewShowDB(s)
|
||||
@ -357,7 +304,7 @@ func (s *Show) Delete(db *sqlx.DB) error {
|
||||
}
|
||||
|
||||
// GetEpisodes from database
|
||||
func (s *Show) GetEpisodes(env *web.Env) error {
|
||||
func (s *Show) GetEpisodes(env *web.Env, force bool) error {
|
||||
// We retrive episode's info from database populate the s.Episodes member
|
||||
var episodesDB = []*EpisodeDB{}
|
||||
err := env.Database.Select(&episodesDB, getEpisodesQuery, s.ImdbID)
|
||||
@ -372,7 +319,7 @@ func (s *Show) GetEpisodes(env *web.Env) error {
|
||||
episode := NewEpisode()
|
||||
episode.FillFromDB(episodeDB)
|
||||
s.Episodes = append(s.Episodes, episode)
|
||||
err = episode.GetTorrents(env, false)
|
||||
err = episode.GetTorrents(env, force)
|
||||
if err != nil {
|
||||
env.Log.Debugf("error while getting episode torrent: %q", err)
|
||||
}
|
||||
|
87
src/internal/shows/wishlist.go
Normal file
87
src/internal/shows/wishlist.go
Normal file
@ -0,0 +1,87 @@
|
||||
package shows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||
)
|
||||
|
||||
const (
|
||||
upsertWishlistQuery = `
|
||||
INSERT INTO shows_tracked (imdb_id, user_id, season, episode)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (imdb_id, user_id)
|
||||
DO UPDATE
|
||||
SET imdb_id=$1, user_id=$2, season=$3, episode=$4;`
|
||||
|
||||
getWishlistQueryByUserID = `
|
||||
SELECT
|
||||
shows.id,
|
||||
shows.imdb_id,
|
||||
shows.title,
|
||||
shows.rating,
|
||||
shows.plot,
|
||||
shows.tvdb_id,
|
||||
shows.year,
|
||||
shows.first_aired,
|
||||
shows_tracked.season,
|
||||
shows_tracked.episode,
|
||||
shows.updated_at,
|
||||
shows.created_at
|
||||
FROM shows INNER JOIN shows_tracked
|
||||
ON shows.imdb_id=shows_tracked.imdb_id AND shows_tracked.user_id=$1;`
|
||||
|
||||
deleteWishlistedQueryByID = `DELETE FROM shows_tracked WHERE imdb_id=$1 AND user_id=$2;`
|
||||
)
|
||||
|
||||
// AddToWishlist Adds a show to a user's wishlist
|
||||
func (s *Show) AddToWishlist(env *web.Env, user *users.User, season, episode int) error {
|
||||
_, err := env.Database.Exec(upsertWishlistQuery, s.ImdbID, user.ID, season, episode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsTracked returns true if the show is tracked use this function
|
||||
// after retrieve the show with GetAsUser or other *AsUser functions
|
||||
func (s *Show) IsTracked() bool {
|
||||
if s.TrackedSeason != nil && s.TrackedEpisode != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DeleteFromWishlist deletes a show from a user's wishlist
|
||||
func (s *Show) DeleteFromWishlist(env *web.Env, user *users.User) error {
|
||||
r, err := env.Database.Exec(deleteWishlistedQueryByID, s.ImdbID, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, _ := r.RowsAffected()
|
||||
if count != 1 {
|
||||
return fmt.Errorf("Unexpected number of row deleted: %d", count)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetWishlist returns a list of shows tracked by user
|
||||
func GetWishlist(env *web.Env, user *users.User) ([]*Show, error) {
|
||||
var showsDB = []*ShowDB{}
|
||||
err := env.Database.Select(&showsDB, getWishlistQueryByUserID, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var shows []*Show
|
||||
for _, showDB := range showsDB {
|
||||
// Set the poster url
|
||||
show := New(showDB.ImdbID)
|
||||
show.PosterURL = show.GetPosterURL(env)
|
||||
show.FillFromDB(showDB)
|
||||
shows = append(shows, show)
|
||||
}
|
||||
|
||||
return shows, nil
|
||||
}
|
@ -20,9 +20,9 @@ func init() {
|
||||
|
||||
// BaseModel have to be embeded in all your struct which reflect a table
|
||||
type BaseModel struct {
|
||||
ID string
|
||||
Updated time.Time `db:"updated_at"`
|
||||
Created time.Time `db:"created_at"`
|
||||
ID string `json:"id"`
|
||||
Updated time.Time `db:"updated_at" json:"updated_at"`
|
||||
Created time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
// RunWithLastestMigration runs your test with database migration set to the lastest
|
||||
|
@ -4,6 +4,7 @@ import "net/http"
|
||||
|
||||
// RenderError renders an error
|
||||
func (e *Env) RenderError(w http.ResponseWriter, err error) error {
|
||||
e.Log.Warn(err)
|
||||
return e.render(w, "error", err.Error())
|
||||
}
|
||||
|
||||
|
10
src/main.go
10
src/main.go
@ -85,11 +85,21 @@ func main() {
|
||||
|
||||
// env.Handle("/shows/polochon", shows.FromPolochon).WithRole(users.UserRole)
|
||||
env.Handle("/shows/{id:tt[0-9]+}", shows.GetDetailsHandler).WithRole(users.UserRole).Methods("GET")
|
||||
env.Handle("/shows/{id:tt[0-9]+}/refresh", shows.RefreshDetailsHandler).WithRole(users.UserRole).Methods("POST")
|
||||
env.Handle("/shows/refresh", extmedias.RefreshShows).WithRole(users.UserRole).Methods("POST")
|
||||
env.Handle("/shows/explore", extmedias.ExploreShows).WithRole(users.UserRole).Methods("GET")
|
||||
env.Handle("/shows/search", shows.SearchShow).WithRole(users.UserRole).Methods("POST")
|
||||
|
||||
env.Handle("/torrents", torrents.DownloadHandler).WithRole(users.UserRole).Methods("POST")
|
||||
|
||||
env.Handle("/wishlist/shows", shows.GetWishlistHandler).WithRole(users.UserRole).Methods("GET")
|
||||
env.Handle("/wishlist/shows/{id:tt[0-9]+}", shows.AddToWishlist).WithRole(users.UserRole).Methods("POST")
|
||||
env.Handle("/wishlist/shows/{id:tt[0-9]+}", shows.DeleteFromWishlist).WithRole(users.UserRole).Methods("DELETE")
|
||||
|
||||
env.Handle("/wishlist/movies", movies.GetWishlistHandler).WithRole(users.UserRole).Methods("GET")
|
||||
env.Handle("/wishlist/movies/{id:tt[0-9]+}", movies.AddToWishlist).WithRole(users.UserRole).Methods("POST")
|
||||
env.Handle("/wishlist/movies/{id:tt[0-9]+}", movies.DeleteFromWishlist).WithRole(users.UserRole).Methods("DELETE")
|
||||
|
||||
n := negroni.Classic()
|
||||
n.Use(authMiddleware)
|
||||
n.Use(negroni.NewStatic(http.Dir(cf.PublicDir)))
|
||||
|
Loading…
x
Reference in New Issue
Block a user