Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TMDB images quality (tmdb_images_quality) option. #111

Merged
merged 30 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
092c899
Add TMDB images quality (tmdb_images_quality) option.
antonsoroko Mar 1, 2024
2b7aedd
Improve TMDB->Kodi Art mapping
antonsoroko Mar 2, 2024
fffd76c
use TMDB backdrop as Thumbnail
antonsoroko Mar 3, 2024
d4c2925
Kodi does not support svg images for logos
antonsoroko Mar 3, 2024
b45c976
Debug logging for SetArt
antonsoroko Mar 3, 2024
53536e5
Do not set Banner to BackdropPath b/c of significant mismatch of need…
antonsoroko Mar 5, 2024
cfcac88
For tmdb episode take additional art from season/show
antonsoroko Mar 5, 2024
6b45359
fix for AvailableArtworks: FanArt should be without text, Landscape w…
antonsoroko Mar 5, 2024
2cd0860
Remove debug logging
antonsoroko Mar 6, 2024
485abf9
refactor GetImageQualities to use struct
antonsoroko Mar 6, 2024
82bdcf0
set TMDB item.Art.Thumbnail to poster if backdrop is empty
antonsoroko Mar 6, 2024
390b78d
Use fallback art for poster/fanart/thumbnail
antonsoroko Mar 7, 2024
307254a
Merge branch 'master' into tmdb_images_quality
antonsoroko Mar 7, 2024
d8597f4
ImageQualityIdentifier
antonsoroko Mar 7, 2024
d13512b
Kodi does not support svg images for logos
antonsoroko Mar 7, 2024
fe19ada
fix typo
antonsoroko Mar 7, 2024
011c1bb
Fix missing logos in show.go
antonsoroko Mar 7, 2024
22b0dce
Revert "Use fallback art for poster/fanart/thumbnail"
antonsoroko Mar 11, 2024
df387e4
Revert "set TMDB item.Art.Thumbnail to poster if backdrop is empty"
antonsoroko Mar 11, 2024
3def429
Add SetLocalizedArt function to be used with any video type.
antonsoroko Mar 12, 2024
8713e24
Do not get data about unaired episodes if not needed
antonsoroko Mar 12, 2024
8e09700
w500 for Low quality for Thumbnail and Landscape
antonsoroko Mar 13, 2024
c8b84be
Use SecondLanguage as fallback for Artworks that must have text
antonsoroko Mar 14, 2024
7a04b32
Merge branch 'master' into tmdb_images_quality
antonsoroko Mar 14, 2024
21e8668
Merge branch 'master' into tmdb_images_quality
antonsoroko Mar 15, 2024
e332713
Move TMDB ImageQuality types to types.go
antonsoroko Mar 15, 2024
793a828
Merge branch 'master' into tmdb_images_quality
antonsoroko Mar 16, 2024
bf3edbd
fix localization fallback logic in GetLocalizedImages
antonsoroko Mar 16, 2024
8fbee7d
Add English as fallback for TMDB images
antonsoroko Mar 16, 2024
b3b8e4e
GetUserLanguages returns list of user languages to be used with inclu…
antonsoroko Mar 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ type Configuration struct {
PlayResumeBack int
TMDBApiKey string
TMDBShowUseProdCompanyAsStudio bool
TMDBImagesQuality int

OSDBUser string
OSDBPass string
Expand Down Expand Up @@ -701,6 +702,7 @@ func Reload() (ret *Configuration, err error) {
PlayResumeBack: settings.ToInt("play_resume_back"),
TMDBApiKey: settings.ToString("tmdb_api_key"),
TMDBShowUseProdCompanyAsStudio: settings.ToBool("tmdb_show_use_prod_company_as_studio"),
TMDBImagesQuality: settings.ToInt("tmdb_images_quality"),

OSDBUser: settings.ToString("osdb_user"),
OSDBPass: settings.ToString("osdb_pass"),
Expand Down
7 changes: 7 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ const (
StorageMemory
)

const (
ImageQualityOriginal int = iota
ImageQualityHigh
ImageQualityMedium
ImageQualityLow
)

var (
// Storages ...
Storages = []string{
Expand Down
36 changes: 26 additions & 10 deletions tmdb/episode.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ func GetEpisode(showID int, seasonNumber int, episodeNumber int, language string
defer perf.ScopeTimer()()

var episode *Episode
languagesList := GetUserLanguages()

req := reqapi.Request{
API: reqapi.TMDBAPI,
URL: fmt.Sprintf("/tv/%d/season/%d/episode/%d", showID, seasonNumber, episodeNumber),
Params: napping.Params{
"api_key": apiKey,
"append_to_response": "credits,images,videos,alternative_titles,translations,external_ids,trailers",
"include_image_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_video_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_image_language": languagesList,
"include_video_language": languagesList,
"language": language,
}.AsUrlValues(),
Result: &episode,
Expand Down Expand Up @@ -83,17 +84,30 @@ func (episode *Episode) SetArt(show *Show, season *Season, item *xbmc.ListItem)
item.Art = &xbmc.ListItemArt{}
}

// Episode only have Still aka Thumbnail, thus we take other artworks from the season/show
season.SetArt(show, item)

imageQualities := GetImageQualities()

if episode.StillPath != "" {
item.Art.FanArt = ImageURL(episode.StillPath, "w1280")
item.Art.Banner = ImageURL(episode.StillPath, "w1280")
item.Art.Poster = ImageURL(episode.StillPath, "w1280")
item.Art.Thumbnail = ImageURL(episode.StillPath, "w1280")
item.Art.TvShowPoster = ImageURL(episode.StillPath, "w1280")
} else {
// Use the season's artwork as a fallback
season.SetArt(show, item)
item.Art.Thumbnail = ImageURL(episode.StillPath, imageQualities.Thumbnail)

// Last resort: if show and season does not have Poster/FanArt - we use Still
if item.Art.Poster == "" {
item.Art.Poster = ImageURL(episode.StillPath, imageQualities.Thumbnail)
}
if item.Art.FanArt == "" {
item.Art.FanArt = ImageURL(episode.StillPath, imageQualities.Thumbnail)
}
}

if item.Art.AvailableArtworks == nil {
item.Art.AvailableArtworks = &xbmc.Artworks{}
}

// This only will set available thumbnails
SetLocalizedArt(&episode.Entity, item)

if config.Get().UseFanartTv {
if show.FanArt == nil && show.ExternalIDs != nil {
show.FanArt = fanart.GetShow(util.StrInterfaceToInt(show.ExternalIDs.TVDBID))
Expand All @@ -102,6 +116,8 @@ func (episode *Episode) SetArt(show *Show, season *Season, item *xbmc.ListItem)
item.Art = show.FanArt.ToEpisodeListItemArt(season.Season, item.Art)
}
}

item.Thumbnail = item.Art.Thumbnail
}

// ToListItem ...
Expand Down
67 changes: 17 additions & 50 deletions tmdb/movie.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ func GetImages(movieID int) *Images {
defer perf.ScopeTimer()()

var images *Images
languagesList := GetUserLanguages()

req := reqapi.Request{
API: reqapi.TMDBAPI,
URL: fmt.Sprintf("/movie/%d/images", movieID),
Params: napping.Params{
"api_key": apiKey,
"include_image_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_video_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_image_language": languagesList,
"include_video_language": languagesList,
}.AsUrlValues(),
Result: &images,
Description: "movie images",
Expand All @@ -64,14 +66,16 @@ func GetMovieByID(movieID string, language string) *Movie {
defer perf.ScopeTimer()()

var movie *Movie
languagesList := GetUserLanguages()

req := reqapi.Request{
API: reqapi.TMDBAPI,
URL: fmt.Sprintf("/movie/%s", movieID),
Params: napping.Params{
"api_key": apiKey,
"append_to_response": "credits,images,alternative_titles,translations,external_ids,trailers,release_dates",
"include_image_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_video_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_image_language": languagesList,
"include_video_language": languagesList,
"language": language,
}.AsUrlValues(),
Result: &movie,
Expand Down Expand Up @@ -414,57 +418,20 @@ func (movie *Movie) Year() int {
// SetArt sets artworks for movie
func (movie *Movie) SetArt(item *xbmc.ListItem) {
if item.Art == nil {
item.Art = &xbmc.ListItemArt{
FanArt: ImageURL(movie.BackdropPath, "w1280"),
Banner: ImageURL(movie.BackdropPath, "w1280"),
Poster: ImageURL(movie.PosterPath, "w1280"),
Thumbnail: ImageURL(movie.PosterPath, "w300"),
}
item.Art = &xbmc.ListItemArt{}
}

if item.Art.AvailableArtworks == nil {
item.Art.AvailableArtworks = &xbmc.Artworks{}
}
imageQualities := GetImageQualities()

if movie.Images != nil && movie.Images.Backdrops != nil {
fanarts := make([]string, 0)
foundLanguageSpecificImage := false
for _, backdrop := range movie.Images.Backdrops {
// for AvailableArtworks
fanarts = append(fanarts, ImageURL(backdrop.FilePath, "w1280"))
item.Art.FanArt = ImageURL(movie.BackdropPath, imageQualities.FanArt)
item.Art.Thumbnail = ImageURL(movie.BackdropPath, imageQualities.Thumbnail)
item.Art.Poster = ImageURL(movie.PosterPath, imageQualities.Poster)

// try to use language specific art
if !foundLanguageSpecificImage && backdrop.Iso639_1 == config.Get().Language {
item.Art.FanArt = ImageURL(backdrop.FilePath, "w1280")
item.Art.Banner = ImageURL(backdrop.FilePath, "w1280")
foundLanguageSpecificImage = true // we take first image, it has top rating
}
}
if len(fanarts) > 0 {
item.Art.FanArts = fanarts
item.Art.AvailableArtworks.FanArt = fanarts
item.Art.AvailableArtworks.Banner = fanarts
}
if item.Art.AvailableArtworks == nil {
item.Art.AvailableArtworks = &xbmc.Artworks{}
}

if movie.Images != nil && movie.Images.Posters != nil {
posters := make([]string, 0)
foundLanguageSpecificImage := false
for _, poster := range movie.Images.Posters {
// for AvailableArtworks
posters = append(posters, ImageURL(poster.FilePath, "w1280"))

// try to use language specific art
if !foundLanguageSpecificImage && poster.Iso639_1 == config.Get().Language {
item.Art.Poster = ImageURL(poster.FilePath, "w1280")
item.Art.Thumbnail = ImageURL(poster.FilePath, "w1280")
foundLanguageSpecificImage = true // we take first image, it has top rating
}
}
if len(posters) > 0 {
item.Art.AvailableArtworks.Poster = posters
}
}
SetLocalizedArt(&movie.Entity, item)

if config.Get().UseFanartTv {
if movie.FanArt == nil {
Expand All @@ -475,7 +442,7 @@ func (movie *Movie) SetArt(item *xbmc.ListItem) {
}
}

item.Thumbnail = item.Art.Poster
item.Thumbnail = item.Art.Thumbnail
}

// ToListItem ...
Expand Down
72 changes: 25 additions & 47 deletions tmdb/season.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ func GetSeason(showID int, seasonNumber int, language string, seasonsCount int,
defer perf.ScopeTimer()()

var season *Season
languagesList := GetUserLanguages()

req := reqapi.Request{
API: reqapi.TMDBAPI,
URL: fmt.Sprintf("/tv/%d/season/%d", showID, seasonNumber),
Params: napping.Params{
"api_key": apiKey,
"append_to_response": "credits,images,videos,external_ids,alternative_titles,translations,trailers",
"include_image_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_video_language": fmt.Sprintf("%s,%s,null", config.Get().Language, config.Get().SecondLanguage),
"include_image_language": languagesList,
"include_video_language": languagesList,
"language": language,
}.AsUrlValues(),
Result: &season,
Expand All @@ -61,7 +63,19 @@ func GetSeason(showID int, seasonNumber int, language string, seasonsCount int,
// If we have empty Names/Overviews then we need to collect Translations separately
wg := sync.WaitGroup{}
for i, episode := range season.Episodes {
// TODO: episode.Translations is always nil when we get episode from season endpoint, so check is useless
// TODO: episode.Images is always nil when we get episode from season endpoint, thus no extra Thumbnails for Kodi
if episode.Translations == nil && (episode.Name == "" || episode.Overview == "") {
// Usually unaired episode does not have translated info, so we get inside if,
// but we should not get data about unaired episodes if not needed
if !config.Get().ShowUnairedEpisodes {
if episode.AirDate == "" {
continue
}
if _, isAired := util.AirDateWithAiredCheck(episode.AirDate, time.DateOnly, config.Get().ShowEpisodesOnReleaseDay); !isAired {
continue
}
}
wg.Add(1)
go func(idx int, episode *Episode) {
defer wg.Done()
Expand Down Expand Up @@ -144,58 +158,22 @@ func (season *Season) SetArt(show *Show, item *xbmc.ListItem) {
// Use the show's artwork as a fallback
show.SetArt(item)

if season.Poster != "" {
item.Art.Poster = ImageURL(season.Poster, "w1280")
item.Art.Thumbnail = ImageURL(season.Poster, "w1280")
imageQualities := GetImageQualities()

if season.BackdropPath != "" { // TODO: looks like BackdropPath is always empty for season
item.Art.FanArt = ImageURL(season.BackdropPath, imageQualities.FanArt)
item.Art.Thumbnail = ImageURL(season.BackdropPath, imageQualities.Thumbnail)
}
if season.Backdrop != "" {
item.Art.FanArt = ImageURL(season.Backdrop, "w1280")
item.Art.Banner = ImageURL(season.Backdrop, "w1280")
if season.PosterPath != "" { // Try to use Poster of season if available
item.Art.Poster = ImageURL(season.PosterPath, imageQualities.Poster)
item.Art.TvShowPoster = ImageURL(season.PosterPath, imageQualities.Poster)
}

if item.Art.AvailableArtworks == nil {
item.Art.AvailableArtworks = &xbmc.Artworks{}
}

if season.Images != nil && season.Images.Backdrops != nil {
fanarts := make([]string, 0)
foundLanguageSpecificImage := false
for _, backdrop := range season.Images.Backdrops {
// for AvailableArtworks
fanarts = append(fanarts, ImageURL(backdrop.FilePath, "w1280"))

// try to use language specific art
if !foundLanguageSpecificImage && backdrop.Iso639_1 == config.Get().Language {
item.Art.FanArt = ImageURL(backdrop.FilePath, "w1280")
item.Art.Banner = ImageURL(backdrop.FilePath, "w1280")
foundLanguageSpecificImage = true // we take first image, it has top rating
}
}
if len(fanarts) > 0 {
item.Art.FanArts = fanarts
item.Art.AvailableArtworks.FanArt = fanarts
item.Art.AvailableArtworks.Banner = fanarts
}
}

if season.Images != nil && season.Images.Posters != nil {
posters := make([]string, 0)
foundLanguageSpecificImage := false
for _, poster := range season.Images.Posters {
// for AvailableArtworks
posters = append(posters, ImageURL(poster.FilePath, "w1280"))

// try to use language specific art
if !foundLanguageSpecificImage && poster.Iso639_1 == config.Get().Language {
item.Art.Poster = ImageURL(poster.FilePath, "w1280")
item.Art.Thumbnail = ImageURL(poster.FilePath, "w1280")
foundLanguageSpecificImage = true // we take first image, it has top rating
}
}
if len(posters) > 0 {
item.Art.AvailableArtworks.Poster = posters
}
}
SetLocalizedArt(&season.Entity, item)

if config.Get().UseFanartTv {
if show.FanArt == nil && show.ExternalIDs != nil {
Expand Down
Loading