canape/backend/movies/movies.go
Grégoire Delattre d585a59c83 Download movie fanart and better torrent list on mobile
Show the thumb on wide screens and the fanart on small screens.
2021-08-22 11:33:36 -10:00

296 lines
7.5 KiB
Go

package movies
import (
"database/sql"
"encoding/json"
"fmt"
"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"
polochon "github.com/odwrtw/polochon/lib"
"github.com/odwrtw/polochon/lib/papi"
"github.com/sirupsen/logrus"
)
// Movie represents a movie
type Movie struct {
*polochon.Movie
client *papi.Client
pMovie *papi.Movie
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),
// TODO: remove this field to use m.Thumb
PosterURL: m.Thumb,
Subtitles: []subtitles.Subtitle{},
}
if m.pMovie != nil {
// Get the DownloadURL
movieToMarshal.PolochonURL, _ = m.client.DownloadURLWithToken(m.pMovie)
// Get the metadata
movieToMarshal.DateAdded = m.pMovie.DateAdded
movieToMarshal.Quality = string(m.pMovie.Quality)
movieToMarshal.AudioCodec = m.pMovie.AudioCodec
movieToMarshal.VideoCodec = m.pMovie.VideoCodec
movieToMarshal.Container = m.pMovie.Container
// Append the Subtitles
for _, s := range m.pMovie.Subtitles {
sub := subtitles.Subtitle{Subtitle: s.Subtitle}
if !sub.Embedded {
subtitleURL, _ := m.client.DownloadURLWithToken(s)
sub.URL = subtitleURL
sub.VVTFile = fmt.Sprintf("/movies/%s/subtitles/%s", m.ImdbID, s.Lang)
}
movieToMarshal.Subtitles = append(movieToMarshal.Subtitles, sub)
}
}
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) *Movie {
var m *polochon.Movie
if pMovie != nil && pMovie.Movie != nil {
m = pMovie.Movie
} else {
m = &polochon.Movie{ImdbID: imdbID}
}
return &Movie{
client: client,
pMovie: pMovie,
Wishlisted: isWishlisted,
Movie: m,
}
}
// 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, 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("thumb"), 300)
if err != nil {
log.Errorf("got error trying to download the poster %q", err)
}
// Download fanart
err = web.Download(m.Fanart, m.imgFile("fanart"), 960)
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, 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(imgType string) string {
var location string
if imgType == "thumb" {
location = m.ImdbID
} else {
location = m.ImdbID + "-" + imgType
}
return fmt.Sprintf("movies/%s.jpg", location)
}
// imgFile returns the image location on disk
func (m *Movie) imgFile(imgType string) string {
return filepath.Join(models.PublicDir, "img", m.imgURL(imgType))
}
// 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),
)
movies = append(movies, movie)
}
return movies, nil
}