/*
* Copyright (C) 2012-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* .
*
*/
#include "EdenVideoArtUpdater.h"
#include "video/VideoDatabase.h"
#include "video/VideoInfoScanner.h"
#include "FileItem.h"
#include "utils/log.h"
#include "utils/Crc32.h"
#include "utils/URIUtils.h"
#include "utils/ScraperUrl.h"
#include "utils/StringUtils.h"
#include "TextureCache.h"
#include "TextureCacheJob.h"
#include "pictures/Picture.h"
#include "profiles/ProfilesManager.h"
#include "settings/AdvancedSettings.h"
#include "settings/Settings.h"
#include "guilib/Texture.h"
#include "guilib/GUIWindowManager.h"
#include "guilib/LocalizeStrings.h"
#include "filesystem/File.h"
#include "filesystem/StackDirectory.h"
#include "dialogs/GUIDialogExtendedProgressBar.h"
#include "interfaces/AnnouncementManager.h"
using namespace std;
using namespace VIDEO;
using namespace XFILE;
CEdenVideoArtUpdater::CEdenVideoArtUpdater() : CThread("VideoArtUpdater")
{
m_textureDB.Open();
}
CEdenVideoArtUpdater::~CEdenVideoArtUpdater()
{
m_textureDB.Close();
}
void CEdenVideoArtUpdater::Start()
{
CEdenVideoArtUpdater *updater = new CEdenVideoArtUpdater();
updater->Create(true); // autodelete
}
void CEdenVideoArtUpdater::Process()
{
// grab all movies...
CVideoDatabase db;
if (!db.Open())
return;
CFileItemList items;
CGUIDialogExtendedProgressBar* dialog =
(CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
CGUIDialogProgressBarHandle *handle = dialog->GetHandle(g_localizeStrings.Get(314));
handle->SetTitle(g_localizeStrings.Get(12349));
// movies
db.GetMoviesByWhere("videodb://movies/titles/", CDatabase::Filter(), items);
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr item = items[i];
handle->SetProgress(i, items.Size());
handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
string cachedThumb = GetCachedVideoThumb(*item);
string cachedFanart = GetCachedFanart(*item);
item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
item->GetVideoInfoTag()->m_fanart.Unpack();
item->GetVideoInfoTag()->m_strPictureURL.Parse();
map artwork;
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
|| (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
{
CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
std::string type;
if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
artwork.insert(make_pair(type, art));
art = CVideoInfoScanner::GetFanart(item.get(), true);
if (CacheTexture(art, cachedFanart, item->GetLabel()))
artwork.insert(make_pair("fanart", art));
if (artwork.empty())
artwork.insert(make_pair("thumb", ""));
db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
}
}
items.Clear();
// music videos
db.GetMusicVideosNav("videodb://musicvideos/titles/", items, false);
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr item = items[i];
handle->SetProgress(i, items.Size());
handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
string cachedThumb = GetCachedVideoThumb(*item);
string cachedFanart = GetCachedFanart(*item);
item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
item->GetVideoInfoTag()->m_fanart.Unpack();
item->GetVideoInfoTag()->m_strPictureURL.Parse();
map artwork;
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
|| (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
{
CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
std::string type;
if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
artwork.insert(make_pair(type, art));
art = CVideoInfoScanner::GetFanart(item.get(), true);
if (CacheTexture(art, cachedFanart, item->GetLabel()))
artwork.insert(make_pair("fanart", art));
if (artwork.empty())
artwork.insert(make_pair("thumb", ""));
db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
}
}
items.Clear();
// tvshows
// count the number of episodes
db.GetTvShowsNav("videodb://tvshows/titles/", items);
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr item = items[i];
handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
string cachedThumb = GetCachedVideoThumb(*item);
string cachedFanart = GetCachedFanart(*item);
item->SetPath(item->GetVideoInfoTag()->m_strPath);
item->GetVideoInfoTag()->m_fanart.Unpack();
item->GetVideoInfoTag()->m_strPictureURL.Parse();
map artwork;
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
|| (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
{
CStdString art = CVideoInfoScanner::GetImage(item.get(), true, false, "thumb");
std::string type;
if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
artwork.insert(make_pair(type, art));
art = CVideoInfoScanner::GetFanart(item.get(), true);
if (CacheTexture(art, cachedFanart, item->GetLabel()))
artwork.insert(make_pair("fanart", art));
if (artwork.empty())
artwork.insert(make_pair("thumb", ""));
db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
}
// now season art...
map > seasons;
vector artTypes; artTypes.push_back("thumb");
CVideoInfoScanner::GetSeasonThumbs(*item->GetVideoInfoTag(), seasons, artTypes, true);
for (map >::const_iterator j = seasons.begin(); j != seasons.end(); ++j)
{
if (j->second.empty())
continue;
int idSeason = db.AddSeason(item->GetVideoInfoTag()->m_iDbId, j->first);
map seasonArt;
if (idSeason > -1 && !db.GetArtForItem(idSeason, MediaTypeSeason, seasonArt))
{
std::string cachedSeason = GetCachedSeasonThumb(j->first, item->GetVideoInfoTag()->m_strPath);
std::string type;
std::string originalUrl = j->second.begin()->second;
if (CacheTexture(originalUrl, cachedSeason, "", type))
db.SetArtForItem(idSeason, MediaTypeSeason, type, originalUrl);
}
}
// now episodes...
CFileItemList items2;
db.GetEpisodesByWhere("videodb://tvshows/titles/-1/-1/", db.PrepareSQL("episodeview.idShow=%d", item->GetVideoInfoTag()->m_iDbId), items2);
for (int j = 0; j < items2.Size(); j++)
{
handle->SetProgress(j, items2.Size());
CFileItemPtr episode = items2[j];
string cachedThumb = GetCachedEpisodeThumb(*episode);
if (!CFile::Exists(cachedThumb))
cachedThumb = GetCachedVideoThumb(*episode);
episode->SetPath(episode->GetVideoInfoTag()->m_strFileNameAndPath);
episode->GetVideoInfoTag()->m_strPictureURL.Parse();
map artwork;
if (!db.GetArtForItem(episode->GetVideoInfoTag()->m_iDbId, episode->GetVideoInfoTag()->m_type, artwork)
|| (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
{
CStdString art = CVideoInfoScanner::GetImage(episode.get(), true, episode->GetVideoInfoTag()->m_basePath != episode->GetPath(), "thumb");
if (CacheTexture(art, cachedThumb, episode->GetLabel()))
artwork.insert(make_pair("thumb", art));
else
artwork.insert(make_pair("thumb", ""));
db.SetArtForItem(episode->GetVideoInfoTag()->m_iDbId, episode->GetVideoInfoTag()->m_type, artwork);
}
}
}
items.Clear();
// now sets
db.GetSetsNav("videodb://movies/sets/", items, VIDEODB_CONTENT_MOVIES);
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr item = items[i];
handle->SetProgress(i, items.Size());
handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
map artwork;
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
{ // grab the first movie from this set
CFileItemList items2;
db.GetMoviesNav("videodb://movies/titles/", items2, -1, -1, -1, -1, -1, -1, item->GetVideoInfoTag()->m_iDbId);
if (items2.Size() > 1)
{
if (db.GetArtForItem(items2[0]->GetVideoInfoTag()->m_iDbId, items2[0]->GetVideoInfoTag()->m_type, artwork))
db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
}
}
}
items.Clear();
// now actors
if (CSettings::Get().GetBool("videolibrary.actorthumbs"))
{
db.GetActorsNav("videodb://movies/titles/", items, VIDEODB_CONTENT_MOVIES);
db.GetActorsNav("videodb://tvshows/titles/", items, VIDEODB_CONTENT_TVSHOWS);
db.GetActorsNav("videodb://tvshows/titles/", items, VIDEODB_CONTENT_EPISODES);
db.GetActorsNav("videodb://musicvideos/titles/", items, VIDEODB_CONTENT_MUSICVIDEOS);
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr item = items[i];
handle->SetProgress(i, items.Size());
handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
map artwork;
if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
{
item->GetVideoInfoTag()->m_strPictureURL.Parse();
string cachedThumb = GetCachedActorThumb(*item);
string art = CScraperUrl::GetThumbURL(item->GetVideoInfoTag()->m_strPictureURL.GetFirstThumb());
if (CacheTexture(art, cachedThumb, item->GetLabel()))
artwork.insert(make_pair("thumb", art));
else
artwork.insert(make_pair("thumb", ""));
db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
}
}
}
handle->MarkFinished();
ANNOUNCEMENT::CAnnouncementManager::Get().Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnScanFinished");
items.Clear();
}
bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label)
{
std::string type;
return CacheTexture(originalUrl, cachedFile, label, type);
}
bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type)
{
if (!CFile::Exists(cachedFile))
{
CLog::Log(LOGERROR, "%s No cached art for item %s (should be %s)", __FUNCTION__, label.c_str(), cachedFile.c_str());
return false;
}
if (originalUrl.empty())
{
originalUrl = GetThumb(cachedFile, "http://unknown/video/", true);
CLog::Log(LOGERROR, "%s No original url for item %s, but cached art exists, using %s", __FUNCTION__, label.c_str(), originalUrl.c_str());
}
CTextureDetails details;
details.updateable = false;
details.hash = "NOHASH";
type = "thumb"; // unknown art type
CBaseTexture *texture = CTextureCacheJob::LoadImage(cachedFile, 0, 0, "");
if (texture)
{
if (texture->HasAlpha())
details.file = CTextureCache::GetCacheFile(originalUrl) + ".png";
else
details.file = CTextureCache::GetCacheFile(originalUrl) + ".jpg";
CLog::Log(LOGDEBUG, "Caching image '%s' ('%s') to '%s' for item '%s'", originalUrl.c_str(), cachedFile.c_str(), details.file.c_str(), label.c_str());
uint32_t width = 0, height = 0;
if (CPicture::CacheTexture(texture, width, height, CTextureCache::GetCachedPath(details.file)))
{
details.width = width;
details.height = height;
type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
delete texture;
m_textureDB.AddCachedTexture(originalUrl, details);
return true;
}
}
CLog::Log(LOGERROR, "Can't cache image '%s' ('%s') for item '%s'", originalUrl.c_str(), cachedFile.c_str(), label.c_str());
return false;
}
CStdString CEdenVideoArtUpdater::GetCachedActorThumb(const CFileItem &item)
{
return GetThumb("actor" + item.GetLabel(), CProfilesManager::Get().GetVideoThumbFolder(), true);
}
CStdString CEdenVideoArtUpdater::GetCachedSeasonThumb(int season, const CStdString &path)
{
CStdString label;
if (season == -1)
label = g_localizeStrings.Get(20366);
else if (season == 0)
label = g_localizeStrings.Get(20381);
else
label = StringUtils::Format(g_localizeStrings.Get(20358).c_str(), season);
return GetThumb("season" + path + label, CProfilesManager::Get().GetVideoThumbFolder(), true);
}
CStdString CEdenVideoArtUpdater::GetCachedEpisodeThumb(const CFileItem &item)
{
// get the locally cached thumb
CStdString strCRC = StringUtils::Format("%sepisode%i",
item.GetVideoInfoTag()->m_strFileNameAndPath.c_str(),
item.GetVideoInfoTag()->m_iEpisode);
return GetThumb(strCRC, CProfilesManager::Get().GetVideoThumbFolder(), true);
}
CStdString CEdenVideoArtUpdater::GetCachedVideoThumb(const CFileItem &item)
{
if (item.m_bIsFolder && !item.GetVideoInfoTag()->m_strPath.empty())
return GetThumb(item.GetVideoInfoTag()->m_strPath, CProfilesManager::Get().GetVideoThumbFolder(), true);
else if (!item.GetVideoInfoTag()->m_strFileNameAndPath.empty())
{
CStdString path = item.GetVideoInfoTag()->m_strFileNameAndPath;
if (URIUtils::IsStack(path))
path = CStackDirectory::GetFirstStackedFile(path);
return GetThumb(path, CProfilesManager::Get().GetVideoThumbFolder(), true);
}
return GetThumb(item.GetPath(), CProfilesManager::Get().GetVideoThumbFolder(), true);
}
CStdString CEdenVideoArtUpdater::GetCachedFanart(const CFileItem &item)
{
if (!item.GetVideoInfoTag()->m_artist.empty())
return GetThumb(StringUtils::Join(item.GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator), URIUtils::AddFileToFolder(CProfilesManager::Get().GetThumbnailsFolder(), "Music/Fanart/"), false);
CStdString path = item.GetVideoInfoTag()->GetPath();
if (path.empty())
return "";
return GetThumb(path, URIUtils::AddFileToFolder(CProfilesManager::Get().GetVideoThumbFolder(), "Fanart/"), false);
}
CStdString CEdenVideoArtUpdater::GetThumb(const CStdString &path, const CStdString &path2, bool split)
{
// get the locally cached thumb
Crc32 crc;
crc.ComputeFromLowerCase(path);
CStdString thumb;
if (split)
{
CStdString hex = StringUtils::Format("%08x", (__int32)crc);
thumb = StringUtils::Format("%c\\%08x.tbn", hex[0], (unsigned __int32)crc);
}
else
thumb = StringUtils::Format("%08x.tbn", (unsigned __int32)crc);
return URIUtils::AddFileToFolder(path2, thumb);
}