357 lines
8.5 KiB
Go
357 lines
8.5 KiB
Go
package movies
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/lib/pq"
|
|
"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/users"
|
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
|
)
|
|
|
|
const (
|
|
upsertMovieQuery = `
|
|
INSERT INTO movies (imdb_id, title, rating, votes, plot, tmdb_id, year,
|
|
genres, original_title, runtime, sort_title, tagline)
|
|
VALUES (:imdb_id, :title, :rating, :votes, :plot, :tmdb_id, :year, :genres,
|
|
:original_title, :runtime, :sort_title, :tagline)
|
|
ON CONFLICT (imdb_id)
|
|
DO UPDATE
|
|
SET imdb_id=:imdb_id, title=:title, rating=:rating, votes=:votes,
|
|
plot=:plot, tmdb_id=:tmdb_id, year=:year, genres=:genres,
|
|
original_title=:original_title, runtime=:runtime, sort_title=:sort_title,
|
|
tagline=:tagline
|
|
RETURNING id;`
|
|
|
|
getMovieQueryByImdbID = `
|
|
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
|
|
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;`
|
|
)
|
|
|
|
// MovieDB represents the Movie in the DB
|
|
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"`
|
|
Rating float32 `db:"rating"`
|
|
Votes int `db:"votes"`
|
|
Plot string `db:"plot"`
|
|
Year int `db:"year"`
|
|
Runtime int `db:"runtime"`
|
|
Tagline string `db:"tagline"`
|
|
Genres pq.StringArray `db:"genres"`
|
|
Created time.Time `db:"created_at"`
|
|
Updated time.Time `db:"updated_at"`
|
|
}
|
|
|
|
// NewMovieDB returns a Movie ready to be put in DB from a
|
|
// Movie
|
|
func NewMovieDB(m *Movie) MovieDB {
|
|
genres := []string{}
|
|
if m.Genres != nil {
|
|
genres = m.Genres
|
|
}
|
|
return MovieDB{
|
|
ID: m.ID,
|
|
ImdbID: m.ImdbID,
|
|
Title: m.Title,
|
|
Rating: m.Rating,
|
|
Votes: m.Votes,
|
|
Plot: m.Plot,
|
|
TmdbID: m.TmdbID,
|
|
Year: m.Year,
|
|
OriginalTitle: m.OriginalTitle,
|
|
Runtime: m.Runtime,
|
|
SortTitle: m.SortTitle,
|
|
Tagline: m.Tagline,
|
|
Genres: genres,
|
|
Created: m.Created,
|
|
Updated: m.Updated,
|
|
}
|
|
}
|
|
|
|
// FillFromDB returns a Movie from a MovieDB extracted from the DB
|
|
func (m *Movie) FillFromDB(mDB *MovieDB) {
|
|
m.Created = mDB.Created
|
|
m.Updated = mDB.Updated
|
|
m.ID = mDB.ID
|
|
m.ImdbID = mDB.ImdbID
|
|
m.Title = mDB.Title
|
|
m.Rating = mDB.Rating
|
|
m.Votes = mDB.Votes
|
|
m.Plot = mDB.Plot
|
|
m.TmdbID = mDB.TmdbID
|
|
m.Year = mDB.Year
|
|
m.OriginalTitle = mDB.OriginalTitle
|
|
m.Runtime = mDB.Runtime
|
|
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"`
|
|
}
|
|
|
|
// New returns a new Movie with an ImDB id
|
|
func New(imdbID string) *Movie {
|
|
return &Movie{
|
|
Movie: polochon.Movie{
|
|
ImdbID: imdbID,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Get returns show details in database from id or imdbid or an 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, user.ID).StructScan(&mDB)
|
|
} else if m.ImdbID != "" {
|
|
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")
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set the poster url
|
|
m.PosterURL = m.GetPosterURL(env)
|
|
|
|
m.FillFromDB(&mDB)
|
|
return nil
|
|
}
|
|
|
|
// GetDetails retrieves details for the movie, first try to get info from db,
|
|
// if not exists, use polochon.Detailer and save informations in the database
|
|
// for future use
|
|
//
|
|
// If force is used, the detailer will be used even if the movie is found in
|
|
// database
|
|
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",
|
|
})
|
|
log.Debugf("getting details")
|
|
|
|
if len(m.Detailers) == 0 {
|
|
m.Detailers = env.Config.MovieDetailers
|
|
}
|
|
|
|
var err error
|
|
err = m.Get(env, user)
|
|
switch err {
|
|
case nil:
|
|
log.Debug("movie found in database")
|
|
case sql.ErrNoRows:
|
|
log.Debug("movie not found in database")
|
|
default:
|
|
// Unexpected error
|
|
return err
|
|
}
|
|
// If force is not specified, don't go further
|
|
if !force {
|
|
// Will return ErrNoRows if the movie wasn't found
|
|
return err
|
|
}
|
|
|
|
// GetDetail
|
|
err = m.Movie.GetDetails(env.Log)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debug("got details from detailers")
|
|
|
|
err = m.Upsert(env.Database)
|
|
if err != nil {
|
|
log.Debug("error while doing db func")
|
|
return err
|
|
}
|
|
|
|
log.Debug("movie added in database")
|
|
|
|
// Download poster
|
|
err = web.Download(m.Thumb, m.imgFile(env))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debug("poster downloaded")
|
|
|
|
// Set the poster url
|
|
m.PosterURL = m.GetPosterURL(env)
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetTorrents retrieves torrents for the movie, 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 (m *Movie) GetTorrents(env *web.Env, force bool) error {
|
|
log := env.Log.WithFields(logrus.Fields{
|
|
"imdb_id": m.ImdbID,
|
|
"function": "movies.GetTorrents",
|
|
})
|
|
log.Debugf("getting torrents")
|
|
|
|
if len(m.Torrenters) == 0 {
|
|
m.Torrenters = env.Config.MovieTorrenters
|
|
}
|
|
|
|
movieTorrents, err := torrents.GetMovieTorrents(env.Database, m.ImdbID)
|
|
switch err {
|
|
case nil:
|
|
log.Debug("torrents found in database")
|
|
case sql.ErrNoRows:
|
|
log.Debug("torrent not found in database")
|
|
default:
|
|
// Unexpected error
|
|
return err
|
|
}
|
|
if !force {
|
|
log.Debugf("returning %d torrents from db", len(movieTorrents))
|
|
// Add the torrents to the movie
|
|
for _, t := range movieTorrents {
|
|
m.Torrents = append(m.Torrents, t.Torrent)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
err = m.Movie.GetTorrents(env.Log)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debugf("got %d torrents from torrenters", len(m.Movie.Torrents))
|
|
|
|
for _, t := range m.Movie.Torrents {
|
|
torrent := torrents.NewMovie(m.ImdbID, t)
|
|
err = torrent.Upsert(env.Database)
|
|
if err != nil {
|
|
log.Error("error while adding torrent", err)
|
|
continue
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Upsert a movie in the database
|
|
func (m *Movie) Upsert(db *sqlx.DB) error {
|
|
mDB := NewMovieDB(m)
|
|
var id string
|
|
r, err := db.NamedQuery(upsertMovieQuery, mDB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for r.Next() {
|
|
r.Scan(&id)
|
|
}
|
|
m.ID = id
|
|
|
|
return nil
|
|
}
|
|
|
|
// Delete movie from database
|
|
func (m *Movie) Delete(db *sqlx.DB) error {
|
|
r, err := db.Exec(deleteMovieQuery, m.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
count, _ := r.RowsAffected()
|
|
if count != 1 {
|
|
return fmt.Errorf("Unexpected number of row deleted: %d", count)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// imgURL returns the default image url
|
|
func (m *Movie) imgURL(env *web.Env) string {
|
|
return fmt.Sprintf("img/movies/%s.jpg", m.ImdbID)
|
|
}
|
|
|
|
// imgFile returns the image location on disk
|
|
func (m *Movie) imgFile(env *web.Env) string {
|
|
return filepath.Join(env.Config.PublicDir, m.imgURL(env))
|
|
}
|
|
|
|
// GetPosterURL returns the image URL or the default image if the poster is not yet downloaded
|
|
func (m *Movie) GetPosterURL(env *web.Env) string {
|
|
// Check if the movie image exists
|
|
if _, err := os.Stat(m.imgFile(env)); os.IsNotExist(err) {
|
|
// TODO image in the config ?
|
|
return "img/noimage.png"
|
|
}
|
|
return m.imgURL(env)
|
|
}
|