Add shows to the party
This commit is contained in:
parent
cd6a6f26fd
commit
828c34830e
@ -42,13 +42,14 @@ CREATE TABLE shows (
|
|||||||
first_aired timestamp with time zone,
|
first_aired timestamp with time zone,
|
||||||
LIKE base INCLUDING DEFAULTS
|
LIKE base INCLUDING DEFAULTS
|
||||||
);
|
);
|
||||||
CREATE INDEX ON shows (imdb_id);
|
CREATE UNIQUE INDEX ON shows (imdb_id);
|
||||||
CREATE TRIGGER update_shows_updated_at BEFORE UPDATE ON shows FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
CREATE TRIGGER update_shows_updated_at BEFORE UPDATE ON shows FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||||
|
|
||||||
CREATE TABLE episodes (
|
CREATE TABLE episodes (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
imdb_id text NOT NULL,
|
imdb_id text NOT NULL,
|
||||||
show_id uuid REFERENCES shows (id) ON DELETE CASCADE,
|
show_imdb_id text REFERENCES shows (imdb_id) ON DELETE CASCADE,
|
||||||
|
show_tvdb_id text NOT NULL,
|
||||||
title text NOT NULL,
|
title text NOT NULL,
|
||||||
season smallint NOT NULL,
|
season smallint NOT NULL,
|
||||||
episode smallint NOT NULL,
|
episode smallint NOT NULL,
|
||||||
@ -59,17 +60,18 @@ CREATE TABLE episodes (
|
|||||||
rating real NOT NULL,
|
rating real NOT NULL,
|
||||||
LIKE base INCLUDING DEFAULTS
|
LIKE base INCLUDING DEFAULTS
|
||||||
);
|
);
|
||||||
CREATE INDEX ON episodes (show_id, season);
|
|
||||||
CREATE INDEX ON episodes (show_id, season, episode);
|
CREATE INDEX ON episodes (show_imdb_id, season);
|
||||||
|
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 TRIGGER update_episodes_updated_at BEFORE UPDATE ON episodes FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||||
|
|
||||||
CREATE TABLE shows_tracked (
|
CREATE TABLE shows_tracked (
|
||||||
show_id uuid NOT NULL REFERENCES shows (id) ON DELETE CASCADE,
|
show_imdb_id text NOT NULL REFERENCES shows (imdb_id) ON DELETE CASCADE,
|
||||||
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||||
season smallint NOT NULL,
|
season smallint NOT NULL,
|
||||||
episode smallint NOT NULL
|
episode smallint NOT NULL
|
||||||
);
|
);
|
||||||
CREATE INDEX ON shows_tracked (show_id, user_id);
|
CREATE INDEX ON shows_tracked (show_imdb_id, user_id);
|
||||||
CREATE INDEX ON shows_tracked (user_id);
|
CREATE INDEX ON shows_tracked (user_id);
|
||||||
|
|
||||||
CREATE TABLE movies (
|
CREATE TABLE movies (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
CREATE TYPE media_type AS ENUM ('movie', 'show');
|
CREATE TYPE media_type AS ENUM ('movie', 'show');
|
||||||
CREATE TYPE media_category AS ENUM ('trending', 'popular', 'anticipated', 'box_office');
|
CREATE TYPE media_category AS ENUM ('trending', 'popular', 'anticipated', 'box_office');
|
||||||
CREATE TYPE media_source AS ENUM ('trakttv', 'yts');
|
CREATE TYPE media_source AS ENUM ('trakttv', 'yts', 'eztv');
|
||||||
|
|
||||||
CREATE TABLE external_medias (
|
CREATE TABLE external_medias (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
@ -23,7 +23,7 @@ INHERITS (torrents_abstract);
|
|||||||
CREATE INDEX ON movie_torrents (imdb_id);
|
CREATE INDEX ON movie_torrents (imdb_id);
|
||||||
CREATE UNIQUE INDEX ON movie_torrents (imdb_id, source, quality);
|
CREATE UNIQUE INDEX ON movie_torrents (imdb_id, source, quality);
|
||||||
CREATE INDEX ON episode_torrents (imdb_id);
|
CREATE INDEX ON episode_torrents (imdb_id);
|
||||||
CREATE INDEX ON episode_torrents (imdb_id, season, episode);
|
CREATE UNIQUE INDEX ON episode_torrents (imdb_id, season, episode, source, quality);
|
||||||
CREATE INDEX ON torrents_abstract (imdb_id);
|
CREATE INDEX ON torrents_abstract (imdb_id);
|
||||||
|
|
||||||
CREATE TRIGGER update_movie_torrents_updated_at BEFORE UPDATE ON movie_torrents FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
CREATE TRIGGER update_movie_torrents_updated_at BEFORE UPDATE ON movie_torrents FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||||
|
@ -5,7 +5,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
polochon "github.com/odwrtw/polochon/lib"
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
|
"github.com/odwrtw/polochon/modules/eztv"
|
||||||
"github.com/odwrtw/polochon/modules/tmdb"
|
"github.com/odwrtw/polochon/modules/tmdb"
|
||||||
|
"github.com/odwrtw/polochon/modules/tvdb"
|
||||||
"github.com/odwrtw/polochon/modules/yts"
|
"github.com/odwrtw/polochon/modules/yts"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
@ -22,6 +24,8 @@ type Config struct {
|
|||||||
TmdbAPIKey string `yaml:"tmdb_api_key"`
|
TmdbAPIKey string `yaml:"tmdb_api_key"`
|
||||||
MovieDetailers []polochon.Detailer
|
MovieDetailers []polochon.Detailer
|
||||||
MovieTorrenters []polochon.Torrenter
|
MovieTorrenters []polochon.Torrenter
|
||||||
|
ShowDetailers []polochon.Detailer
|
||||||
|
ShowTorrenters []polochon.Torrenter
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorizerConfig struct {
|
type AuthorizerConfig struct {
|
||||||
@ -67,5 +71,21 @@ func Load(path string) (*Config, error) {
|
|||||||
}
|
}
|
||||||
cf.MovieTorrenters = append(cf.MovieTorrenters, d)
|
cf.MovieTorrenters = append(cf.MovieTorrenters, d)
|
||||||
|
|
||||||
|
// Default detailers
|
||||||
|
cf.ShowDetailers = []polochon.Detailer{}
|
||||||
|
showDetailer, err := tvdb.NewDetailer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cf.ShowDetailers = append(cf.ShowDetailers, showDetailer)
|
||||||
|
|
||||||
|
// Default torrenters
|
||||||
|
cf.ShowTorrenters = []polochon.Torrenter{}
|
||||||
|
showTorrenter, err := eztv.New()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cf.ShowTorrenters = append(cf.ShowTorrenters, showTorrenter)
|
||||||
|
|
||||||
return cf, nil
|
return cf, nil
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/shows"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
polochon "github.com/odwrtw/polochon/lib"
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
|
eztvExplorer "github.com/odwrtw/polochon/modules/eztv"
|
||||||
traktExplorer "github.com/odwrtw/polochon/modules/trakttv"
|
traktExplorer "github.com/odwrtw/polochon/modules/trakttv"
|
||||||
ytsExplorer "github.com/odwrtw/polochon/modules/yts"
|
ytsExplorer "github.com/odwrtw/polochon/modules/yts"
|
||||||
)
|
)
|
||||||
@ -28,13 +30,13 @@ func GetMediaIDs(env *web.Env, mediaType string, source string, category string,
|
|||||||
media, err := Get(env.Database, mediaType, source, category)
|
media, err := Get(env.Database, mediaType, source, category)
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
log.Debug("medias found in database")
|
log.Debugf("%s medias found in database", mediaType)
|
||||||
if !force {
|
if !force {
|
||||||
log.Debug("returning medias from db")
|
log.Debug("returning medias from db")
|
||||||
return media.IDs, nil
|
return media.IDs, nil
|
||||||
}
|
}
|
||||||
case sql.ErrNoRows:
|
case sql.ErrNoRows:
|
||||||
log.Debug("medias not found in database")
|
log.Debugf("%s medias not found in database", mediaType)
|
||||||
default:
|
default:
|
||||||
// Unexpected error
|
// Unexpected error
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -46,9 +48,25 @@ func GetMediaIDs(env *web.Env, mediaType string, source string, category string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ids []string
|
var ids []string
|
||||||
movies, err := explorer.GetMovieList(polochon.ExploreByRate, log)
|
if mediaType == "movie" {
|
||||||
for _, movie := range movies {
|
medias, err := explorer.GetMovieList(polochon.ExploreByRate, log)
|
||||||
ids = append(ids, movie.ImdbID)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, media := range medias {
|
||||||
|
ids = append(ids, media.ImdbID)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
medias, err := explorer.GetShowList(polochon.ExploreByRate, log)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i, media := range medias {
|
||||||
|
if i > 5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ids = append(ids, media.ImdbID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("got %d medias from %s", len(ids), source)
|
log.Debugf("got %d medias from %s", len(ids), source)
|
||||||
@ -71,8 +89,8 @@ func GetMediaIDs(env *web.Env, mediaType string, source string, category string,
|
|||||||
return ids, nil
|
return ids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMedias get some movies
|
// GetMovies get some movies
|
||||||
func GetMedias(env *web.Env, source string, category string, force bool) ([]*movies.Movie, error) {
|
func GetMovies(env *web.Env, source string, category string, force bool) ([]*movies.Movie, error) {
|
||||||
movieIds, err := GetMediaIDs(env, "movie", source, category, force)
|
movieIds, err := GetMediaIDs(env, "movie", source, category, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -83,12 +101,12 @@ func GetMedias(env *web.Env, source string, category string, force bool) ([]*mov
|
|||||||
movie := movies.New(id)
|
movie := movies.New(id)
|
||||||
err := movie.GetDetails(env, force)
|
err := movie.GetDetails(env, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.Log.Error(err)
|
env.Log.Errorf("error while getting movie details : %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = movie.GetTorrents(env, force)
|
err = movie.GetTorrents(env, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.Log.Error(err)
|
env.Log.Errorf("error while getting movie torrents : %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
movieList = append(movieList, movie)
|
movieList = append(movieList, movie)
|
||||||
@ -96,6 +114,31 @@ func GetMedias(env *web.Env, source string, category string, force bool) ([]*mov
|
|||||||
return movieList, nil
|
return movieList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetShows get some shows
|
||||||
|
func GetShows(env *web.Env, source string, category string, force bool) ([]*shows.Show, error) {
|
||||||
|
showIds, err := GetMediaIDs(env, "show", source, category, force)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
showList := []*shows.Show{}
|
||||||
|
for _, id := range showIds {
|
||||||
|
show := shows.New(id)
|
||||||
|
err := show.GetDetails(env, force)
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Errorf("error while getting show details : %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = show.GetTorrents(env, force)
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Errorf("error while getting show torrents : %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
showList = append(showList, show)
|
||||||
|
}
|
||||||
|
return showList, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Explore will explore some movies
|
// Explore will explore some movies
|
||||||
func Explore(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func Explore(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
@ -116,7 +159,7 @@ func Explore(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the medias without trying to refresh them
|
// Get the medias without trying to refresh them
|
||||||
movies, err := GetMedias(env, source, category, false)
|
movies, err := GetMovies(env, source, category, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -124,12 +167,45 @@ func Explore(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
return env.RenderJSON(w, movies)
|
return env.RenderJSON(w, movies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExploreShows will explore some shows
|
||||||
|
func ExploreShows(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source := r.FormValue("source")
|
||||||
|
// Default source
|
||||||
|
if source == "" {
|
||||||
|
source = "eztv"
|
||||||
|
}
|
||||||
|
|
||||||
|
category := r.FormValue("category")
|
||||||
|
// Default category
|
||||||
|
if category == "" {
|
||||||
|
category = "popular"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the medias without trying to refresh them
|
||||||
|
shows, err := GetShows(env, source, category, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.RenderJSON(w, shows)
|
||||||
|
}
|
||||||
|
|
||||||
// MediaSources represents the implemented media sources
|
// MediaSources represents the implemented media sources
|
||||||
var MediaSources = []string{
|
var MediaSources = []string{
|
||||||
"trakttv",
|
"trakttv",
|
||||||
"yts",
|
"yts",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowMediaSources represents the implemented media sources for shows
|
||||||
|
var ShowMediaSources = []string{
|
||||||
|
"eztv",
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh will refresh the movie list
|
// Refresh will refresh the movie list
|
||||||
func Refresh(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func Refresh(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
env.Log.Debugf("refreshing infos ...")
|
env.Log.Debugf("refreshing infos ...")
|
||||||
@ -147,7 +223,33 @@ func Refresh(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
for _, source := range MediaSources {
|
for _, source := range MediaSources {
|
||||||
env.Log.Debugf("refreshing %s", source)
|
env.Log.Debugf("refreshing %s", source)
|
||||||
// GetMedias and refresh them
|
// GetMedias and refresh them
|
||||||
_, err := GetMedias(env, source, category, true)
|
_, err := GetMovies(env, source, category, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.RenderJSON(w, map[string]string{"message": "Refresh is done"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshShows will refresh the movie list
|
||||||
|
func RefreshShows(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
env.Log.Debugf("refreshing shows ...")
|
||||||
|
source := r.FormValue("source")
|
||||||
|
if source == "" {
|
||||||
|
source = "eztv"
|
||||||
|
}
|
||||||
|
|
||||||
|
category := r.FormValue("category")
|
||||||
|
if category == "" {
|
||||||
|
category = "popular"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -165,6 +267,8 @@ func NewExplorer(env *web.Env, source string) (polochon.Explorer, error) {
|
|||||||
})
|
})
|
||||||
case "yts":
|
case "yts":
|
||||||
return ytsExplorer.NewExplorer()
|
return ytsExplorer.NewExplorer()
|
||||||
|
case "eztv":
|
||||||
|
return eztvExplorer.NewExplorer()
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown explorer")
|
return nil, fmt.Errorf("unknown explorer")
|
||||||
}
|
}
|
||||||
|
180
src/internal/shows/episodes.go
Normal file
180
src/internal/shows/episodes.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
package shows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
|
"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/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
upsertEpisodeQuery = `
|
||||||
|
INSERT INTO episodes (show_imdb_id, show_tvdb_id, title, season, episode, tvdb_id, aired, plot, runtime, rating, imdb_id)
|
||||||
|
VALUES (:show_imdb_id, :show_tvdb_id, :title, :season, :episode, :tvdb_id, :aired, :plot, :runtime, :rating, :imdb_id)
|
||||||
|
ON CONFLICT (show_imdb_id, season, episode)
|
||||||
|
DO UPDATE
|
||||||
|
SET show_imdb_id=:show_imdb_id, show_tvdb_id=:show_tvdb_id, title=:title,
|
||||||
|
season=:season, episode=:episode, tvdb_id=:tvdb_id, aired=:aired,
|
||||||
|
plot=:plot, runtime=:runtime, rating=:rating, imdb_id=:imdb_id
|
||||||
|
RETURNING id;`
|
||||||
|
|
||||||
|
getEpisodesQuery = `
|
||||||
|
SELECT *
|
||||||
|
FROM episodes WHERE show_imdb_id=$1;`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Episode represents an episode
|
||||||
|
type Episode struct {
|
||||||
|
sqly.BaseModel
|
||||||
|
polochon.ShowEpisode
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpisodeDB represents the Episode in the DB
|
||||||
|
type EpisodeDB struct {
|
||||||
|
ID string `db:"id"`
|
||||||
|
TvdbID int `db:"tvdb_id"`
|
||||||
|
ImdbID string `db:"imdb_id"`
|
||||||
|
ShowImdbID string `db:"show_imdb_id"`
|
||||||
|
ShowTvdbID int `db:"show_tvdb_id"`
|
||||||
|
Season int `db:"season"`
|
||||||
|
Episode int `db:"episode"`
|
||||||
|
Title string `db:"title"`
|
||||||
|
Rating float32 `db:"rating"`
|
||||||
|
Plot string `db:"plot"`
|
||||||
|
Thumb string `db:"thumb"`
|
||||||
|
Runtime int `db:"runtime"`
|
||||||
|
Aired string `db:"aired"`
|
||||||
|
ReleaseGroup string `db:"release_group"`
|
||||||
|
Created time.Time `db:"created_at"`
|
||||||
|
Updated time.Time `db:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEpisode returns an Episode
|
||||||
|
func NewEpisode() *Episode {
|
||||||
|
return &Episode{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEpisodeDB returns an Episode ready to be put in DB from an
|
||||||
|
// Episode
|
||||||
|
func NewEpisodeDB(e *Episode) EpisodeDB {
|
||||||
|
return EpisodeDB{
|
||||||
|
ID: e.ID,
|
||||||
|
TvdbID: e.TvdbID,
|
||||||
|
ImdbID: e.EpisodeImdbID,
|
||||||
|
ShowImdbID: e.ShowImdbID,
|
||||||
|
ShowTvdbID: e.ShowTvdbID,
|
||||||
|
Season: e.Season,
|
||||||
|
Episode: e.Episode,
|
||||||
|
Title: e.Title,
|
||||||
|
Rating: e.Rating,
|
||||||
|
Plot: e.Plot,
|
||||||
|
Thumb: e.Thumb,
|
||||||
|
Runtime: e.Runtime,
|
||||||
|
Aired: e.Aired,
|
||||||
|
ReleaseGroup: e.ReleaseGroup,
|
||||||
|
Created: e.Created,
|
||||||
|
Updated: e.Updated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillFromDB returns a Show from a ShowDB extracted from the DB
|
||||||
|
func (e *Episode) FillFromDB(eDB *EpisodeDB) {
|
||||||
|
e.ID = eDB.ID
|
||||||
|
e.TvdbID = eDB.TvdbID
|
||||||
|
e.EpisodeImdbID = eDB.ImdbID
|
||||||
|
e.ShowImdbID = eDB.ShowImdbID
|
||||||
|
e.ShowTvdbID = eDB.ShowTvdbID
|
||||||
|
e.Season = eDB.Season
|
||||||
|
e.Episode = eDB.Episode
|
||||||
|
e.Title = eDB.Title
|
||||||
|
e.Rating = eDB.Rating
|
||||||
|
e.Plot = eDB.Plot
|
||||||
|
e.Thumb = eDB.Thumb
|
||||||
|
e.Runtime = eDB.Runtime
|
||||||
|
e.Aired = eDB.Aired
|
||||||
|
e.Created = eDB.Created
|
||||||
|
e.Updated = eDB.Updated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upsert episode to the database
|
||||||
|
func (e *Episode) Upsert(db *sqlx.DB) error {
|
||||||
|
eDB := NewEpisodeDB(e)
|
||||||
|
var id string
|
||||||
|
r, err := db.NamedQuery(upsertEpisodeQuery, eDB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for r.Next() {
|
||||||
|
r.Scan(&id)
|
||||||
|
}
|
||||||
|
e.ID = id
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTorrents retrieves torrents for the show, first try to get info from db,
|
||||||
|
// if not exists, use polochon.Torrenter and save informations in the database
|
||||||
|
// for future use
|
||||||
|
//
|
||||||
|
// If force is used, the torrenter will be used even if the torrent is found in
|
||||||
|
// database
|
||||||
|
func (e *Episode) GetTorrents(env *web.Env, force bool) error {
|
||||||
|
log := env.Log.WithFields(logrus.Fields{
|
||||||
|
"imdb_id": e.ShowEpisode.ShowImdbID,
|
||||||
|
"season": e.ShowEpisode.Season,
|
||||||
|
"episode": e.ShowEpisode.Episode,
|
||||||
|
"function": "shows.GetTorrents",
|
||||||
|
})
|
||||||
|
log.Debugf("getting torrents")
|
||||||
|
|
||||||
|
if len(e.Torrenters) == 0 {
|
||||||
|
e.Torrenters = env.Config.ShowTorrenters
|
||||||
|
}
|
||||||
|
|
||||||
|
episodeTorrents, err := torrents.GetEpisodeTorrents(
|
||||||
|
env.Database,
|
||||||
|
e.ShowEpisode.ShowImdbID,
|
||||||
|
e.ShowEpisode.Season,
|
||||||
|
e.ShowEpisode.Episode,
|
||||||
|
)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
log.Debug("torrents found in database")
|
||||||
|
case sql.ErrNoRows:
|
||||||
|
log.Debug("torrent not found in database")
|
||||||
|
// We'll need to GetTorrents from torrenters
|
||||||
|
default:
|
||||||
|
// Unexpected error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !force {
|
||||||
|
log.Debugf("returning %d torrents from db", len(episodeTorrents))
|
||||||
|
// Add the torrents to the episode
|
||||||
|
for _, t := range episodeTorrents {
|
||||||
|
e.Torrents = append(e.Torrents, t.Torrent)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = e.ShowEpisode.GetTorrents(env.Log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("got %d torrents from torrenters", len(e.ShowEpisode.Torrents))
|
||||||
|
|
||||||
|
for _, t := range e.ShowEpisode.Torrents {
|
||||||
|
torrent := torrents.NewEpisodeFromPolochon(e.ShowEpisode, t)
|
||||||
|
err = torrent.Upsert(env.Database)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error while adding torrent : %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
24
src/internal/shows/handlers.go
Normal file
24
src/internal/shows/handlers.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package shows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetDetailsHandler retrieves details for a movie
|
||||||
|
func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
id := vars["id"]
|
||||||
|
|
||||||
|
s := New(id)
|
||||||
|
if err := s.GetDetails(env, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.GetEpisodes(env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.RenderJSON(w, s)
|
||||||
|
}
|
@ -1,10 +1,15 @@
|
|||||||
package shows
|
package shows
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
@ -12,70 +17,59 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
addShowQuery = `
|
upsertShowQuery = `
|
||||||
INSERT INTO shows (imdb_id, title, rating, plot, tvdb_id, year, first_aired)
|
INSERT INTO shows (imdb_id, title, rating, plot, tvdb_id, year, first_aired)
|
||||||
VALUES (:imdbid, :title, :rating, :plot, :tvdbid, :year, :firstaired) RETURNING id;`
|
VALUES (:imdb_id, :title, :rating, :plot, :tvdb_id, :year, :first_aired)
|
||||||
|
ON CONFLICT (imdb_id)
|
||||||
|
DO UPDATE
|
||||||
|
SET imdb_id=:imdb_id, title=:title, rating=:rating, plot=:plot,
|
||||||
|
tvdb_id=:tvdb_id, year=:year, first_aired=:first_aired
|
||||||
|
RETURNING id;`
|
||||||
|
|
||||||
getShowQueryByImdbID = `
|
getShowQueryByImdbID = `
|
||||||
SELECT
|
SELECT *
|
||||||
id, imdb_id AS imdbid,
|
|
||||||
title, rating, plot,
|
|
||||||
tvdb_id AS tvdbid,
|
|
||||||
year, first_aired AS firstaired,
|
|
||||||
created_at, updated_at
|
|
||||||
FROM shows WHERE imdb_id=$1;`
|
FROM shows WHERE imdb_id=$1;`
|
||||||
|
|
||||||
getShowQueryByID = `
|
getShowQueryByID = `
|
||||||
SELECT
|
SELECT *
|
||||||
id, imdb_id AS imdbid,
|
|
||||||
title, rating, plot,
|
|
||||||
tvdb_id AS tvdbid,
|
|
||||||
year, first_aired AS firstaired,
|
|
||||||
created_at, updated_at
|
|
||||||
FROM shows WHERE id=$1;`
|
FROM shows WHERE id=$1;`
|
||||||
|
|
||||||
deleteShowQuery = `DELETE FROM shows WHERE id=$1;`
|
deleteShowQueryByID = `DELETE FROM shows WHERE id=$1;`
|
||||||
|
|
||||||
addEpisodeQuery = `
|
deleteShowQueryByImdbID = `DELETE FROM shows WHERE imdb_id=$1;`
|
||||||
INSERT INTO episodes (show_id, title, season, episode, tvdb_id, aired, plot, runtime, rating, imdb_id)
|
|
||||||
VALUES (:showid, :title, :season, :episode, :tvdbid, :aired, :plot, :runtime, :rating, :episodeimdbid) RETURNING id;`
|
|
||||||
|
|
||||||
getEpisodesQuery = `
|
|
||||||
SELECT title, season, episode, tvdb_id AS tvdbid, aired, plot, runtime, rating, imdb_id AS episodeimdbid, show_id AS showid
|
|
||||||
FROM episodes WHERE show_id=$1;`
|
|
||||||
|
|
||||||
getShowWithUserQueryByImdbID = `
|
getShowWithUserQueryByImdbID = `
|
||||||
SELECT
|
SELECT
|
||||||
shows.id,
|
shows.id,
|
||||||
shows.imdb_id AS imdbid,
|
shows.imdb_id,
|
||||||
shows.title,
|
shows.title,
|
||||||
shows.rating,
|
shows.rating,
|
||||||
shows.plot,
|
shows.plot,
|
||||||
shows.tvdb_id AS tvdbid,
|
shows.tvdb_id,
|
||||||
shows.year,
|
shows.year,
|
||||||
shows.first_aired AS firstaired,
|
shows.first_aired,
|
||||||
shows.created_at,
|
shows.created_at,
|
||||||
shows.updated_at,
|
shows.updated_at,
|
||||||
COALESCE(shows_tracked.season,0) AS trackedseason,
|
COALESCE(shows_tracked.season,0),
|
||||||
COALESCE(shows_tracked.episode,0) AS trackedepisode
|
COALESCE(shows_tracked.episode,0) AS trackedepisode
|
||||||
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.show_id AND shows_tracked.user_id=$2
|
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.show_imdb_id AND shows_tracked.user_id=$2
|
||||||
WHERE shows.imdb_id=$1;`
|
WHERE shows.imdb_id=$1;`
|
||||||
|
|
||||||
getShowWithUserQueryByID = `
|
getShowWithUserQueryByID = `
|
||||||
SELECT
|
SELECT
|
||||||
shows.id,
|
shows.id,
|
||||||
shows.imdb_id AS imdbid,
|
shows.imdb_id,
|
||||||
shows.title,
|
shows.title,
|
||||||
shows.rating,
|
shows.rating,
|
||||||
shows.plot,
|
shows.plot,
|
||||||
shows.tvdb_id AS tvdbid,
|
shows.tvdb_id,
|
||||||
shows.year,
|
shows.year,
|
||||||
shows.first_aired AS firstaired,
|
shows.first_aired,
|
||||||
shows.created_at,
|
shows.created_at,
|
||||||
shows.updated_at,
|
shows.updated_at,
|
||||||
COALESCE(shows_tracked.season,0) AS trackedseason,
|
COALESCE(shows_tracked.season,0),
|
||||||
COALESCE(shows_tracked.episode,0) AS trackedepisode
|
COALESCE(shows_tracked.episode,0)
|
||||||
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.show_id AND shows_tracked.user_id=$2
|
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.show_imdb_id AND shows_tracked.user_id=$2
|
||||||
WHERE shows.id=$1;`
|
WHERE shows.id=$1;`
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -91,29 +85,85 @@ type Show struct {
|
|||||||
Episodes []*Episode
|
Episodes []*Episode
|
||||||
TrackedSeason int
|
TrackedSeason int
|
||||||
TrackedEpisode int
|
TrackedEpisode int
|
||||||
|
BannerURL string `json:"banner_url"`
|
||||||
|
FanartURL string `json:"fanart_url"`
|
||||||
|
PosterURL string `json:"poster_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowDB represents the Show in the DB
|
||||||
|
type ShowDB struct {
|
||||||
|
ID string `db:"id"`
|
||||||
|
ImdbID string `db:"imdb_id"`
|
||||||
|
TvdbID int `db:"tvdb_id"`
|
||||||
|
Title string `db:"title"`
|
||||||
|
Rating float32 `db:"rating"`
|
||||||
|
Plot string `db:"plot"`
|
||||||
|
Year int `db:"year"`
|
||||||
|
FirstAired time.Time `db:"first_aired"`
|
||||||
|
Created time.Time `db:"created_at"`
|
||||||
|
Updated time.Time `db:"updated_at"`
|
||||||
|
// URL string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShowDB returns a Show ready to be put in DB from a
|
||||||
|
// Show
|
||||||
|
func NewShowDB(s *Show) ShowDB {
|
||||||
|
return ShowDB{
|
||||||
|
ID: s.ID,
|
||||||
|
ImdbID: s.ImdbID,
|
||||||
|
Title: s.Title,
|
||||||
|
Rating: s.Rating,
|
||||||
|
Plot: s.Plot,
|
||||||
|
TvdbID: s.TvdbID,
|
||||||
|
Year: s.Year,
|
||||||
|
FirstAired: *s.FirstAired,
|
||||||
|
Created: s.Created,
|
||||||
|
Updated: s.Updated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillFromDB returns a Show from a ShowDB extracted from the DB
|
||||||
|
func (s *Show) FillFromDB(sDB *ShowDB) {
|
||||||
|
s.ID = sDB.ID
|
||||||
|
s.ImdbID = sDB.ImdbID
|
||||||
|
s.Title = sDB.Title
|
||||||
|
s.Rating = sDB.Rating
|
||||||
|
s.Plot = sDB.Plot
|
||||||
|
s.TvdbID = sDB.TvdbID
|
||||||
|
s.Year = sDB.Year
|
||||||
|
s.FirstAired = &sDB.FirstAired
|
||||||
|
s.Created = sDB.Created
|
||||||
|
s.Updated = sDB.Updated
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Show with a polochon ShowConfig
|
// New returns a new Show with a polochon ShowConfig
|
||||||
func New(conf polochon.ShowConfig) *Show {
|
func New(imdbID string) *Show {
|
||||||
return &Show{Show: polochon.Show{ShowConfig: conf}}
|
return &Show{
|
||||||
|
Show: polochon.Show{
|
||||||
|
ImdbID: imdbID,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns show details in database from id or imdbid or an error
|
// Get returns show details in database from id or imdbid or an error
|
||||||
func (s *Show) Get(db *sqlx.DB) error {
|
func (s *Show) Get(env *web.Env) error {
|
||||||
|
var sDB ShowDB
|
||||||
var err error
|
var err error
|
||||||
if s.ID != "" {
|
if s.ID != "" {
|
||||||
err = db.QueryRowx(getShowQueryByID, s.ID).StructScan(s)
|
err = env.Database.QueryRowx(getShowQueryByID, s.ID).StructScan(&sDB)
|
||||||
} else if s.ImdbID != "" {
|
} else if s.ImdbID != "" {
|
||||||
err = db.QueryRowx(getShowQueryByImdbID, s.ImdbID).StructScan(s)
|
err = env.Database.QueryRowx(getShowQueryByImdbID, s.ImdbID).StructScan(&sDB)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Can't get show details, you have to specify an ID or ImdbID")
|
err = fmt.Errorf("Can't get show details, you have to specify an ID or ImdbID")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "sql: no rows in result set" {
|
|
||||||
return ErrNotFound
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the poster url
|
||||||
|
s.PosterURL = s.GetPosterURL(env)
|
||||||
|
|
||||||
|
s.FillFromDB(&sDB)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,36 +189,97 @@ func (s *Show) GetAsUser(db *sqlx.DB, user *users.User) error {
|
|||||||
// GetDetails retrieves details for the show, first try to
|
// GetDetails retrieves details for the show, first try to
|
||||||
// get info from db, if not exists, use polochon.Detailer
|
// get info from db, if not exists, use polochon.Detailer
|
||||||
// and save informations in the database for future use
|
// and save informations in the database for future use
|
||||||
func (s *Show) GetDetails(db *sqlx.DB, log *logrus.Entry) error {
|
func (s *Show) GetDetails(env *web.Env, force bool) error {
|
||||||
|
log := env.Log.WithFields(logrus.Fields{
|
||||||
|
"imdb_id": s.ImdbID,
|
||||||
|
"function": "shows.GetDetails",
|
||||||
|
})
|
||||||
|
log.Debugf("getting details")
|
||||||
|
|
||||||
|
if len(s.Detailers) == 0 {
|
||||||
|
s.Detailers = env.Config.ShowDetailers
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
err = s.Get(db)
|
err = s.Get(env)
|
||||||
if err == nil {
|
switch err {
|
||||||
// found ok
|
case nil:
|
||||||
|
log.Debug("show found in database")
|
||||||
|
if !force {
|
||||||
|
log.Debug("returning show from db")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != ErrNotFound {
|
case sql.ErrNoRows:
|
||||||
|
log.Debug("show not found in database")
|
||||||
|
default:
|
||||||
// Unexpected error
|
// Unexpected error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// so we got ErrNotFound so GetDetails from a detailer
|
// GetDetail
|
||||||
err = s.Show.GetDetails(log)
|
err = s.Show.GetDetails(env.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug("got details from detailers")
|
||||||
s.Episodes = []*Episode{}
|
s.Episodes = []*Episode{}
|
||||||
for _, pe := range s.Show.Episodes {
|
for _, pe := range s.Show.Episodes {
|
||||||
s.Episodes = append(s.Episodes, &Episode{ShowEpisode: *pe})
|
s.Episodes = append(s.Episodes, &Episode{ShowEpisode: *pe})
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Add(db)
|
err = s.Upsert(env.Database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debug("error while doing show upsert func", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug("show added in database")
|
||||||
|
|
||||||
|
// Download show images
|
||||||
|
s.downloadImages(env)
|
||||||
|
|
||||||
|
log.Debug("images downloaded")
|
||||||
|
|
||||||
|
// Set the poster url
|
||||||
|
s.PosterURL = s.GetPosterURL(env)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPosterURL returns the image URL or the default image if the poster is not yet downloaded
|
||||||
|
func (s *Show) GetPosterURL(env *web.Env) string {
|
||||||
|
// Check if the movie image exists
|
||||||
|
if _, err := os.Stat(s.imgURL(env, "poster")); os.IsNotExist(err) {
|
||||||
|
// TODO image in the config ?
|
||||||
|
return "img/noimage.png"
|
||||||
|
}
|
||||||
|
return s.imgURL(env, "poster")
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadImages will download the show images
|
||||||
|
func (s *Show) downloadImages(env *web.Env) {
|
||||||
|
// Download the banner
|
||||||
|
err := web.Download(s.Show.Banner, s.imgURL(env, "banner"))
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Errorf("failed to dowload banner: %s", err)
|
||||||
|
}
|
||||||
|
err = web.Download(s.Show.Fanart, s.imgURL(env, "fanart"))
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Errorf("failed to dowload fanart: %s", err)
|
||||||
|
}
|
||||||
|
err = web.Download(s.Show.Poster, s.imgURL(env, "poster"))
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Errorf("failed to dowload poster: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// imgFile returns the image location on disk
|
||||||
|
func (s *Show) imgURL(env *web.Env, imgType string) string {
|
||||||
|
fileURL := fmt.Sprintf("img/shows/%s-%s.jpg", s.ImdbID, imgType)
|
||||||
|
return filepath.Join(env.Config.PublicDir, fileURL)
|
||||||
|
}
|
||||||
|
|
||||||
// GetDetailsAsUser like GetDetails but with User context
|
// GetDetailsAsUser like GetDetails but with User context
|
||||||
func (s *Show) GetDetailsAsUser(db *sqlx.DB, user *users.User, log *logrus.Entry) error {
|
func (s *Show) GetDetailsAsUser(db *sqlx.DB, user *users.User, log *logrus.Entry) error {
|
||||||
var err error
|
var err error
|
||||||
@ -191,7 +302,7 @@ func (s *Show) GetDetailsAsUser(db *sqlx.DB, user *users.User, log *logrus.Entry
|
|||||||
s.Episodes = append(s.Episodes, &Episode{ShowEpisode: *pe})
|
s.Episodes = append(s.Episodes, &Episode{ShowEpisode: *pe})
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Add(db)
|
err = s.Upsert(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -207,10 +318,11 @@ func (s *Show) IsTracked() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a show in the database
|
// Upsert a show in the database
|
||||||
func (s *Show) Add(db *sqlx.DB) error {
|
func (s *Show) Upsert(db *sqlx.DB) error {
|
||||||
|
sDB := NewShowDB(s)
|
||||||
var id string
|
var id string
|
||||||
r, err := db.NamedQuery(addShowQuery, s)
|
r, err := db.NamedQuery(upsertShowQuery, sDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -220,8 +332,8 @@ func (s *Show) Add(db *sqlx.DB) error {
|
|||||||
s.ID = id
|
s.ID = id
|
||||||
|
|
||||||
for _, e := range s.Episodes {
|
for _, e := range s.Episodes {
|
||||||
e.ShowID = s.ID
|
e.ShowImdbID = s.ImdbID
|
||||||
err = e.Add(db)
|
err = e.Upsert(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -231,7 +343,7 @@ func (s *Show) Add(db *sqlx.DB) error {
|
|||||||
|
|
||||||
// Delete show from database
|
// Delete show from database
|
||||||
func (s *Show) Delete(db *sqlx.DB) error {
|
func (s *Show) Delete(db *sqlx.DB) error {
|
||||||
r, err := db.Exec(deleteShowQuery, s.ID)
|
r, err := db.Exec(deleteShowQueryByID, s.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -242,36 +354,60 @@ func (s *Show) Delete(db *sqlx.DB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // GetMovieTorrents returns show details in database from id or imdbid or an error
|
||||||
|
// func GetMovieTorrents(db *sqlx.DB, imdbID string) ([]*MovieTorrent, error) {
|
||||||
|
// var torrentsDB = []*MovieTorrentDB{}
|
||||||
|
// err := db.Select(&torrentsDB, getMovieTorrentQueryByImdbID, imdbID)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if len(torrentsDB) == 0 {
|
||||||
|
// return nil, sql.ErrNoRows
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var torrents []*MovieTorrent
|
||||||
|
// for _, torrentDB := range torrentsDB {
|
||||||
|
// movie := NewMovieFromImDB(imdbID)
|
||||||
|
// movie.FillFromDB(torrentDB)
|
||||||
|
// torrents = append(torrents, movie)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return torrents, nil
|
||||||
|
// }
|
||||||
|
|
||||||
// GetEpisodes from database
|
// GetEpisodes from database
|
||||||
func (s *Show) GetEpisodes(db *sqlx.DB) error {
|
func (s *Show) GetEpisodes(env *web.Env) error {
|
||||||
// When retrive episode's info from database populate the s.Episodes member
|
// We retrive episode's info from database populate the s.Episodes member
|
||||||
// and not s.Show.Episodes
|
var episodesDB = []*EpisodeDB{}
|
||||||
s.Episodes = []*Episode{}
|
err := env.Database.Select(&episodesDB, getEpisodesQuery, s.ImdbID)
|
||||||
err := db.Select(&s.Episodes, getEpisodesQuery, s.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(episodesDB) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, episodeDB := range episodesDB {
|
||||||
|
episode := NewEpisode()
|
||||||
|
episode.FillFromDB(episodeDB)
|
||||||
|
s.Episodes = append(s.Episodes, episode)
|
||||||
|
err = episode.GetTorrents(env, false)
|
||||||
|
if err != nil {
|
||||||
|
env.Log.Debugf("error while getting episode torrent: %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Episode represents an episode
|
// GetTorrents from the database or fetch them if needed
|
||||||
type Episode struct {
|
func (s *Show) GetTorrents(env *web.Env, force bool) error {
|
||||||
sqly.BaseModel
|
for _, e := range s.Episodes {
|
||||||
polochon.ShowEpisode
|
err := e.GetTorrents(env, force)
|
||||||
ShowID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add episode to the database
|
|
||||||
func (e *Episode) Add(db *sqlx.DB) error {
|
|
||||||
var id string
|
|
||||||
r, err := db.NamedQuery(addEpisodeQuery, e)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
env.Log.Errorf("error while getting episode torrent: %s", err)
|
||||||
}
|
}
|
||||||
for r.Next() {
|
|
||||||
r.Scan(&id)
|
|
||||||
}
|
}
|
||||||
e.ID = id
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ func TestTrackedShow(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := show.Add(db)
|
err := show.Upsert(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
160
src/internal/torrents/episode_torrents.go
Normal file
160
src/internal/torrents/episode_torrents.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package torrents
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
polochon "github.com/odwrtw/polochon/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
upsertEpisodeTorrentQuery = `
|
||||||
|
INSERT INTO episode_torrents (imdb_id, url, source, quality, upload_user,
|
||||||
|
season, episode, seeders, leechers)
|
||||||
|
VALUES (:imdb_id, :url, :source, :quality, :upload_user, :season, :episode,
|
||||||
|
:seeders, :leechers)
|
||||||
|
ON CONFLICT (imdb_id, season, episode, quality, source)
|
||||||
|
DO UPDATE SET imdb_id=:imdb_id, url=:url, source=:source, quality=:quality,
|
||||||
|
upload_user=:upload_user, season=:season, episode=:episode,
|
||||||
|
seeders=:seeders, leechers=:leechers
|
||||||
|
RETURNING id;`
|
||||||
|
|
||||||
|
getEpisodeTorrentQuery = `
|
||||||
|
SELECT *
|
||||||
|
FROM episode_torrents WHERE imdb_id=$1 AND season=$2 AND episode=$3;`
|
||||||
|
|
||||||
|
getEpisodeTorrentQueryByID = `
|
||||||
|
SELECT *
|
||||||
|
FROM episode_torrents WHERE id=$1;`
|
||||||
|
|
||||||
|
deleteEpisodeTorrentQuery = `DELETE FROM movie_torrents WHERE id=$1;`
|
||||||
|
)
|
||||||
|
|
||||||
|
// EpisodeTorrent represents an episode torrent
|
||||||
|
type EpisodeTorrent struct {
|
||||||
|
sqly.BaseModel
|
||||||
|
polochon.Torrent
|
||||||
|
ShowImdbID string `json:"show_imdb_id"`
|
||||||
|
Season int `json:"season"`
|
||||||
|
Episode int `json:"episode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpisodeTorrentDB represents the EpisodeTorrent in the DB
|
||||||
|
type EpisodeTorrentDB struct {
|
||||||
|
ID string `db:"id"`
|
||||||
|
ImdbID string `db:"imdb_id"`
|
||||||
|
URL string `db:"url"`
|
||||||
|
Source string `db:"source"`
|
||||||
|
Quality string `db:"quality"`
|
||||||
|
UploadUser string `db:"upload_user"`
|
||||||
|
Season int `db:"season"`
|
||||||
|
Episode int `db:"episode"`
|
||||||
|
Seeders int `db:"seeders"`
|
||||||
|
Leechers int `db:"leechers"`
|
||||||
|
Created time.Time `db:"created_at"`
|
||||||
|
Updated time.Time `db:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEpisodeFromPolochon returns a new EpisodeTorrent from a polochon.ShowEpisode
|
||||||
|
func NewEpisodeFromPolochon(se polochon.ShowEpisode, poTo polochon.Torrent) *EpisodeTorrent {
|
||||||
|
return &EpisodeTorrent{
|
||||||
|
ShowImdbID: se.ShowImdbID,
|
||||||
|
Season: se.Season,
|
||||||
|
Episode: se.Episode,
|
||||||
|
Torrent: poTo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEpisode returns a new EpisodeTorrent
|
||||||
|
func NewEpisode() *EpisodeTorrent {
|
||||||
|
return &EpisodeTorrent{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEpisodeTorrents returns show details in database from id or imdbid or an error
|
||||||
|
func GetEpisodeTorrents(db *sqlx.DB, imdbID string, season, episode int) ([]*EpisodeTorrent, error) {
|
||||||
|
var torrentsDB = []*EpisodeTorrentDB{}
|
||||||
|
err := db.Select(&torrentsDB, getEpisodeTorrentQuery, imdbID, season, episode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(torrentsDB) == 0 {
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
var torrents []*EpisodeTorrent
|
||||||
|
for _, torrentDB := range torrentsDB {
|
||||||
|
episode := NewEpisode()
|
||||||
|
episode.FillFromDB(torrentDB)
|
||||||
|
torrents = append(torrents, episode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return torrents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete episode from database
|
||||||
|
func (e *EpisodeTorrent) Delete(db *sqlx.DB) error {
|
||||||
|
r, err := db.Exec(deleteEpisodeTorrentQuery, e.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count, _ := r.RowsAffected()
|
||||||
|
if count != 1 {
|
||||||
|
return fmt.Errorf("Unexpected number of row deleted: %d", count)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillFromDB will fill a MovieTorrent from a MovieTorrentDB extracted from the DB
|
||||||
|
func (e *EpisodeTorrent) FillFromDB(eDB *EpisodeTorrentDB) {
|
||||||
|
q, _ := polochon.StringToQuality(eDB.Quality)
|
||||||
|
e.ShowImdbID = eDB.ImdbID
|
||||||
|
e.Created = eDB.Created
|
||||||
|
e.Updated = eDB.Updated
|
||||||
|
e.ID = eDB.ID
|
||||||
|
e.URL = eDB.URL
|
||||||
|
e.Source = eDB.Source
|
||||||
|
e.Quality = *q
|
||||||
|
e.UploadUser = eDB.UploadUser
|
||||||
|
e.Seeders = eDB.Seeders
|
||||||
|
e.Leechers = eDB.Leechers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upsert an episode torrent in the database
|
||||||
|
func (e *EpisodeTorrent) Upsert(db *sqlx.DB) error {
|
||||||
|
eDB := NewEpisodeTorrentDB(e)
|
||||||
|
var id string
|
||||||
|
r, err := db.NamedQuery(upsertEpisodeTorrentQuery, eDB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for r.Next() {
|
||||||
|
r.Scan(&id)
|
||||||
|
}
|
||||||
|
e.ID = id
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEpisodeTorrentDB returns an EpisodeTorrent ready to be put in DB from a
|
||||||
|
// EspisodeTorrent
|
||||||
|
func NewEpisodeTorrentDB(e *EpisodeTorrent) EpisodeTorrentDB {
|
||||||
|
return EpisodeTorrentDB{
|
||||||
|
ID: e.ID,
|
||||||
|
ImdbID: e.ShowImdbID,
|
||||||
|
Season: e.Season,
|
||||||
|
Episode: e.Episode,
|
||||||
|
URL: e.URL,
|
||||||
|
Source: e.Source,
|
||||||
|
Quality: string(e.Quality),
|
||||||
|
UploadUser: e.UploadUser,
|
||||||
|
Seeders: e.Seeders,
|
||||||
|
Leechers: e.Leechers,
|
||||||
|
Created: e.Created,
|
||||||
|
Updated: e.Updated,
|
||||||
|
}
|
||||||
|
}
|
@ -54,15 +54,6 @@ type MovieTorrentDB struct {
|
|||||||
Updated time.Time `db:"updated_at"`
|
Updated time.Time `db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EpisodeTorrent represents an episode torrent
|
|
||||||
type EpisodeTorrent struct {
|
|
||||||
sqly.BaseModel
|
|
||||||
polochon.Torrent
|
|
||||||
ShowImdbID string `json:"show_imdb_id"`
|
|
||||||
Season int `json:"season"`
|
|
||||||
Episode int `json:"episode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMovieFromImDB returns a new MovieTorrent with an ImDB id
|
// NewMovieFromImDB returns a new MovieTorrent with an ImDB id
|
||||||
func NewMovieFromImDB(imdbID string) *MovieTorrent {
|
func NewMovieFromImDB(imdbID string) *MovieTorrent {
|
||||||
return &MovieTorrent{
|
return &MovieTorrent{
|
@ -5,6 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/shows"
|
||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
"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/config"
|
||||||
@ -80,6 +81,11 @@ func main() {
|
|||||||
env.Handle("/movies/explore", extmedias.Explore)
|
env.Handle("/movies/explore", extmedias.Explore)
|
||||||
env.Handle("/movies/refresh", extmedias.Refresh)
|
env.Handle("/movies/refresh", extmedias.Refresh)
|
||||||
|
|
||||||
|
// env.Handle("/shows/polochon", shows.FromPolochon).WithRole(users.UserRole)
|
||||||
|
env.Handle("/shows/{id:tt[0-9]+}", shows.GetDetailsHandler)
|
||||||
|
env.Handle("/shows/refresh", extmedias.RefreshShows)
|
||||||
|
env.Handle("/shows/explore", extmedias.ExploreShows)
|
||||||
|
|
||||||
n := negroni.Classic()
|
n := negroni.Classic()
|
||||||
n.Use(authMiddleware)
|
n.Use(authMiddleware)
|
||||||
n.Use(negroni.NewStatic(http.Dir(cf.PublicDir)))
|
n.Use(negroni.NewStatic(http.Dir(cf.PublicDir)))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user