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 }