canape/backend/shows/shows.go

273 lines
6.8 KiB
Go

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]
}