package shows import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "git.quimbo.fr/odwrtw/canape/backend/models" "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" ) // Show represents a show type Show struct { client *papi.Client pShow *papi.Show *polochon.Show TrackedSeason *int `json:"tracked_season"` TrackedEpisode *int `json:"tracked_episode"` } // MarshalJSON implements the Marshal interface func (s *Show) MarshalJSON() ([]byte, error) { type alias Show // Create the structure that we want to marshal showToMarshal := &struct { *alias Episodes []Episode `json:"episodes"` BannerURL string `json:"banner_url"` FanartURL string `json:"fanart_url"` PosterURL string `json:"poster_url"` }{ alias: (*alias)(s), BannerURL: s.Banner, FanartURL: s.Fanart, PosterURL: s.Poster, } // Create Episode obj from polochon.Episodes and add them to the object to // marshal for _, e := range s.Show.Episodes { showToMarshal.Episodes = append(showToMarshal.Episodes, Episode{ ShowEpisode: e, show: s, }) } return json.Marshal(showToMarshal) } // New returns a new Show with a polochon ShowConfig func New(imdbID string) *Show { return &Show{ Show: &polochon.Show{ ImdbID: imdbID, }, } } // NewWithClient returns a new Show with a polochon ShowConfig func NewWithClient(show *polochon.Show, client *papi.Client, pShow *papi.Show, wShow *models.WishedShow) *Show { s := &Show{ Show: show, client: client, pShow: pShow, } if wShow != nil { s.TrackedSeason = &wShow.Season s.TrackedEpisode = &wShow.Episode } return s } // GetDetails retrieves details for the show with the given detailers func (s *Show) GetDetails(env *web.Env, detailers []polochon.Detailer) error { log := env.Log.WithFields(logrus.Fields{ "imdb_id": s.ImdbID, "function": "shows.GetDetails", }) var detailersName []string for _, d := range detailers { detailersName = append(detailersName, d.Name()) } log.Debugf("getting details with %s", strings.Join(detailersName, ", ")) s.Detailers = detailers // Get the details err := polochon.GetDetails(s.Show, log) if err != nil { if errors.IsFatal(err) { return err } } log.Debugf("got details from detailers") return nil } // GetAndFetch retrieves details for the show with the given // detailers 'before' // If found, return // If not, retrives details with the detailers 'after' and update them in // database func (s *Show) GetAndFetch(env *web.Env, before []polochon.Detailer, after []polochon.Detailer) error { log := env.Log.WithFields(logrus.Fields{ "imdb_id": s.ImdbID, "function": "shows.GetAndFetch", }) // Try to get details with the first batch of Detailers err := s.GetDetails(env, before) if err == nil { log.Debug("show found in first try") return nil } log.Debugf("show not found in database: %s", err) // If not found, try the second batch and upsert return s.Refresh(env, after) } // Refresh retrieves details for the show with the given detailers // and update them in database func (s *Show) Refresh(env *web.Env, detailers []polochon.Detailer) error { // Refresh err := s.GetDetails(env, detailers) if err != nil { return err } // Download show images s.downloadImages(env) env.Log.Debug("images downloaded") // If found, update in database return models.UpsertShow(env.Database, s.Show) } // GetImageURL returns the image URL or the default image if the poster is not yet downloaded func (s *Show) GetImageURL(imgType string) string { // Check if the show image exists if _, err := os.Stat(s.imgFile(imgType)); os.IsNotExist(err) { return "" } return models.ImgURLPrefix + s.imgURL(imgType) } // downloadImages will download the show images func (s *Show) downloadImages(env *web.Env) { // Create the directory for the show if it doesn't exist if _, err := os.Stat(s.imgDirectory()); err != nil { if err = os.Mkdir(s.imgDirectory(), os.ModePerm); err != nil { env.Log.Warnf("couldn't create show directory %s: %s", s.imgDirectory(), err) return } } // Download the show images for _, img := range []struct { url string urlType string scale bool }{ { url: s.Show.Banner, urlType: "banner", scale: false, }, { url: s.Show.Fanart, urlType: "fanart", scale: false, }, { url: s.Show.Poster, urlType: "poster", scale: true, }, } { if img.url == "" { continue } // Don't download image if we already have it if _, err := os.Stat(s.imgFile(img.urlType)); err == nil { continue } if err := web.Download(img.url, s.imgFile(img.urlType), img.scale); err != nil { env.Log.Errorf("failed to dowload %s: %s", img.urlType, err) } } // Download episode thumbs for _, e := range s.Episodes { if e.Thumb == "" { continue } fileName := s.imgFile(fmt.Sprintf("%d-%d", e.Season, e.Episode)) // Don't download image if we already have it if _, err := os.Stat(fileName); err == nil { continue } err := web.Download(e.Thumb, fileName, false) if err != nil { env.Log.Errorf("failed to dowload the thumb for season %d episode %d ( %s ) : %s", e.Season, e.Episode, e.Thumb, err) } } } // imgURL returns the default image url func (s *Show) imgURL(imgType string) string { return fmt.Sprintf("shows/%s/%s.jpg", s.ImdbID, imgType) } // imgDirectory returns the directory containing all the show images func (s *Show) imgDirectory() string { return filepath.Join(models.PublicDir, "img", fmt.Sprintf("shows/%s", s.ImdbID)) } // imgFile returns the image location on disk func (s *Show) imgFile(imgType string) string { return filepath.Join(models.PublicDir, "img", s.imgURL(imgType)) } // getPolochonShows returns all the Shows from the polochon of a user func getPolochonShows(env *web.Env, user *models.User) ([]*Show, error) { shows := []*Show{} client, err := user.NewPapiClient(env.Database) if err != nil { return shows, err } // Get the polochon's shows pshows, err := client.GetShows() if err != nil { return shows, err } wShows, err := models.GetShowWishlist(env.Database, user.ID) if err != nil { return shows, err } // Create Shows objects from the shows retrieved for _, pShow := range pshows.List() { wShow, _ := wShows.IsShowInWishlist(pShow.ImdbID) show := NewWithClient(&polochon.Show{ImdbID: pShow.ImdbID}, client, pShow, wShow) shows = append(shows, show) } return shows, nil } // LastSeasonEpisodes will return the episodes of the last season of the show func (s *Show) LastSeasonEpisodes() []*polochon.ShowEpisode { episodes := make(map[int][]*polochon.ShowEpisode) lastSeason := 0 for _, e := range s.Episodes { if lastSeason < e.Season { lastSeason = e.Season } episodes[e.Season] = append(episodes[e.Season], e) } return episodes[lastSeason] }