1 /*
2  *  Copyright (C) 2005-2020 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "FileItem.h"
10 
11 #include "CueDocument.h"
12 #include "ServiceBroker.h"
13 #include "URL.h"
14 #include "Util.h"
15 #include "events/IEvent.h"
16 #include "filesystem/CurlFile.h"
17 #include "filesystem/Directory.h"
18 #include "filesystem/File.h"
19 #include "filesystem/MultiPathDirectory.h"
20 #include "filesystem/MusicDatabaseDirectory.h"
21 #include "filesystem/StackDirectory.h"
22 #include "filesystem/VideoDatabaseDirectory.h"
23 #include "filesystem/VideoDatabaseDirectory/QueryParams.h"
24 #include "games/GameUtils.h"
25 #include "games/addons/GameClient.h"
26 #include "games/tags/GameInfoTag.h"
27 #include "guilib/LocalizeStrings.h"
28 #include "media/MediaLockState.h"
29 #include "music/Album.h"
30 #include "music/Artist.h"
31 #include "music/MusicDatabase.h"
32 #include "music/tags/MusicInfoTag.h"
33 #include "music/tags/MusicInfoTagLoaderFactory.h"
34 #include "pictures/PictureInfoTag.h"
35 #include "playlists/PlayListFactory.h"
36 #include "pvr/PVRManager.h"
37 #include "pvr/channels/PVRChannel.h"
38 #include "pvr/channels/PVRChannelGroupsContainer.h"
39 #include "pvr/epg/EpgInfoTag.h"
40 #include "pvr/recordings/PVRRecording.h"
41 #include "pvr/timers/PVRTimerInfoTag.h"
42 #include "settings/AdvancedSettings.h"
43 #include "settings/SettingUtils.h"
44 #include "settings/Settings.h"
45 #include "settings/SettingsComponent.h"
46 #include "settings/lib/Setting.h"
47 #include "threads/SingleLock.h"
48 #include "utils/Archive.h"
49 #include "utils/Crc32.h"
50 #include "utils/FileExtensionProvider.h"
51 #include "utils/Mime.h"
52 #include "utils/Random.h"
53 #include "utils/RegExp.h"
54 #include "utils/StringUtils.h"
55 #include "utils/URIUtils.h"
56 #include "utils/Variant.h"
57 #include "utils/log.h"
58 #include "video/Bookmark.h"
59 #include "video/VideoDatabase.h"
60 #include "video/VideoInfoTag.h"
61 
62 #include <algorithm>
63 #include <cstdlib>
64 
65 using namespace KODI;
66 using namespace XFILE;
67 using namespace PLAYLIST;
68 using namespace MUSIC_INFO;
69 using namespace PVR;
70 using namespace GAME;
71 
CFileItem(const CSong & song)72 CFileItem::CFileItem(const CSong& song)
73 {
74   Initialize();
75   SetFromSong(song);
76 }
77 
CFileItem(const CSong & song,const CMusicInfoTag & music)78 CFileItem::CFileItem(const CSong& song, const CMusicInfoTag& music)
79 {
80   Initialize();
81   SetFromSong(song);
82   *GetMusicInfoTag() = music;
83 }
84 
CFileItem(const CURL & url,const CAlbum & album)85 CFileItem::CFileItem(const CURL &url, const CAlbum& album)
86 {
87   Initialize();
88 
89   m_strPath = url.Get();
90   URIUtils::AddSlashAtEnd(m_strPath);
91   SetFromAlbum(album);
92 }
93 
CFileItem(const std::string & path,const CAlbum & album)94 CFileItem::CFileItem(const std::string &path, const CAlbum& album)
95 {
96   Initialize();
97 
98   m_strPath = path;
99   URIUtils::AddSlashAtEnd(m_strPath);
100   SetFromAlbum(album);
101 }
102 
CFileItem(const CMusicInfoTag & music)103 CFileItem::CFileItem(const CMusicInfoTag& music)
104 {
105   Initialize();
106   SetLabel(music.GetTitle());
107   m_strPath = music.GetURL();
108   m_bIsFolder = URIUtils::HasSlashAtEnd(m_strPath);
109   *GetMusicInfoTag() = music;
110   FillInDefaultIcon();
111   FillInMimeType(false);
112 }
113 
CFileItem(const CVideoInfoTag & movie)114 CFileItem::CFileItem(const CVideoInfoTag& movie)
115 {
116   Initialize();
117   SetFromVideoInfoTag(movie);
118 }
119 
120 namespace
121 {
GetEpgTagTitle(const std::shared_ptr<CPVREpgInfoTag> & epgTag)122   std::string GetEpgTagTitle(const std::shared_ptr<CPVREpgInfoTag>& epgTag)
123   {
124     if (CServiceBroker::GetPVRManager().IsParentalLocked(epgTag))
125       return g_localizeStrings.Get(19266); // Parental locked
126     else if (epgTag->Title().empty() &&
127              !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_HIDENOINFOAVAILABLE))
128       return g_localizeStrings.Get(19055); // no information available
129     else
130       return epgTag->Title();
131   }
132 } // unnamed namespace
133 
FillMusicInfoTag(const std::shared_ptr<CPVRChannel> & channel,const std::shared_ptr<CPVREpgInfoTag> & tag)134 void CFileItem::FillMusicInfoTag(const std::shared_ptr<CPVRChannel>& channel, const std::shared_ptr<CPVREpgInfoTag>& tag)
135 {
136   if (channel && channel->IsRadio() && !HasMusicInfoTag())
137   {
138     CMusicInfoTag* musictag = GetMusicInfoTag(); // create (!) the music tag.
139 
140     if (tag)
141     {
142       musictag->SetTitle(GetEpgTagTitle(tag));
143       musictag->SetGenre(tag->Genre());
144       musictag->SetDuration(tag->GetDuration());
145     }
146     else if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
147                  CSettings::SETTING_EPG_HIDENOINFOAVAILABLE))
148     {
149       musictag->SetTitle(g_localizeStrings.Get(19055)); // no information available
150     }
151 
152     musictag->SetURL(channel->Path());
153     musictag->SetLoaded(true);
154   }
155 }
156 
CFileItem(const std::shared_ptr<CPVREpgInfoTag> & tag)157 CFileItem::CFileItem(const std::shared_ptr<CPVREpgInfoTag>& tag)
158 {
159   Initialize();
160 
161   m_bIsFolder = false;
162   m_epgInfoTag = tag;
163   m_strPath = tag->Path();
164   SetLabel(GetEpgTagTitle(tag));
165   m_dateTime = tag->StartAsLocalTime();
166 
167   const std::shared_ptr<CPVRChannel> channel =
168       CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag);
169 
170   if (!tag->Icon().empty())
171     SetArt("icon", tag->Icon());
172   else if (channel && !channel->IconPath().empty())
173     SetArt("icon", channel->IconPath());
174   else if (tag->IsRadio())
175     SetArt("icon", "DefaultMusicSongs.png");
176   else
177     SetArt("icon", "DefaultTVShows.png");
178 
179   FillMusicInfoTag(channel, tag);
180   FillInMimeType(false);
181 }
182 
CFileItem(const std::shared_ptr<CPVRChannel> & channel)183 CFileItem::CFileItem(const std::shared_ptr<CPVRChannel>& channel)
184 {
185   Initialize();
186 
187   std::shared_ptr<CPVREpgInfoTag> epgNow(channel->GetEPGNow());
188 
189   m_strPath = channel->Path();
190   m_bIsFolder = false;
191   m_pvrChannelInfoTag = channel;
192   SetLabel(channel->ChannelName());
193 
194   if (!channel->IconPath().empty())
195     SetArt("icon", channel->IconPath());
196   else if (channel->IsRadio())
197     SetArt("icon", "DefaultMusicSongs.png");
198   else
199     SetArt("icon", "DefaultTVShows.png");
200 
201   SetProperty("channelid", channel->ChannelID());
202   SetProperty("path", channel->Path());
203   SetArt("thumb", channel->IconPath());
204 
205   FillMusicInfoTag(channel, epgNow);
206   FillInMimeType(false);
207 }
208 
CFileItem(const std::shared_ptr<CPVRRecording> & record)209 CFileItem::CFileItem(const std::shared_ptr<CPVRRecording>& record)
210 {
211   Initialize();
212 
213   m_bIsFolder = false;
214   m_pvrRecordingInfoTag = record;
215   m_strPath = record->m_strFileNameAndPath;
216   SetLabel(record->m_strTitle);
217   m_dateTime = record->RecordingTimeAsLocalTime();
218   m_dwSize = record->GetSizeInBytes();
219 
220   // Set art
221   if (!record->m_strIconPath.empty())
222     SetArt("icon", record->m_strIconPath);
223   else
224   {
225     const std::shared_ptr<CPVRChannel> channel = record->Channel();
226     if (channel && !channel->IconPath().empty())
227       SetArt("icon", channel->IconPath());
228     else if (record->IsRadio())
229       SetArt("icon", "DefaultMusicSongs.png");
230     else
231       SetArt("icon", "DefaultTVShows.png");
232   }
233 
234   if (!record->m_strThumbnailPath.empty())
235     SetArt("thumb", record->m_strThumbnailPath);
236 
237   if (!record->m_strFanartPath.empty())
238     SetArt("fanart", record->m_strFanartPath);
239 
240   FillInMimeType(false);
241 }
242 
CFileItem(const std::shared_ptr<CPVRTimerInfoTag> & timer)243 CFileItem::CFileItem(const std::shared_ptr<CPVRTimerInfoTag>& timer)
244 {
245   Initialize();
246 
247   m_bIsFolder = timer->IsTimerRule();
248   m_pvrTimerInfoTag = timer;
249   m_strPath = timer->Path();
250   SetLabel(timer->Title());
251   m_dateTime = timer->StartAsLocalTime();
252 
253   if (!timer->ChannelIcon().empty())
254     SetArt("icon", timer->ChannelIcon());
255   else if (timer->m_bIsRadio)
256     SetArt("icon", "DefaultMusicSongs.png");
257   else
258     SetArt("icon", "DefaultTVShows.png");
259 
260   FillInMimeType(false);
261 }
262 
CFileItem(const CArtist & artist)263 CFileItem::CFileItem(const CArtist& artist)
264 {
265   Initialize();
266   SetLabel(artist.strArtist);
267   m_strPath = artist.strArtist;
268   m_bIsFolder = true;
269   URIUtils::AddSlashAtEnd(m_strPath);
270   GetMusicInfoTag()->SetArtist(artist);
271   FillInMimeType(false);
272 }
273 
CFileItem(const CGenre & genre)274 CFileItem::CFileItem(const CGenre& genre)
275 {
276   Initialize();
277   SetLabel(genre.strGenre);
278   m_strPath = genre.strGenre;
279   m_bIsFolder = true;
280   URIUtils::AddSlashAtEnd(m_strPath);
281   GetMusicInfoTag()->SetGenre(genre.strGenre);
282   FillInMimeType(false);
283 }
284 
CFileItem(const CFileItem & item)285 CFileItem::CFileItem(const CFileItem& item)
286 : m_musicInfoTag(NULL),
287   m_videoInfoTag(NULL),
288   m_pictureInfoTag(NULL),
289   m_gameInfoTag(NULL)
290 {
291   *this = item;
292 }
293 
CFileItem(const CGUIListItem & item)294 CFileItem::CFileItem(const CGUIListItem& item)
295 {
296   Initialize();
297   // not particularly pretty, but it gets around the issue of Initialize() defaulting
298   // parameters in the CGUIListItem base class.
299   *static_cast<CGUIListItem*>(this) = item;
300 
301   FillInMimeType(false);
302 }
303 
CFileItem(void)304 CFileItem::CFileItem(void)
305 {
306   Initialize();
307 }
308 
CFileItem(const std::string & strLabel)309 CFileItem::CFileItem(const std::string& strLabel)
310 {
311   Initialize();
312   SetLabel(strLabel);
313 }
314 
CFileItem(const char * strLabel)315 CFileItem::CFileItem(const char* strLabel)
316 {
317   Initialize();
318   SetLabel(std::string(strLabel));
319 }
320 
CFileItem(const CURL & path,bool bIsFolder)321 CFileItem::CFileItem(const CURL& path, bool bIsFolder)
322 {
323   Initialize();
324   m_strPath = path.Get();
325   m_bIsFolder = bIsFolder;
326   if (m_bIsFolder && !m_strPath.empty() && !IsFileFolder())
327     URIUtils::AddSlashAtEnd(m_strPath);
328   FillInMimeType(false);
329 }
330 
CFileItem(const std::string & strPath,bool bIsFolder)331 CFileItem::CFileItem(const std::string& strPath, bool bIsFolder)
332 {
333   Initialize();
334   m_strPath = strPath;
335   m_bIsFolder = bIsFolder;
336   if (m_bIsFolder && !m_strPath.empty() && !IsFileFolder())
337     URIUtils::AddSlashAtEnd(m_strPath);
338   FillInMimeType(false);
339 }
340 
CFileItem(const CMediaSource & share)341 CFileItem::CFileItem(const CMediaSource& share)
342 {
343   Initialize();
344   m_bIsFolder = true;
345   m_bIsShareOrDrive = true;
346   m_strPath = share.strPath;
347   if (!IsRSS()) // no slash at end for rss feeds
348     URIUtils::AddSlashAtEnd(m_strPath);
349   std::string label = share.strName;
350   if (!share.strStatus.empty())
351     label = StringUtils::Format("%s (%s)", share.strName.c_str(), share.strStatus.c_str());
352   SetLabel(label);
353   m_iLockMode = share.m_iLockMode;
354   m_strLockCode = share.m_strLockCode;
355   m_iHasLock = share.m_iHasLock;
356   m_iBadPwdCount = share.m_iBadPwdCount;
357   m_iDriveType = share.m_iDriveType;
358   SetArt("thumb", share.m_strThumbnailImage);
359   SetLabelPreformatted(true);
360   if (IsDVD())
361     GetVideoInfoTag()->m_strFileNameAndPath = share.strDiskUniqueId; // share.strDiskUniqueId contains disc unique id
362   FillInMimeType(false);
363 }
364 
CFileItem(std::shared_ptr<const ADDON::IAddon> addonInfo)365 CFileItem::CFileItem(std::shared_ptr<const ADDON::IAddon> addonInfo) : m_addonInfo(std::move(addonInfo))
366 {
367   Initialize();
368 }
369 
CFileItem(const EventPtr & eventLogEntry)370 CFileItem::CFileItem(const EventPtr& eventLogEntry)
371 {
372   Initialize();
373 
374   m_eventLogEntry = eventLogEntry;
375   SetLabel(eventLogEntry->GetLabel());
376   m_dateTime = eventLogEntry->GetDateTime();
377   if (!eventLogEntry->GetIcon().empty())
378     SetArt("icon", eventLogEntry->GetIcon());
379 }
380 
~CFileItem(void)381 CFileItem::~CFileItem(void)
382 {
383   delete m_musicInfoTag;
384   delete m_videoInfoTag;
385   delete m_pictureInfoTag;
386   delete m_gameInfoTag;
387 
388   m_musicInfoTag = NULL;
389   m_videoInfoTag = NULL;
390   m_pictureInfoTag = NULL;
391   m_gameInfoTag = NULL;
392 }
393 
operator =(const CFileItem & item)394 CFileItem& CFileItem::operator=(const CFileItem& item)
395 {
396   if (this == &item)
397     return *this;
398 
399   CGUIListItem::operator=(item);
400   m_bLabelPreformatted=item.m_bLabelPreformatted;
401   FreeMemory();
402   m_strPath = item.m_strPath;
403   m_strDynPath = item.m_strDynPath;
404   m_bIsParentFolder = item.m_bIsParentFolder;
405   m_iDriveType = item.m_iDriveType;
406   m_bIsShareOrDrive = item.m_bIsShareOrDrive;
407   m_dateTime = item.m_dateTime;
408   m_dwSize = item.m_dwSize;
409 
410   if (item.m_musicInfoTag)
411   {
412     if (m_musicInfoTag)
413       *m_musicInfoTag = *item.m_musicInfoTag;
414     else
415       m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag(*item.m_musicInfoTag);
416   }
417   else
418   {
419     delete m_musicInfoTag;
420     m_musicInfoTag = NULL;
421   }
422 
423   if (item.m_videoInfoTag)
424   {
425     if (m_videoInfoTag)
426       *m_videoInfoTag = *item.m_videoInfoTag;
427     else
428       m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
429   }
430   else
431   {
432     delete m_videoInfoTag;
433     m_videoInfoTag = NULL;
434   }
435 
436   if (item.m_pictureInfoTag)
437   {
438     if (m_pictureInfoTag)
439       *m_pictureInfoTag = *item.m_pictureInfoTag;
440     else
441       m_pictureInfoTag = new CPictureInfoTag(*item.m_pictureInfoTag);
442   }
443   else
444   {
445     delete m_pictureInfoTag;
446     m_pictureInfoTag = NULL;
447   }
448 
449   if (item.m_gameInfoTag)
450   {
451     if (m_gameInfoTag)
452       *m_gameInfoTag = *item.m_gameInfoTag;
453     else
454       m_gameInfoTag = new CGameInfoTag(*item.m_gameInfoTag);
455   }
456   else
457   {
458     delete m_gameInfoTag;
459     m_gameInfoTag = NULL;
460   }
461 
462   m_epgInfoTag = item.m_epgInfoTag;
463   m_pvrChannelInfoTag = item.m_pvrChannelInfoTag;
464   m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
465   m_pvrTimerInfoTag = item.m_pvrTimerInfoTag;
466   m_addonInfo = item.m_addonInfo;
467   m_eventLogEntry = item.m_eventLogEntry;
468 
469   m_lStartOffset = item.m_lStartOffset;
470   m_lStartPartNumber = item.m_lStartPartNumber;
471   m_lEndOffset = item.m_lEndOffset;
472   m_strDVDLabel = item.m_strDVDLabel;
473   m_strTitle = item.m_strTitle;
474   m_iprogramCount = item.m_iprogramCount;
475   m_idepth = item.m_idepth;
476   m_iLockMode = item.m_iLockMode;
477   m_strLockCode = item.m_strLockCode;
478   m_iHasLock = item.m_iHasLock;
479   m_iBadPwdCount = item.m_iBadPwdCount;
480   m_bCanQueue=item.m_bCanQueue;
481   m_mimetype = item.m_mimetype;
482   m_extrainfo = item.m_extrainfo;
483   m_specialSort = item.m_specialSort;
484   m_bIsAlbum = item.m_bIsAlbum;
485   m_doContentLookup = item.m_doContentLookup;
486   return *this;
487 }
488 
Initialize()489 void CFileItem::Initialize()
490 {
491   m_musicInfoTag = NULL;
492   m_videoInfoTag = NULL;
493   m_pictureInfoTag = NULL;
494   m_gameInfoTag = NULL;
495   m_bLabelPreformatted = false;
496   m_bIsAlbum = false;
497   m_dwSize = 0;
498   m_bIsParentFolder = false;
499   m_bIsShareOrDrive = false;
500   m_iDriveType = CMediaSource::SOURCE_TYPE_UNKNOWN;
501   m_lStartOffset = 0;
502   m_lStartPartNumber = 1;
503   m_lEndOffset = 0;
504   m_iprogramCount = 0;
505   m_idepth = 1;
506   m_iLockMode = LOCK_MODE_EVERYONE;
507   m_iBadPwdCount = 0;
508   m_iHasLock = LOCK_STATE_NO_LOCK;
509   m_bCanQueue = true;
510   m_specialSort = SortSpecialNone;
511   m_doContentLookup = true;
512 }
513 
Reset()514 void CFileItem::Reset()
515 {
516   // CGUIListItem members...
517   m_strLabel2.clear();
518   SetLabel("");
519   FreeIcons();
520   m_overlayIcon = ICON_OVERLAY_NONE;
521   m_bSelected = false;
522   m_bIsFolder = false;
523 
524   m_strDVDLabel.clear();
525   m_strTitle.clear();
526   m_strPath.clear();
527   m_strDynPath.clear();
528   m_dateTime.Reset();
529   m_strLockCode.clear();
530   m_mimetype.clear();
531   delete m_musicInfoTag;
532   m_musicInfoTag=NULL;
533   delete m_videoInfoTag;
534   m_videoInfoTag=NULL;
535   m_epgInfoTag.reset();
536   m_pvrChannelInfoTag.reset();
537   m_pvrRecordingInfoTag.reset();
538   m_pvrTimerInfoTag.reset();
539   delete m_pictureInfoTag;
540   m_pictureInfoTag=NULL;
541   delete m_gameInfoTag;
542   m_gameInfoTag = NULL;
543   m_extrainfo.clear();
544   ClearProperties();
545   m_eventLogEntry.reset();
546 
547   Initialize();
548   SetInvalid();
549 }
550 
551 // do not archive dynamic path
Archive(CArchive & ar)552 void CFileItem::Archive(CArchive& ar)
553 {
554   CGUIListItem::Archive(ar);
555 
556   if (ar.IsStoring())
557   {
558     ar << m_bIsParentFolder;
559     ar << m_bLabelPreformatted;
560     ar << m_strPath;
561     ar << m_bIsShareOrDrive;
562     ar << m_iDriveType;
563     ar << m_dateTime;
564     ar << m_dwSize;
565     ar << m_strDVDLabel;
566     ar << m_strTitle;
567     ar << m_iprogramCount;
568     ar << m_idepth;
569     ar << m_lStartOffset;
570     ar << m_lStartPartNumber;
571     ar << m_lEndOffset;
572     ar << m_iLockMode;
573     ar << m_strLockCode;
574     ar << m_iBadPwdCount;
575 
576     ar << m_bCanQueue;
577     ar << m_mimetype;
578     ar << m_extrainfo;
579     ar << m_specialSort;
580     ar << m_doContentLookup;
581 
582     if (m_musicInfoTag)
583     {
584       ar << 1;
585       ar << *m_musicInfoTag;
586     }
587     else
588       ar << 0;
589     if (m_videoInfoTag)
590     {
591       ar << 1;
592       ar << *m_videoInfoTag;
593     }
594     else
595       ar << 0;
596     if (m_pictureInfoTag)
597     {
598       ar << 1;
599       ar << *m_pictureInfoTag;
600     }
601     else
602       ar << 0;
603     if (m_gameInfoTag)
604     {
605       ar << 1;
606       ar << *m_gameInfoTag;
607     }
608     else
609       ar << 0;
610   }
611   else
612   {
613     ar >> m_bIsParentFolder;
614     ar >> m_bLabelPreformatted;
615     ar >> m_strPath;
616     ar >> m_bIsShareOrDrive;
617     ar >> m_iDriveType;
618     ar >> m_dateTime;
619     ar >> m_dwSize;
620     ar >> m_strDVDLabel;
621     ar >> m_strTitle;
622     ar >> m_iprogramCount;
623     ar >> m_idepth;
624     ar >> m_lStartOffset;
625     ar >> m_lStartPartNumber;
626     ar >> m_lEndOffset;
627     int temp;
628     ar >> temp;
629     m_iLockMode = (LockType)temp;
630     ar >> m_strLockCode;
631     ar >> m_iBadPwdCount;
632 
633     ar >> m_bCanQueue;
634     ar >> m_mimetype;
635     ar >> m_extrainfo;
636     ar >> temp;
637     m_specialSort = (SortSpecial)temp;
638     ar >> m_doContentLookup;
639 
640     int iType;
641     ar >> iType;
642     if (iType == 1)
643       ar >> *GetMusicInfoTag();
644     ar >> iType;
645     if (iType == 1)
646       ar >> *GetVideoInfoTag();
647     ar >> iType;
648     if (iType == 1)
649       ar >> *GetPictureInfoTag();
650     ar >> iType;
651     if (iType == 1)
652       ar >> *GetGameInfoTag();
653 
654     SetInvalid();
655   }
656 }
657 
Serialize(CVariant & value) const658 void CFileItem::Serialize(CVariant& value) const
659 {
660   //CGUIListItem::Serialize(value["CGUIListItem"]);
661 
662   value["strPath"] = m_strPath;
663   value["dateTime"] = (m_dateTime.IsValid()) ? m_dateTime.GetAsRFC1123DateTime() : "";
664   value["lastmodified"] = m_dateTime.IsValid() ? m_dateTime.GetAsDBDateTime() : "";
665   value["size"] = m_dwSize;
666   value["DVDLabel"] = m_strDVDLabel;
667   value["title"] = m_strTitle;
668   value["mimetype"] = m_mimetype;
669   value["extrainfo"] = m_extrainfo;
670 
671   if (m_musicInfoTag)
672     (*m_musicInfoTag).Serialize(value["musicInfoTag"]);
673 
674   if (m_videoInfoTag)
675     (*m_videoInfoTag).Serialize(value["videoInfoTag"]);
676 
677   if (m_pictureInfoTag)
678     (*m_pictureInfoTag).Serialize(value["pictureInfoTag"]);
679 
680   if (m_gameInfoTag)
681     (*m_gameInfoTag).Serialize(value["gameInfoTag"]);
682 
683   if (!m_mapProperties.empty())
684   {
685     auto& customProperties = value["customproperties"];
686     for (const auto& prop : m_mapProperties)
687       customProperties[prop.first] = prop.second;
688   }
689 }
690 
ToSortable(SortItem & sortable,Field field) const691 void CFileItem::ToSortable(SortItem &sortable, Field field) const
692 {
693   switch (field)
694   {
695     case FieldPath:
696       sortable[FieldPath] = m_strPath;
697       break;
698     case FieldDate:
699       sortable[FieldDate] = (m_dateTime.IsValid()) ? m_dateTime.GetAsDBDateTime() : "";
700       break;
701     case FieldSize:
702       sortable[FieldSize] = m_dwSize;
703       break;
704     case FieldDriveType:
705       sortable[FieldDriveType] = m_iDriveType;
706       break;
707     case FieldStartOffset:
708       sortable[FieldStartOffset] = m_lStartOffset;
709       break;
710     case FieldEndOffset:
711       sortable[FieldEndOffset] = m_lEndOffset;
712       break;
713     case FieldProgramCount:
714       sortable[FieldProgramCount] = m_iprogramCount;
715       break;
716     case FieldBitrate:
717       sortable[FieldBitrate] = m_dwSize;
718       break;
719     case FieldTitle:
720       sortable[FieldTitle] = m_strTitle;
721       break;
722 
723     // If there's ever a need to convert more properties from CGUIListItem it might be
724     // worth to make CGUIListItem implement ISortable as well and call it from here
725 
726     default:
727       break;
728   }
729 
730   if (HasMusicInfoTag())
731     GetMusicInfoTag()->ToSortable(sortable, field);
732 
733   if (HasVideoInfoTag())
734     GetVideoInfoTag()->ToSortable(sortable, field);
735 
736   if (HasPictureInfoTag())
737     GetPictureInfoTag()->ToSortable(sortable, field);
738 
739   if (HasPVRChannelInfoTag())
740     GetPVRChannelInfoTag()->ToSortable(sortable, field);
741 
742   if (HasAddonInfo())
743   {
744     switch (field)
745     {
746       case FieldInstallDate:
747         sortable[FieldInstallDate] = GetAddonInfo()->InstallDate().GetAsDBDateTime();
748         break;
749       case FieldLastUpdated:
750         sortable[FieldLastUpdated] = GetAddonInfo()->LastUpdated().GetAsDBDateTime();
751         break;
752       case FieldLastUsed:
753         sortable[FieldLastUsed] = GetAddonInfo()->LastUsed().GetAsDBDateTime();
754         break;
755       default:
756         break;
757     }
758   }
759 
760   if (HasGameInfoTag())
761     GetGameInfoTag()->ToSortable(sortable, field);
762 
763   if (m_eventLogEntry)
764     m_eventLogEntry->ToSortable(sortable, field);
765 }
766 
ToSortable(SortItem & sortable,const Fields & fields) const767 void CFileItem::ToSortable(SortItem &sortable, const Fields &fields) const
768 {
769   Fields::const_iterator it;
770   for (it = fields.begin(); it != fields.end(); it++)
771     ToSortable(sortable, *it);
772 
773   /* FieldLabel is used as a fallback by all sorters and therefore has to be present as well */
774   sortable[FieldLabel] = GetLabel();
775   /* FieldSortSpecial and FieldFolder are required in conjunction with all other sorters as well */
776   sortable[FieldSortSpecial] = m_specialSort;
777   sortable[FieldFolder] = m_bIsFolder;
778 }
779 
Exists(bool bUseCache) const780 bool CFileItem::Exists(bool bUseCache /* = true */) const
781 {
782   if (m_strPath.empty()
783    || IsPath("add")
784    || IsInternetStream()
785    || IsParentFolder()
786    || IsVirtualDirectoryRoot()
787    || IsPlugin()
788    || IsPVR())
789     return true;
790 
791   if (IsVideoDb() && HasVideoInfoTag())
792   {
793     CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
794     return dbItem.Exists();
795   }
796 
797   std::string strPath = m_strPath;
798 
799   if (URIUtils::IsMultiPath(strPath))
800     strPath = CMultiPathDirectory::GetFirstPath(strPath);
801 
802   if (URIUtils::IsStack(strPath))
803     strPath = CStackDirectory::GetFirstStackedFile(strPath);
804 
805   if (m_bIsFolder)
806     return CDirectory::Exists(strPath, bUseCache);
807   else
808     return CFile::Exists(strPath, bUseCache);
809 
810   return false;
811 }
812 
IsVideo() const813 bool CFileItem::IsVideo() const
814 {
815   /* check preset mime type */
816   if(StringUtils::StartsWithNoCase(m_mimetype, "video/"))
817     return true;
818 
819   if (HasVideoInfoTag())
820     return true;
821 
822   if (HasGameInfoTag())
823     return false;
824 
825   if (HasMusicInfoTag())
826     return false;
827 
828   if (HasPictureInfoTag())
829     return false;
830 
831   // only tv recordings are videos...
832   if (IsPVRRecording())
833     return !GetPVRRecordingInfoTag()->IsRadio();
834 
835   // ... all other PVR items are not.
836   if (IsPVR())
837     return false;
838 
839   if (URIUtils::IsDVD(m_strPath))
840     return true;
841 
842   std::string extension;
843   if(StringUtils::StartsWithNoCase(m_mimetype, "application/"))
844   { /* check for some standard types */
845     extension = m_mimetype.substr(12);
846     if( StringUtils::EqualsNoCase(extension, "ogg")
847      || StringUtils::EqualsNoCase(extension, "mp4")
848      || StringUtils::EqualsNoCase(extension, "mxf") )
849      return true;
850   }
851 
852   //! @todo If the file is a zip file, ask the game clients if any support this
853   // file before assuming it is video.
854 
855   return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions());
856 }
857 
IsEPG() const858 bool CFileItem::IsEPG() const
859 {
860   return HasEPGInfoTag();
861 }
862 
IsPVRChannel() const863 bool CFileItem::IsPVRChannel() const
864 {
865   return HasPVRChannelInfoTag();
866 }
867 
IsPVRChannelGroup() const868 bool CFileItem::IsPVRChannelGroup() const
869 {
870   return URIUtils::IsPVRChannelGroup(m_strPath);
871 }
872 
IsPVRRecording() const873 bool CFileItem::IsPVRRecording() const
874 {
875   return HasPVRRecordingInfoTag();
876 }
877 
IsUsablePVRRecording() const878 bool CFileItem::IsUsablePVRRecording() const
879 {
880   return (m_pvrRecordingInfoTag && !m_pvrRecordingInfoTag->IsDeleted());
881 }
882 
IsDeletedPVRRecording() const883 bool CFileItem::IsDeletedPVRRecording() const
884 {
885   return (m_pvrRecordingInfoTag && m_pvrRecordingInfoTag->IsDeleted());
886 }
887 
IsInProgressPVRRecording() const888 bool CFileItem::IsInProgressPVRRecording() const
889 {
890   return (m_pvrRecordingInfoTag && m_pvrRecordingInfoTag->IsInProgress());
891 }
892 
IsPVRTimer() const893 bool CFileItem::IsPVRTimer() const
894 {
895   return HasPVRTimerInfoTag();
896 }
897 
IsDiscStub() const898 bool CFileItem::IsDiscStub() const
899 {
900   if (IsVideoDb() && HasVideoInfoTag())
901   {
902     CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
903     return dbItem.IsDiscStub();
904   }
905 
906   return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetDiscStubExtensions());
907 }
908 
IsAudio() const909 bool CFileItem::IsAudio() const
910 {
911   /* check preset mime type */
912   if(StringUtils::StartsWithNoCase(m_mimetype, "audio/"))
913     return true;
914 
915   if (HasMusicInfoTag())
916     return true;
917 
918   if (HasVideoInfoTag())
919     return false;
920 
921   if (HasPictureInfoTag())
922     return false;
923 
924   if (HasGameInfoTag())
925     return false;
926 
927   if (IsCDDA())
928     return true;
929 
930   if(StringUtils::StartsWithNoCase(m_mimetype, "application/"))
931   { /* check for some standard types */
932     std::string extension = m_mimetype.substr(12);
933     if( StringUtils::EqualsNoCase(extension, "ogg")
934      || StringUtils::EqualsNoCase(extension, "mp4")
935      || StringUtils::EqualsNoCase(extension, "mxf") )
936      return true;
937   }
938 
939   //! @todo If the file is a zip file, ask the game clients if any support this
940   // file before assuming it is audio
941 
942   return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetMusicExtensions());
943 }
944 
IsDeleted() const945 bool CFileItem::IsDeleted() const
946 {
947   if (HasPVRRecordingInfoTag())
948     return GetPVRRecordingInfoTag()->IsDeleted();
949 
950   return false;
951 }
952 
IsAudioBook() const953 bool CFileItem::IsAudioBook() const
954 {
955   return IsType(".m4b") || IsType(".mka");
956 }
957 
IsGame() const958 bool CFileItem::IsGame() const
959 {
960   if (HasGameInfoTag())
961     return true;
962 
963   if (HasVideoInfoTag())
964     return false;
965 
966   if (HasMusicInfoTag())
967     return false;
968 
969   if (HasPictureInfoTag())
970     return false;
971 
972   if (IsPVR())
973     return false;
974 
975   if (HasAddonInfo())
976     return CGameUtils::IsStandaloneGame(std::const_pointer_cast<ADDON::IAddon>(GetAddonInfo()));
977 
978   return CGameUtils::HasGameExtension(m_strPath);
979 }
980 
IsPicture() const981 bool CFileItem::IsPicture() const
982 {
983   if(StringUtils::StartsWithNoCase(m_mimetype, "image/"))
984     return true;
985 
986   if (HasPictureInfoTag())
987     return true;
988 
989   if (HasGameInfoTag())
990     return false;
991 
992   if (HasMusicInfoTag())
993     return false;
994 
995   if (HasVideoInfoTag())
996     return false;
997 
998   return CUtil::IsPicture(m_strPath);
999 }
1000 
IsLyrics() const1001 bool CFileItem::IsLyrics() const
1002 {
1003   return URIUtils::HasExtension(m_strPath, ".cdg|.lrc");
1004 }
1005 
IsSubtitle() const1006 bool CFileItem::IsSubtitle() const
1007 {
1008   return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions());
1009 }
1010 
IsCUESheet() const1011 bool CFileItem::IsCUESheet() const
1012 {
1013   return URIUtils::HasExtension(m_strPath, ".cue");
1014 }
1015 
IsInternetStream(const bool bStrictCheck) const1016 bool CFileItem::IsInternetStream(const bool bStrictCheck /* = false */) const
1017 {
1018   if (HasProperty("IsHTTPDirectory"))
1019     return false;
1020 
1021   if (!m_strDynPath.empty())
1022     return URIUtils::IsInternetStream(m_strDynPath, bStrictCheck);
1023 
1024   return URIUtils::IsInternetStream(m_strPath, bStrictCheck);
1025 }
1026 
IsFileFolder(EFileFolderType types) const1027 bool CFileItem::IsFileFolder(EFileFolderType types) const
1028 {
1029   EFileFolderType always_type = EFILEFOLDER_TYPE_ALWAYS;
1030 
1031   /* internet streams are not directly expanded */
1032   if(IsInternetStream())
1033     always_type = EFILEFOLDER_TYPE_ONCLICK;
1034 
1035   if(types & always_type)
1036   {
1037     if(IsSmartPlayList()
1038     || (IsPlayList() && CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders)
1039     || IsAPK()
1040     || IsZIP()
1041     || IsRAR()
1042     || IsRSS()
1043     || IsAudioBook()
1044     || IsType(".ogg|.oga|.xbt")
1045 #if defined(TARGET_ANDROID)
1046     || IsType(".apk")
1047 #endif
1048     )
1049     return true;
1050   }
1051 
1052   if (CServiceBroker::IsBinaryAddonCacheUp() &&
1053       IsType(CServiceBroker::GetFileExtensionProvider().GetFileFolderExtensions().c_str()) &&
1054       CServiceBroker::GetFileExtensionProvider().CanOperateExtension(m_strPath))
1055     return true;
1056 
1057   if(types & EFILEFOLDER_TYPE_ONBROWSE)
1058   {
1059     if((IsPlayList() && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders)
1060     || IsDiscImage())
1061       return true;
1062   }
1063 
1064   return false;
1065 }
1066 
IsSmartPlayList() const1067 bool CFileItem::IsSmartPlayList() const
1068 {
1069   if (HasProperty("library.smartplaylist") && GetProperty("library.smartplaylist").asBoolean())
1070     return true;
1071 
1072   return URIUtils::HasExtension(m_strPath, ".xsp");
1073 }
1074 
IsLibraryFolder() const1075 bool CFileItem::IsLibraryFolder() const
1076 {
1077   if (HasProperty("library.filter") && GetProperty("library.filter").asBoolean())
1078     return true;
1079 
1080   return URIUtils::IsLibraryFolder(m_strPath);
1081 }
1082 
IsPlayList() const1083 bool CFileItem::IsPlayList() const
1084 {
1085   return CPlayListFactory::IsPlaylist(*this);
1086 }
1087 
IsPythonScript() const1088 bool CFileItem::IsPythonScript() const
1089 {
1090   return URIUtils::HasExtension(m_strPath, ".py");
1091 }
1092 
IsType(const char * ext) const1093 bool CFileItem::IsType(const char *ext) const
1094 {
1095   if (!m_strDynPath.empty())
1096     return URIUtils::HasExtension(m_strDynPath, ext);
1097 
1098   return URIUtils::HasExtension(m_strPath, ext);
1099 }
1100 
IsNFO() const1101 bool CFileItem::IsNFO() const
1102 {
1103   return URIUtils::HasExtension(m_strPath, ".nfo");
1104 }
1105 
IsDiscImage() const1106 bool CFileItem::IsDiscImage() const
1107 {
1108   return URIUtils::HasExtension(GetDynPath(), ".img|.iso|.nrg|.udf");
1109 }
1110 
IsOpticalMediaFile() const1111 bool CFileItem::IsOpticalMediaFile() const
1112 {
1113   if (IsDVDFile(false, true))
1114     return true;
1115 
1116   return IsBDFile();
1117 }
1118 
IsDVDFile(bool bVobs,bool bIfos) const1119 bool CFileItem::IsDVDFile(bool bVobs /*= true*/, bool bIfos /*= true*/) const
1120 {
1121   std::string strFileName = URIUtils::GetFileName(GetDynPath());
1122   if (bIfos)
1123   {
1124     if (StringUtils::EqualsNoCase(strFileName, "video_ts.ifo"))
1125       return true;
1126     if (StringUtils::StartsWithNoCase(strFileName, "vts_") && StringUtils::EndsWithNoCase(strFileName, "_0.ifo") && strFileName.length() == 12)
1127       return true;
1128   }
1129   if (bVobs)
1130   {
1131     if (StringUtils::EqualsNoCase(strFileName, "video_ts.vob"))
1132       return true;
1133     if (StringUtils::StartsWithNoCase(strFileName, "vts_") && StringUtils::EndsWithNoCase(strFileName, ".vob"))
1134       return true;
1135   }
1136 
1137   return false;
1138 }
1139 
IsBDFile() const1140 bool CFileItem::IsBDFile() const
1141 {
1142   std::string strFileName = URIUtils::GetFileName(GetDynPath());
1143   return (StringUtils::EqualsNoCase(strFileName, "index.bdmv") || StringUtils::EqualsNoCase(strFileName, "MovieObject.bdmv")
1144           || StringUtils::EqualsNoCase(strFileName, "INDEX.BDM") || StringUtils::EqualsNoCase(strFileName, "MOVIEOBJ.BDM"));
1145 }
1146 
IsRAR() const1147 bool CFileItem::IsRAR() const
1148 {
1149   return URIUtils::IsRAR(m_strPath);
1150 }
1151 
IsAPK() const1152 bool CFileItem::IsAPK() const
1153 {
1154   return URIUtils::IsAPK(m_strPath);
1155 }
1156 
IsZIP() const1157 bool CFileItem::IsZIP() const
1158 {
1159   return URIUtils::IsZIP(m_strPath);
1160 }
1161 
IsCBZ() const1162 bool CFileItem::IsCBZ() const
1163 {
1164   return URIUtils::HasExtension(m_strPath, ".cbz");
1165 }
1166 
IsCBR() const1167 bool CFileItem::IsCBR() const
1168 {
1169   return URIUtils::HasExtension(m_strPath, ".cbr");
1170 }
1171 
IsRSS() const1172 bool CFileItem::IsRSS() const
1173 {
1174   return StringUtils::StartsWithNoCase(m_strPath, "rss://") || URIUtils::HasExtension(m_strPath, ".rss")
1175       || StringUtils::StartsWithNoCase(m_strPath, "rsss://")
1176       || m_mimetype == "application/rss+xml";
1177 }
1178 
IsAndroidApp() const1179 bool CFileItem::IsAndroidApp() const
1180 {
1181   return URIUtils::IsAndroidApp(m_strPath);
1182 }
1183 
IsStack() const1184 bool CFileItem::IsStack() const
1185 {
1186   return URIUtils::IsStack(m_strPath);
1187 }
1188 
IsPlugin() const1189 bool CFileItem::IsPlugin() const
1190 {
1191   return URIUtils::IsPlugin(m_strPath);
1192 }
1193 
IsScript() const1194 bool CFileItem::IsScript() const
1195 {
1196   return URIUtils::IsScript(m_strPath);
1197 }
1198 
IsAddonsPath() const1199 bool CFileItem::IsAddonsPath() const
1200 {
1201   return URIUtils::IsAddonsPath(m_strPath);
1202 }
1203 
IsSourcesPath() const1204 bool CFileItem::IsSourcesPath() const
1205 {
1206   return URIUtils::IsSourcesPath(m_strPath);
1207 }
1208 
IsMultiPath() const1209 bool CFileItem::IsMultiPath() const
1210 {
1211   return URIUtils::IsMultiPath(m_strPath);
1212 }
1213 
IsBluray() const1214 bool CFileItem::IsBluray() const
1215 {
1216   if (URIUtils::IsBluray(m_strPath))
1217     return true;
1218 
1219   CFileItem item = CFileItem(GetOpticalMediaPath(), false);
1220 
1221   return item.IsBDFile();
1222 }
1223 
IsProtectedBlurayDisc() const1224 bool CFileItem::IsProtectedBlurayDisc() const
1225 {
1226   std::string path;
1227   path = URIUtils::AddFileToFolder(GetPath(), "AACS", "Unit_Key_RO.inf");
1228   if (CFile::Exists(path))
1229     return true;
1230 
1231   return false;
1232 }
1233 
IsCDDA() const1234 bool CFileItem::IsCDDA() const
1235 {
1236   return URIUtils::IsCDDA(m_strPath);
1237 }
1238 
IsDVD() const1239 bool CFileItem::IsDVD() const
1240 {
1241   return URIUtils::IsDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
1242 }
1243 
IsOnDVD() const1244 bool CFileItem::IsOnDVD() const
1245 {
1246   return URIUtils::IsOnDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
1247 }
1248 
IsNfs() const1249 bool CFileItem::IsNfs() const
1250 {
1251   return URIUtils::IsNfs(m_strPath);
1252 }
1253 
IsOnLAN() const1254 bool CFileItem::IsOnLAN() const
1255 {
1256   return URIUtils::IsOnLAN(m_strPath);
1257 }
1258 
IsISO9660() const1259 bool CFileItem::IsISO9660() const
1260 {
1261   return URIUtils::IsISO9660(m_strPath);
1262 }
1263 
IsRemote() const1264 bool CFileItem::IsRemote() const
1265 {
1266   return URIUtils::IsRemote(m_strPath);
1267 }
1268 
IsSmb() const1269 bool CFileItem::IsSmb() const
1270 {
1271   return URIUtils::IsSmb(m_strPath);
1272 }
1273 
IsURL() const1274 bool CFileItem::IsURL() const
1275 {
1276   return URIUtils::IsURL(m_strPath);
1277 }
1278 
IsPVR() const1279 bool CFileItem::IsPVR() const
1280 {
1281   return URIUtils::IsPVR(m_strPath);
1282 }
1283 
IsLiveTV() const1284 bool CFileItem::IsLiveTV() const
1285 {
1286   return URIUtils::IsLiveTV(m_strPath);
1287 }
1288 
IsHD() const1289 bool CFileItem::IsHD() const
1290 {
1291   return URIUtils::IsHD(m_strPath);
1292 }
1293 
IsMusicDb() const1294 bool CFileItem::IsMusicDb() const
1295 {
1296   return URIUtils::IsMusicDb(m_strPath);
1297 }
1298 
IsVideoDb() const1299 bool CFileItem::IsVideoDb() const
1300 {
1301   return URIUtils::IsVideoDb(m_strPath);
1302 }
1303 
IsVirtualDirectoryRoot() const1304 bool CFileItem::IsVirtualDirectoryRoot() const
1305 {
1306   return (m_bIsFolder && m_strPath.empty());
1307 }
1308 
IsRemovable() const1309 bool CFileItem::IsRemovable() const
1310 {
1311   return IsOnDVD() || IsCDDA() || m_iDriveType == CMediaSource::SOURCE_TYPE_REMOVABLE;
1312 }
1313 
IsReadOnly() const1314 bool CFileItem::IsReadOnly() const
1315 {
1316   if (IsParentFolder())
1317     return true;
1318 
1319   if (m_bIsShareOrDrive)
1320     return true;
1321 
1322   return !CUtil::SupportsWriteFileOperations(m_strPath);
1323 }
1324 
FillInDefaultIcon()1325 void CFileItem::FillInDefaultIcon()
1326 {
1327   if (URIUtils::IsPVRGuideItem(m_strPath))
1328   {
1329     // epg items never have a default icon. no need to execute this expensive method.
1330     // when filling epg grid window, easily tens of thousands of epg items are processed.
1331     return;
1332   }
1333 
1334   //CLog::Log(LOGINFO, "FillInDefaultIcon(%s)", pItem->GetLabel().c_str());
1335   // find the default icon for a file or folder item
1336   // for files this can be the (depending on the file type)
1337   //   default picture for photo's
1338   //   default picture for songs
1339   //   default picture for videos
1340   //   default picture for shortcuts
1341   //   default picture for playlists
1342   //
1343   // for folders
1344   //   for .. folders the default picture for parent folder
1345   //   for other folders the defaultFolder.png
1346 
1347   if (GetArt("icon").empty())
1348   {
1349     if (!m_bIsFolder)
1350     {
1351       /* To reduce the average runtime of this code, this list should
1352        * be ordered with most frequently seen types first.  Also bear
1353        * in mind the complexity of the code behind the check in the
1354        * case of IsWhatever() returns false.
1355        */
1356       if (IsPVRChannel())
1357       {
1358         if (GetPVRChannelInfoTag()->IsRadio())
1359           SetArt("icon", "DefaultMusicSongs.png");
1360         else
1361           SetArt("icon", "DefaultTVShows.png");
1362       }
1363       else if ( IsLiveTV() )
1364       {
1365         // Live TV Channel
1366         SetArt("icon", "DefaultTVShows.png");
1367       }
1368       else if ( URIUtils::IsArchive(m_strPath) )
1369       { // archive
1370         SetArt("icon", "DefaultFile.png");
1371       }
1372       else if ( IsUsablePVRRecording() )
1373       {
1374         // PVR recording
1375         SetArt("icon", "DefaultVideo.png");
1376       }
1377       else if ( IsDeletedPVRRecording() )
1378       {
1379         // PVR deleted recording
1380         SetArt("icon", "DefaultVideoDeleted.png");
1381       }
1382       else if ( IsAudio() )
1383       {
1384         // audio
1385         SetArt("icon", "DefaultAudio.png");
1386       }
1387       else if ( IsVideo() )
1388       {
1389         // video
1390         SetArt("icon", "DefaultVideo.png");
1391       }
1392       else if (IsPVRTimer())
1393       {
1394         SetArt("icon", "DefaultVideo.png");
1395       }
1396       else if ( IsPicture() )
1397       {
1398         // picture
1399         SetArt("icon", "DefaultPicture.png");
1400       }
1401       else if ( IsPlayList() || IsSmartPlayList())
1402       {
1403         SetArt("icon", "DefaultPlaylist.png");
1404       }
1405       else if ( IsPythonScript() )
1406       {
1407         SetArt("icon", "DefaultScript.png");
1408       }
1409       else
1410       {
1411         // default icon for unknown file type
1412         SetArt("icon", "DefaultFile.png");
1413       }
1414     }
1415     else
1416     {
1417       if ( IsPlayList() || IsSmartPlayList())
1418       {
1419         SetArt("icon", "DefaultPlaylist.png");
1420       }
1421       else if (IsParentFolder())
1422       {
1423         SetArt("icon", "DefaultFolderBack.png");
1424       }
1425       else
1426       {
1427         SetArt("icon", "DefaultFolder.png");
1428       }
1429     }
1430   }
1431   // Set the icon overlays (if applicable)
1432   if (!HasOverlay() && !HasProperty("icon_never_overlay"))
1433   {
1434     if (URIUtils::IsInRAR(m_strPath))
1435       SetOverlayImage(CGUIListItem::ICON_OVERLAY_RAR);
1436     else if (URIUtils::IsInZIP(m_strPath))
1437       SetOverlayImage(CGUIListItem::ICON_OVERLAY_ZIP);
1438   }
1439 }
1440 
RemoveExtension()1441 void CFileItem::RemoveExtension()
1442 {
1443   if (m_bIsFolder)
1444     return;
1445 
1446   std::string strLabel = GetLabel();
1447   URIUtils::RemoveExtension(strLabel);
1448   SetLabel(strLabel);
1449 }
1450 
CleanString()1451 void CFileItem::CleanString()
1452 {
1453   if (IsLiveTV())
1454     return;
1455 
1456   std::string strLabel = GetLabel();
1457   std::string strTitle, strTitleAndYear, strYear;
1458   CUtil::CleanString(strLabel, strTitle, strTitleAndYear, strYear, true);
1459   SetLabel(strTitleAndYear);
1460 }
1461 
SetLabel(const std::string & strLabel)1462 void CFileItem::SetLabel(const std::string &strLabel)
1463 {
1464   if (strLabel == "..")
1465   {
1466     m_bIsParentFolder = true;
1467     m_bIsFolder = true;
1468     m_specialSort = SortSpecialOnTop;
1469     SetLabelPreformatted(true);
1470   }
1471   CGUIListItem::SetLabel(strLabel);
1472 }
1473 
SetFileSizeLabel()1474 void CFileItem::SetFileSizeLabel()
1475 {
1476   if(m_bIsFolder && m_dwSize == 0)
1477     SetLabel2("");
1478   else
1479     SetLabel2(StringUtils::SizeToString(m_dwSize));
1480 }
1481 
CanQueue() const1482 bool CFileItem::CanQueue() const
1483 {
1484   return m_bCanQueue;
1485 }
1486 
SetCanQueue(bool bYesNo)1487 void CFileItem::SetCanQueue(bool bYesNo)
1488 {
1489   m_bCanQueue = bYesNo;
1490 }
1491 
IsParentFolder() const1492 bool CFileItem::IsParentFolder() const
1493 {
1494   return m_bIsParentFolder;
1495 }
1496 
FillInMimeType(bool lookup)1497 void CFileItem::FillInMimeType(bool lookup /*= true*/)
1498 {
1499   //! @todo adapt this to use CMime::GetMimeType()
1500   if (m_mimetype.empty())
1501   {
1502     if( m_bIsFolder )
1503       m_mimetype = "x-directory/normal";
1504     else if( m_pvrChannelInfoTag )
1505       m_mimetype = m_pvrChannelInfoTag->MimeType();
1506     else if( StringUtils::StartsWithNoCase(GetDynPath(), "shout://")
1507           || StringUtils::StartsWithNoCase(GetDynPath(), "http://")
1508           || StringUtils::StartsWithNoCase(GetDynPath(), "https://"))
1509     {
1510       // If lookup is false, bail out early to leave mime type empty
1511       if (!lookup)
1512         return;
1513 
1514       CCurlFile::GetMimeType(GetDynURL(), m_mimetype);
1515 
1516       // try to get mime-type again but with an NSPlayer User-Agent
1517       // in order for server to provide correct mime-type.  Allows us
1518       // to properly detect an MMS stream
1519       if (StringUtils::StartsWithNoCase(m_mimetype, "video/x-ms-"))
1520         CCurlFile::GetMimeType(GetDynURL(), m_mimetype, "NSPlayer/11.00.6001.7000");
1521 
1522       // make sure there are no options set in mime-type
1523       // mime-type can look like "video/x-ms-asf ; charset=utf8"
1524       size_t i = m_mimetype.find(';');
1525       if(i != std::string::npos)
1526         m_mimetype.erase(i, m_mimetype.length() - i);
1527       StringUtils::Trim(m_mimetype);
1528     }
1529     else
1530       m_mimetype = CMime::GetMimeType(*this);
1531 
1532     // if it's still empty set to an unknown type
1533     if (m_mimetype.empty())
1534       m_mimetype = "application/octet-stream";
1535   }
1536 
1537   // change protocol to mms for the following mime-type.  Allows us to create proper FileMMS.
1538   if(StringUtils::StartsWithNoCase(m_mimetype, "application/vnd.ms.wms-hdr.asfv1") ||
1539      StringUtils::StartsWithNoCase(m_mimetype, "application/x-mms-framed"))
1540   {
1541     if (m_strDynPath.empty())
1542       m_strDynPath = m_strPath;
1543 
1544     StringUtils::Replace(m_strDynPath, "http:", "mms:");
1545   }
1546 }
1547 
SetMimeTypeForInternetFile()1548 void CFileItem::SetMimeTypeForInternetFile()
1549 {
1550   if (m_doContentLookup && IsInternetStream())
1551   {
1552     SetMimeType("");
1553     FillInMimeType(true);
1554   }
1555 }
1556 
IsSamePath(const CFileItem * item) const1557 bool CFileItem::IsSamePath(const CFileItem *item) const
1558 {
1559   if (!item)
1560     return false;
1561 
1562   if (!m_strPath.empty() && item->GetPath() == m_strPath)
1563   {
1564     if (item->HasProperty("item_start") || HasProperty("item_start"))
1565       return (item->GetProperty("item_start") == GetProperty("item_start"));
1566     return true;
1567   }
1568   if (HasMusicInfoTag() && item->HasMusicInfoTag())
1569   {
1570     if (GetMusicInfoTag()->GetDatabaseId() != -1 && item->GetMusicInfoTag()->GetDatabaseId() != -1)
1571       return ((GetMusicInfoTag()->GetDatabaseId() == item->GetMusicInfoTag()->GetDatabaseId()) &&
1572         (GetMusicInfoTag()->GetType() == item->GetMusicInfoTag()->GetType()));
1573   }
1574   if (HasVideoInfoTag() && item->HasVideoInfoTag())
1575   {
1576     if (GetVideoInfoTag()->m_iDbId != -1 && item->GetVideoInfoTag()->m_iDbId != -1)
1577       return ((GetVideoInfoTag()->m_iDbId == item->GetVideoInfoTag()->m_iDbId) &&
1578         (GetVideoInfoTag()->m_type == item->GetVideoInfoTag()->m_type));
1579   }
1580   if (IsMusicDb() && HasMusicInfoTag())
1581   {
1582     CFileItem dbItem(m_musicInfoTag->GetURL(), false);
1583     if (HasProperty("item_start"))
1584       dbItem.SetProperty("item_start", GetProperty("item_start"));
1585     return dbItem.IsSamePath(item);
1586   }
1587   if (IsVideoDb() && HasVideoInfoTag())
1588   {
1589     CFileItem dbItem(GetVideoInfoTag()->m_strFileNameAndPath, false);
1590     if (HasProperty("item_start"))
1591       dbItem.SetProperty("item_start", GetProperty("item_start"));
1592     return dbItem.IsSamePath(item);
1593   }
1594   if (item->IsMusicDb() && item->HasMusicInfoTag())
1595   {
1596     CFileItem dbItem(item->m_musicInfoTag->GetURL(), false);
1597     if (item->HasProperty("item_start"))
1598       dbItem.SetProperty("item_start", item->GetProperty("item_start"));
1599     return IsSamePath(&dbItem);
1600   }
1601   if (item->IsVideoDb() && item->HasVideoInfoTag())
1602   {
1603     CFileItem dbItem(item->GetVideoInfoTag()->m_strFileNameAndPath, false);
1604     if (item->HasProperty("item_start"))
1605       dbItem.SetProperty("item_start", item->GetProperty("item_start"));
1606     return IsSamePath(&dbItem);
1607   }
1608   if (HasProperty("original_listitem_url"))
1609     return (GetProperty("original_listitem_url") == item->GetPath());
1610   return false;
1611 }
1612 
IsAlbum() const1613 bool CFileItem::IsAlbum() const
1614 {
1615   return m_bIsAlbum;
1616 }
1617 
UpdateInfo(const CFileItem & item,bool replaceLabels)1618 void CFileItem::UpdateInfo(const CFileItem &item, bool replaceLabels /*=true*/)
1619 {
1620   if (item.HasVideoInfoTag())
1621   { // copy info across
1622     //! @todo premiered info is normally stored in m_dateTime by the db
1623 
1624     if (item.m_videoInfoTag)
1625     {
1626       if (m_videoInfoTag)
1627         *m_videoInfoTag = *item.m_videoInfoTag;
1628       else
1629         m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
1630     }
1631     else
1632     {
1633       if (m_videoInfoTag)
1634         delete m_videoInfoTag;
1635 
1636       m_videoInfoTag = new CVideoInfoTag;
1637     }
1638 
1639     m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
1640 
1641     SetOverlayImage(ICON_OVERLAY_UNWATCHED, GetVideoInfoTag()->GetPlayCount() > 0);
1642     SetInvalid();
1643   }
1644   if (item.HasMusicInfoTag())
1645   {
1646     *GetMusicInfoTag() = *item.GetMusicInfoTag();
1647     SetInvalid();
1648   }
1649   if (item.HasPictureInfoTag())
1650   {
1651     *GetPictureInfoTag() = *item.GetPictureInfoTag();
1652     SetInvalid();
1653   }
1654   if (item.HasGameInfoTag())
1655   {
1656     *GetGameInfoTag() = *item.GetGameInfoTag();
1657     SetInvalid();
1658   }
1659   SetDynPath(item.GetDynPath());
1660   if (replaceLabels && !item.GetLabel().empty())
1661     SetLabel(item.GetLabel());
1662   if (replaceLabels && !item.GetLabel2().empty())
1663     SetLabel2(item.GetLabel2());
1664   if (!item.GetArt().empty())
1665     SetArt(item.GetArt());
1666   AppendProperties(item);
1667 }
1668 
MergeInfo(const CFileItem & item)1669 void CFileItem::MergeInfo(const CFileItem& item)
1670 {
1671   // TODO: Currently merge the metadata/art info is implemented for video case only
1672   if (item.HasVideoInfoTag())
1673   {
1674     if (item.m_videoInfoTag)
1675     {
1676       if (m_videoInfoTag)
1677         m_videoInfoTag->Merge(*item.m_videoInfoTag);
1678       else
1679         m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
1680     }
1681 
1682     m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
1683 
1684     SetOverlayImage(ICON_OVERLAY_UNWATCHED, GetVideoInfoTag()->GetPlayCount() > 0);
1685     SetInvalid();
1686   }
1687   if (item.HasMusicInfoTag())
1688   {
1689     *GetMusicInfoTag() = *item.GetMusicInfoTag();
1690     SetInvalid();
1691   }
1692   if (item.HasPictureInfoTag())
1693   {
1694     *GetPictureInfoTag() = *item.GetPictureInfoTag();
1695     SetInvalid();
1696   }
1697   if (item.HasGameInfoTag())
1698   {
1699     *GetGameInfoTag() = *item.GetGameInfoTag();
1700     SetInvalid();
1701   }
1702   SetDynPath(item.GetDynPath());
1703   if (!item.GetLabel().empty())
1704     SetLabel(item.GetLabel());
1705   if (!item.GetLabel2().empty())
1706     SetLabel2(item.GetLabel2());
1707   if (!item.GetArt().empty())
1708   {
1709     if (item.IsVideo())
1710       AppendArt(item.GetArt());
1711     else
1712       SetArt(item.GetArt());
1713   }
1714   AppendProperties(item);
1715 }
1716 
SetFromVideoInfoTag(const CVideoInfoTag & video)1717 void CFileItem::SetFromVideoInfoTag(const CVideoInfoTag &video)
1718 {
1719   if (!video.m_strTitle.empty())
1720     SetLabel(video.m_strTitle);
1721   if (video.m_strFileNameAndPath.empty())
1722   {
1723     m_strPath = video.m_strPath;
1724     URIUtils::AddSlashAtEnd(m_strPath);
1725     m_bIsFolder = true;
1726   }
1727   else
1728   {
1729     m_strPath = video.m_strFileNameAndPath;
1730     m_bIsFolder = false;
1731   }
1732 
1733   if (m_videoInfoTag)
1734     *m_videoInfoTag = video;
1735   else
1736     m_videoInfoTag = new CVideoInfoTag(video);
1737 
1738   if (video.m_iSeason == 0)
1739     SetProperty("isspecial", "true");
1740   FillInDefaultIcon();
1741   FillInMimeType(false);
1742 }
1743 
1744 namespace
1745 {
1746 class CPropertySaveHelper
1747 {
1748 public:
CPropertySaveHelper(CFileItem & item,const std::string & property,const std::string & value)1749   CPropertySaveHelper(CFileItem& item, const std::string& property, const std::string& value)
1750     : m_item(item), m_property(property), m_value(value)
1751   {
1752   }
1753 
NeedsSave() const1754   bool NeedsSave() const { return !m_value.empty() || m_item.HasProperty(m_property); }
1755 
GetValueToSave(const std::string & currentValue) const1756   std::string GetValueToSave(const std::string& currentValue) const
1757   {
1758     std::string value;
1759 
1760     if (!m_value.empty())
1761     {
1762       // Overwrite whatever we have; remember what we had originally.
1763       if (!m_item.HasProperty(m_property))
1764         m_item.SetProperty(m_property, currentValue);
1765 
1766       value = m_value;
1767     }
1768     else if (m_item.HasProperty(m_property))
1769     {
1770       // Restore original value
1771       value = m_item.GetProperty(m_property).asString();
1772       m_item.ClearProperty(m_property);
1773     }
1774 
1775     return value;
1776   }
1777 
1778 private:
1779   CFileItem& m_item;
1780   const std::string m_property;
1781   const std::string m_value;
1782 };
1783 } // unnamed namespace
1784 
SetFromMusicInfoTag(const MUSIC_INFO::CMusicInfoTag & music)1785 void CFileItem::SetFromMusicInfoTag(const MUSIC_INFO::CMusicInfoTag& music)
1786 {
1787   const std::string path = GetPath();
1788   if (path.empty())
1789   {
1790     SetPath(music.GetURL());
1791   }
1792   else
1793   {
1794     const CPropertySaveHelper dynpath(*this, "OriginalDynPath", music.GetURL());
1795     if (dynpath.NeedsSave())
1796       SetDynPath(dynpath.GetValueToSave(m_strDynPath));
1797   }
1798 
1799   const CPropertySaveHelper label(*this, "OriginalLabel", music.GetTitle());
1800   if (label.NeedsSave())
1801     SetLabel(label.GetValueToSave(GetLabel()));
1802 
1803   const CPropertySaveHelper thumb(*this, "OriginalThumb", music.GetStationArt());
1804   if (thumb.NeedsSave())
1805     SetArt("thumb", thumb.GetValueToSave(GetArt("thumb")));
1806 
1807   *GetMusicInfoTag() = music;
1808   FillInDefaultIcon();
1809   FillInMimeType(false);
1810 }
1811 
SetFromAlbum(const CAlbum & album)1812 void CFileItem::SetFromAlbum(const CAlbum &album)
1813 {
1814   if (!album.strAlbum.empty())
1815     SetLabel(album.strAlbum);
1816   m_bIsFolder = true;
1817   m_strLabel2 = album.GetAlbumArtistString();
1818   GetMusicInfoTag()->SetAlbum(album);
1819   SetArt(album.art);
1820   m_bIsAlbum = true;
1821   CMusicDatabase::SetPropertiesFromAlbum(*this,album);
1822   FillInMimeType(false);
1823 }
1824 
SetFromSong(const CSong & song)1825 void CFileItem::SetFromSong(const CSong &song)
1826 {
1827   if (!song.strTitle.empty())
1828     SetLabel(song.strTitle);
1829   if (song.idSong > 0)
1830   {
1831     std::string strExt = URIUtils::GetExtension(song.strFileName);
1832     m_strPath = StringUtils::Format("musicdb://songs/%li%s", song.idSong, strExt.c_str());
1833   }
1834   else if (!song.strFileName.empty())
1835     m_strPath = song.strFileName;
1836   GetMusicInfoTag()->SetSong(song);
1837   m_lStartOffset = song.iStartOffset;
1838   m_lStartPartNumber = 1;
1839   SetProperty("item_start", song.iStartOffset);
1840   m_lEndOffset = song.iEndOffset;
1841   if (!song.strThumb.empty())
1842     SetArt("thumb", song.strThumb);
1843   FillInMimeType(false);
1844 }
1845 
GetOpticalMediaPath() const1846 std::string CFileItem::GetOpticalMediaPath() const
1847 {
1848   std::string path;
1849   path = URIUtils::AddFileToFolder(GetPath(), "VIDEO_TS.IFO");
1850   if (CFile::Exists(path))
1851     return path;
1852 
1853   path = URIUtils::AddFileToFolder(GetPath(), "VIDEO_TS", "VIDEO_TS.IFO");
1854   if (CFile::Exists(path))
1855     return path;
1856 
1857 #ifdef HAVE_LIBBLURAY
1858   path = URIUtils::AddFileToFolder(GetPath(), "index.bdmv");
1859   if (CFile::Exists(path))
1860     return path;
1861 
1862   path = URIUtils::AddFileToFolder(GetPath(), "BDMV", "index.bdmv");
1863   if (CFile::Exists(path))
1864     return path;
1865 
1866   path = URIUtils::AddFileToFolder(GetPath(), "INDEX.BDM");
1867   if (CFile::Exists(path))
1868     return path;
1869 
1870   path = URIUtils::AddFileToFolder(GetPath(), "BDMV", "INDEX.BDM");
1871   if (CFile::Exists(path))
1872     return path;
1873 #endif
1874   return std::string();
1875 }
1876 
1877 /**
1878 * @todo Ideally this (and SetPath) would not be available outside of construction
1879 * for CFileItem objects, or at least restricted to essentially be equivalent
1880 * to construction. This would require re-formulating a bunch of CFileItem
1881 * construction, and also allowing CFileItemList to have its own (public)
1882 * SetURL() function, so for now we give direct access.
1883 */
SetURL(const CURL & url)1884 void CFileItem::SetURL(const CURL& url)
1885 {
1886   m_strPath = url.Get();
1887 }
1888 
GetURL() const1889 const CURL CFileItem::GetURL() const
1890 {
1891   CURL url(m_strPath);
1892   return url;
1893 }
1894 
IsURL(const CURL & url) const1895 bool CFileItem::IsURL(const CURL& url) const
1896 {
1897   return IsPath(url.Get());
1898 }
1899 
IsPath(const std::string & path,bool ignoreURLOptions) const1900 bool CFileItem::IsPath(const std::string& path, bool ignoreURLOptions /* = false */) const
1901 {
1902   return URIUtils::PathEquals(m_strPath, path, false, ignoreURLOptions);
1903 }
1904 
SetDynURL(const CURL & url)1905 void CFileItem::SetDynURL(const CURL& url)
1906 {
1907   m_strDynPath = url.Get();
1908 }
1909 
GetDynURL() const1910 const CURL CFileItem::GetDynURL() const
1911 {
1912   if (!m_strDynPath.empty())
1913   {
1914     CURL url(m_strDynPath);
1915     return url;
1916   }
1917   else
1918   {
1919     CURL url(m_strPath);
1920     return url;
1921   }
1922 }
1923 
GetDynPath() const1924 const std::string &CFileItem::GetDynPath() const
1925 {
1926   if (!m_strDynPath.empty())
1927     return m_strDynPath;
1928   else
1929     return m_strPath;
1930 }
1931 
SetDynPath(const std::string & path)1932 void CFileItem::SetDynPath(const std::string &path)
1933 {
1934   m_strDynPath = path;
1935 }
1936 
SetCueDocument(const CCueDocumentPtr & cuePtr)1937 void CFileItem::SetCueDocument(const CCueDocumentPtr& cuePtr)
1938 {
1939   m_cueDocument = cuePtr;
1940 }
1941 
LoadEmbeddedCue()1942 void CFileItem::LoadEmbeddedCue()
1943 {
1944   CMusicInfoTag& tag = *GetMusicInfoTag();
1945   if (!tag.Loaded())
1946     return;
1947 
1948   const std::string embeddedCue = tag.GetCueSheet();
1949   if (!embeddedCue.empty())
1950   {
1951     CCueDocumentPtr cuesheet(new CCueDocument);
1952     if (cuesheet->ParseTag(embeddedCue))
1953     {
1954       std::vector<std::string> MediaFileVec;
1955       cuesheet->GetMediaFiles(MediaFileVec);
1956       for (std::vector<std::string>::iterator itMedia = MediaFileVec.begin(); itMedia != MediaFileVec.end(); itMedia++)
1957         cuesheet->UpdateMediaFile(*itMedia, GetPath());
1958       SetCueDocument(cuesheet);
1959     }
1960     // Clear cuesheet tag having added it to item
1961     tag.SetCueSheet("");
1962   }
1963 }
1964 
HasCueDocument() const1965 bool CFileItem::HasCueDocument() const
1966 {
1967   return (m_cueDocument.get() != nullptr);
1968 }
1969 
LoadTracksFromCueDocument(CFileItemList & scannedItems)1970 bool CFileItem::LoadTracksFromCueDocument(CFileItemList& scannedItems)
1971 {
1972   if (!m_cueDocument)
1973     return false;
1974 
1975   CMusicInfoTag& tag = *GetMusicInfoTag();
1976 
1977   VECSONGS tracks;
1978   m_cueDocument->GetSongs(tracks);
1979 
1980   bool oneFilePerTrack = m_cueDocument->IsOneFilePerTrack();
1981   m_cueDocument.reset();
1982 
1983   int tracksFound = 0;
1984   for (VECSONGS::iterator it = tracks.begin(); it != tracks.end(); ++it)
1985   {
1986     CSong& song = *it;
1987     if (song.strFileName == GetPath())
1988     {
1989       if (tag.Loaded())
1990       {
1991         if (song.strAlbum.empty() && !tag.GetAlbum().empty())
1992           song.strAlbum = tag.GetAlbum();
1993         //Pass album artist to final MusicInfoTag object via setting song album artist vector.
1994         if (song.GetAlbumArtist().empty() && !tag.GetAlbumArtist().empty())
1995           song.SetAlbumArtist(tag.GetAlbumArtist());
1996         if (song.genre.empty() && !tag.GetGenre().empty())
1997           song.genre = tag.GetGenre();
1998         //Pass artist to final MusicInfoTag object via setting song artist description string only.
1999         //Artist credits not used during loading from cue sheet.
2000         if (song.strArtistDesc.empty() && !tag.GetArtistString().empty())
2001           song.strArtistDesc = tag.GetArtistString();
2002         if (tag.GetDiscNumber())
2003           song.iTrack |= (tag.GetDiscNumber() << 16); // see CMusicInfoTag::GetDiscNumber()
2004         if (!tag.GetCueSheet().empty())
2005           song.strCueSheet = tag.GetCueSheet();
2006 
2007         if (tag.GetYear())
2008           song.strReleaseDate = tag.GetReleaseDate();
2009         if (song.embeddedArt.Empty() && !tag.GetCoverArtInfo().Empty())
2010           song.embeddedArt = tag.GetCoverArtInfo();
2011       }
2012 
2013       if (!song.iDuration && tag.GetDuration() > 0)
2014       { // must be the last song
2015         song.iDuration = CUtil::ConvertMilliSecsToSecsIntRounded(CUtil::ConvertSecsToMilliSecs(tag.GetDuration()) - song.iStartOffset);
2016       }
2017       if ( tag.Loaded() && oneFilePerTrack && ! ( tag.GetAlbum().empty() || tag.GetArtist().empty() || tag.GetTitle().empty() ) )
2018       {
2019         // If there are multiple files in a cue file, the tags from the files should be prefered if they exist.
2020         scannedItems.Add(CFileItemPtr(new CFileItem(song, tag)));
2021       }
2022       else
2023       {
2024         scannedItems.Add(CFileItemPtr(new CFileItem(song)));
2025       }
2026       ++tracksFound;
2027     }
2028   }
2029   return tracksFound != 0;
2030 }
2031 
2032 /////////////////////////////////////////////////////////////////////////////////
2033 /////
2034 ///// CFileItemList
2035 /////
2036 //////////////////////////////////////////////////////////////////////////////////
2037 
CFileItemList()2038 CFileItemList::CFileItemList()
2039 : CFileItem("", true)
2040 {
2041 }
2042 
CFileItemList(const std::string & strPath)2043 CFileItemList::CFileItemList(const std::string& strPath)
2044 : CFileItem(strPath, true)
2045 {
2046 }
2047 
~CFileItemList()2048 CFileItemList::~CFileItemList()
2049 {
2050   Clear();
2051 }
2052 
operator [](int iItem)2053 CFileItemPtr CFileItemList::operator[] (int iItem)
2054 {
2055   return Get(iItem);
2056 }
2057 
operator [](int iItem) const2058 const CFileItemPtr CFileItemList::operator[] (int iItem) const
2059 {
2060   return Get(iItem);
2061 }
2062 
operator [](const std::string & strPath)2063 CFileItemPtr CFileItemList::operator[] (const std::string& strPath)
2064 {
2065   return Get(strPath);
2066 }
2067 
operator [](const std::string & strPath) const2068 const CFileItemPtr CFileItemList::operator[] (const std::string& strPath) const
2069 {
2070   return Get(strPath);
2071 }
2072 
SetIgnoreURLOptions(bool ignoreURLOptions)2073 void CFileItemList::SetIgnoreURLOptions(bool ignoreURLOptions)
2074 {
2075   m_ignoreURLOptions = ignoreURLOptions;
2076 
2077   if (m_fastLookup)
2078   {
2079     m_fastLookup = false; // Force SetFastlookup to clear map
2080     SetFastLookup(true);  // and regenerate map
2081   }
2082 }
2083 
SetFastLookup(bool fastLookup)2084 void CFileItemList::SetFastLookup(bool fastLookup)
2085 {
2086   CSingleLock lock(m_lock);
2087 
2088   if (fastLookup && !m_fastLookup)
2089   { // generate the map
2090     m_map.clear();
2091     for (unsigned int i=0; i < m_items.size(); i++)
2092     {
2093       CFileItemPtr pItem = m_items[i];
2094       m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2095     }
2096   }
2097   if (!fastLookup && m_fastLookup)
2098     m_map.clear();
2099   m_fastLookup = fastLookup;
2100 }
2101 
Contains(const std::string & fileName) const2102 bool CFileItemList::Contains(const std::string& fileName) const
2103 {
2104   CSingleLock lock(m_lock);
2105 
2106   if (m_fastLookup)
2107     return m_map.find(m_ignoreURLOptions ? CURL(fileName).GetWithoutOptions() : fileName) != m_map.end();
2108 
2109   // slow method...
2110   for (unsigned int i = 0; i < m_items.size(); i++)
2111   {
2112     const CFileItemPtr pItem = m_items[i];
2113     if (pItem->IsPath(m_ignoreURLOptions ? CURL(fileName).GetWithoutOptions() : fileName))
2114       return true;
2115   }
2116   return false;
2117 }
2118 
Clear()2119 void CFileItemList::Clear()
2120 {
2121   CSingleLock lock(m_lock);
2122 
2123   ClearItems();
2124   m_sortDescription.sortBy = SortByNone;
2125   m_sortDescription.sortOrder = SortOrderNone;
2126   m_sortDescription.sortAttributes = SortAttributeNone;
2127   m_sortIgnoreFolders = false;
2128   m_cacheToDisc = CACHE_IF_SLOW;
2129   m_sortDetails.clear();
2130   m_replaceListing = false;
2131   m_content.clear();
2132 }
2133 
ClearItems()2134 void CFileItemList::ClearItems()
2135 {
2136   CSingleLock lock(m_lock);
2137   // make sure we free the memory of the items (these are GUIControls which may have allocated resources)
2138   FreeMemory();
2139   for (unsigned int i = 0; i < m_items.size(); i++)
2140   {
2141     CFileItemPtr item = m_items[i];
2142     item->FreeMemory();
2143   }
2144   m_items.clear();
2145   m_map.clear();
2146 }
2147 
Add(CFileItemPtr pItem)2148 void CFileItemList::Add(CFileItemPtr pItem)
2149 {
2150   CSingleLock lock(m_lock);
2151   if (m_fastLookup)
2152     m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2153   m_items.emplace_back(std::move(pItem));
2154 }
2155 
Add(CFileItem && item)2156 void CFileItemList::Add(CFileItem&& item)
2157 {
2158   CSingleLock lock(m_lock);
2159   auto ptr = std::make_shared<CFileItem>(std::move(item));
2160   if (m_fastLookup)
2161     m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(ptr->GetPath()).GetWithoutOptions() : ptr->GetPath(), ptr));
2162   m_items.emplace_back(std::move(ptr));
2163 }
2164 
AddFront(const CFileItemPtr & pItem,int itemPosition)2165 void CFileItemList::AddFront(const CFileItemPtr &pItem, int itemPosition)
2166 {
2167   CSingleLock lock(m_lock);
2168 
2169   if (itemPosition >= 0)
2170   {
2171     m_items.insert(m_items.begin()+itemPosition, pItem);
2172   }
2173   else
2174   {
2175     m_items.insert(m_items.begin()+(m_items.size()+itemPosition), pItem);
2176   }
2177   if (m_fastLookup)
2178   {
2179     m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2180   }
2181 }
2182 
Remove(CFileItem * pItem)2183 void CFileItemList::Remove(CFileItem* pItem)
2184 {
2185   CSingleLock lock(m_lock);
2186 
2187   for (IVECFILEITEMS it = m_items.begin(); it != m_items.end(); ++it)
2188   {
2189     if (pItem == it->get())
2190     {
2191       m_items.erase(it);
2192       if (m_fastLookup)
2193       {
2194         m_map.erase(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath());
2195       }
2196       break;
2197     }
2198   }
2199 }
2200 
Remove(int iItem)2201 void CFileItemList::Remove(int iItem)
2202 {
2203   CSingleLock lock(m_lock);
2204 
2205   if (iItem >= 0 && iItem < Size())
2206   {
2207     CFileItemPtr pItem = *(m_items.begin() + iItem);
2208     if (m_fastLookup)
2209     {
2210       m_map.erase(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath());
2211     }
2212     m_items.erase(m_items.begin() + iItem);
2213   }
2214 }
2215 
Append(const CFileItemList & itemlist)2216 void CFileItemList::Append(const CFileItemList& itemlist)
2217 {
2218   CSingleLock lock(m_lock);
2219 
2220   for (int i = 0; i < itemlist.Size(); ++i)
2221     Add(itemlist[i]);
2222 }
2223 
Assign(const CFileItemList & itemlist,bool append)2224 void CFileItemList::Assign(const CFileItemList& itemlist, bool append)
2225 {
2226   CSingleLock lock(m_lock);
2227   if (!append)
2228     Clear();
2229   Append(itemlist);
2230   SetPath(itemlist.GetPath());
2231   SetLabel(itemlist.GetLabel());
2232   m_sortDetails = itemlist.m_sortDetails;
2233   m_sortDescription = itemlist.m_sortDescription;
2234   m_replaceListing = itemlist.m_replaceListing;
2235   m_content = itemlist.m_content;
2236   m_mapProperties = itemlist.m_mapProperties;
2237   m_cacheToDisc = itemlist.m_cacheToDisc;
2238 }
2239 
Copy(const CFileItemList & items,bool copyItems)2240 bool CFileItemList::Copy(const CFileItemList& items, bool copyItems /* = true */)
2241 {
2242   // assign all CFileItem parts
2243   *static_cast<CFileItem*>(this) = static_cast<const CFileItem&>(items);
2244 
2245   // assign the rest of the CFileItemList properties
2246   m_replaceListing  = items.m_replaceListing;
2247   m_content         = items.m_content;
2248   m_mapProperties   = items.m_mapProperties;
2249   m_cacheToDisc     = items.m_cacheToDisc;
2250   m_sortDetails     = items.m_sortDetails;
2251   m_sortDescription = items.m_sortDescription;
2252   m_sortIgnoreFolders = items.m_sortIgnoreFolders;
2253 
2254   if (copyItems)
2255   {
2256     // make a copy of each item
2257     for (int i = 0; i < items.Size(); i++)
2258     {
2259       CFileItemPtr newItem(new CFileItem(*items[i]));
2260       Add(newItem);
2261     }
2262   }
2263 
2264   return true;
2265 }
2266 
Get(int iItem)2267 CFileItemPtr CFileItemList::Get(int iItem)
2268 {
2269   CSingleLock lock(m_lock);
2270 
2271   if (iItem > -1 && iItem < (int)m_items.size())
2272     return m_items[iItem];
2273 
2274   return CFileItemPtr();
2275 }
2276 
Get(int iItem) const2277 const CFileItemPtr CFileItemList::Get(int iItem) const
2278 {
2279   CSingleLock lock(m_lock);
2280 
2281   if (iItem > -1 && iItem < (int)m_items.size())
2282     return m_items[iItem];
2283 
2284   return CFileItemPtr();
2285 }
2286 
Get(const std::string & strPath)2287 CFileItemPtr CFileItemList::Get(const std::string& strPath)
2288 {
2289   CSingleLock lock(m_lock);
2290 
2291   if (m_fastLookup)
2292   {
2293     IMAPFILEITEMS it = m_map.find(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath);
2294     if (it != m_map.end())
2295       return it->second;
2296 
2297     return CFileItemPtr();
2298   }
2299   // slow method...
2300   for (unsigned int i = 0; i < m_items.size(); i++)
2301   {
2302     CFileItemPtr pItem = m_items[i];
2303     if (pItem->IsPath(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath))
2304       return pItem;
2305   }
2306 
2307   return CFileItemPtr();
2308 }
2309 
Get(const std::string & strPath) const2310 const CFileItemPtr CFileItemList::Get(const std::string& strPath) const
2311 {
2312   CSingleLock lock(m_lock);
2313 
2314   if (m_fastLookup)
2315   {
2316     std::map<std::string, CFileItemPtr>::const_iterator it = m_map.find(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath);
2317     if (it != m_map.end())
2318       return it->second;
2319 
2320     return CFileItemPtr();
2321   }
2322   // slow method...
2323   for (unsigned int i = 0; i < m_items.size(); i++)
2324   {
2325     CFileItemPtr pItem = m_items[i];
2326     if (pItem->IsPath(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath))
2327       return pItem;
2328   }
2329 
2330   return CFileItemPtr();
2331 }
2332 
Size() const2333 int CFileItemList::Size() const
2334 {
2335   CSingleLock lock(m_lock);
2336   return (int)m_items.size();
2337 }
2338 
IsEmpty() const2339 bool CFileItemList::IsEmpty() const
2340 {
2341   CSingleLock lock(m_lock);
2342   return m_items.empty();
2343 }
2344 
Reserve(size_t iCount)2345 void CFileItemList::Reserve(size_t iCount)
2346 {
2347   CSingleLock lock(m_lock);
2348   m_items.reserve(iCount);
2349 }
2350 
Sort(FILEITEMLISTCOMPARISONFUNC func)2351 void CFileItemList::Sort(FILEITEMLISTCOMPARISONFUNC func)
2352 {
2353   CSingleLock lock(m_lock);
2354   std::stable_sort(m_items.begin(), m_items.end(), func);
2355 }
2356 
FillSortFields(FILEITEMFILLFUNC func)2357 void CFileItemList::FillSortFields(FILEITEMFILLFUNC func)
2358 {
2359   CSingleLock lock(m_lock);
2360   std::for_each(m_items.begin(), m_items.end(), func);
2361 }
2362 
Sort(SortBy sortBy,SortOrder sortOrder,SortAttribute sortAttributes)2363 void CFileItemList::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute sortAttributes /* = SortAttributeNone */)
2364 {
2365   if (sortBy == SortByNone ||
2366      (m_sortDescription.sortBy == sortBy && m_sortDescription.sortOrder == sortOrder &&
2367       m_sortDescription.sortAttributes == sortAttributes))
2368     return;
2369 
2370   SortDescription sorting;
2371   sorting.sortBy = sortBy;
2372   sorting.sortOrder = sortOrder;
2373   sorting.sortAttributes = sortAttributes;
2374 
2375   Sort(sorting);
2376   m_sortDescription = sorting;
2377 }
2378 
Sort(SortDescription sortDescription)2379 void CFileItemList::Sort(SortDescription sortDescription)
2380 {
2381   if (sortDescription.sortBy == SortByFile ||
2382       sortDescription.sortBy == SortBySortTitle ||
2383       sortDescription.sortBy == SortByDateAdded ||
2384       sortDescription.sortBy == SortByRating ||
2385       sortDescription.sortBy == SortByYear ||
2386       sortDescription.sortBy == SortByPlaylistOrder ||
2387       sortDescription.sortBy == SortByLastPlayed ||
2388       sortDescription.sortBy == SortByPlaycount)
2389     sortDescription.sortAttributes = (SortAttribute)((int)sortDescription.sortAttributes | SortAttributeIgnoreFolders);
2390 
2391   if (sortDescription.sortBy == SortByNone ||
2392      (m_sortDescription.sortBy == sortDescription.sortBy && m_sortDescription.sortOrder == sortDescription.sortOrder &&
2393       m_sortDescription.sortAttributes == sortDescription.sortAttributes))
2394     return;
2395 
2396   if (m_sortIgnoreFolders)
2397     sortDescription.sortAttributes = (SortAttribute)((int)sortDescription.sortAttributes | SortAttributeIgnoreFolders);
2398 
2399   const Fields fields = SortUtils::GetFieldsForSorting(sortDescription.sortBy);
2400   SortItems sortItems((size_t)Size());
2401   for (int index = 0; index < Size(); index++)
2402   {
2403     sortItems[index] = std::shared_ptr<SortItem>(new SortItem);
2404     m_items[index]->ToSortable(*sortItems[index], fields);
2405     (*sortItems[index])[FieldId] = index;
2406   }
2407 
2408   // do the sorting
2409   SortUtils::Sort(sortDescription, sortItems);
2410 
2411   // apply the new order to the existing CFileItems
2412   VECFILEITEMS sortedFileItems;
2413   sortedFileItems.reserve(Size());
2414   for (SortItems::const_iterator it = sortItems.begin(); it != sortItems.end(); it++)
2415   {
2416     CFileItemPtr item = m_items[(int)(*it)->at(FieldId).asInteger()];
2417     // Set the sort label in the CFileItem
2418     item->SetSortLabel((*it)->at(FieldSort).asWideString());
2419 
2420     sortedFileItems.push_back(item);
2421   }
2422 
2423   // replace the current list with the re-ordered one
2424   m_items = std::move(sortedFileItems);
2425 }
2426 
Randomize()2427 void CFileItemList::Randomize()
2428 {
2429   CSingleLock lock(m_lock);
2430   KODI::UTILS::RandomShuffle(m_items.begin(), m_items.end());
2431 }
2432 
Archive(CArchive & ar)2433 void CFileItemList::Archive(CArchive& ar)
2434 {
2435   CSingleLock lock(m_lock);
2436   if (ar.IsStoring())
2437   {
2438     CFileItem::Archive(ar);
2439 
2440     int i = 0;
2441     if (!m_items.empty() && m_items[0]->IsParentFolder())
2442       i = 1;
2443 
2444     ar << (int)(m_items.size() - i);
2445 
2446     ar << m_ignoreURLOptions;
2447 
2448     ar << m_fastLookup;
2449 
2450     ar << (int)m_sortDescription.sortBy;
2451     ar << (int)m_sortDescription.sortOrder;
2452     ar << (int)m_sortDescription.sortAttributes;
2453     ar << m_sortIgnoreFolders;
2454     ar << (int)m_cacheToDisc;
2455 
2456     ar << (int)m_sortDetails.size();
2457     for (unsigned int j = 0; j < m_sortDetails.size(); ++j)
2458     {
2459       const GUIViewSortDetails &details = m_sortDetails[j];
2460       ar << (int)details.m_sortDescription.sortBy;
2461       ar << (int)details.m_sortDescription.sortOrder;
2462       ar << (int)details.m_sortDescription.sortAttributes;
2463       ar << details.m_buttonLabel;
2464       ar << details.m_labelMasks.m_strLabelFile;
2465       ar << details.m_labelMasks.m_strLabelFolder;
2466       ar << details.m_labelMasks.m_strLabel2File;
2467       ar << details.m_labelMasks.m_strLabel2Folder;
2468     }
2469 
2470     ar << m_content;
2471 
2472     for (; i < (int)m_items.size(); ++i)
2473     {
2474       CFileItemPtr pItem = m_items[i];
2475       ar << *pItem;
2476     }
2477   }
2478   else
2479   {
2480     CFileItemPtr pParent;
2481     if (!IsEmpty())
2482     {
2483       CFileItemPtr pItem=m_items[0];
2484       if (pItem->IsParentFolder())
2485         pParent.reset(new CFileItem(*pItem));
2486     }
2487 
2488     SetIgnoreURLOptions(false);
2489     SetFastLookup(false);
2490     Clear();
2491 
2492     CFileItem::Archive(ar);
2493 
2494     int iSize = 0;
2495     ar >> iSize;
2496     if (iSize <= 0)
2497       return ;
2498 
2499     if (pParent)
2500     {
2501       m_items.reserve(iSize + 1);
2502       m_items.push_back(pParent);
2503     }
2504     else
2505       m_items.reserve(iSize);
2506 
2507     bool ignoreURLOptions = false;
2508     ar >> ignoreURLOptions;
2509 
2510     bool fastLookup = false;
2511     ar >> fastLookup;
2512 
2513     int tempint;
2514     ar >> tempint;
2515     m_sortDescription.sortBy = (SortBy)tempint;
2516     ar >> tempint;
2517     m_sortDescription.sortOrder = (SortOrder)tempint;
2518     ar >> tempint;
2519     m_sortDescription.sortAttributes = (SortAttribute)tempint;
2520     ar >> m_sortIgnoreFolders;
2521     ar >> tempint;
2522     m_cacheToDisc = CACHE_TYPE(tempint);
2523 
2524     unsigned int detailSize = 0;
2525     ar >> detailSize;
2526     for (unsigned int j = 0; j < detailSize; ++j)
2527     {
2528       GUIViewSortDetails details;
2529       ar >> tempint;
2530       details.m_sortDescription.sortBy = (SortBy)tempint;
2531       ar >> tempint;
2532       details.m_sortDescription.sortOrder = (SortOrder)tempint;
2533       ar >> tempint;
2534       details.m_sortDescription.sortAttributes = (SortAttribute)tempint;
2535       ar >> details.m_buttonLabel;
2536       ar >> details.m_labelMasks.m_strLabelFile;
2537       ar >> details.m_labelMasks.m_strLabelFolder;
2538       ar >> details.m_labelMasks.m_strLabel2File;
2539       ar >> details.m_labelMasks.m_strLabel2Folder;
2540       m_sortDetails.push_back(details);
2541     }
2542 
2543     ar >> m_content;
2544 
2545     for (int i = 0; i < iSize; ++i)
2546     {
2547       CFileItemPtr pItem(new CFileItem);
2548       ar >> *pItem;
2549       Add(pItem);
2550     }
2551 
2552     SetIgnoreURLOptions(ignoreURLOptions);
2553     SetFastLookup(fastLookup);
2554   }
2555 }
2556 
FillInDefaultIcons()2557 void CFileItemList::FillInDefaultIcons()
2558 {
2559   CSingleLock lock(m_lock);
2560   for (int i = 0; i < (int)m_items.size(); ++i)
2561   {
2562     CFileItemPtr pItem = m_items[i];
2563     pItem->FillInDefaultIcon();
2564   }
2565 }
2566 
GetFolderCount() const2567 int CFileItemList::GetFolderCount() const
2568 {
2569   CSingleLock lock(m_lock);
2570   int nFolderCount = 0;
2571   for (int i = 0; i < (int)m_items.size(); i++)
2572   {
2573     CFileItemPtr pItem = m_items[i];
2574     if (pItem->m_bIsFolder)
2575       nFolderCount++;
2576   }
2577 
2578   return nFolderCount;
2579 }
2580 
GetObjectCount() const2581 int CFileItemList::GetObjectCount() const
2582 {
2583   CSingleLock lock(m_lock);
2584 
2585   int numObjects = (int)m_items.size();
2586   if (numObjects && m_items[0]->IsParentFolder())
2587     numObjects--;
2588 
2589   return numObjects;
2590 }
2591 
GetFileCount() const2592 int CFileItemList::GetFileCount() const
2593 {
2594   CSingleLock lock(m_lock);
2595   int nFileCount = 0;
2596   for (int i = 0; i < (int)m_items.size(); i++)
2597   {
2598     CFileItemPtr pItem = m_items[i];
2599     if (!pItem->m_bIsFolder)
2600       nFileCount++;
2601   }
2602 
2603   return nFileCount;
2604 }
2605 
GetSelectedCount() const2606 int CFileItemList::GetSelectedCount() const
2607 {
2608   CSingleLock lock(m_lock);
2609   int count = 0;
2610   for (int i = 0; i < (int)m_items.size(); i++)
2611   {
2612     CFileItemPtr pItem = m_items[i];
2613     if (pItem->IsSelected())
2614       count++;
2615   }
2616 
2617   return count;
2618 }
2619 
FilterCueItems()2620 void CFileItemList::FilterCueItems()
2621 {
2622   CSingleLock lock(m_lock);
2623   // Handle .CUE sheet files...
2624   std::vector<std::string> itemstodelete;
2625   for (int i = 0; i < (int)m_items.size(); i++)
2626   {
2627     CFileItemPtr pItem = m_items[i];
2628     if (!pItem->m_bIsFolder)
2629     { // see if it's a .CUE sheet
2630       if (pItem->IsCUESheet())
2631       {
2632         CCueDocumentPtr cuesheet(new CCueDocument);
2633         if (cuesheet->ParseFile(pItem->GetPath()))
2634         {
2635           std::vector<std::string> MediaFileVec;
2636           cuesheet->GetMediaFiles(MediaFileVec);
2637 
2638           // queue the cue sheet and the underlying media file for deletion
2639           for(std::vector<std::string>::iterator itMedia = MediaFileVec.begin(); itMedia != MediaFileVec.end(); itMedia++)
2640           {
2641             std::string strMediaFile = *itMedia;
2642             std::string fileFromCue = strMediaFile; // save the file from the cue we're matching against,
2643                                                    // as we're going to search for others here...
2644             bool bFoundMediaFile = CFile::Exists(strMediaFile);
2645             if (!bFoundMediaFile)
2646             {
2647               // try file in same dir, not matching case...
2648               if (Contains(strMediaFile))
2649               {
2650                 bFoundMediaFile = true;
2651               }
2652               else
2653               {
2654                 // try removing the .cue extension...
2655                 strMediaFile = pItem->GetPath();
2656                 URIUtils::RemoveExtension(strMediaFile);
2657                 CFileItem item(strMediaFile, false);
2658                 if (item.IsAudio() && Contains(strMediaFile))
2659                 {
2660                   bFoundMediaFile = true;
2661                 }
2662                 else
2663                 { // try replacing the extension with one of our allowed ones.
2664                   std::vector<std::string> extensions = StringUtils::Split(CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
2665                   for (std::vector<std::string>::const_iterator i = extensions.begin(); i != extensions.end(); ++i)
2666                   {
2667                     strMediaFile = URIUtils::ReplaceExtension(pItem->GetPath(), *i);
2668                     CFileItem item(strMediaFile, false);
2669                     if (!item.IsCUESheet() && !item.IsPlayList() && Contains(strMediaFile))
2670                     {
2671                       bFoundMediaFile = true;
2672                       break;
2673                     }
2674                   }
2675                 }
2676               }
2677             }
2678             if (bFoundMediaFile)
2679             {
2680               cuesheet->UpdateMediaFile(fileFromCue, strMediaFile);
2681               // apply CUE for later processing
2682               for (int j = 0; j < (int)m_items.size(); j++)
2683               {
2684                 CFileItemPtr pItem = m_items[j];
2685                 if (StringUtils::CompareNoCase(pItem->GetPath(), strMediaFile) == 0)
2686                   pItem->SetCueDocument(cuesheet);
2687               }
2688             }
2689           }
2690         }
2691         itemstodelete.push_back(pItem->GetPath());
2692       }
2693     }
2694   }
2695   // now delete the .CUE files.
2696   for (int i = 0; i < (int)itemstodelete.size(); i++)
2697   {
2698     for (int j = 0; j < (int)m_items.size(); j++)
2699     {
2700       CFileItemPtr pItem = m_items[j];
2701       if (StringUtils::CompareNoCase(pItem->GetPath(), itemstodelete[i]) == 0)
2702       { // delete this item
2703         m_items.erase(m_items.begin() + j);
2704         break;
2705       }
2706     }
2707   }
2708 }
2709 
2710 // Remove the extensions from the filenames
RemoveExtensions()2711 void CFileItemList::RemoveExtensions()
2712 {
2713   CSingleLock lock(m_lock);
2714   for (int i = 0; i < Size(); ++i)
2715     m_items[i]->RemoveExtension();
2716 }
2717 
Stack(bool stackFiles)2718 void CFileItemList::Stack(bool stackFiles /* = true */)
2719 {
2720   CSingleLock lock(m_lock);
2721 
2722   // not allowed here
2723   if (IsVirtualDirectoryRoot() ||
2724       IsLiveTV() ||
2725       IsSourcesPath() ||
2726       IsLibraryFolder())
2727     return;
2728 
2729   SetProperty("isstacked", true);
2730 
2731   // items needs to be sorted for stuff below to work properly
2732   Sort(SortByLabel, SortOrderAscending);
2733 
2734   StackFolders();
2735 
2736   if (stackFiles)
2737     StackFiles();
2738 }
2739 
StackFolders()2740 void CFileItemList::StackFolders()
2741 {
2742   // Precompile our REs
2743   VECCREGEXP folderRegExps;
2744   CRegExp folderRegExp(true, CRegExp::autoUtf8);
2745   const std::vector<std::string>& strFolderRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_folderStackRegExps;
2746 
2747   std::vector<std::string>::const_iterator strExpression = strFolderRegExps.begin();
2748   while (strExpression != strFolderRegExps.end())
2749   {
2750     if (!folderRegExp.RegComp(*strExpression))
2751       CLog::Log(LOGERROR, "%s: Invalid folder stack RegExp:'%s'", __FUNCTION__, strExpression->c_str());
2752     else
2753       folderRegExps.push_back(folderRegExp);
2754 
2755     strExpression++;
2756   }
2757 
2758   if (!folderRegExp.IsCompiled())
2759   {
2760     CLog::Log(LOGDEBUG, "%s: No stack expressions available. Skipping folder stacking", __FUNCTION__);
2761     return;
2762   }
2763 
2764   // stack folders
2765   for (int i = 0; i < Size(); i++)
2766   {
2767     CFileItemPtr item = Get(i);
2768     // combined the folder checks
2769     if (item->m_bIsFolder)
2770     {
2771       // only check known fast sources?
2772       // NOTES:
2773       // 1. rars and zips may be on slow sources? is this supposed to be allowed?
2774       if( !item->IsRemote()
2775         || item->IsSmb()
2776         || item->IsNfs()
2777         || URIUtils::IsInRAR(item->GetPath())
2778         || URIUtils::IsInZIP(item->GetPath())
2779         || URIUtils::IsOnLAN(item->GetPath())
2780         )
2781       {
2782         // stack cd# folders if contains only a single video file
2783 
2784         bool bMatch(false);
2785 
2786         VECCREGEXP::iterator expr = folderRegExps.begin();
2787         while (!bMatch && expr != folderRegExps.end())
2788         {
2789           //CLog::Log(LOGDEBUG,"%s: Running expression %s on %s", __FUNCTION__, expr->GetPattern().c_str(), item->GetLabel().c_str());
2790           bMatch = (expr->RegFind(item->GetLabel().c_str()) != -1);
2791           if (bMatch)
2792           {
2793             CFileItemList items;
2794             CDirectory::GetDirectory(item->GetPath(), items,
2795                                      CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(),
2796                                      DIR_FLAG_DEFAULTS);
2797             // optimized to only traverse listing once by checking for filecount
2798             // and recording last file item for later use
2799             int nFiles = 0;
2800             int index = -1;
2801             for (int j = 0; j < items.Size(); j++)
2802             {
2803               if (!items[j]->m_bIsFolder)
2804               {
2805                 nFiles++;
2806                 index = j;
2807               }
2808 
2809               if (nFiles > 1)
2810                 break;
2811             }
2812 
2813             if (nFiles == 1)
2814               *item = *items[index];
2815           }
2816           expr++;
2817         }
2818 
2819         // check for dvd folders
2820         if (!bMatch)
2821         {
2822           std::string dvdPath = item->GetOpticalMediaPath();
2823 
2824           if (!dvdPath.empty())
2825           {
2826             // NOTE: should this be done for the CD# folders too?
2827             item->m_bIsFolder = false;
2828             item->SetPath(dvdPath);
2829             item->SetLabel2("");
2830             item->SetLabelPreformatted(true);
2831             m_sortDescription.sortBy = SortByNone; /* sorting is now broken */
2832           }
2833         }
2834       }
2835     }
2836   }
2837 }
2838 
StackFiles()2839 void CFileItemList::StackFiles()
2840 {
2841   // Precompile our REs
2842   VECCREGEXP stackRegExps;
2843   CRegExp tmpRegExp(true, CRegExp::autoUtf8);
2844   const std::vector<std::string>& strStackRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoStackRegExps;
2845   std::vector<std::string>::const_iterator strRegExp = strStackRegExps.begin();
2846   while (strRegExp != strStackRegExps.end())
2847   {
2848     if (tmpRegExp.RegComp(*strRegExp))
2849     {
2850       if (tmpRegExp.GetCaptureTotal() == 4)
2851         stackRegExps.push_back(tmpRegExp);
2852       else
2853         CLog::Log(LOGERROR, "Invalid video stack RE (%s). Must have 4 captures.", strRegExp->c_str());
2854     }
2855     strRegExp++;
2856   }
2857 
2858   // now stack the files, some of which may be from the previous stack iteration
2859   int i = 0;
2860   while (i < Size())
2861   {
2862     CFileItemPtr item1 = Get(i);
2863 
2864     // skip folders, nfo files, playlists
2865     if (item1->m_bIsFolder
2866       || item1->IsParentFolder()
2867       || item1->IsNFO()
2868       || item1->IsPlayList()
2869       )
2870     {
2871       // increment index
2872       i++;
2873       continue;
2874     }
2875 
2876     int64_t               size        = 0;
2877     size_t                offset      = 0;
2878     std::string           stackName;
2879     std::string           file1;
2880     std::string           filePath;
2881     std::vector<int>      stack;
2882     VECCREGEXP::iterator  expr        = stackRegExps.begin();
2883 
2884     URIUtils::Split(item1->GetPath(), filePath, file1);
2885     if (URIUtils::HasEncodedFilename(CURL(filePath)))
2886       file1 = CURL::Decode(file1);
2887 
2888     int j;
2889     while (expr != stackRegExps.end())
2890     {
2891       if (expr->RegFind(file1, offset) != -1)
2892       {
2893         std::string Title1      = expr->GetMatch(1),
2894                     Volume1     = expr->GetMatch(2),
2895                     Ignore1     = expr->GetMatch(3),
2896                     Extension1  = expr->GetMatch(4);
2897         if (offset)
2898           Title1 = file1.substr(0, expr->GetSubStart(2));
2899         j = i + 1;
2900         while (j < Size())
2901         {
2902           CFileItemPtr item2 = Get(j);
2903 
2904           // skip folders, nfo files, playlists
2905           if (item2->m_bIsFolder
2906             || item2->IsParentFolder()
2907             || item2->IsNFO()
2908             || item2->IsPlayList()
2909             )
2910           {
2911             // increment index
2912             j++;
2913             continue;
2914           }
2915 
2916           std::string file2, filePath2;
2917           URIUtils::Split(item2->GetPath(), filePath2, file2);
2918           if (URIUtils::HasEncodedFilename(CURL(filePath2)) )
2919             file2 = CURL::Decode(file2);
2920 
2921           if (expr->RegFind(file2, offset) != -1)
2922           {
2923             std::string  Title2      = expr->GetMatch(1),
2924                         Volume2     = expr->GetMatch(2),
2925                         Ignore2     = expr->GetMatch(3),
2926                         Extension2  = expr->GetMatch(4);
2927             if (offset)
2928               Title2 = file2.substr(0, expr->GetSubStart(2));
2929             if (StringUtils::EqualsNoCase(Title1, Title2))
2930             {
2931               if (!StringUtils::EqualsNoCase(Volume1, Volume2))
2932               {
2933                 if (StringUtils::EqualsNoCase(Ignore1, Ignore2) &&
2934                     StringUtils::EqualsNoCase(Extension1, Extension2))
2935                 {
2936                   if (stack.empty())
2937                   {
2938                     stackName = Title1 + Ignore1 + Extension1;
2939                     stack.push_back(i);
2940                     size += item1->m_dwSize;
2941                   }
2942                   stack.push_back(j);
2943                   size += item2->m_dwSize;
2944                 }
2945                 else // Sequel
2946                 {
2947                   offset = 0;
2948                   expr++;
2949                   break;
2950                 }
2951               }
2952               else if (!StringUtils::EqualsNoCase(Ignore1, Ignore2)) // False positive, try again with offset
2953               {
2954                 offset = expr->GetSubStart(3);
2955                 break;
2956               }
2957               else // Extension mismatch
2958               {
2959                 offset = 0;
2960                 expr++;
2961                 break;
2962               }
2963             }
2964             else // Title mismatch
2965             {
2966               offset = 0;
2967               expr++;
2968               break;
2969             }
2970           }
2971           else // No match 2, next expression
2972           {
2973             offset = 0;
2974             expr++;
2975             break;
2976           }
2977           j++;
2978         }
2979         if (j == Size())
2980           expr = stackRegExps.end();
2981       }
2982       else // No match 1
2983       {
2984         offset = 0;
2985         expr++;
2986       }
2987       if (stack.size() > 1)
2988       {
2989         // have a stack, remove the items and add the stacked item
2990         // dont actually stack a multipart rar set, just remove all items but the first
2991         std::string stackPath;
2992         if (Get(stack[0])->IsRAR())
2993           stackPath = Get(stack[0])->GetPath();
2994         else
2995         {
2996           CStackDirectory dir;
2997           stackPath = dir.ConstructStackPath(*this, stack);
2998         }
2999         item1->SetPath(stackPath);
3000         // clean up list
3001         for (unsigned k = 1; k < stack.size(); k++)
3002           Remove(i+1);
3003         // item->m_bIsFolder = true;  // don't treat stacked files as folders
3004         // the label may be in a different char set from the filename (eg over smb
3005         // the label is converted from utf8, but the filename is not)
3006         if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS))
3007           URIUtils::RemoveExtension(stackName);
3008 
3009         item1->SetLabel(stackName);
3010         item1->m_dwSize = size;
3011         break;
3012       }
3013     }
3014     i++;
3015   }
3016 }
3017 
Load(int windowID)3018 bool CFileItemList::Load(int windowID)
3019 {
3020   CFile file;
3021   auto path = GetDiscFileCache(windowID);
3022   try
3023   {
3024     if (file.Open(path))
3025     {
3026       CArchive ar(&file, CArchive::load);
3027       ar >> *this;
3028       CLog::Log(LOGDEBUG,"Loading items: %i, directory: %s sort method: %i, ascending: %s", Size(), CURL::GetRedacted(GetPath()).c_str(), m_sortDescription.sortBy,
3029         m_sortDescription.sortOrder == SortOrderAscending ? "true" : "false");
3030       ar.Close();
3031       file.Close();
3032       return true;
3033     }
3034   }
3035   catch(const std::out_of_range&)
3036   {
3037     CLog::Log(LOGERROR, "Corrupt archive: %s", CURL::GetRedacted(path).c_str());
3038   }
3039 
3040   return false;
3041 }
3042 
Save(int windowID)3043 bool CFileItemList::Save(int windowID)
3044 {
3045   int iSize = Size();
3046   if (iSize <= 0)
3047     return false;
3048 
3049   CLog::Log(LOGDEBUG,"Saving fileitems [%s]", CURL::GetRedacted(GetPath()).c_str());
3050 
3051   CFile file;
3052   std::string cachefile = GetDiscFileCache(windowID);
3053   if (file.OpenForWrite(cachefile, true)) // overwrite always
3054   {
3055     // Before caching save simplified cache file name in every item so the cache file can be
3056     // identifed and removed if the item is updated. List path and options (used for file
3057     // name when list cached) can not be accurately derived from item path.
3058     StringUtils::Replace(cachefile, "special://temp/archive_cache/", "");
3059     StringUtils::Replace(cachefile, ".fi", "");
3060     for (const auto& item : m_items)
3061       item->SetProperty("cachefilename", cachefile);
3062 
3063     CArchive ar(&file, CArchive::store);
3064     ar << *this;
3065     CLog::Log(LOGDEBUG,"  -- items: %i, sort method: %i, ascending: %s", iSize, m_sortDescription.sortBy, m_sortDescription.sortOrder == SortOrderAscending ? "true" : "false");
3066     ar.Close();
3067     file.Close();
3068     return true;
3069   }
3070 
3071   return false;
3072 }
3073 
RemoveDiscCache(int windowID) const3074 void CFileItemList::RemoveDiscCache(int windowID) const
3075 {
3076   RemoveDiscCache(GetDiscFileCache(windowID));
3077 }
3078 
RemoveDiscCache(const std::string & cacheFile) const3079 void CFileItemList::RemoveDiscCache(const std::string& cacheFile) const
3080 {
3081   if (CFile::Exists(cacheFile))
3082   {
3083     CLog::Log(LOGDEBUG,"Clearing cached fileitems [%s]", CURL::GetRedacted(GetPath()).c_str());
3084     CFile::Delete(cacheFile);
3085   }
3086 }
3087 
RemoveDiscCacheCRC(const std::string & crc) const3088 void CFileItemList::RemoveDiscCacheCRC(const std::string& crc) const
3089 {
3090   std::string cachefile = StringUtils::Format("special://temp/archive_cache/%s.fi", crc);
3091   RemoveDiscCache(cachefile);
3092 }
3093 
GetDiscFileCache(int windowID) const3094 std::string CFileItemList::GetDiscFileCache(int windowID) const
3095 {
3096   std::string strPath(GetPath());
3097   URIUtils::RemoveSlashAtEnd(strPath);
3098 
3099   uint32_t crc = Crc32::ComputeFromLowerCase(strPath);
3100 
3101   std::string cacheFile;
3102   if (IsCDDA() || IsOnDVD())
3103     return StringUtils::Format("special://temp/archive_cache/r-%08x.fi", crc);
3104 
3105   if (IsMusicDb())
3106     return StringUtils::Format("special://temp/archive_cache/mdb-%08x.fi", crc);
3107 
3108   if (IsVideoDb())
3109     return StringUtils::Format("special://temp/archive_cache/vdb-%08x.fi", crc);
3110 
3111   if (IsSmartPlayList())
3112     return StringUtils::Format("special://temp/archive_cache/sp-%08x.fi", crc);
3113 
3114   if (windowID)
3115     return StringUtils::Format("special://temp/archive_cache/%i-%08x.fi", windowID, crc);
3116 
3117   return StringUtils::Format("special://temp/archive_cache/%08x.fi", crc);
3118 }
3119 
AlwaysCache() const3120 bool CFileItemList::AlwaysCache() const
3121 {
3122   // some database folders are always cached
3123   if (IsMusicDb())
3124     return CMusicDatabaseDirectory::CanCache(GetPath());
3125   if (IsVideoDb())
3126     return CVideoDatabaseDirectory::CanCache(GetPath());
3127   if (IsEPG())
3128     return true; // always cache
3129   return false;
3130 }
3131 
GetUserMusicThumb(bool alwaysCheckRemote,bool fallbackToFolder) const3132 std::string CFileItem::GetUserMusicThumb(bool alwaysCheckRemote /* = false */, bool fallbackToFolder /* = false */) const
3133 {
3134   if (m_strPath.empty()
3135    || StringUtils::StartsWithNoCase(m_strPath, "newsmartplaylist://")
3136    || StringUtils::StartsWithNoCase(m_strPath, "newplaylist://")
3137    || m_bIsShareOrDrive
3138    || IsInternetStream()
3139    || URIUtils::IsUPnP(m_strPath)
3140    || (URIUtils::IsFTP(m_strPath) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3141    || IsPlugin()
3142    || IsAddonsPath()
3143    || IsLibraryFolder()
3144    || IsParentFolder()
3145    || IsMusicDb())
3146     return "";
3147 
3148   // we first check for <filename>.tbn or <foldername>.tbn
3149   std::string fileThumb(GetTBNFile());
3150   if (CFile::Exists(fileThumb))
3151     return fileThumb;
3152 
3153   // Fall back to folder thumb, if requested
3154   if (!m_bIsFolder && fallbackToFolder)
3155   {
3156     CFileItem item(URIUtils::GetDirectory(m_strPath), true);
3157     return item.GetUserMusicThumb(alwaysCheckRemote);
3158   }
3159 
3160   // if a folder, check for folder.jpg
3161   if (m_bIsFolder && !IsFileFolder() && (!IsRemote() || alwaysCheckRemote || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICFILES_FINDREMOTETHUMBS)))
3162   {
3163     std::vector<CVariant> thumbs = CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(
3164         CSettings::SETTING_MUSICLIBRARY_MUSICTHUMBS);
3165     for (const auto& i : thumbs)
3166     {
3167       std::string strFileName = i.asString();
3168       std::string folderThumb(GetFolderThumb(strFileName));
3169       if (CFile::Exists(folderThumb))   // folder.jpg
3170         return folderThumb;
3171       size_t period = strFileName.find_last_of('.');
3172       if (period != std::string::npos)
3173       {
3174         std::string ext;
3175         std::string name = strFileName;
3176         std::string folderThumb1 = folderThumb;
3177         name.erase(period);
3178         ext = strFileName.substr(period);
3179         StringUtils::ToUpper(ext);
3180         StringUtils::Replace(folderThumb1, strFileName, name + ext);
3181         if (CFile::Exists(folderThumb1)) // folder.JPG
3182           return folderThumb1;
3183 
3184         folderThumb1 = folderThumb;
3185         std::string firstletter = name.substr(0, 1);
3186         StringUtils::ToUpper(firstletter);
3187         name.replace(0, 1, firstletter);
3188         StringUtils::Replace(folderThumb1, strFileName, name + ext);
3189         if (CFile::Exists(folderThumb1)) // Folder.JPG
3190           return folderThumb1;
3191 
3192         folderThumb1 = folderThumb;
3193         StringUtils::ToLower(ext);
3194         StringUtils::Replace(folderThumb1, strFileName, name + ext);
3195         if (CFile::Exists(folderThumb1)) // Folder.jpg
3196           return folderThumb1;
3197       }
3198     }
3199   }
3200   // No thumb found
3201   return "";
3202 }
3203 
3204 // Gets the .tbn filename from a file or folder name.
3205 // <filename>.ext -> <filename>.tbn
3206 // <foldername>/ -> <foldername>.tbn
GetTBNFile() const3207 std::string CFileItem::GetTBNFile() const
3208 {
3209   std::string thumbFile;
3210   std::string strFile = m_strPath;
3211 
3212   if (IsStack())
3213   {
3214     std::string strPath, strReturn;
3215     URIUtils::GetParentPath(m_strPath,strPath);
3216     CFileItem item(CStackDirectory::GetFirstStackedFile(strFile),false);
3217     std::string strTBNFile = item.GetTBNFile();
3218     strReturn = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strTBNFile));
3219     if (CFile::Exists(strReturn))
3220       return strReturn;
3221 
3222     strFile = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(strFile)));
3223   }
3224 
3225   if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3226   {
3227     std::string strPath = URIUtils::GetDirectory(strFile);
3228     std::string strParent;
3229     URIUtils::GetParentPath(strPath,strParent);
3230     strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(m_strPath));
3231   }
3232 
3233   CURL url(strFile);
3234   strFile = url.GetFileName();
3235 
3236   if (m_bIsFolder && !IsFileFolder())
3237     URIUtils::RemoveSlashAtEnd(strFile);
3238 
3239   if (!strFile.empty())
3240   {
3241     if (m_bIsFolder && !IsFileFolder())
3242       thumbFile = strFile + ".tbn"; // folder, so just add ".tbn"
3243     else
3244       thumbFile = URIUtils::ReplaceExtension(strFile, ".tbn");
3245     url.SetFileName(thumbFile);
3246     thumbFile = url.Get();
3247   }
3248   return thumbFile;
3249 }
3250 
SkipLocalArt() const3251 bool CFileItem::SkipLocalArt() const
3252 {
3253   return (m_strPath.empty()
3254        || StringUtils::StartsWithNoCase(m_strPath, "newsmartplaylist://")
3255        || StringUtils::StartsWithNoCase(m_strPath, "newplaylist://")
3256        || m_bIsShareOrDrive
3257        || IsInternetStream()
3258        || URIUtils::IsUPnP(m_strPath)
3259        || (URIUtils::IsFTP(m_strPath) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3260        || IsPlugin()
3261        || IsAddonsPath()
3262        || IsLibraryFolder()
3263        || IsParentFolder()
3264        || IsLiveTV()
3265        || IsPVRRecording()
3266        || IsDVD());
3267 }
3268 
GetThumbHideIfUnwatched(const CFileItem * item) const3269 std::string CFileItem::GetThumbHideIfUnwatched(const CFileItem* item) const
3270 {
3271   const std::shared_ptr<CSettingList> setting(std::dynamic_pointer_cast<CSettingList>(
3272       CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(
3273           CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS)));
3274   if (setting && item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_type == MediaTypeEpisode &&
3275       item->GetVideoInfoTag()->GetPlayCount() == 0 &&
3276       !CSettingUtils::FindIntInList(setting,
3277                                     CSettings::VIDEOLIBRARY_THUMB_SHOW_UNWATCHED_EPISODE) &&
3278       item->HasArt("thumb"))
3279   {
3280     const std::string fanArt = item->GetArt("fanart");
3281     if (fanArt.empty())
3282       return "OverlaySpoiler.png";
3283     else
3284       return fanArt;
3285   }
3286 
3287   return item->GetArt("thumb");
3288 }
3289 
FindLocalArt(const std::string & artFile,bool useFolder) const3290 std::string CFileItem::FindLocalArt(const std::string &artFile, bool useFolder) const
3291 {
3292   if (SkipLocalArt())
3293     return "";
3294 
3295   std::string thumb;
3296   if (!m_bIsFolder)
3297   {
3298     thumb = GetLocalArt(artFile, false);
3299     if (!thumb.empty() && CFile::Exists(thumb))
3300       return thumb;
3301   }
3302   if ((useFolder || (m_bIsFolder && !IsFileFolder())) && !artFile.empty())
3303   {
3304     std::string thumb2 = GetLocalArt(artFile, true);
3305     if (!thumb2.empty() && thumb2 != thumb && CFile::Exists(thumb2))
3306       return thumb2;
3307   }
3308   return "";
3309 }
3310 
GetLocalArtBaseFilename() const3311 std::string CFileItem::GetLocalArtBaseFilename() const
3312 {
3313   bool useFolder = false;
3314   return GetLocalArtBaseFilename(useFolder);
3315 }
3316 
GetLocalArtBaseFilename(bool & useFolder) const3317 std::string CFileItem::GetLocalArtBaseFilename(bool& useFolder) const
3318 {
3319   std::string strFile = m_strPath;
3320   if (IsStack())
3321   {
3322     std::string strPath;
3323     URIUtils::GetParentPath(m_strPath,strPath);
3324     strFile = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(strFile)));
3325   }
3326 
3327   if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3328   {
3329     std::string strPath = URIUtils::GetDirectory(strFile);
3330     std::string strParent;
3331     URIUtils::GetParentPath(strPath,strParent);
3332     strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(strFile));
3333   }
3334 
3335   if (IsMultiPath())
3336     strFile = CMultiPathDirectory::GetFirstPath(m_strPath);
3337 
3338   if (IsOpticalMediaFile())
3339   { // optical media files should be treated like folders
3340     useFolder = true;
3341     strFile = GetLocalMetadataPath();
3342   }
3343   else if (useFolder && !(m_bIsFolder && !IsFileFolder()))
3344     strFile = URIUtils::GetDirectory(strFile);
3345 
3346   return strFile;
3347 }
3348 
GetLocalArt(const std::string & artFile,bool useFolder) const3349 std::string CFileItem::GetLocalArt(const std::string& artFile, bool useFolder) const
3350 {
3351   // no retrieving of empty art files from folders
3352   if (useFolder && artFile.empty())
3353     return "";
3354 
3355   std::string strFile = GetLocalArtBaseFilename(useFolder);
3356   if (strFile.empty()) // empty filepath -> nothing to find
3357     return "";
3358 
3359   if (useFolder)
3360   {
3361     if (!artFile.empty())
3362       return URIUtils::AddFileToFolder(strFile, artFile);
3363   }
3364   else
3365   {
3366     if (artFile.empty()) // old thumbnail matching
3367       return URIUtils::ReplaceExtension(strFile, ".tbn");
3368     else
3369       return URIUtils::ReplaceExtension(strFile, "-" + artFile);
3370   }
3371   return "";
3372 }
3373 
GetFolderThumb(const std::string & folderJPG) const3374 std::string CFileItem::GetFolderThumb(const std::string &folderJPG /* = "folder.jpg" */) const
3375 {
3376   std::string strFolder = m_strPath;
3377 
3378   if (IsStack() ||
3379       URIUtils::IsInRAR(strFolder) ||
3380       URIUtils::IsInZIP(strFolder))
3381   {
3382     URIUtils::GetParentPath(m_strPath,strFolder);
3383   }
3384 
3385   if (IsMultiPath())
3386     strFolder = CMultiPathDirectory::GetFirstPath(m_strPath);
3387 
3388   if (IsPlugin())
3389     return "";
3390 
3391   return URIUtils::AddFileToFolder(strFolder, folderJPG);
3392 }
3393 
GetMovieName(bool bUseFolderNames) const3394 std::string CFileItem::GetMovieName(bool bUseFolderNames /* = false */) const
3395 {
3396   if (IsPlugin() && HasVideoInfoTag() && !GetVideoInfoTag()->m_strTitle.empty())
3397     return GetVideoInfoTag()->m_strTitle;
3398 
3399   if (IsLabelPreformatted())
3400     return GetLabel();
3401 
3402   if (m_pvrRecordingInfoTag)
3403     return m_pvrRecordingInfoTag->m_strTitle;
3404   else if (URIUtils::IsPVRRecording(m_strPath))
3405   {
3406     std::string title = CPVRRecording::GetTitleFromURL(m_strPath);
3407     if (!title.empty())
3408       return title;
3409   }
3410 
3411   std::string strMovieName;
3412   if (URIUtils::IsStack(m_strPath))
3413     strMovieName = CStackDirectory::GetStackedTitlePath(m_strPath);
3414   else
3415     strMovieName = GetBaseMoviePath(bUseFolderNames);
3416 
3417   URIUtils::RemoveSlashAtEnd(strMovieName);
3418 
3419   return CURL::Decode(URIUtils::GetFileName(strMovieName));
3420 }
3421 
GetBaseMoviePath(bool bUseFolderNames) const3422 std::string CFileItem::GetBaseMoviePath(bool bUseFolderNames) const
3423 {
3424   std::string strMovieName = m_strPath;
3425 
3426   if (IsMultiPath())
3427     strMovieName = CMultiPathDirectory::GetFirstPath(m_strPath);
3428 
3429   if (IsOpticalMediaFile())
3430     return GetLocalMetadataPath();
3431 
3432   if (bUseFolderNames &&
3433      (!m_bIsFolder || URIUtils::IsInArchive(m_strPath) ||
3434      (HasVideoInfoTag() && GetVideoInfoTag()->m_iDbId > 0 && !CMediaTypes::IsContainer(GetVideoInfoTag()->m_type))))
3435   {
3436     std::string name2(strMovieName);
3437     URIUtils::GetParentPath(name2,strMovieName);
3438     if (URIUtils::IsInArchive(m_strPath))
3439     {
3440       // Try to get archive itself, if empty take path before
3441       name2 = CURL(m_strPath).GetHostName();
3442       if (name2.empty())
3443         name2 = strMovieName;
3444 
3445       URIUtils::GetParentPath(name2, strMovieName);
3446     }
3447   }
3448 
3449   return strMovieName;
3450 }
3451 
GetLocalFanart() const3452 std::string CFileItem::GetLocalFanart() const
3453 {
3454   if (IsVideoDb())
3455   {
3456     if (!HasVideoInfoTag())
3457       return ""; // nothing can be done
3458     CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
3459     return dbItem.GetLocalFanart();
3460   }
3461 
3462   std::string strFile2;
3463   std::string strFile = m_strPath;
3464   if (IsStack())
3465   {
3466     std::string strPath;
3467     URIUtils::GetParentPath(m_strPath,strPath);
3468     CStackDirectory dir;
3469     std::string strPath2;
3470     strPath2 = dir.GetStackedTitlePath(strFile);
3471     strFile = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strPath2));
3472     CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
3473     std::string strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-fanart"));
3474     strFile2 = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strTBNFile));
3475   }
3476   if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3477   {
3478     std::string strPath = URIUtils::GetDirectory(strFile);
3479     std::string strParent;
3480     URIUtils::GetParentPath(strPath,strParent);
3481     strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(m_strPath));
3482   }
3483 
3484   // no local fanart available for these
3485   if (IsInternetStream()
3486    || URIUtils::IsUPnP(strFile)
3487    || URIUtils::IsBluray(strFile)
3488    || IsLiveTV()
3489    || IsPlugin()
3490    || IsAddonsPath()
3491    || IsDVD()
3492    || (URIUtils::IsFTP(strFile) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3493    || m_strPath.empty())
3494     return "";
3495 
3496   std::string strDir = URIUtils::GetDirectory(strFile);
3497 
3498   if (strDir.empty())
3499     return "";
3500 
3501   CFileItemList items;
3502   CDirectory::GetDirectory(strDir, items, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
3503   if (IsOpticalMediaFile())
3504   { // grab from the optical media parent folder as well
3505     CFileItemList moreItems;
3506     CDirectory::GetDirectory(GetLocalMetadataPath(), moreItems, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
3507     items.Append(moreItems);
3508   }
3509 
3510   std::vector<std::string> fanarts = { "fanart" };
3511 
3512   strFile = URIUtils::ReplaceExtension(strFile, "-fanart");
3513   fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile));
3514 
3515   if (!strFile2.empty())
3516     fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile2));
3517 
3518   for (std::vector<std::string>::const_iterator i = fanarts.begin(); i != fanarts.end(); ++i)
3519   {
3520     for (int j = 0; j < items.Size(); j++)
3521     {
3522       std::string strCandidate = URIUtils::GetFileName(items[j]->m_strPath);
3523       URIUtils::RemoveExtension(strCandidate);
3524       std::string strFanart = *i;
3525       URIUtils::RemoveExtension(strFanart);
3526       if (StringUtils::EqualsNoCase(strCandidate, strFanart))
3527         return items[j]->m_strPath;
3528     }
3529   }
3530 
3531   return "";
3532 }
3533 
GetLocalMetadataPath() const3534 std::string CFileItem::GetLocalMetadataPath() const
3535 {
3536   if (m_bIsFolder && !IsFileFolder())
3537     return m_strPath;
3538 
3539   std::string parent(URIUtils::GetParentPath(m_strPath));
3540   std::string parentFolder(parent);
3541   URIUtils::RemoveSlashAtEnd(parentFolder);
3542   parentFolder = URIUtils::GetFileName(parentFolder);
3543   if (StringUtils::EqualsNoCase(parentFolder, "VIDEO_TS") || StringUtils::EqualsNoCase(parentFolder, "BDMV"))
3544   { // go back up another one
3545     parent = URIUtils::GetParentPath(parent);
3546   }
3547   return parent;
3548 }
3549 
LoadMusicTag()3550 bool CFileItem::LoadMusicTag()
3551 {
3552   // not audio
3553   if (!IsAudio())
3554     return false;
3555   // already loaded?
3556   if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
3557     return true;
3558   // check db
3559   CMusicDatabase musicDatabase;
3560   if (musicDatabase.Open())
3561   {
3562     CSong song;
3563     if (musicDatabase.GetSongByFileName(m_strPath, song))
3564     {
3565       GetMusicInfoTag()->SetSong(song);
3566       return true;
3567     }
3568     musicDatabase.Close();
3569   }
3570   // load tag from file
3571   CLog::Log(LOGDEBUG, "%s: loading tag information for file: %s", __FUNCTION__, m_strPath.c_str());
3572   CMusicInfoTagLoaderFactory factory;
3573   std::unique_ptr<IMusicInfoTagLoader> pLoader (factory.CreateLoader(*this));
3574   if (pLoader)
3575   {
3576     if (pLoader->Load(m_strPath, *GetMusicInfoTag()))
3577       return true;
3578   }
3579   // no tag - try some other things
3580   if (IsCDDA())
3581   {
3582     // we have the tracknumber...
3583     int iTrack = GetMusicInfoTag()->GetTrackNumber();
3584     if (iTrack >= 1)
3585     {
3586       std::string strText = g_localizeStrings.Get(554); // "Track"
3587       if (!strText.empty() && strText[strText.size() - 1] != ' ')
3588         strText += " ";
3589       std::string strTrack = StringUtils::Format((strText + "%i").c_str(), iTrack);
3590       GetMusicInfoTag()->SetTitle(strTrack);
3591       GetMusicInfoTag()->SetLoaded(true);
3592       return true;
3593     }
3594   }
3595   else
3596   {
3597     std::string fileName = URIUtils::GetFileName(m_strPath);
3598     URIUtils::RemoveExtension(fileName);
3599     for (const std::string& fileFilter : CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicTagsFromFileFilters)
3600     {
3601       CLabelFormatter formatter(fileFilter, "");
3602       if (formatter.FillMusicTag(fileName, GetMusicInfoTag()))
3603       {
3604         GetMusicInfoTag()->SetLoaded(true);
3605         return true;
3606       }
3607     }
3608   }
3609   return false;
3610 }
3611 
LoadGameTag()3612 bool CFileItem::LoadGameTag()
3613 {
3614   // Already loaded?
3615   if (HasGameInfoTag() && m_gameInfoTag->IsLoaded())
3616     return true;
3617 
3618   //! @todo
3619   GetGameInfoTag();
3620 
3621   m_gameInfoTag->SetLoaded(true);
3622 
3623   return false;
3624 }
3625 
Swap(unsigned int item1,unsigned int item2)3626 void CFileItemList::Swap(unsigned int item1, unsigned int item2)
3627 {
3628   if (item1 != item2 && item1 < m_items.size() && item2 < m_items.size())
3629     std::swap(m_items[item1], m_items[item2]);
3630 }
3631 
UpdateItem(const CFileItem * item)3632 bool CFileItemList::UpdateItem(const CFileItem *item)
3633 {
3634   if (!item)
3635     return false;
3636 
3637   CSingleLock lock(m_lock);
3638   for (unsigned int i = 0; i < m_items.size(); i++)
3639   {
3640     CFileItemPtr pItem = m_items[i];
3641     if (pItem->IsSamePath(item))
3642     {
3643       pItem->UpdateInfo(*item);
3644       return true;
3645     }
3646   }
3647   return false;
3648 }
3649 
AddSortMethod(SortBy sortBy,int buttonLabel,const LABEL_MASKS & labelMasks,SortAttribute sortAttributes)3650 void CFileItemList::AddSortMethod(SortBy sortBy, int buttonLabel, const LABEL_MASKS &labelMasks, SortAttribute sortAttributes /* = SortAttributeNone */)
3651 {
3652   AddSortMethod(sortBy, sortAttributes, buttonLabel, labelMasks);
3653 }
3654 
AddSortMethod(SortBy sortBy,SortAttribute sortAttributes,int buttonLabel,const LABEL_MASKS & labelMasks)3655 void CFileItemList::AddSortMethod(SortBy sortBy, SortAttribute sortAttributes, int buttonLabel, const LABEL_MASKS &labelMasks)
3656 {
3657   SortDescription sorting;
3658   sorting.sortBy = sortBy;
3659   sorting.sortAttributes = sortAttributes;
3660 
3661   AddSortMethod(sorting, buttonLabel, labelMasks);
3662 }
3663 
AddSortMethod(SortDescription sortDescription,int buttonLabel,const LABEL_MASKS & labelMasks)3664 void CFileItemList::AddSortMethod(SortDescription sortDescription, int buttonLabel, const LABEL_MASKS &labelMasks)
3665 {
3666   GUIViewSortDetails sort;
3667   sort.m_sortDescription = sortDescription;
3668   sort.m_buttonLabel = buttonLabel;
3669   sort.m_labelMasks = labelMasks;
3670 
3671   m_sortDetails.push_back(sort);
3672 }
3673 
SetReplaceListing(bool replace)3674 void CFileItemList::SetReplaceListing(bool replace)
3675 {
3676   m_replaceListing = replace;
3677 }
3678 
ClearSortState()3679 void CFileItemList::ClearSortState()
3680 {
3681   m_sortDescription.sortBy = SortByNone;
3682   m_sortDescription.sortOrder = SortOrderNone;
3683   m_sortDescription.sortAttributes = SortAttributeNone;
3684 }
3685 
HasVideoInfoTag() const3686 bool CFileItem::HasVideoInfoTag() const
3687 {
3688   // Note: CPVRRecording is derived from CVideoInfoTag
3689   return m_pvrRecordingInfoTag.get() != nullptr || m_videoInfoTag != nullptr;
3690 }
3691 
GetVideoInfoTag()3692 CVideoInfoTag* CFileItem::GetVideoInfoTag()
3693 {
3694   // Note: CPVRRecording is derived from CVideoInfoTag
3695   if (m_pvrRecordingInfoTag)
3696     return m_pvrRecordingInfoTag.get();
3697   else if (!m_videoInfoTag)
3698     m_videoInfoTag = new CVideoInfoTag;
3699 
3700   return m_videoInfoTag;
3701 }
3702 
GetVideoInfoTag() const3703 const CVideoInfoTag* CFileItem::GetVideoInfoTag() const
3704 {
3705   // Note: CPVRRecording is derived from CVideoInfoTag
3706   return m_pvrRecordingInfoTag ? m_pvrRecordingInfoTag.get() : m_videoInfoTag;
3707 }
3708 
GetPictureInfoTag()3709 CPictureInfoTag* CFileItem::GetPictureInfoTag()
3710 {
3711   if (!m_pictureInfoTag)
3712     m_pictureInfoTag = new CPictureInfoTag;
3713 
3714   return m_pictureInfoTag;
3715 }
3716 
GetMusicInfoTag()3717 MUSIC_INFO::CMusicInfoTag* CFileItem::GetMusicInfoTag()
3718 {
3719   if (!m_musicInfoTag)
3720     m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag;
3721 
3722   return m_musicInfoTag;
3723 }
3724 
GetGameInfoTag()3725 CGameInfoTag* CFileItem::GetGameInfoTag()
3726 {
3727   if (!m_gameInfoTag)
3728     m_gameInfoTag = new CGameInfoTag;
3729 
3730   return m_gameInfoTag;
3731 }
3732 
FindTrailer() const3733 std::string CFileItem::FindTrailer() const
3734 {
3735   std::string strFile2;
3736   std::string strFile = m_strPath;
3737   if (IsStack())
3738   {
3739     std::string strPath;
3740     URIUtils::GetParentPath(m_strPath,strPath);
3741     CStackDirectory dir;
3742     std::string strPath2;
3743     strPath2 = dir.GetStackedTitlePath(strFile);
3744     strFile = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2));
3745     CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
3746     std::string strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-trailer"));
3747     strFile2 = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile));
3748   }
3749   if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3750   {
3751     std::string strPath = URIUtils::GetDirectory(strFile);
3752     std::string strParent;
3753     URIUtils::GetParentPath(strPath,strParent);
3754     strFile = URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath));
3755   }
3756 
3757   // no local trailer available for these
3758   if (IsInternetStream()
3759    || URIUtils::IsUPnP(strFile)
3760    || URIUtils::IsBluray(strFile)
3761    || IsLiveTV()
3762    || IsPlugin()
3763    || IsDVD())
3764     return "";
3765 
3766   std::string strDir = URIUtils::GetDirectory(strFile);
3767   CFileItemList items;
3768   CDirectory::GetDirectory(strDir, items, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO | DIR_FLAG_NO_FILE_DIRS);
3769   URIUtils::RemoveExtension(strFile);
3770   strFile += "-trailer";
3771   std::string strFile3 = URIUtils::AddFileToFolder(strDir, "movie-trailer");
3772 
3773   // Precompile our REs
3774   VECCREGEXP matchRegExps;
3775   CRegExp tmpRegExp(true, CRegExp::autoUtf8);
3776   const std::vector<std::string>& strMatchRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_trailerMatchRegExps;
3777 
3778   std::vector<std::string>::const_iterator strRegExp = strMatchRegExps.begin();
3779   while (strRegExp != strMatchRegExps.end())
3780   {
3781     if (tmpRegExp.RegComp(*strRegExp))
3782     {
3783       matchRegExps.push_back(tmpRegExp);
3784     }
3785     strRegExp++;
3786   }
3787 
3788   std::string strTrailer;
3789   for (int i = 0; i < items.Size(); i++)
3790   {
3791     std::string strCandidate = items[i]->m_strPath;
3792     URIUtils::RemoveExtension(strCandidate);
3793     if (StringUtils::EqualsNoCase(strCandidate, strFile) ||
3794         StringUtils::EqualsNoCase(strCandidate, strFile2) ||
3795         StringUtils::EqualsNoCase(strCandidate, strFile3))
3796     {
3797       strTrailer = items[i]->m_strPath;
3798       break;
3799     }
3800     else
3801     {
3802       VECCREGEXP::iterator expr = matchRegExps.begin();
3803 
3804       while (expr != matchRegExps.end())
3805       {
3806         if (expr->RegFind(strCandidate) != -1)
3807         {
3808           strTrailer = items[i]->m_strPath;
3809           i = items.Size();
3810           break;
3811         }
3812         expr++;
3813       }
3814     }
3815   }
3816 
3817   return strTrailer;
3818 }
3819 
GetVideoContentType() const3820 int CFileItem::GetVideoContentType() const
3821 {
3822   VIDEODB_CONTENT_TYPE type = VIDEODB_CONTENT_MOVIES;
3823   if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeTvShow)
3824     type = VIDEODB_CONTENT_TVSHOWS;
3825   if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeEpisode)
3826     return VIDEODB_CONTENT_EPISODES;
3827   if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeMusicVideo)
3828     return VIDEODB_CONTENT_MUSICVIDEOS;
3829   if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeAlbum)
3830     return VIDEODB_CONTENT_MUSICALBUMS;
3831 
3832   CVideoDatabaseDirectory dir;
3833   VIDEODATABASEDIRECTORY::CQueryParams params;
3834   dir.GetQueryParams(m_strPath, params);
3835   if (params.GetSetId() != -1 && params.GetMovieId() == -1) // movie set
3836     return VIDEODB_CONTENT_MOVIE_SETS;
3837 
3838   return type;
3839 }
3840 
GetItemToPlay() const3841 CFileItem CFileItem::GetItemToPlay() const
3842 {
3843   if (HasEPGInfoTag())
3844   {
3845     const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(GetEPGInfoTag());
3846     if (channel)
3847       return CFileItem(channel);
3848   }
3849   return *this;
3850 }
3851 
GetResumePoint() const3852 CBookmark CFileItem::GetResumePoint() const
3853 {
3854   if (HasVideoInfoTag())
3855     return GetVideoInfoTag()->GetResumePoint();
3856   return CBookmark();
3857 }
3858 
IsResumePointSet() const3859 bool CFileItem::IsResumePointSet() const
3860 {
3861   return GetResumePoint().IsSet();
3862 }
3863 
GetCurrentResumeTime() const3864 double CFileItem::GetCurrentResumeTime() const
3865 {
3866   return lrint(GetResumePoint().timeInSeconds);
3867 }
3868 
GetCurrentResumeTimeAndPartNumber(int64_t & startOffset,int & partNumber) const3869 bool CFileItem::GetCurrentResumeTimeAndPartNumber(int64_t& startOffset, int& partNumber) const
3870 {
3871   CBookmark resumePoint(GetResumePoint());
3872   if (resumePoint.IsSet())
3873   {
3874     startOffset = llrint(resumePoint.timeInSeconds);
3875     partNumber = resumePoint.partNumber;
3876     return true;
3877   }
3878   return false;
3879 }
3880