2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "VideoThumbLoader.h"
12 #include "GUIUserMessages.h"
13 #include "ServiceBroker.h"
14 #include "TextureCache.h"
16 #include "cores/VideoPlayer/DVDFileInfo.h"
17 #include "cores/VideoSettings.h"
18 #include "filesystem/Directory.h"
19 #include "filesystem/DirectoryCache.h"
20 #include "filesystem/StackDirectory.h"
21 #include "guilib/GUIComponent.h"
22 #include "guilib/GUIWindowManager.h"
23 #include "guilib/StereoscopicsManager.h"
24 #include "music/MusicDatabase.h"
25 #include "music/tags/MusicInfoTag.h"
26 #include "settings/AdvancedSettings.h"
27 #include "settings/SettingUtils.h"
28 #include "settings/Settings.h"
29 #include "settings/SettingsComponent.h"
30 #include "utils/EmbeddedArt.h"
31 #include "utils/StringUtils.h"
32 #include "utils/URIUtils.h"
33 #include "utils/log.h"
34 #include "video/VideoDatabase.h"
35 #include "video/VideoInfoTag.h"
36 #include "video/tags/VideoInfoTagLoaderFactory.h"
42 using namespace XFILE
;
43 using namespace VIDEO
;
45 CThumbExtractor::CThumbExtractor(const CFileItem
& item
,
46 const std::string
& listpath
,
48 const std::string
& target
,
50 bool fillStreamDetails
)
51 : m_target(target
), m_listpath(listpath
), m_item(item
)
55 m_fillStreamDetails
= fillStreamDetails
;
57 if (item
.IsVideoDb() && item
.HasVideoInfoTag())
58 m_item
.SetPath(item
.GetVideoInfoTag()->m_strFileNameAndPath
);
61 m_item
.SetPath(CStackDirectory::GetFirstStackedFile(m_item
.GetPath()));
64 CThumbExtractor::~CThumbExtractor() = default;
66 bool CThumbExtractor::operator==(const CJob
* job
) const
68 if (strcmp(job
->GetType(),GetType()) == 0)
70 const CThumbExtractor
* jobExtract
= dynamic_cast<const CThumbExtractor
*>(job
);
71 if (jobExtract
&& jobExtract
->m_listpath
== m_listpath
72 && jobExtract
->m_target
== m_target
)
78 bool CThumbExtractor::DoWork()
81 // Due to a pvr addon api design flaw (no support for multiple concurrent streams
82 // per addon instance), pvr recording thumbnail extraction does not work (reliably).
83 || URIUtils::IsPVRRecording(m_item
.GetDynPath())
84 || URIUtils::IsUPnP(m_item
.GetPath())
85 || URIUtils::IsBluray(m_item
.GetPath())
86 || URIUtils::IsPlugin(m_item
.GetDynPath()) // plugin path not fully resolved
89 || m_item
.IsDiscImage()
90 || m_item
.IsDVDFile(false, true)
91 || m_item
.IsInternetStream()
92 || m_item
.IsDiscStub()
93 || m_item
.IsPlayList())
96 // For HTTP/FTP we only allow extraction when on a LAN
97 if (URIUtils::IsRemote(m_item
.GetPath()) &&
98 !URIUtils::IsOnLAN(m_item
.GetPath()) &&
99 (URIUtils::IsFTP(m_item
.GetPath()) ||
100 URIUtils::IsHTTP(m_item
.GetPath())))
106 CLog::Log(LOGDEBUG
, "{} - trying to extract thumb from video file {}", __FUNCTION__
,
107 CURL::GetRedacted(m_item
.GetPath()));
108 // construct the thumb cache file
109 CTextureDetails details
;
110 details
.file
= CTextureCache::GetCacheFile(m_target
) + ".jpg";
111 result
= CDVDFileInfo::ExtractThumb(m_item
, details
, m_fillStreamDetails
? &m_item
.GetVideoInfoTag()->m_streamDetails
: nullptr, m_pos
);
114 CServiceBroker::GetTextureCache()->AddCachedTexture(m_target
, details
);
115 m_item
.SetProperty("HasAutoThumb", true);
116 m_item
.SetProperty("AutoThumbImage", m_target
);
117 m_item
.SetArt("thumb", m_target
);
119 CVideoInfoTag
* info
= m_item
.GetVideoInfoTag();
120 if (info
->m_iDbId
> 0 && !info
->m_type
.empty())
125 db
.SetArtForItem(info
->m_iDbId
, info
->m_type
, "thumb", m_item
.GetArt("thumb"));
131 else if (!m_item
.IsPlugin() &&
132 (!m_item
.HasVideoInfoTag() ||
133 !m_item
.GetVideoInfoTag()->HasStreamDetails()))
135 // No tag or no details set, so extract them
136 CLog::Log(LOGDEBUG
, "{} - trying to extract filestream details from video file {}",
137 __FUNCTION__
, CURL::GetRedacted(m_item
.GetPath()));
138 result
= CDVDFileInfo::GetFileStreamDetails(&m_item
);
143 CVideoInfoTag
* info
= m_item
.GetVideoInfoTag();
147 if (URIUtils::IsStack(m_listpath
))
149 // Don't know the total time of the stack, so set duration to zero to avoid confusion
150 info
->m_streamDetails
.SetVideoDuration(0, 0);
152 // Restore original stack path
153 m_item
.SetPath(m_listpath
);
156 db
.BeginTransaction();
158 if (info
->m_iFileId
< 0)
159 db
.SetStreamDetailsForFile(info
->m_streamDetails
, !info
->m_strFileNameAndPath
.empty() ? info
->m_strFileNameAndPath
: m_item
.GetPath());
161 db
.SetStreamDetailsForFileId(info
->m_streamDetails
, info
->m_iFileId
);
163 // overwrite the runtime value if the one from streamdetails is available
164 if (info
->m_iDbId
> 0
165 && info
->GetStaticDuration() != info
->GetDuration())
167 info
->SetDuration(info
->GetDuration());
169 // store the updated information in the database
170 db
.SetDetailsForItem(info
->m_iDbId
, info
->m_type
, *info
, m_item
.GetArt());
173 db
.CommitTransaction();
182 CVideoThumbLoader::CVideoThumbLoader() :
183 CThumbLoader(), CJobQueue(true, 1, CJob::PRIORITY_LOW_PAUSABLE
)
185 m_videoDatabase
= new CVideoDatabase();
188 CVideoThumbLoader::~CVideoThumbLoader()
191 delete m_videoDatabase
;
194 void CVideoThumbLoader::OnLoaderStart()
196 m_videoDatabase
->Open();
198 CThumbLoader::OnLoaderStart();
201 void CVideoThumbLoader::OnLoaderFinish()
203 m_videoDatabase
->Close();
205 CThumbLoader::OnLoaderFinish();
208 static void SetupRarOptions(CFileItem
& item
, const std::string
& path
)
210 std::string
path2(path
);
211 if (item
.IsVideoDb() && item
.HasVideoInfoTag())
212 path2
= item
.GetVideoInfoTag()->m_strFileNameAndPath
;
214 std::string opts
= url
.GetOptions();
215 if (opts
.find("flags") != std::string::npos
)
221 url
.SetOptions(opts
);
222 if (item
.IsVideoDb() && item
.HasVideoInfoTag())
223 item
.GetVideoInfoTag()->m_strFileNameAndPath
= url
.Get();
225 item
.SetPath(url
.Get());
226 g_directoryCache
.ClearDirectory(url
.GetWithoutFilename());
231 std::vector
<std::string
> GetSettingListAsString(const std::string
& settingID
)
233 std::vector
<CVariant
> values
=
234 CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(settingID
);
235 std::vector
<std::string
> result
;
236 std::transform(values
.begin(), values
.end(), std::back_inserter(result
),
237 [](const CVariant
& s
) { return s
.asString(); });
241 const std::map
<std::string
, std::vector
<std::string
>> artTypeDefaults
= {
242 {MediaTypeEpisode
, {"thumb"}},
243 {MediaTypeTvShow
, {"poster", "fanart", "banner"}},
244 {MediaTypeSeason
, {"poster", "fanart", "banner"}},
245 {MediaTypeMovie
, {"poster", "fanart"}},
246 {MediaTypeVideoCollection
, {"poster", "fanart"}},
247 {MediaTypeMusicVideo
, {"poster", "fanart"}},
248 {MediaTypeNone
, { "poster", "fanart", "banner", "thumb" }},
251 const std::vector
<std::string
> artTypeDefaultsFallback
= {};
253 const std::vector
<std::string
>& GetArtTypeDefault(const std::string
& mediaType
)
255 auto defaults
= artTypeDefaults
.find(mediaType
);
256 if (defaults
!= artTypeDefaults
.end())
257 return defaults
->second
;
258 return artTypeDefaultsFallback
;
261 const std::map
<std::string
, std::string
> artTypeSettings
= {
262 {MediaTypeEpisode
, CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST
},
263 {MediaTypeTvShow
, CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST
},
264 {MediaTypeSeason
, CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST
},
265 {MediaTypeMovie
, CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST
},
266 {MediaTypeVideoCollection
, CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST
},
267 {MediaTypeMusicVideo
, CSettings::SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST
},
271 std::vector
<std::string
> CVideoThumbLoader::GetArtTypes(const std::string
&type
)
273 int artworkLevel
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
274 CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL
);
275 if (artworkLevel
== CSettings::VIDEOLIBRARY_ARTWORK_LEVEL_NONE
)
280 std::vector
<std::string
> result
= GetArtTypeDefault(type
);
281 if (artworkLevel
!= CSettings::VIDEOLIBRARY_ARTWORK_LEVEL_CUSTOM
)
286 auto settings
= artTypeSettings
.find(type
);
287 if (settings
== artTypeSettings
.end())
290 for (auto& artType
: GetSettingListAsString(settings
->second
))
292 if (find(result
.begin(), result
.end(), artType
) == result
.end())
293 result
.push_back(artType
);
299 bool CVideoThumbLoader::IsValidArtType(const std::string
& potentialArtType
)
301 return !potentialArtType
.empty() && potentialArtType
.length() <= 25 &&
303 potentialArtType
.begin(), potentialArtType
.end(),
304 StringUtils::isasciialphanum
305 ) == potentialArtType
.end();
308 bool CVideoThumbLoader::IsArtTypeInWhitelist(const std::string
& artType
, const std::vector
<std::string
>& whitelist
, bool exact
)
310 // whitelist contains art "families", 'fanart' also matches 'fanart1', 'fanart2', and so on
311 std::string compareArtType
= artType
;
313 StringUtils::TrimRight(compareArtType
, "0123456789");
315 return std::find(whitelist
.begin(), whitelist
.end(), compareArtType
) != whitelist
.end();
319 * Look for a thumbnail for pItem. If one does not exist, look for an autogenerated
320 * thumbnail. If that does not exist, attempt to autogenerate one. Finally, check
321 * for the existence of fanart and set properties accordingly.
322 * @return: true if pItem has been modified
324 bool CVideoThumbLoader::LoadItem(CFileItem
* pItem
)
326 bool result
= LoadItemCached(pItem
);
327 result
|= LoadItemLookup(pItem
);
332 bool CVideoThumbLoader::LoadItemCached(CFileItem
* pItem
)
334 if (pItem
->m_bIsShareOrDrive
335 || pItem
->IsParentFolder())
338 m_videoDatabase
->Open();
340 if (!pItem
->HasVideoInfoTag() || !pItem
->GetVideoInfoTag()->HasStreamDetails()) // no stream details
342 if ((pItem
->HasVideoInfoTag() && pItem
->GetVideoInfoTag()->m_iFileId
>= 0) // file (or maybe folder) is in the database
343 || (!pItem
->m_bIsFolder
&& pItem
->IsVideo())) // Some other video file for which we haven't yet got any database details
345 if (m_videoDatabase
->GetStreamDetails(*pItem
))
350 // video db items normally have info in the database
351 if (pItem
->HasVideoInfoTag() && !pItem
->GetProperty("libraryartfilled").asBoolean())
353 FillLibraryArt(*pItem
);
355 if (!pItem
->GetVideoInfoTag()->m_type
.empty() &&
356 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeMovie
&&
357 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeTvShow
&&
358 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeEpisode
&&
359 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeMusicVideo
)
361 m_videoDatabase
->Close();
362 return true; // nothing else to be done
366 // if we have no art, look for it all
367 std::map
<std::string
, std::string
> artwork
= pItem
->GetArt();
370 std::vector
<std::string
> artTypes
= GetArtTypes(pItem
->HasVideoInfoTag() ? pItem
->GetVideoInfoTag()->m_type
: "");
371 if (find(artTypes
.begin(), artTypes
.end(), "thumb") == artTypes
.end())
372 artTypes
.emplace_back("thumb"); // always look for "thumb" art for files
373 for (std::vector
<std::string
>::const_iterator i
= artTypes
.begin(); i
!= artTypes
.end(); ++i
)
375 std::string type
= *i
;
376 std::string art
= GetCachedImage(*pItem
, type
);
378 artwork
.insert(std::make_pair(type
, art
));
380 pItem
->AppendArt(artwork
);
383 m_videoDatabase
->Close();
388 bool CVideoThumbLoader::LoadItemLookup(CFileItem
* pItem
)
390 if (pItem
->m_bIsShareOrDrive
|| pItem
->IsParentFolder() || pItem
->GetPath() == "add")
393 if (pItem
->HasVideoInfoTag() &&
394 !pItem
->GetVideoInfoTag()->m_type
.empty() &&
395 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeMovie
&&
396 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeTvShow
&&
397 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeEpisode
&&
398 pItem
->GetVideoInfoTag()->m_type
!= MediaTypeMusicVideo
)
399 return false; // Nothing to do here
401 DetectAndAddMissingItemData(*pItem
);
403 m_videoDatabase
->Open();
405 std::map
<std::string
, std::string
> artwork
= pItem
->GetArt();
406 std::vector
<std::string
> artTypes
= GetArtTypes(pItem
->HasVideoInfoTag() ? pItem
->GetVideoInfoTag()->m_type
: "");
407 if (find(artTypes
.begin(), artTypes
.end(), "thumb") == artTypes
.end())
408 artTypes
.emplace_back("thumb"); // always look for "thumb" art for files
409 for (std::vector
<std::string
>::const_iterator i
= artTypes
.begin(); i
!= artTypes
.end(); ++i
)
411 std::string type
= *i
;
412 if (!pItem
->HasArt(type
))
414 std::string art
= GetLocalArt(*pItem
, type
, type
=="fanart");
415 if (!art
.empty()) // cache it
417 SetCachedImage(*pItem
, type
, art
);
418 CServiceBroker::GetTextureCache()->BackgroundCacheImage(art
);
419 artwork
.insert(std::make_pair(type
, art
));
423 // If nothing was found, try embedded art
424 if (pItem
->HasVideoInfoTag() && !pItem
->GetVideoInfoTag()->m_coverArt
.empty())
426 for (auto& it
: pItem
->GetVideoInfoTag()->m_coverArt
)
428 if (it
.m_type
== type
)
430 art
= CTextureUtils::GetWrappedImageURL(pItem
->GetPath(), "video_" + type
);
431 artwork
.insert(std::make_pair(type
, art
));
438 pItem
->AppendArt(artwork
);
440 // We can only extract flags/thumbs for file-like items
441 if (!pItem
->m_bIsFolder
&& pItem
->IsVideo())
443 // An auto-generated thumb may have been cached on a different device - check we have it here
444 std::string url
= pItem
->GetArt("thumb");
445 if (StringUtils::StartsWith(url
, "image://video@") &&
446 !CServiceBroker::GetTextureCache()->HasCachedImage(url
))
447 pItem
->SetArt("thumb", "");
449 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
450 if (!pItem
->HasArt("thumb"))
452 // create unique thumb for auto generated thumbs
453 std::string thumbURL
= GetEmbeddedThumbURL(*pItem
);
454 if (CServiceBroker::GetTextureCache()->HasCachedImage(thumbURL
))
456 CServiceBroker::GetTextureCache()->BackgroundCacheImage(thumbURL
);
457 pItem
->SetProperty("HasAutoThumb", true);
458 pItem
->SetProperty("AutoThumbImage", thumbURL
);
459 pItem
->SetArt("thumb", thumbURL
);
461 if (pItem
->HasVideoInfoTag())
463 // Item has cached autogen image but no art entry. Save it to db.
464 CVideoInfoTag
* info
= pItem
->GetVideoInfoTag();
465 if (info
->m_iDbId
> 0 && !info
->m_type
.empty())
466 m_videoDatabase
->SetArtForItem(info
->m_iDbId
, info
->m_type
, "thumb", thumbURL
);
469 else if (settings
->GetBool(CSettings::SETTING_MYVIDEOS_EXTRACTTHUMB
) &&
470 settings
->GetBool(CSettings::SETTING_MYVIDEOS_EXTRACTFLAGS
) &&
471 settings
->GetInt(CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL
) !=
472 CSettings::VIDEOLIBRARY_ARTWORK_LEVEL_NONE
)
474 CFileItem
item(*pItem
);
475 std::string
path(item
.GetPath());
476 if (URIUtils::IsInRAR(item
.GetPath()))
477 SetupRarOptions(item
,path
);
479 CThumbExtractor
* extract
= new CThumbExtractor(item
, path
, true, thumbURL
);
482 m_videoDatabase
->Close();
488 if (settings
->GetBool(CSettings::SETTING_MYVIDEOS_EXTRACTFLAGS
) &&
489 (!pItem
->HasVideoInfoTag() ||
490 !pItem
->GetVideoInfoTag()->HasStreamDetails() ) )
492 CFileItem
item(*pItem
);
493 std::string
path(item
.GetPath());
494 if (URIUtils::IsInRAR(item
.GetPath()))
495 SetupRarOptions(item
,path
);
496 CThumbExtractor
* extract
= new CThumbExtractor(item
,path
,false);
501 m_videoDatabase
->Close();
505 bool CVideoThumbLoader::FillLibraryArt(CFileItem
&item
)
507 CVideoInfoTag
&tag
= *item
.GetVideoInfoTag();
508 std::map
<std::string
, std::string
> artwork
;
509 // Video item can be an album - either a
510 // a) search result with full details including music library album id, or
511 // b) musicvideo album that needs matching to a music album, storing id as well as fetch art.
512 if (tag
.m_type
== MediaTypeAlbum
)
515 if (item
.HasMusicInfoTag()) // Album is a search result
516 idAlbum
= item
.GetMusicInfoTag()->GetAlbumId();
517 CMusicDatabase database
;
519 if (idAlbum
< 0 && !tag
.m_strAlbum
.empty() &&
520 item
.GetProperty("musicvideomediatype") == MediaTypeAlbum
)
522 // Musicvideo album - try to match album in music db on artist(s) and album name.
523 // Get review if available and save the matching music library album id.
524 std::string strArtist
= StringUtils::Join(
526 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator
);
527 std::string strReview
;
528 if (database
.GetMatchingMusicVideoAlbum(
529 tag
.m_strAlbum
, strArtist
, idAlbum
, strReview
))
531 item
.SetProperty("album_musicid", idAlbum
);
532 item
.SetProperty("album_description", strReview
);
535 // Get album art only (not related artist art)
536 if (database
.GetArtForItem(idAlbum
, MediaTypeAlbum
, artwork
))
537 item
.SetArt(artwork
);
540 else if (tag
.m_type
== "actor" && !tag
.m_artist
.empty() &&
541 item
.GetProperty("musicvideomediatype") == MediaTypeArtist
)
543 // Try to match artist in music db on name, get bio if available and fetch artist art
544 // Save the matching music library artist id.
545 CMusicDatabase database
;
548 int idArtist
= database
.GetArtistByName(tag
.m_artist
[0]);
551 database
.GetArtist(idArtist
, artist
);
552 tag
.m_strPlot
= artist
.strBiography
;
553 item
.SetProperty("artist_musicid", idArtist
);
555 if (database
.GetArtForItem(idArtist
, MediaTypeArtist
, artwork
))
556 item
.SetArt(artwork
);
560 if (tag
.m_iDbId
> -1 && !tag
.m_type
.empty())
562 m_videoDatabase
->Open();
563 if (m_videoDatabase
->GetArtForItem(tag
.m_iDbId
, tag
.m_type
, artwork
))
564 item
.AppendArt(artwork
);
565 else if (tag
.m_type
== "actor" && !tag
.m_artist
.empty() &&
566 item
.GetProperty("musicvideomediatype") != MediaTypeArtist
)
568 // Fallback to music library for actors without art
569 //! @todo Is m_artist set other than musicvideo? Remove this fallback if not.
570 CMusicDatabase database
;
572 int idArtist
= database
.GetArtistByName(item
.GetLabel());
573 if (database
.GetArtForItem(idArtist
, MediaTypeArtist
, artwork
))
574 item
.SetArt(artwork
);
578 if (tag
.m_type
== MediaTypeEpisode
|| tag
.m_type
== MediaTypeSeason
)
580 // For episodes and seasons, we want to set fanart for that of the show
581 if (!item
.HasArt("tvshow.fanart") && tag
.m_iIdShow
>= 0)
583 const ArtMap
& artmap
= GetArtFromCache(MediaTypeTvShow
, tag
.m_iIdShow
);
586 item
.AppendArt(artmap
, MediaTypeTvShow
);
587 item
.SetArtFallback("fanart", "tvshow.fanart");
588 item
.SetArtFallback("tvshow.thumb", "tvshow.poster");
592 if (tag
.m_type
== MediaTypeEpisode
&& !item
.HasArt("season.poster") && tag
.m_iSeason
> -1)
594 const ArtMap
& artmap
= GetArtFromCache(MediaTypeSeason
, tag
.m_iIdSeason
);
596 item
.AppendArt(artmap
, MediaTypeSeason
);
599 else if (tag
.m_type
== MediaTypeMovie
&& tag
.m_set
.id
>= 0 && !item
.HasArt("set.fanart"))
601 const ArtMap
& artmap
= GetArtFromCache(MediaTypeVideoCollection
, tag
.m_set
.id
);
603 item
.AppendArt(artmap
, MediaTypeVideoCollection
);
605 m_videoDatabase
->Close();
607 item
.SetProperty("libraryartfilled", true);
608 return !item
.GetArt().empty();
611 bool CVideoThumbLoader::FillThumb(CFileItem
&item
)
613 if (item
.HasArt("thumb"))
615 std::string thumb
= GetCachedImage(item
, "thumb");
618 thumb
= GetLocalArt(item
, "thumb");
620 SetCachedImage(item
, "thumb", thumb
);
623 item
.SetArt("thumb", thumb
);
626 // If nothing was found, try embedded art
627 if (item
.HasVideoInfoTag() && !item
.GetVideoInfoTag()->m_coverArt
.empty())
629 for (auto& it
: item
.GetVideoInfoTag()->m_coverArt
)
631 if (it
.m_type
== "thumb")
633 thumb
= CTextureUtils::GetWrappedImageURL(item
.GetPath(), "video_" + it
.m_type
);
634 item
.SetArt(it
.m_type
, thumb
);
640 return !thumb
.empty();
643 std::string
CVideoThumbLoader::GetLocalArt(const CFileItem
&item
, const std::string
&type
, bool checkFolder
)
645 if (item
.SkipLocalArt())
648 /* Cache directory for (sub) folders with Curl("streamed") filesystems. We need to do this
649 else entering (new) directories from the app thread becomes much slower. This
650 is caused by the fact that Curl Stat/Exist() is really slow and that the
651 thumbloader thread accesses the streamed filesystem at the same time as the
652 app thread and the latter has to wait for it.
654 if (item
.m_bIsFolder
&&
655 (item
.IsStreamedFilesystem() ||
656 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cacheBufferMode
==
657 CACHE_BUFFER_MODE_ALL
))
659 CFileItemList items
; // Dummy list
660 CDirectory::GetDirectory(item
.GetPath(), items
, "", DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_READ_CACHE
| DIR_FLAG_NO_FILE_INFO
);
666 art
= item
.FindLocalArt(type
+ ".jpg", checkFolder
);
668 art
= item
.FindLocalArt(type
+ ".png", checkFolder
);
670 if (art
.empty() && (type
.empty() || type
== "thumb"))
671 { // backward compatibility
672 art
= item
.FindLocalArt("", false);
673 if (art
.empty() && (checkFolder
|| (item
.m_bIsFolder
&& !item
.IsFileFolder()) || item
.IsOpticalMediaFile()))
675 art
= item
.FindLocalArt("movie.tbn", true);
676 if (art
.empty()) // try folder.jpg
677 art
= item
.FindLocalArt("folder.jpg", true);
684 std::string
CVideoThumbLoader::GetEmbeddedThumbURL(const CFileItem
&item
)
686 std::string
path(item
.GetPath());
687 if (item
.IsVideoDb() && item
.HasVideoInfoTag())
688 path
= item
.GetVideoInfoTag()->m_strFileNameAndPath
;
689 if (URIUtils::IsStack(path
))
690 path
= CStackDirectory::GetFirstStackedFile(path
);
692 return CTextureUtils::GetWrappedImageURL(path
, "video");
695 bool CVideoThumbLoader::GetEmbeddedThumb(const std::string
& path
,
696 const std::string
& type
, EmbeddedArt
& art
)
698 CFileItem
item(path
, false);
699 std::unique_ptr
<IVideoInfoTagLoader
> pLoader
;
700 pLoader
.reset(CVideoInfoTagLoaderFactory::CreateLoader(item
,ADDON::ScraperPtr(),false));
702 std::vector
<EmbeddedArt
> artv
;
704 pLoader
->Load(tag
, false, &artv
);
706 for (const EmbeddedArt
& it
: artv
)
708 if (it
.m_type
== type
)
718 void CVideoThumbLoader::OnJobComplete(unsigned int jobID
, bool success
, CJob
* job
)
722 CThumbExtractor
* loader
= static_cast<CThumbExtractor
*>(job
);
723 loader
->m_item
.SetPath(loader
->m_listpath
);
726 m_pObserver
->OnItemLoaded(&loader
->m_item
);
727 CFileItemPtr
pItem(new CFileItem(loader
->m_item
));
728 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_UPDATE_ITEM
, 0, pItem
);
729 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg
);
731 CJobQueue::OnJobComplete(jobID
, success
, job
);
734 void CVideoThumbLoader::DetectAndAddMissingItemData(CFileItem
&item
)
736 if (item
.m_bIsFolder
) return;
738 if (item
.HasVideoInfoTag())
740 CStreamDetails
& details
= item
.GetVideoInfoTag()->m_streamDetails
;
742 // add audio language properties
743 for (int i
= 1; i
<= details
.GetAudioStreamCount(); i
++)
745 std::string index
= std::to_string(i
);
746 item
.SetProperty("AudioChannels." + index
, details
.GetAudioChannels(i
));
747 item
.SetProperty("AudioCodec." + index
, details
.GetAudioCodec(i
).c_str());
748 item
.SetProperty("AudioLanguage." + index
, details
.GetAudioLanguage(i
).c_str());
751 // add subtitle language properties
752 for (int i
= 1; i
<= details
.GetSubtitleStreamCount(); i
++)
754 std::string index
= std::to_string(i
);
755 item
.SetProperty("SubtitleLanguage." + index
, details
.GetSubtitleLanguage(i
).c_str());
759 const CStereoscopicsManager
&stereoscopicsManager
= CServiceBroker::GetGUI()->GetStereoscopicsManager();
761 std::string stereoMode
;
763 // detect stereomode for videos
764 if (item
.HasVideoInfoTag())
765 stereoMode
= item
.GetVideoInfoTag()->m_streamDetails
.GetStereoMode();
767 if (stereoMode
.empty())
769 std::string path
= item
.GetPath();
770 if (item
.IsVideoDb() && item
.HasVideoInfoTag())
771 path
= item
.GetVideoInfoTag()->GetPath();
773 // check for custom stereomode setting in video settings
774 CVideoSettings itemVideoSettings
;
775 m_videoDatabase
->Open();
776 if (m_videoDatabase
->GetVideoSettings(item
, itemVideoSettings
) && itemVideoSettings
.m_StereoMode
!= RENDER_STEREO_MODE_OFF
)
778 stereoMode
= CStereoscopicsManager::ConvertGuiStereoModeToString(static_cast<RENDER_STEREO_MODE
>(itemVideoSettings
.m_StereoMode
));
780 m_videoDatabase
->Close();
782 // still empty, try grabbing from filename
783 //! @todo in case of too many false positives due to using the full path, extract the filename only using string utils
784 if (stereoMode
.empty())
785 stereoMode
= stereoscopicsManager
.DetectStereoModeByString(path
);
788 if (!stereoMode
.empty())
789 item
.SetProperty("stereomode", CStereoscopicsManager::NormalizeStereoMode(stereoMode
));
792 const ArtMap
& CVideoThumbLoader::GetArtFromCache(const std::string
&mediaType
, const int id
)
794 std::pair
<MediaType
, int> key
= std::make_pair(mediaType
, id
);
795 auto it
= m_artCache
.find(key
);
796 if (it
== m_artCache
.end())
799 m_videoDatabase
->GetArtForItem(id
, mediaType
, newart
);
800 it
= m_artCache
.insert(std::make_pair(key
, std::move(newart
))).first
;