canape/backend/movies/movies.go
Lucas BEE 4206d09954
Some checks reported errors
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build was killed
Fix upsert of torrents
We're missing some infos from the Torrents
We ensure that we set them for now, we'll need to edit the polochon
torrenters so that they put this info
And add a DB constraint, a torrent shouldn't be able to be created
without an imdbID
2020-04-10 17:48:34 +02:00

293 lines
7.5 KiB
Go

package movies
import (
"database/sql"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"git.quimbo.fr/odwrtw/canape/backend/models"
"git.quimbo.fr/odwrtw/canape/backend/subtitles"
"git.quimbo.fr/odwrtw/canape/backend/web"
"github.com/odwrtw/errors"
"github.com/odwrtw/papi"
polochon "github.com/odwrtw/polochon/lib"
"github.com/sirupsen/logrus"
)
// Movie represents a movie
type Movie struct {
*polochon.Movie
client *papi.Client
pMovie *papi.Movie
publicDir string
imgURLPrefix string
Wishlisted bool `json:"wishlisted"`
}
// MarshalJSON implements the Marshal interface
func (m *Movie) MarshalJSON() ([]byte, error) {
type Alias Movie
// Marshal the movie with its polochon_url
movieToMarshal := &struct {
*Alias
PolochonURL string `json:"polochon_url"`
PosterURL string `json:"poster_url"`
Subtitles []subtitles.Subtitle `json:"subtitles"`
DateAdded time.Time `json:"date_added"`
Quality string `json:"quality"`
AudioCodec string `json:"audio_codec"`
VideoCodec string `json:"video_codec"`
Container string `json:"container"`
}{
Alias: (*Alias)(m),
PosterURL: m.PosterURL(),
Subtitles: []subtitles.Subtitle{},
}
if m.pMovie != nil {
// Get the DownloadURL
movieToMarshal.PolochonURL, _ = m.client.DownloadURL(m.pMovie)
// Get the metadata
movieToMarshal.DateAdded = m.pMovie.DateAdded
movieToMarshal.Quality = m.pMovie.Quality
movieToMarshal.AudioCodec = m.pMovie.AudioCodec
movieToMarshal.VideoCodec = m.pMovie.VideoCodec
movieToMarshal.Container = m.pMovie.Container
// Append the Subtitles
for _, l := range m.pMovie.Subtitles {
subtitleURL, _ := m.client.SubtitleURL(m.pMovie, l)
movieToMarshal.Subtitles = append(movieToMarshal.Subtitles, subtitles.Subtitle{
Language: l,
URL: subtitleURL,
VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", m.ImdbID, l),
})
}
}
return json.Marshal(movieToMarshal)
}
// New returns a new Movie with all the needed infos
func New(imdbID string, client *papi.Client, pMovie *papi.Movie, isWishlisted bool, publicDir, imgURLPrefix string) *Movie {
return &Movie{
client: client,
pMovie: pMovie,
publicDir: publicDir,
imgURLPrefix: imgURLPrefix,
Wishlisted: isWishlisted,
Movie: &polochon.Movie{
ImdbID: imdbID,
},
}
}
// GetDetails retrieves details for the movie with the given detailers
func (m *Movie) GetDetails(env *web.Env, detailers []polochon.Detailer) error {
log := env.Log.WithFields(logrus.Fields{
"imdb_id": m.ImdbID,
"function": "movies.GetDetails",
})
log.Debugf("getting details")
m.Detailers = detailers
// GetDetail
err := polochon.GetDetails(m.Movie, log)
if err != nil {
if errors.IsFatal(err) {
return err
}
}
log.Debug("got details from detailers")
return nil
}
// GetAndFetch retrieves details for the movie with the given
// detailers 'before'
// If found, return
// If not, retrives details with the detailers 'after' and update them in
// database
func (m *Movie) GetAndFetch(env *web.Env, before []polochon.Detailer, after []polochon.Detailer) error {
log := env.Log.WithFields(logrus.Fields{
"imdb_id": m.ImdbID,
"function": "movies.GetAndFetch",
})
// Try to get details with the first batch of Detailers
err := m.GetDetails(env, before)
if err == nil {
log.Debug("movie found in first try")
return nil
}
log.Debugf("movie not found in database: %s", err)
// If not found, try the second batch and upsert
return m.Refresh(env, after)
}
// Refresh retrieves details for the movie with the given detailers
// and update them in database
func (m *Movie) Refresh(env *web.Env, detailers []polochon.Detailer) error {
log := env.Log.WithFields(logrus.Fields{
"imdb_id": m.ImdbID,
"function": "movies.Refresh",
})
// Refresh
err := m.GetDetails(env, detailers)
if err != nil {
return err
}
// Download poster
err = web.Download(m.Thumb, m.imgFile(), true)
if err != nil {
log.Errorf("got error trying to download the poster %q", err)
}
// If found, update in database
return models.UpsertMovie(env.Database, m.Movie)
}
// GetTorrents retrieves torrents for the movie with the given torrenters
func (m *Movie) GetTorrents(env *web.Env, torrenters []polochon.Torrenter) error {
log := env.Log.WithFields(logrus.Fields{
"imdb_id": m.ImdbID,
"function": "movies.GetTorrents",
})
log.Debugf("getting torrents")
m.Torrenters = torrenters
err := polochon.GetTorrents(m.Movie, env.Log)
if err != nil {
return err
}
log.Debugf("got %d torrents from torrenters", len(m.Movie.Torrents))
return nil
}
// GetAndFetchTorrents retrieves torrents for the movie with the given
// torrenters 'before'
// If found, return
// If not, retrives torrents with the torrenters 'after' and update them in
// database
func (m *Movie) GetAndFetchTorrents(env *web.Env, before []polochon.Torrenter, after []polochon.Torrenter) error {
log := env.Log.WithFields(logrus.Fields{
"imdb_id": m.ImdbID,
"function": "movies.GetAndFetchTorrents",
})
// Try to get torrents with the first batch of Torrenters
err := m.GetTorrents(env, before)
switch err {
case nil:
log.Debug("movie torrent's found in first try")
return nil
case sql.ErrNoRows:
log.Debug("movie's torrents not found in database")
default:
// Unexpected error
return err
}
// If not found, try the second batch and upsert
return m.RefreshTorrents(env, after)
}
// RefreshTorrents retrieves torrents for the movie with the given torrenters
// and update them in database
func (m *Movie) RefreshTorrents(env *web.Env, torrenters []polochon.Torrenter) error {
log := env.Log.WithFields(logrus.Fields{
"imdb_id": m.ImdbID,
"function": "movies.RefreshTorrents",
})
// Get torrents with de torrenters
err := m.GetTorrents(env, torrenters)
if err != nil {
return err
}
log.Debugf("got %d torrents from torrenters", len(m.Movie.Torrents))
// Update them in database
for _, t := range m.Movie.Torrents {
t.ImdbID = m.ImdbID
err = models.UpsertMovieTorrent(env.Database, t)
if err != nil {
log.Error("error while adding torrent", err)
continue
}
}
return nil
}
// imgURL returns the default image url
func (m *Movie) imgURL() string {
return fmt.Sprintf("movies/%s.jpg", m.ImdbID)
}
// imgFile returns the image location on disk
func (m *Movie) imgFile() string {
return filepath.Join(m.publicDir, "img", m.imgURL())
}
// PosterURL returns the image URL or the default image if the poster is not yet downloaded
func (m *Movie) PosterURL() string {
// Check if the movie image exists
if _, err := os.Stat(m.imgFile()); os.IsNotExist(err) {
return ""
}
return m.imgURLPrefix + m.imgURL()
}
// getPolochonMovies returns an array of the user's polochon movies
func getPolochonMovies(user *models.User, env *web.Env) ([]*Movie, error) {
movies := []*Movie{}
// Create a papi client
client, err := user.NewPapiClient(env.Database)
if err != nil {
return movies, err
}
// Retrieve the user's polochon movies
pmovies, err := client.GetMovies()
if err != nil {
return movies, err
}
// Get the user's wishlisted movies
moviesWishlist, err := models.GetMovieWishlist(env.Database, user.ID)
if err != nil {
return movies, err
}
// Create Movies objects from the movies retrieved
for _, pmovie := range pmovies.List() {
movie := New(
pmovie.ImdbID,
client,
pmovie,
moviesWishlist.IsMovieInWishlist(pmovie.ImdbID),
env.Config.PublicDir,
env.Config.ImgURLPrefix,
)
movies = append(movies, movie)
}
return movies, nil
}