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/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, env.Log) if err != nil { 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 { err = models.UpsertMovieTorrent(env.Database, &t, m.ImdbID) 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 }