Skip to content

Loading…

Drop thumb type from (most) movies, shows, seasons + sets + fixes #1694

Merged
15 commits merged into from

1 participant

@jmarshallnz
Team Kodi member

The main thing that this PR handles is dropping the "thumb" art type from movies, shows, seasons and sets. Essentially we demand a poster for movies and sets, and a banner or poster for shows and seasons.

It works as follows:
1. Local art, if known (poster.jpg etc.) we assign directly.
2. Local art, if unknown (thumb.jpg, *.tbn etc.) we cache, and based on the aspect ratio determine poster or banner status and assign accordingly. If in-between, we cache using thumb normally.
3. Online art, we assign directly if the aspect attribute is specified.
4. Online art, we assign to the preferred type (poster for movie, banner for shows) if no aspect attribute is specified.

There's update code in place to move all the existing thumbs to poster/banner (both Eden + pre-Frodo).

Finally, thumb is always available as a fallback for skinners (it falls "forward" to poster then banner if a thumb type isn't there).

In addition, this moves to support banner and poster (and fanart) for seasons.

Finally, we have a bunch of fixes for the "Season Thumb" and "Movie set thumb/fanart" context menu items, which have been replaced with a "Choose art" item, which throws up the Choose Art dialog.

Jonathan Mar... added some commits
Jonathan Marshall change GetCachedImage to return CTextureDetails, fixes local art not …
…being checked periodically, as the hash from the database was never returned.
061f786
Jonathan Marshall adds routine to classify art into poster or banner based on aspect ratio 70638e4
Jonathan Marshall when finding video art where we don't want a generic 'thumb', use the…
… size of local art to classify 'thumb' into the correct types (poster/banner)
87d9b2b
Jonathan Marshall don't set the 'thumb' art type if not found during video scan 66e43a3
Jonathan Marshall don't have a 'thumb' art type for movies, shows, sets, or musicvideos 0c6bf46
Jonathan Marshall add in any art found in the database in addition to the known art typ…
…es in the Choose Art dialog
798f2b8
Jonathan Marshall if no poster or banner exists, but a thumb does, then list that when …
…choosing poster or banner
aa12f35
Jonathan Marshall when updating video art from Eden, correctly assign to banner or post…
…er based on aspect ratio
89e40ca
Jonathan Marshall update the pre-Frodo art in the database to poster/banner based on as…
…pect ratio
412b018
Jonathan Marshall move tvshowthumb and seasonthumb ListItem.Art to tvshow.thumb and sea…
…son.thumb, making tvshow.poster/banner and season.poster/banner also available. Container.TVShowThumb/SeasonThumb are still available for backward compatibility
03423db
Jonathan Marshall allow a full art map (banner/poster/fanart) for seasons a8a933d
Jonathan Marshall allow Choose Art to work for seasons in the video info dialog be24f47
Jonathan Marshall factor out the choosing art dialog for re-use 7971ae5
Jonathan Marshall factor out choosing of fanart from CONTEXT_BUTTON_SET_MOVIESET_FANART…
… handler
2ca9479
Jonathan Marshall rehash the set thumbs routine for seasons + sets so it aligns with ch…
…oose art on the video info dialog
02caece
@ghost ghost merged commit 1821096 into xbmc:master
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 31, 2012
  1. change GetCachedImage to return CTextureDetails, fixes local art not …

    Jonathan Marshall committed
    …being checked periodically, as the hash from the database was never returned.
  2. when finding video art where we don't want a generic 'thumb', use the…

    Jonathan Marshall committed
    … size of local art to classify 'thumb' into the correct types (poster/banner)
  3. don't set the 'thumb' art type if not found during video scan

    Jonathan Marshall committed
  4. add in any art found in the database in addition to the known art typ…

    Jonathan Marshall committed
    …es in the Choose Art dialog
  5. if no poster or banner exists, but a thumb does, then list that when …

    Jonathan Marshall committed
    …choosing poster or banner
  6. when updating video art from Eden, correctly assign to banner or post…

    Jonathan Marshall committed
    …er based on aspect ratio
  7. update the pre-Frodo art in the database to poster/banner based on as…

    Jonathan Marshall committed
    …pect ratio
  8. move tvshowthumb and seasonthumb ListItem.Art to tvshow.thumb and sea…

    Jonathan Marshall committed
    …son.thumb, making tvshow.poster/banner and season.poster/banner also available. Container.TVShowThumb/SeasonThumb are still available for backward compatibility
  9. allow a full art map (banner/poster/fanart) for seasons

    Jonathan Marshall committed
  10. allow Choose Art to work for seasons in the video info dialog

    Jonathan Marshall committed
  11. factor out the choosing art dialog for re-use

    Jonathan Marshall committed
  12. factor out choosing of fanart from CONTEXT_BUTTON_SET_MOVIESET_FANART…

    Jonathan Marshall committed
    … handler
  13. rehash the set thumbs routine for seasons + sets so it aligns with ch…

    Jonathan Marshall committed
    …oose art on the video info dialog
View
8 language/English/strings.po
@@ -9156,9 +9156,7 @@ msgctxt "#20434"
msgid "Sets"
msgstr ""
-msgctxt "#20435"
-msgid "Set movieset thumb"
-msgstr ""
+#empty string with id 20435
msgctxt "#20436"
msgid "Export actor thumbs?"
@@ -9240,9 +9238,7 @@ msgctxt "#20455"
msgid "Listeners"
msgstr ""
-msgctxt "#20456"
-msgid "Set movieset fanart"
-msgstr ""
+#empty string with id 20456
msgctxt "#20457"
msgid "Movie set"
View
3 xbmc/DatabaseManager.cpp
@@ -54,6 +54,9 @@ void CDatabaseManager::Initialize(bool addonsOnly)
if (addonsOnly)
return;
CLog::Log(LOGDEBUG, "%s, updating databases...", __FUNCTION__);
+
+ // NOTE: Order here is important. In particular, CTextureDatabase has to be updated
+ // before CVideoDatabase.
{ CViewDatabase db; UpdateDatabase(db); }
{ CTextureDatabase db; UpdateDatabase(db); }
{ CMusicDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseMusic); }
View
4 xbmc/GUIInfoManager.cpp
@@ -3136,13 +3136,13 @@ CStdString CGUIInfoManager::GetImage(int info, int contextWindow, CStdString *fa
{
CGUIWindow *window = GetWindowWithCondition(contextWindow, WINDOW_CONDITION_IS_MEDIA_WINDOW);
if (window)
- return ((CGUIMediaWindow *)window)->CurrentDirectory().GetArt("tvshowthumb");
+ return ((CGUIMediaWindow *)window)->CurrentDirectory().GetArt("tvshow.thumb");
}
else if (info == CONTAINER_SEASONTHUMB)
{
CGUIWindow *window = GetWindowWithCondition(contextWindow, WINDOW_CONDITION_IS_MEDIA_WINDOW);
if (window)
- return ((CGUIMediaWindow *)window)->CurrentDirectory().GetArt("seasonthumb");
+ return ((CGUIMediaWindow *)window)->CurrentDirectory().GetArt("season.thumb");
}
else if (info == LISTITEM_THUMB || info == LISTITEM_ICON || info == LISTITEM_ACTUAL_ICON ||
info == LISTITEM_OVERLAY || info == LISTITEM_RATING || info == LISTITEM_STAR_RATING)
View
49 xbmc/TextureCache.cpp
@@ -72,21 +72,19 @@ bool CTextureCache::IsCachedImage(const CStdString &url) const
bool CTextureCache::HasCachedImage(const CStdString &url)
{
- CStdString cachedHash;
- CStdString cachedImage(GetCachedImage(url, cachedHash));
+ CTextureDetails details;
+ CStdString cachedImage(GetCachedImage(url, details));
return (!cachedImage.IsEmpty() && cachedImage != url);
}
-CStdString CTextureCache::GetCachedImage(const CStdString &image, CStdString &cachedHash, bool trackUsage)
+CStdString CTextureCache::GetCachedImage(const CStdString &image, CTextureDetails &details, bool trackUsage)
{
- cachedHash.clear();
CStdString url = UnwrapImageURL(image);
if (IsCachedImage(url))
return url;
// lookup the item in the database
- CTextureDetails details;
if (GetCachedTexture(url, details))
{
if (trackUsage)
@@ -136,9 +134,9 @@ bool CTextureCache::CanCacheImageURL(const CURL &url)
CStdString CTextureCache::CheckCachedImage(const CStdString &url, bool returnDDS, bool &needsRecaching)
{
- CStdString cachedHash;
- CStdString path(GetCachedImage(url, cachedHash, true));
- needsRecaching = !cachedHash.IsEmpty();
+ CTextureDetails details;
+ CStdString path(GetCachedImage(url, details, true));
+ needsRecaching = !details.hash.empty();
if (!path.IsEmpty())
{
if (!needsRecaching && returnDDS && !URIUtils::IsInPath(url, "special://skin/")) // TODO: should skin images be .dds'd (currently they're not necessarily writeable)
@@ -156,16 +154,25 @@ CStdString CTextureCache::CheckCachedImage(const CStdString &url, bool returnDDS
void CTextureCache::BackgroundCacheImage(const CStdString &url)
{
- CStdString cacheHash;
- CStdString path(GetCachedImage(url, cacheHash));
- if (!path.IsEmpty() && cacheHash.IsEmpty())
+ CTextureDetails details;
+ CStdString path(GetCachedImage(url, details));
+ if (!path.IsEmpty() && details.hash.empty())
return; // image is already cached and doesn't need to be checked further
// needs (re)caching
- AddJob(new CTextureCacheJob(UnwrapImageURL(url), cacheHash));
+ AddJob(new CTextureCacheJob(UnwrapImageURL(url), details.hash));
+}
+
+bool CTextureCache::CacheImage(const CStdString &image, CTextureDetails &details)
+{
+ CStdString path = GetCachedImage(image, details);
+ if (path.empty()) // not cached
+ path = CacheImage(image, NULL, &details);
+
+ return !path.empty();
}
-CStdString CTextureCache::CacheImage(const CStdString &image, CBaseTexture **texture)
+CStdString CTextureCache::CacheImage(const CStdString &image, CBaseTexture **texture, CTextureDetails *details)
{
CStdString url = UnwrapImageURL(image);
CSingleLock lock(m_processingSection);
@@ -177,6 +184,8 @@ CStdString CTextureCache::CacheImage(const CStdString &image, CBaseTexture **tex
CTextureCacheJob job(url);
bool success = job.CacheTexture(texture);
OnCachingComplete(success, &job);
+ if (success && details)
+ *details = job.m_details;
return success ? GetCachedPath(job.m_details.file) : "";
}
lock.Leave();
@@ -191,8 +200,10 @@ CStdString CTextureCache::CacheImage(const CStdString &image, CBaseTexture **tex
break;
}
}
- CStdString cachedHash;
- return GetCachedImage(url, cachedHash, true);
+ CTextureDetails tempDetails;
+ if (!details)
+ details = &tempDetails;
+ return GetCachedImage(url, *details, true);
}
void CTextureCache::ClearCachedImage(const CStdString &url, bool deleteSource /*= false */)
@@ -315,8 +326,8 @@ void CTextureCache::OnJobProgress(unsigned int jobID, unsigned int progress, uns
bool CTextureCache::Export(const CStdString &image, const CStdString &destination, bool overwrite)
{
- CStdString cachedHash;
- CStdString cachedImage(GetCachedImage(image, cachedHash));
+ CTextureDetails details;
+ CStdString cachedImage(GetCachedImage(image, details));
if (!cachedImage.IsEmpty())
{
CStdString dest = destination + URIUtils::GetExtension(cachedImage);
@@ -332,8 +343,8 @@ bool CTextureCache::Export(const CStdString &image, const CStdString &destinatio
bool CTextureCache::Export(const CStdString &image, const CStdString &destination)
{
- CStdString cachedHash;
- CStdString cachedImage(GetCachedImage(image, cachedHash));
+ CTextureDetails details;
+ CStdString cachedImage(GetCachedImage(image, details));
if (!cachedImage.IsEmpty())
{
if (CFile::Cache(cachedImage, destination))
View
17 xbmc/TextureCache.h
@@ -87,10 +87,19 @@ class CTextureCache : public CJobQueue
\param image url of the image to cache
\param texture [out] the loaded image
+ \param details [out] details of the cached image
\return cached url of this image
\sa CTextureCacheJob::CacheTexture
*/
- CStdString CacheImage(const CStdString &url, CBaseTexture **texture = NULL);
+ CStdString CacheImage(const CStdString &url, CBaseTexture **texture = NULL, CTextureDetails *details = NULL);
+
+ /*! \brief Cache an image to image cache if not already cached, returning the image details.
+ \param image url of the image to cache.
+ \param details [out] the image details.
+ \return true if the image is in the cache, false otherwise.
+ \sa CTextureCacheJob::CacheTexture
+ */
+ bool CacheImage(const CStdString &image, CTextureDetails &details);
/*! \brief Check whether an image is in the cache
Note: If the image url won't normally be cached (eg a skin image) this function will return false.
@@ -172,12 +181,12 @@ class CTextureCache : public CJobQueue
/*! \brief retrieve the cached version of the given image (if it exists)
\param image url of the image
- \param cacheHash [out] set to the hash of the cached image if it needs checking
+ \param details [out] the details of the texture.
\param trackUsage whether this call should track usage of the image (defaults to false)
\return cached url of this image, empty if none exists
- \sa ClearCachedImage
+ \sa ClearCachedImage, CTextureDetails
*/
- CStdString GetCachedImage(const CStdString &image, CStdString &cacheHash, bool trackUsage = false);
+ CStdString GetCachedImage(const CStdString &image, CTextureDetails &details, bool trackUsage = false);
/*! \brief Get an image from the database
Thread-safe wrapper of CTextureDatabase::GetCachedTexture
View
5 xbmc/dialogs/GUIDialogContextMenu.h
@@ -80,7 +80,7 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0,
CONTEXT_BUTTON_SCAN,
CONTEXT_BUTTON_STOP_SCANNING,
CONTEXT_BUTTON_SET_ARTIST_THUMB,
- CONTEXT_BUTTON_SET_SEASON_THUMB,
+ CONTEXT_BUTTON_SET_SEASON_ART,
CONTEXT_BUTTON_NOW_PLAYING,
CONTEXT_BUTTON_CANCEL_PARTYMODE,
CONTEXT_BUTTON_MARK_WATCHED,
@@ -109,11 +109,10 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0,
CONTEXT_BUTTON_GROUP_MANAGER,
CONTEXT_BUTTON_CHANNEL_MANAGER,
CONTEXT_BUTTON_FILTER,
- CONTEXT_BUTTON_SET_MOVIESET_THUMB,
+ CONTEXT_BUTTON_SET_MOVIESET_ART,
CONTEXT_BUTTON_BEGIN,
CONTEXT_BUTTON_END,
CONTEXT_BUTTON_FIND,
- CONTEXT_BUTTON_SET_MOVIESET_FANART,
CONTEXT_BUTTON_DELETE_PLUGIN,
CONTEXT_BUTTON_SORTASC,
CONTEXT_BUTTON_SORTBY,
View
4 xbmc/guilib/GUIListItem.cpp
@@ -119,11 +119,11 @@ void CGUIListItem::SetArt(const std::string &type, const std::string &url)
}
}
-void CGUIListItem::SetArt(const ArtMap &art)
+void CGUIListItem::SetArt(const ArtMap &art, bool setFallback /* = true */)
{
m_art = art;
// ensure that the fallback "thumb" is available
- if (m_art.find("thumb") == m_art.end())
+ if (setFallback && m_art.find("thumb") == m_art.end())
{
if (HasArt("poster"))
m_art["thumb"] = m_art["poster"];
View
3 xbmc/guilib/GUIListItem.h
@@ -85,9 +85,10 @@ class CGUIListItem
/*! \brief set artwork for an item
\param art a type:url map for artwork
+ \param setFallback whether to set the "thumb" fallback, defaults to true.
\sa GetArt
*/
- void SetArt(const ArtMap &art);
+ void SetArt(const ArtMap &art, bool setFallback = true);
/*! \brief append artwork to an item
\param art a type:url map for artwork
View
2 xbmc/interfaces/json-rpc/VideoLibrary.cpp
@@ -527,7 +527,7 @@ JSONRPC_STATUS CVideoLibrary::SetTVShowDetails(const CStdString &method, ITransp
std::map<std::string, std::string> artwork;
videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
- std::map<int, std::string> seasonArt;
+ std::map<int, std::map<std::string, std::string> > seasonArt;
videodatabase.GetTvShowSeasonArt(infos.m_iDbId, seasonArt);
int playcount = infos.m_playCount;
View
45 xbmc/utils/EdenVideoArtUpdater.cpp
@@ -93,8 +93,9 @@ void CEdenVideoArtUpdater::Process()
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
{
CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
- if (!art.empty() && CacheTexture(art, cachedThumb))
- artwork.insert(make_pair("thumb", art));
+ std::string type;
+ if (!art.empty() && CacheTexture(art, cachedThumb, type))
+ artwork.insert(make_pair(type, art));
art = CVideoInfoScanner::GetFanart(item.get(), true);
if (!art.empty() && CacheTexture(art, cachedFanart))
@@ -125,8 +126,9 @@ void CEdenVideoArtUpdater::Process()
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
{
CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
- if (!art.empty() && CacheTexture(art, cachedThumb))
- artwork.insert(make_pair("thumb", art));
+ std::string type;
+ if (!art.empty() && CacheTexture(art, cachedThumb, type))
+ artwork.insert(make_pair(type, art));
art = CVideoInfoScanner::GetFanart(item.get(), true);
if (!art.empty() && CacheTexture(art, cachedFanart))
@@ -157,8 +159,9 @@ void CEdenVideoArtUpdater::Process()
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
{
CStdString art = CVideoInfoScanner::GetImage(item.get(), true, false, "thumb");
- if (!art.empty() && CacheTexture(art, cachedThumb))
- artwork.insert(make_pair("thumb", art));
+ std::string type;
+ if (!art.empty() && CacheTexture(art, cachedThumb, type))
+ artwork.insert(make_pair(type, art));
art = CVideoInfoScanner::GetFanart(item.get(), true);
if (!art.empty() && CacheTexture(art, cachedFanart))
@@ -170,19 +173,21 @@ void CEdenVideoArtUpdater::Process()
}
// now season art...
- map<int, string> seasons;
- CVideoInfoScanner::GetSeasonThumbs(*item->GetVideoInfoTag(), seasons, true);
- for (map<int, string>::const_iterator j = seasons.begin(); j != seasons.end(); ++j)
+ map<int, map<string, string> > seasons;
+ vector<string> artTypes; artTypes.push_back("thumb");
+ CVideoInfoScanner::GetSeasonThumbs(*item->GetVideoInfoTag(), seasons, artTypes, true);
+ for (map<int, map<string, string> >::const_iterator j = seasons.begin(); j != seasons.end(); ++j)
{
+ if (j->second.empty())
+ continue;
int idSeason = db.AddSeason(item->GetVideoInfoTag()->m_iDbId, j->first);
- if (!db.GetArtForItem(idSeason, "season", "thumb").empty())
+ map<string, string> seasonArt;
+ if (idSeason > -1 && !db.GetArtForItem(idSeason, "season", seasonArt))
{
std::string cachedSeason = GetCachedSeasonThumb(j->first, item->GetVideoInfoTag()->m_strPath);
- if (CacheTexture(j->second, cachedSeason))
- {
- if (idSeason > -1)
- db.SetArtForItem(idSeason, "season", "thumb", j->second);
- }
+ std::string type;
+ if (CacheTexture(j->second.begin()->second, cachedSeason, type))
+ db.SetArtForItem(idSeason, "season", type, j->second.begin()->second);
}
}
@@ -268,12 +273,19 @@ void CEdenVideoArtUpdater::Process()
bool CEdenVideoArtUpdater::CacheTexture(const std::string &originalUrl, const std::string &cachedFile)
{
+ std::string type;
+ return CacheTexture(originalUrl, cachedFile, type);
+}
+
+bool CEdenVideoArtUpdater::CacheTexture(const std::string &originalUrl, const std::string &cachedFile, std::string &type)
+{
if (!CFile::Exists(cachedFile))
return false;
CTextureDetails details;
- details.updateable = false;;
+ details.updateable = false;
details.hash = "NOHASH";
+ type = "thumb"; // unknown art type
CBaseTexture *texture = CTextureCacheJob::LoadImage(cachedFile, 0, 0, "");
if (texture)
@@ -290,6 +302,7 @@ bool CEdenVideoArtUpdater::CacheTexture(const std::string &originalUrl, const st
{
details.width = width;
details.height = height;
+ type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
delete texture;
m_textureDB.AddCachedTexture(originalUrl, details);
return true;
View
2 xbmc/utils/EdenVideoArtUpdater.h
@@ -39,7 +39,9 @@ class CEdenVideoArtUpdater : CThread
/*! \brief Caches the texture from oldCachedFile as if it came from originalUrl into the texture cache.
\param originalUrl the url that we think the oldCachedFile came from.
\param oldCachedFile the old cached file
+ \param type [out] the type of art (poster/banner/thumb)
*/
+ bool CacheTexture(const std::string &originalUrl, const std::string &cachedFile, std::string &type);
bool CacheTexture(const std::string &originalUrl, const std::string &oldCachedFile);
CStdString GetCachedActorThumb(const CFileItem &item);
View
2 xbmc/utils/RecentlyAddedJob.cpp
@@ -135,7 +135,7 @@ bool CRecentlyAddedJob::UpdateVideo()
seasonThumb = videodatabase.GetArtForItem(item->GetVideoInfoTag()->m_iIdSeason, "season", "thumb");
home->SetProperty("LatestEpisode." + value + ".Thumb" , item->GetArt("thumb"));
- home->SetProperty("LatestEpisode." + value + ".ShowThumb" , item->GetArt("tvshowthumb"));
+ home->SetProperty("LatestEpisode." + value + ".ShowThumb" , item->GetArt("tvshow.thumb"));
home->SetProperty("LatestEpisode." + value + ".SeasonThumb" , seasonThumb);
home->SetProperty("LatestEpisode." + value + ".Fanart" , item->GetArt("fanart"));
}
View
13 xbmc/utils/ScraperUrl.cpp
@@ -168,11 +168,12 @@ const CScraperUrl::SUrlEntry CScraperUrl::GetFirstThumb(const std::string &type)
return result;
}
-const CScraperUrl::SUrlEntry CScraperUrl::GetSeasonThumb(int season) const
+const CScraperUrl::SUrlEntry CScraperUrl::GetSeasonThumb(int season, const std::string &type) const
{
for (vector<SUrlEntry>::const_iterator iter=m_url.begin();iter != m_url.end();++iter)
{
- if (iter->m_type == URL_TYPE_SEASON && iter->m_season == season)
+ if (iter->m_type == URL_TYPE_SEASON && iter->m_season == season &&
+ (type.empty() || type == "thumb" || iter->m_aspect == type))
return *iter;
}
@@ -184,13 +185,15 @@ const CScraperUrl::SUrlEntry CScraperUrl::GetSeasonThumb(int season) const
return result;
}
-void CScraperUrl::GetSeasonThumbs(map<int, string> &thumbs) const
+unsigned int CScraperUrl::GetMaxSeasonThumb() const
{
+ unsigned int maxSeason = 0;
for (vector<SUrlEntry>::const_iterator iter=m_url.begin();iter != m_url.end();++iter)
{
- if (iter->m_type == URL_TYPE_SEASON && thumbs.find(iter->m_season) == thumbs.end())
- thumbs.insert(make_pair(iter->m_season, GetThumbURL(*iter)));
+ if (iter->m_type == URL_TYPE_SEASON && iter->m_season > 0 && (unsigned int)iter->m_season > maxSeason)
+ maxSeason = iter->m_season;
}
+ return maxSeason;
}
bool CScraperUrl::Get(const SUrlEntry& scrURL, std::string& strHTML, XFILE::CCurlFile& http, const CStdString& cacheContext)
View
4 xbmc/utils/ScraperUrl.h
@@ -60,8 +60,8 @@ class CScraperUrl
bool ParseEpisodeGuide(CStdString strUrls); // copies by intention
const SUrlEntry GetFirstThumb(const std::string &type = "") const;
- const SUrlEntry GetSeasonThumb(int season) const;
- void GetSeasonThumbs(std::map<int, std::string> &thumbs) const;
+ const SUrlEntry GetSeasonThumb(int season, const std::string &type = "") const;
+ unsigned int GetMaxSeasonThumb() const;
/*! \brief fetch the full URL (including referrer) of a thumb
\param URL entry to use to create the full URL
View
80 xbmc/video/VideoDatabase.cpp
@@ -2034,7 +2034,7 @@ int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, con
return -1;
}
-int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, string> &seasonArt, int idTvShow /*= -1 */)
+int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
{
try
{
@@ -2095,11 +2095,11 @@ int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoI
AddSeason(idTvShow, -1);
SetArtForItem(idTvShow, "tvshow", artwork);
- for (map<int, string>::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
+ for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
{
int idSeason = AddSeason(idTvShow, i->first);
if (idSeason > -1)
- SetArtForItem(idSeason, "season", "thumb", i->second);
+ SetArtForItem(idSeason, "season", i->second);
}
// and insert the new row
@@ -3629,7 +3629,7 @@ string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const
return GetSingleValue(query, m_pDS2);
}
-bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, string> &seasonArt)
+bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
{
try
{
@@ -3649,7 +3649,11 @@ bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, string> &seasonArt)
m_pDS2->close();
for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
- seasonArt.insert(make_pair(i->second,GetArtForItem(i->first, "season", "thumb")));
+ {
+ map<string, string> art;
+ GetArtForItem(i->first, "season", art);
+ seasonArt.insert(make_pair(i->second,art));
+ }
return true;
}
catch (...)
@@ -3872,6 +3876,17 @@ bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
return false;
}
+class CArtItem
+{
+public:
+ CArtItem() { art_id = 0; media_id = 0; };
+ int art_id;
+ string art_type;
+ string art_url;
+ int media_id;
+ string media_type;
+};
+
bool CVideoDatabase::UpdateOldVersion(int iVersion)
{
if (iVersion < 43)
@@ -4142,6 +4157,39 @@ bool CVideoDatabase::UpdateOldVersion(int iVersion)
m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
}
}
+ if (iVersion < 72)
+ { // Update thumb to poster or banner as applicable
+ CTextureDatabase db;
+ if (db.Open())
+ {
+ m_pDS->query("select art_id,url,media_id,media_type from art where type='thumb' and media_type in ('movie', 'musicvideo', 'tvshow', 'season', 'set')");
+ vector<CArtItem> art;
+ while (!m_pDS->eof())
+ {
+ CTextureDetails details;
+ if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
+ {
+ CArtItem item;
+ item.art_id = m_pDS->fv(0).get_asInt();
+ item.art_url = m_pDS->fv(1).get_asString();
+ item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
+ item.media_id = m_pDS->fv(2).get_asInt();
+ item.media_type = m_pDS->fv(3).get_asString();
+ if (item.art_type != "thumb")
+ art.push_back(item);
+ }
+ m_pDS->next();
+ }
+ m_pDS->close();
+ for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
+ {
+ if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
+ m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
+ else
+ m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
+ }
+ }
+ }
// always recreate the view after any table change
CreateViews();
return true;
@@ -8340,7 +8388,7 @@ void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = f
{
CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
- map<int, string> seasonArt;
+ map<int, map<string, string> > seasonArt;
GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
map<string, string> artwork;
@@ -8349,11 +8397,12 @@ void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = f
TiXmlElement additionalNode("art");
for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
- for (map<int, string>::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
+ for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
{
TiXmlElement seasonNode("season");
seasonNode.SetAttribute("num", i->first);
- XMLUtils::SetString(&seasonNode, "thumb", i->second);
+ for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
+ XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
additionalNode.InsertEndChild(seasonNode);
}
tvshow.Save(pMain, "tvshow", true, &additionalNode);
@@ -8426,18 +8475,21 @@ void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = f
ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
// export season thumbs
- for (map<int, string>::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
+ for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
{
- CStdString seasonThumb;
+ string seasonThumb;
if (i->first == -1)
seasonThumb = "season-all";
else if (i->first == 0)
seasonThumb = "season-specials";
else
- seasonThumb.Format("season%02i", i->first);
- CStdString savedThumb(item.GetLocalArt(seasonThumb, true));
- if (!i->second.empty())
- CTextureCache::Get().Export(i->second, savedThumb, overwrite);
+ seasonThumb = StringUtils::Format("season%02i", i->first);
+ for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
+ {
+ CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
+ if (!i->second.empty())
+ CTextureCache::Get().Export(j->second, savedThumb, overwrite);
+ }
}
}
View
6 xbmc/video/VideoDatabase.h
@@ -443,7 +443,7 @@ class CVideoDatabase : public CDatabase
void GetEpisodesByFile(const CStdString& strFilenameAndPath, std::vector<CVideoInfoTag>& episodes);
int SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idMovie = -1);
- int SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, const std::map<int, std::string> &seasonArt, int idTvShow = -1);
+ int SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, const std::map<int, std::map<std::string, std::string> > &seasonArt, int idTvShow = -1);
int SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idShow, int idEpisode=-1);
int SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idMVideo = -1);
void SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath);
@@ -679,7 +679,7 @@ class CVideoDatabase : public CDatabase
void SetArtForItem(int mediaId, const std::string &mediaType, const std::map<std::string, std::string> &art);
bool GetArtForItem(int mediaId, const std::string &mediaType, std::map<std::string, std::string> &art);
std::string GetArtForItem(int mediaId, const std::string &mediaType, const std::string &artType);
- bool GetTvShowSeasonArt(int mediaId, std::map<int, std::string> &seasonArt);
+ bool GetTvShowSeasonArt(int mediaId, std::map<int, std::map<std::string, std::string> > &seasonArt);
int AddTag(const std::string &tag);
void AddTagToItem(int idItem, int idTag, const std::string &type);
@@ -804,7 +804,7 @@ class CVideoDatabase : public CDatabase
*/
bool LookupByFolders(const CStdString &path, bool shows = false);
- virtual int GetMinVersion() const { return 71; };
+ virtual int GetMinVersion() const { return 72; };
virtual int GetExportVersion() const { return 1; };
const char *GetBaseDBName() const { return "MyVideos"; };
View
181 xbmc/video/VideoInfoScanner.cpp
@@ -1097,10 +1097,11 @@ namespace VIDEO
if (pItem->m_bIsFolder)
{
// get and cache season thumbs
- map<int, string> seasonArt;
- GetSeasonThumbs(movieDetails, seasonArt);
- for (map<int, string>::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
- CTextureCache::Get().BackgroundCacheImage(i->second);
+ map<int, map<string, string> > seasonArt;
+ GetSeasonThumbs(movieDetails, seasonArt, CVideoThumbLoader::GetArtTypes("season"), useLocal);
+ for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
+ for (map<string, string>::iterator j = i->second.begin(); j != i->second.end(); ++j)
+ CTextureCache::Get().BackgroundCacheImage(j->second);
lResult = m_database.SetDetailsForTvShow(pItem->GetPath(), movieDetails, art, seasonArt);
movieDetails.m_iDbId = lResult;
movieDetails.m_type = "tvshow";
@@ -1162,6 +1163,16 @@ namespace VIDEO
}
}
+ std::string CVideoInfoScanner::GetArtTypeFromSize(unsigned int width, unsigned int height)
+ {
+ std::string type = "thumb";
+ if (width*5 < height*4)
+ type = "poster";
+ else if (width*1 > height*4)
+ type = "banner";
+ return type;
+ }
+
void CVideoInfoScanner::GetArtwork(CFileItem *pItem, const CONTENT_TYPE &content, bool bApplyToDir, bool useLocal, const std::string &actorArtPath)
{
CVideoInfoTag &movieDetails = *pItem->GetVideoInfoTag();
@@ -1169,6 +1180,59 @@ namespace VIDEO
movieDetails.m_strPictureURL.Parse();
CGUIListItem::ArtMap art;
+
+ // get and cache thumb images
+ vector<string> artTypes = CVideoThumbLoader::GetArtTypes(ContentToMediaType(content, pItem->m_bIsFolder));
+ vector<string>::iterator i = find(artTypes.begin(), artTypes.end(), "fanart");
+ if (i != artTypes.end())
+ artTypes.erase(i); // fanart is handled below
+ bool lookForThumb = find(artTypes.begin(), artTypes.end(), "thumb") == artTypes.end();
+
+ // find local art
+ if (useLocal)
+ {
+ for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
+ {
+ std::string image = CVideoThumbLoader::GetLocalArt(*pItem, *i, bApplyToDir);
+ if (!image.empty())
+ art.insert(make_pair(*i, image));
+ }
+ // find and classify the local thumb (backcompat) if available
+ if (lookForThumb)
+ {
+ std::string image = CVideoThumbLoader::GetLocalArt(*pItem, "thumb", bApplyToDir);
+ if (!image.empty())
+ { // cache the image and determine sizing
+ CTextureDetails details;
+ if (CTextureCache::Get().CacheImage(image, details))
+ {
+ std::string type = GetArtTypeFromSize(details.width, details.height);
+ if (art.find(type) == art.end())
+ art.insert(make_pair(type, image));
+ }
+ }
+ }
+ }
+
+ // find online art
+ for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
+ {
+ if (art.find(*i) == art.end())
+ {
+ std::string image = GetImage(pItem, false, bApplyToDir, *i);
+ if (!image.empty())
+ art.insert(make_pair(*i, image));
+ }
+ }
+
+ // use the first piece of online art as the first art type if no thumb type is available yet
+ if (art.empty() && lookForThumb)
+ {
+ std::string image = GetImage(pItem, false, bApplyToDir, "thumb");
+ if (!image.empty())
+ art.insert(make_pair(artTypes.front(), image));
+ }
+
// get & save fanart image (treated separately due to it being stored in m_fanart)
bool isEpisode = (content == CONTENT_TVSHOWS && !pItem->m_bIsFolder);
if (!isEpisode)
@@ -1178,22 +1242,10 @@ namespace VIDEO
art.insert(make_pair("fanart", fanart));
}
- // get and cache thumb images
- vector<string> artTypes = CVideoThumbLoader::GetArtTypes(ContentToMediaType(content, pItem->m_bIsFolder));
- for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
- {
- string type = *i;
- if (type == "fanart")
- continue; // handled above
- std::string image = GetImage(pItem, useLocal, bApplyToDir, type);
- if (!image.empty())
- art.insert(make_pair(type, image));
- }
-
for (CGUIListItem::ArtMap::const_iterator i = art.begin(); i != art.end(); ++i)
CTextureCache::Get().BackgroundCacheImage(i->second);
- pItem->SetArt(art);
+ pItem->SetArt(art, false); // don't set fallbacks
// parent folder to apply the thumb to and to search for local actor thumbs
CStdString parentDir = GetParentDir(*pItem);
@@ -1619,36 +1671,87 @@ namespace VIDEO
return "";
}
- void CVideoInfoScanner::GetSeasonThumbs(const CVideoInfoTag &show, map<int, string> &art, bool useLocal)
+ void CVideoInfoScanner::GetSeasonThumbs(const CVideoInfoTag &show, map<int, map<string, string> > &seasonArt, const vector<string> &artTypes, bool useLocal)
{
- show.m_strPictureURL.GetSeasonThumbs(art);
- // override with any local thumbs
- if (useLocal)
+ bool lookForThumb = find(artTypes.begin(), artTypes.end(), "thumb") == artTypes.end();
+
+ // find the maximum number of seasons we have thumbs for (local + remote)
+ int maxSeasons = show.m_strPictureURL.GetMaxSeasonThumb();
+
+ CFileItemList items;
+ CDirectory::GetDirectory(show.m_strPath, items, ".png|.jpg|.tbn", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_NO_FILE_INFO);
+ CRegExp reg;
+ if (items.Size() && reg.RegComp("season-([0-9]+)(-[a-z]+)?\\.(tbn|jpg|png)"))
{
- CFileItemList items;
- CDirectory::GetDirectory(show.m_strPath, items, ".png|.jpg|.tbn", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_NO_FILE_INFO);
- // run through all these items and see which ones match
- CRegExp reg;
- if (items.Size() && reg.RegComp("season[ ._-]?([0-9]+)\\.(tbn|jpg|png)"))
+ for (int i = 0; i < items.Size(); i++)
{
- for (int i = 0; i < items.Size(); i++)
+ CStdString name = URIUtils::GetFileName(items[i]->GetPath());
+ if (reg.RegFind(name) > -1)
{
- CStdString name = URIUtils::GetFileName(items[i]->GetPath());
- URIUtils::RemoveExtension(name);
- if (name == "season-all")
- art[-1] = items[i]->GetPath();
- else if (name == "season-specials")
- art[0] = items[i]->GetPath();
- else if (reg.RegFind(name) > -1)
- {
- char* seasonStr = reg.GetReplaceString("\\1");
- int season = atoi(seasonStr);
- free(seasonStr);
+ char* seasonStr = reg.GetReplaceString("\\1");
+ int season = atoi(seasonStr);
+ free(seasonStr);
+ if (season > maxSeasons)
+ maxSeasons = season;
+ }
+ }
+ }
+ for (int season = -1; season <= maxSeasons; season++)
+ {
+ map<string, string> art;
+ if (useLocal)
+ {
+ string basePath;
+ if (season == -1)
+ basePath = "season-all";
+ else if (season == 0)
+ basePath = "season-specials";
+ else
+ basePath = StringUtils::Format("season%02i", season);
+ CFileItem artItem(URIUtils::AddFileToFolder(show.m_strPath, basePath), false);
- art[season] = items[i]->GetPath();
+ for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
+ {
+ std::string image = CVideoThumbLoader::GetLocalArt(artItem, *i, false);
+ if (!image.empty())
+ art.insert(make_pair(*i, image));
+ }
+ // find and classify the local thumb (backcompat) if available
+ if (lookForThumb)
+ {
+ std::string image = CVideoThumbLoader::GetLocalArt(artItem, "thumb", false);
+ if (!image.empty())
+ { // cache the image and determine sizing
+ CTextureDetails details;
+ if (CTextureCache::Get().CacheImage(image, details))
+ {
+ std::string type = GetArtTypeFromSize(details.width, details.height);
+ if (art.find(type) == art.end())
+ art.insert(make_pair(type, image));
+ }
}
}
}
+
+ // find online art
+ for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
+ {
+ if (art.find(*i) == art.end())
+ {
+ string image = CScraperUrl::GetThumbURL(show.m_strPictureURL.GetSeasonThumb(season, *i));
+ if (!image.empty())
+ art.insert(make_pair(*i, image));
+ }
+ }
+ // use the first piece of online art as the first art type if no thumb type is available yet
+ if (art.empty() && lookForThumb)
+ {
+ string image = CScraperUrl::GetThumbURL(show.m_strPictureURL.GetSeasonThumb(season, "thumb"));
+ if (!image.empty())
+ art.insert(make_pair(artTypes.front(), image));
+ }
+
+ seasonArt.insert(make_pair(season, art));
}
}
View
10 xbmc/video/VideoInfoScanner.h
@@ -102,13 +102,21 @@ namespace VIDEO
*/
void GetArtwork(CFileItem *pItem, const CONTENT_TYPE &content, bool bApplyToDir=false, bool useLocal=true, const std::string &actorArtPath = "");
+ /*! \brief Retrieve the art type for an image from the given size.
+ \param width the width of the image.
+ \param height the height of the image.
+ \return "poster" if the aspect ratio is at most 4:5, "banner" if the aspect ratio
+ is at least 1:4, "thumb" otherwise.
+ */
+ static std::string GetArtTypeFromSize(unsigned int width, unsigned int height);
+
/*! \brief Get season thumbs for a tvshow.
All seasons (regardless of whether the user has episodes) are added to the art map.
\param show tvshow info tag
\param art artwork map to which season thumbs are added.
\param useLocal whether to use local thumbs, defaults to true
*/
- static void GetSeasonThumbs(const CVideoInfoTag &show, std::map<int, std::string> &art, bool useLocal = true);
+ static void GetSeasonThumbs(const CVideoInfoTag &show, std::map<int, std::map<std::string, std::string> > &art, const std::vector<std::string> &artTypes, bool useLocal = true);
static std::string GetImage(CFileItem *pItem, bool useLocal, bool bApplyToDir, const std::string &type = "");
static std::string GetFanart(CFileItem *pItem, bool useLocal);
View
32 xbmc/video/VideoThumbLoader.cpp
@@ -165,14 +165,26 @@ static void SetupRarOptions(CFileItem& item, const CStdString& path)
vector<string> CVideoThumbLoader::GetArtTypes(const string &type)
{
vector<string> ret;
- if (type != "episode")
+ if (type == "episode")
+ ret.push_back("thumb");
+ else if (type == "tvshow" || type == "season")
{
+ ret.push_back("banner");
+ ret.push_back("poster");
ret.push_back("fanart");
+ }
+ else if (type == "movie" || type == "musicvideo" || type == "set")
+ {
ret.push_back("poster");
+ ret.push_back("fanart");
}
- if (type == "tvshow" || type == "season" || type.empty())
+ else if (type.empty()) // unknown - just throw everything in
+ {
+ ret.push_back("poster");
ret.push_back("banner");
- ret.push_back("thumb");
+ ret.push_back("thumb");
+ ret.push_back("fanart");
+ }
return ret;
}
@@ -218,6 +230,8 @@ bool CVideoThumbLoader::LoadItem(CFileItem* pItem)
if (artwork.empty())
{
vector<string> artTypes = GetArtTypes(pItem->HasVideoInfoTag() ? pItem->GetVideoInfoTag()->m_type : "");
+ if (find(artTypes.begin(), artTypes.end(), "thumb") == artTypes.end())
+ artTypes.push_back("thumb"); // always look for "thumb" art for files
for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
{
std::string type = *i;
@@ -320,11 +334,13 @@ bool CVideoThumbLoader::FillLibraryArt(CFileItem &item)
map<string, string> showArt, cacheArt;
if (m_database->GetArtForItem(tag.m_iIdShow, "tvshow", showArt))
{
- map<string, string>::iterator i = showArt.find("fanart");
- if (i != showArt.end())
- cacheArt.insert(make_pair("fanart", i->second));
- if ((i = showArt.find("thumb")) != showArt.end())
- cacheArt.insert(make_pair("tvshowthumb", i->second));
+ for (CGUIListItem::ArtMap::iterator i = showArt.begin(); i != showArt.end(); ++i)
+ {
+ if (i->first == "fanart")
+ cacheArt.insert(*i);
+ else
+ cacheArt.insert(make_pair("tvshow." + i->first, i->second));
+ }
item.AppendArt(cacheArt);
}
m_showArt.insert(make_pair(tag.m_iIdShow, cacheArt));
View
63 xbmc/video/dialogs/GUIDialogVideoInfo.cpp
@@ -315,9 +315,12 @@ void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
if (seasonID < 0)
seasonID = db.GetSeasonId(m_movieItem->GetVideoInfoTag()->m_iIdShow,
m_movieItem->GetVideoInfoTag()->m_iSeason);
- string thumb = db.GetArtForItem(seasonID, "season", "thumb");
- if (!thumb.empty())
- m_movieItem->SetArt("seasonthumb", thumb);
+ CGUIListItem::ArtMap thumbs;
+ if (db.GetArtForItem(seasonID, "season", thumbs))
+ {
+ for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++)
+ m_movieItem->SetArt("season." + i->first, i->second);
+ }
}
db.Close();
}
@@ -592,39 +595,51 @@ void CGUIDialogVideoInfo::Play(bool resume)
}
}
-// Get Thumb from user choice.
-// Options are:
-// 1. Current thumb
-// 2. IMDb thumb
-// 3. Local thumb
-// 4. No thumb (if no Local thumb is available)
-void CGUIDialogVideoInfo::OnGetArt()
+string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem, map<string, string> &currentArt)
{
// prompt for choice
CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
- if (!dialog)
- return;
+ if (!dialog || !videoItem.HasVideoInfoTag())
+ return "";
CFileItemList items;
dialog->SetHeading(13511);
dialog->Reset();
dialog->SetUseDetails(true);
- vector<string> artTypes = CVideoThumbLoader::GetArtTypes(m_movieItem->GetVideoInfoTag()->m_type);
+ CVideoDatabase db;
+ db.Open();
+
+ vector<string> artTypes = CVideoThumbLoader::GetArtTypes(videoItem.GetVideoInfoTag()->m_type);
+
+ // add in any stored art for this item that is non-empty.
+ db.GetArtForItem(videoItem.GetVideoInfoTag()->m_iDbId, videoItem.GetVideoInfoTag()->m_type, currentArt);
+ for (CGUIListItem::ArtMap::iterator i = currentArt.begin(); i != currentArt.end(); ++i)
+ {
+ if (!i->second.empty() && find(artTypes.begin(), artTypes.end(), i->first) == artTypes.end())
+ artTypes.push_back(i->first);
+ }
+
for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
{
string type = *i;
CFileItemPtr item(new CFileItem(type, "false"));
item->SetLabel(type);
- if (m_movieItem->HasArt(type))
- item->SetArt("thumb", m_movieItem->GetArt(type));
+ if (videoItem.HasArt(type))
+ item->SetArt("thumb", videoItem.GetArt(type));
items.Add(item);
}
dialog->SetItems(&items);
dialog->DoModal();
- string type = dialog->GetSelectedItem()->GetLabel();
+ return dialog->GetSelectedItem()->GetLabel();
+}
+
+void CGUIDialogVideoInfo::OnGetArt()
+{
+ map<string, string> currentArt;
+ string type = ChooseArtType(*m_movieItem, currentArt);
if (type.empty())
return; // cancelled
@@ -634,7 +649,8 @@ void CGUIDialogVideoInfo::OnGetArt()
return;
}
- items.Clear();
+ CFileItemList items;
+
// Current thumb
if (m_movieItem->HasArt(type))
{
@@ -643,10 +659,18 @@ void CGUIDialogVideoInfo::OnGetArt()
item->SetLabel(g_localizeStrings.Get(13512));
items.Add(item);
}
+ else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
+ { // add the 'thumb' type in
+ CFileItemPtr item(new CFileItem("thumb://Thumb", false));
+ item->SetArt("thumb", currentArt["thumb"]);
+ item->SetLabel(g_localizeStrings.Get(13512));
+ items.Add(item);
+ }
// Grab the thumbnails from the web
vector<CStdString> thumbs;
- m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type);
+ int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
+ m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
for (unsigned int i = 0; i < thumbs.size(); ++i)
{
@@ -695,6 +719,8 @@ void CGUIDialogVideoInfo::OnGetArt()
int number = atoi(result.Mid(14));
newThumb = thumbs[number];
}
+ else if (result == "thumb://Thumb")
+ newThumb = currentArt["thumb"];
else if (result == "thumb://Local")
newThumb = localThumb;
else if (CFile::Exists(result))
@@ -709,7 +735,6 @@ void CGUIDialogVideoInfo::OnGetArt()
db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
db.Close();
}
-
CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
m_movieItem->SetArt(type, newThumb);
if (m_movieItem->HasProperty("set_folder_thumb"))
View
2 xbmc/video/dialogs/GUIDialogVideoInfo.h
@@ -40,6 +40,8 @@ class CGUIDialogVideoInfo :
virtual CFileItemPtr GetCurrentListItem(int offset = 0) { return m_movieItem; }
const CFileItemList& CurrentDirectory() const { return *m_castList; };
virtual bool HasListItems() const { return true; };
+
+ static std::string ChooseArtType(const CFileItem &item, std::map<std::string, std::string> &currentArt);
protected:
void Update();
void SetLabel(int iControl, const CStdString& strLabel);
View
199 xbmc/video/windows/GUIWindowVideoNav.cpp
@@ -52,6 +52,7 @@
#include "TextureCache.h"
#include "guilib/GUIKeyboardFactory.h"
#include "video/VideoInfoScanner.h"
+#include "video/dialogs/GUIDialogVideoInfo.h"
using namespace XFILE;
using namespace VIDEODATABASEDIRECTORY;
@@ -292,12 +293,20 @@ bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemL
map<string, string> art;
if (m_database.GetArtForItem(details.m_iDbId, details.m_type, art))
{
- if (art.find("thumb") != art.end())
- items.SetArt("tvshowthumb", art["thumb"]);
- if (art.find("fanart") != art.end())
- items.SetArt("fanart", art["fanart"]);
+ for (CGUIListItem::ArtMap::iterator i = art.begin(); i != art.end(); ++i)
+ {
+ if (i->first == "fanart")
+ items.SetArt(i->first, i->second);
+ else
+ items.SetArt("tvshow." + i->first, i->second);
+ }
+ if (node == NODE_TYPE_SEASONS)
+ {
+ CFileItem showItem;
+ showItem.SetArt(art);
+ items.SetArt("thumb", showItem.GetArt("thumb"));
+ }
}
- CFileItem showItem(details.m_strShowPath, true);
// Grab fanart data
items.SetProperty("fanart_color1", details.m_fanart.GetColor(0));
@@ -313,18 +322,18 @@ bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemL
items.SetContent("episodes");
// grab the season thumb as the folder thumb
int seasonID = m_database.GetSeasonId(details.m_iDbId, params.GetSeason());
- string seasonThumb = m_database.GetArtForItem(seasonID, "season", "thumb");
- if (!seasonThumb.empty())
+ CGUIListItem::ArtMap seasonArt;
+ if (m_database.GetArtForItem(seasonID, "season", seasonArt))
{
- items.SetArt("seasonthumb",seasonThumb);
- items.SetArt("thumb", seasonThumb);
+ for (CGUIListItem::ArtMap::iterator i = art.begin(); i != art.end(); ++i)
+ items.SetArt("season." + i->first, i->second);
+ CFileItem seasonItem;
+ seasonItem.SetArt(seasonArt);
+ items.SetArt("thumb", seasonItem.GetArt("thumb"));
}
}
else
- {
items.SetContent("seasons");
- items.SetArt("thumb", showItem.GetArt("thumb"));
- }
}
else if (node == NODE_TYPE_TITLE_MOVIES ||
node == NODE_TYPE_RECENTLY_ADDED_MOVIES)
@@ -978,13 +987,12 @@ void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &butt
}
if (node == NODE_TYPE_SEASONS && item->m_bIsFolder)
- buttons.Add(CONTEXT_BUTTON_SET_SEASON_THUMB, 20371);
+ buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
if (item->GetPath().Left(14).Equals("videodb://1/7/") && item->GetPath().size() > 14 && item->m_bIsFolder) // sets
{
buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
- buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_THUMB, 20435);
- buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_FANART, 20456);
+ buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
buttons.Add(CONTEXT_BUTTON_DELETE, 646);
}
@@ -1094,10 +1102,10 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
Refresh();
return true;
- case CONTEXT_BUTTON_SET_SEASON_THUMB:
+ case CONTEXT_BUTTON_SET_SEASON_ART:
case CONTEXT_BUTTON_SET_ACTOR_THUMB:
case CONTEXT_BUTTON_SET_ARTIST_THUMB:
- case CONTEXT_BUTTON_SET_MOVIESET_THUMB:
+ case CONTEXT_BUTTON_SET_MOVIESET_ART:
{
// Grab the thumbnails from the web
CFileItemList items;
@@ -1105,6 +1113,7 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
CStdString currentThumb;
int idArtist = -1;
CStdString artistPath;
+ string artType = "thumb";
if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
{
CMusicDatabase database;
@@ -1112,31 +1121,51 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
idArtist = database.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel());
database.GetArtistPath(idArtist, artistPath);
currentThumb = database.GetArtForItem(idArtist, "artist", "thumb");
+ if (currentThumb.empty())
+ currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
}
- if (currentThumb.empty())
- currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, "thumb");
- if (!currentThumb.IsEmpty() && CFile::Exists(currentThumb))
+ else if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
+ currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
+ else
+ { // SEASON, SET
+ map<string, string> currentArt;
+ artType = CGUIDialogVideoInfo::ChooseArtType(*m_vecItems->Get(itemNumber), currentArt);
+ if (artType.empty())
+ return false;
+
+ if (artType == "fanart")
+ {
+ OnChooseFanart(*m_vecItems->Get(itemNumber));
+ return true;
+ }
+
+ if (currentArt.find(artType) != currentArt.end())
+ currentThumb = currentArt[artType];
+ else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
+ currentThumb = currentArt["thumb"];
+ }
+ if (!currentThumb.IsEmpty())
{
CFileItemPtr item(new CFileItem("thumb://Current", false));
item->SetArt("thumb", currentThumb);
- item->SetLabel(g_localizeStrings.Get(20016));
+ item->SetLabel(g_localizeStrings.Get(13512));
items.Add(item);
}
noneitem->SetIconImage("DefaultFolder.png");
- noneitem->SetLabel(g_localizeStrings.Get(20018));
+ noneitem->SetLabel(g_localizeStrings.Get(13515));
vector<CStdString> thumbs;
if (button != CONTEXT_BUTTON_SET_ARTIST_THUMB)
{
CVideoInfoTag tag;
- if (button == CONTEXT_BUTTON_SET_SEASON_THUMB)
+ if (button == CONTEXT_BUTTON_SET_SEASON_ART)
m_database.GetTvShowInfo("",tag,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iIdShow);
else
tag = *m_vecItems->Get(itemNumber)->GetVideoInfoTag();
- if (button == CONTEXT_BUTTON_SET_SEASON_THUMB)
- tag.m_strPictureURL.GetThumbURLs(thumbs, "", m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason);
+ if (button == CONTEXT_BUTTON_SET_SEASON_ART)
+ tag.m_strPictureURL.GetThumbURLs(thumbs, artType, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason);
else
- tag.m_strPictureURL.GetThumbURLs(thumbs);
+ tag.m_strPictureURL.GetThumbURLs(thumbs, artType);
for (unsigned int i = 0; i < thumbs.size(); i++)
{
@@ -1145,7 +1174,7 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
CFileItemPtr item(new CFileItem(strItemPath, false));
item->SetArt("thumb", thumbs[i]);
item->SetIconImage("DefaultPicture.png");
- item->SetLabel(g_localizeStrings.Get(20015));
+ item->SetLabel(g_localizeStrings.Get(13513));
items.Add(item);
// TODO: Do we need to clear the cached image?
@@ -1161,7 +1190,7 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
if (XFILE::CFile::Exists(strThumb))
{
CFileItemPtr pItem(new CFileItem(strThumb,false));
- pItem->SetLabel(g_localizeStrings.Get(20017));
+ pItem->SetLabel(g_localizeStrings.Get(13514));
pItem->SetArt("thumb", strThumb);
items.Add(pItem);
local = true;
@@ -1178,7 +1207,7 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
if (XFILE::CFile::Exists(strThumb))
{
CFileItemPtr pItem(new CFileItem(strThumb,false));
- pItem->SetLabel(g_localizeStrings.Get(20017));
+ pItem->SetLabel(g_localizeStrings.Get(13514));
pItem->SetArt("thumb", strThumb);
items.Add(pItem);
local = true;
@@ -1187,7 +1216,7 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
noneitem->SetIconImage("DefaultActor.png");
}
- if (button == CONTEXT_BUTTON_SET_MOVIESET_THUMB)
+ if (button == CONTEXT_BUTTON_SET_MOVIESET_ART)
noneitem->SetIconImage("DefaultVideo.png");
if (!local)
@@ -1197,13 +1226,13 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
g_mediaManager.GetLocalDrives(sources);
CStdString result;
if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources,
- g_localizeStrings.Get(20019), result))
+ g_localizeStrings.Get(13511), result))
{
return false; // user cancelled
}
if (result == "thumb://Current")
- return false; // user chose the one they have
+ result = currentThumb; // user chose the one they have
// delete the thumbnail if that's what the user wants, else overwrite with the
// new thumbnail
@@ -1214,16 +1243,16 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
}
else if (result == "thumb://None")
result.clear();
- if (button == CONTEXT_BUTTON_SET_MOVIESET_THUMB ||
+ if (button == CONTEXT_BUTTON_SET_MOVIESET_ART ||
button == CONTEXT_BUTTON_SET_ACTOR_THUMB ||
- button == CONTEXT_BUTTON_SET_SEASON_THUMB ||
+ button == CONTEXT_BUTTON_SET_SEASON_ART ||
(button == CONTEXT_BUTTON_SET_ARTIST_THUMB && idArtist < 0))
- m_database.SetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, "thumb", result);
+ m_database.SetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType, result);
else
{
CMusicDatabase db;
if (db.Open())
- db.SetArtForItem(idArtist, "artist", "thumb", result);
+ db.SetArtForItem(idArtist, "artist", artType, result);
}
CUtil::DeleteVideoDatabaseDirectoryCache();
@@ -1233,50 +1262,6 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
return true;
}
- case CONTEXT_BUTTON_SET_MOVIESET_FANART:
- {
- CFileItemList items;
-
- CVideoThumbLoader loader;
- loader.LoadItem(item.get());
-
- if (item->HasArt("fanart"))
- {
- CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
- itemCurrent->SetArt("thumb", item->GetArt("fanart"));
- itemCurrent->SetLabel(g_localizeStrings.Get(20440));
- items.Add(itemCurrent);
- }
-
- // add the none option
- {
- CFileItemPtr itemNone(new CFileItem("fanart://None", false));
- itemNone->SetIconImage("DefaultVideo.png");
- itemNone->SetLabel(g_localizeStrings.Get(20439));
- items.Add(itemNone);
- }
-
- CStdString result;
- VECSOURCES sources(g_settings.m_videoSources);
- g_mediaManager.GetLocalDrives(sources);
- bool flip=false;
- if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
- return false;
-
- if (result.Equals("fanart://None") || !CFile::Exists(result))
- result.clear();
- if (!result.IsEmpty() && flip)
- result = CTextureCache::GetWrappedImageURL(result, "", "flipped");
-
- // update the db
- m_database.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, "fanart", result);
-
- // clear view cache and reload images
- CUtil::DeleteVideoDatabaseDirectoryCache();
-
- Refresh();
- return true;
- }
case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
{
CVideoDbUrl videoUrl;
@@ -1406,6 +1391,60 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
}
+void CGUIWindowVideoNav::OnChooseFanart(const CFileItem &videoItem)
+{
+ if (!videoItem.HasVideoInfoTag())
+ return;
+
+ CFileItem item(videoItem);
+
+ CFileItemList items;
+
+ CVideoThumbLoader loader;
+ loader.LoadItem(&item);
+
+ if (item.HasArt("fanart"))
+ {
+ CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
+ itemCurrent->SetArt("thumb", item.GetArt("fanart"));
+ itemCurrent->SetLabel(g_localizeStrings.Get(20440));
+ items.Add(itemCurrent);
+ }
+
+ // add the none option
+ {
+ CFileItemPtr itemNone(new CFileItem("fanart://None", false));
+ itemNone->SetIconImage("DefaultVideo.png");
+ itemNone->SetLabel(g_localizeStrings.Get(20439));
+ items.Add(itemNone);
+ }
+
+ CStdString result;
+ VECSOURCES sources(g_settings.m_videoSources);
+ g_mediaManager.GetLocalDrives(sources);
+ bool flip=false;
+ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
+ return;
+
+ if (result.Equals("fanart://None") || !CFile::Exists(result))
+ result.clear();
+ if (!result.IsEmpty() && flip)
+ result = CTextureCache::GetWrappedImageURL(result, "", "flipped");
+
+ // update the db
+ CVideoDatabase db;
+ if (db.Open())
+ {
+ db.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
+ db.Close();
+ }
+
+ // clear view cache and reload images
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+
+ Refresh();
+}
+
void CGUIWindowVideoNav::OnLinkMovieToTvShow(int itemnumber, bool bRemove)
{
CFileItemList list;
View
5 xbmc/video/windows/GUIWindowVideoNav.h
@@ -47,6 +47,11 @@ class CGUIWindowVideoNav : public CGUIWindowVideoBase
*/
void LoadVideoInfo(CFileItemList &items);
+ /*! \brief Pop up a fanart chooser. Does not utilise remote URLs.
+ \param videoItem the item to choose fanart for.
+ */
+ void OnChooseFanart(const CFileItem &videoItem);
+
bool ApplyWatchedFilter(CFileItemList &items);
virtual bool GetFilteredItems(const CStdString &filter, CFileItemList &items);
Something went wrong with that request. Please try again.