[filesystem][SpecialProtocol] Removed assert from GetPath
[xbmc.git] / xbmc / FileItem.cpp
blob2eb5eb072c34d5d8ca9120b39f6da89f7aea03da
1 /*
2 * Copyright (C) 2005-2020 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "FileItem.h"
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/tags/GameInfoTag.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "media/MediaLockState.h"
28 #include "music/Album.h"
29 #include "music/Artist.h"
30 #include "music/MusicDatabase.h"
31 #include "music/tags/MusicInfoTag.h"
32 #include "music/tags/MusicInfoTagLoaderFactory.h"
33 #include "pictures/PictureInfoTag.h"
34 #include "playlists/PlayListFactory.h"
35 #include "pvr/PVRManager.h"
36 #include "pvr/channels/PVRChannel.h"
37 #include "pvr/channels/PVRChannelGroupMember.h"
38 #include "pvr/epg/EpgInfoTag.h"
39 #include "pvr/epg/EpgSearchFilter.h"
40 #include "pvr/guilib/PVRGUIActionsChannels.h"
41 #include "pvr/recordings/PVRRecording.h"
42 #include "pvr/timers/PVRTimerInfoTag.h"
43 #include "settings/AdvancedSettings.h"
44 #include "settings/SettingUtils.h"
45 #include "settings/Settings.h"
46 #include "settings/SettingsComponent.h"
47 #include "settings/lib/Setting.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"
62 #include <algorithm>
63 #include <cstdlib>
64 #include <mutex>
66 using namespace KODI;
67 using namespace XFILE;
68 using namespace PLAYLIST;
69 using namespace MUSIC_INFO;
70 using namespace PVR;
71 using namespace GAME;
73 CFileItem::CFileItem(const CSong& song)
75 Initialize();
76 SetFromSong(song);
79 CFileItem::CFileItem(const CSong& song, const CMusicInfoTag& music)
81 Initialize();
82 SetFromSong(song);
83 *GetMusicInfoTag() = music;
86 CFileItem::CFileItem(const CURL &url, const CAlbum& album)
88 Initialize();
90 m_strPath = url.Get();
91 URIUtils::AddSlashAtEnd(m_strPath);
92 SetFromAlbum(album);
95 CFileItem::CFileItem(const std::string &path, const CAlbum& album)
97 Initialize();
99 m_strPath = path;
100 URIUtils::AddSlashAtEnd(m_strPath);
101 SetFromAlbum(album);
104 CFileItem::CFileItem(const CMusicInfoTag& music)
106 Initialize();
107 SetLabel(music.GetTitle());
108 m_strPath = music.GetURL();
109 m_bIsFolder = URIUtils::HasSlashAtEnd(m_strPath);
110 *GetMusicInfoTag() = music;
111 FillInDefaultIcon();
112 FillInMimeType(false);
115 CFileItem::CFileItem(const CVideoInfoTag& movie)
117 Initialize();
118 SetFromVideoInfoTag(movie);
121 namespace
123 std::string GetEpgTagTitle(const std::shared_ptr<CPVREpgInfoTag>& epgTag)
125 if (CServiceBroker::GetPVRManager().IsParentalLocked(epgTag))
126 return g_localizeStrings.Get(19266); // Parental locked
127 else if (epgTag->Title().empty() &&
128 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_HIDENOINFOAVAILABLE))
129 return g_localizeStrings.Get(19055); // no information available
130 else
131 return epgTag->Title();
133 } // unnamed namespace
135 void CFileItem::FillMusicInfoTag(const std::shared_ptr<CPVREpgInfoTag>& tag)
137 CMusicInfoTag* musictag = GetMusicInfoTag(); // create (!) the music tag.
139 if (tag)
141 musictag->SetTitle(GetEpgTagTitle(tag));
142 musictag->SetGenre(tag->Genre());
143 musictag->SetDuration(tag->GetDuration());
144 musictag->SetURL(tag->Path());
146 else if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
147 CSettings::SETTING_EPG_HIDENOINFOAVAILABLE))
149 musictag->SetTitle(g_localizeStrings.Get(19055)); // no information available
152 musictag->SetLoaded(true);
155 CFileItem::CFileItem(const std::shared_ptr<CPVREpgInfoTag>& tag)
157 Initialize();
159 m_bIsFolder = false;
160 m_epgInfoTag = tag;
161 m_strPath = tag->Path();
162 m_bCanQueue = false;
163 SetLabel(GetEpgTagTitle(tag));
164 m_dateTime = tag->StartAsLocalTime();
166 if (!tag->IconPath().empty())
168 SetArt("icon", tag->IconPath());
170 else
172 const std::string iconPath = tag->ChannelIconPath();
173 if (!iconPath.empty())
174 SetArt("icon", iconPath);
175 else if (tag->IsRadio())
176 SetArt("icon", "DefaultMusicSongs.png");
177 else
178 SetArt("icon", "DefaultTVShows.png");
181 // Speedup FillInDefaultIcon()
182 SetProperty("icon_never_overlay", true);
184 if (tag->IsRadio() && !HasMusicInfoTag())
185 FillMusicInfoTag(tag);
187 FillInMimeType(false);
190 CFileItem::CFileItem(const std::shared_ptr<PVR::CPVREpgSearchFilter>& filter)
192 Initialize();
194 m_bIsFolder = true;
195 m_epgSearchFilter = filter;
196 m_strPath = filter->GetPath();
197 m_bCanQueue = false;
198 SetLabel(filter->GetTitle());
200 const CDateTime lastExec = filter->GetLastExecutedDateTime();
201 if (lastExec.IsValid())
202 m_dateTime.SetFromUTCDateTime(lastExec);
204 SetArt("icon", "DefaultPVRSearch.png");
206 // Speedup FillInDefaultIcon()
207 SetProperty("icon_never_overlay", true);
209 FillInMimeType(false);
212 CFileItem::CFileItem(const std::shared_ptr<CPVRChannelGroupMember>& channelGroupMember)
214 Initialize();
216 const std::shared_ptr<CPVRChannel> channel = channelGroupMember->Channel();
218 m_pvrChannelGroupMemberInfoTag = channelGroupMember;
220 m_strPath = channelGroupMember->Path();
221 m_bIsFolder = false;
222 m_bCanQueue = false;
223 SetLabel(channel->ChannelName());
225 if (!channel->IconPath().empty())
226 SetArt("icon", channel->IconPath());
227 else if (channel->IsRadio())
228 SetArt("icon", "DefaultMusicSongs.png");
229 else
230 SetArt("icon", "DefaultTVShows.png");
232 SetProperty("channelid", channel->ChannelID());
233 SetProperty("path", channelGroupMember->Path());
234 SetArt("thumb", channel->IconPath());
236 // Speedup FillInDefaultIcon()
237 SetProperty("icon_never_overlay", true);
239 if (channel->IsRadio() && !HasMusicInfoTag())
241 const std::shared_ptr<CPVREpgInfoTag> epgNow = channel->GetEPGNow();
242 FillMusicInfoTag(epgNow);
244 FillInMimeType(false);
247 CFileItem::CFileItem(const std::shared_ptr<CPVRRecording>& record)
249 Initialize();
251 m_bIsFolder = false;
252 m_pvrRecordingInfoTag = record;
253 m_strPath = record->m_strFileNameAndPath;
254 SetLabel(record->m_strTitle);
255 m_dateTime = record->RecordingTimeAsLocalTime();
256 m_dwSize = record->GetSizeInBytes();
257 m_bCanQueue = true;
259 // Set art
260 if (!record->IconPath().empty())
261 SetArt("icon", record->IconPath());
262 else
264 const std::shared_ptr<CPVRChannel> channel = record->Channel();
265 if (channel && !channel->IconPath().empty())
266 SetArt("icon", channel->IconPath());
267 else if (record->IsRadio())
268 SetArt("icon", "DefaultMusicSongs.png");
269 else
270 SetArt("icon", "DefaultTVShows.png");
273 if (!record->ThumbnailPath().empty())
274 SetArt("thumb", record->ThumbnailPath());
276 if (!record->FanartPath().empty())
277 SetArt("fanart", record->FanartPath());
279 // Speedup FillInDefaultIcon()
280 SetProperty("icon_never_overlay", true);
282 FillInMimeType(false);
285 CFileItem::CFileItem(const std::shared_ptr<CPVRTimerInfoTag>& timer)
287 Initialize();
289 m_bIsFolder = timer->IsTimerRule();
290 m_pvrTimerInfoTag = timer;
291 m_strPath = timer->Path();
292 SetLabel(timer->Title());
293 m_dateTime = timer->StartAsLocalTime();
294 m_bCanQueue = false;
296 if (!timer->ChannelIcon().empty())
297 SetArt("icon", timer->ChannelIcon());
298 else if (timer->IsRadio())
299 SetArt("icon", "DefaultMusicSongs.png");
300 else
301 SetArt("icon", "DefaultTVShows.png");
303 // Speedup FillInDefaultIcon()
304 SetProperty("icon_never_overlay", true);
306 FillInMimeType(false);
309 CFileItem::CFileItem(const CArtist& artist)
311 Initialize();
312 SetLabel(artist.strArtist);
313 m_strPath = artist.strArtist;
314 m_bIsFolder = true;
315 URIUtils::AddSlashAtEnd(m_strPath);
316 GetMusicInfoTag()->SetArtist(artist);
317 FillInMimeType(false);
320 CFileItem::CFileItem(const CGenre& genre)
322 Initialize();
323 SetLabel(genre.strGenre);
324 m_strPath = genre.strGenre;
325 m_bIsFolder = true;
326 URIUtils::AddSlashAtEnd(m_strPath);
327 GetMusicInfoTag()->SetGenre(genre.strGenre);
328 FillInMimeType(false);
331 CFileItem::CFileItem(const CFileItem& item)
332 : CGUIListItem(item),
333 m_musicInfoTag(NULL),
334 m_videoInfoTag(NULL),
335 m_pictureInfoTag(NULL),
336 m_gameInfoTag(NULL)
338 *this = item;
341 CFileItem::CFileItem(const CGUIListItem& item)
343 Initialize();
344 // not particularly pretty, but it gets around the issue of Initialize() defaulting
345 // parameters in the CGUIListItem base class.
346 *static_cast<CGUIListItem*>(this) = item;
348 FillInMimeType(false);
351 CFileItem::CFileItem(void)
353 Initialize();
356 CFileItem::CFileItem(const std::string& strLabel)
358 Initialize();
359 SetLabel(strLabel);
362 CFileItem::CFileItem(const char* strLabel)
364 Initialize();
365 SetLabel(std::string(strLabel));
368 CFileItem::CFileItem(const CURL& path, bool bIsFolder)
370 Initialize();
371 m_strPath = path.Get();
372 m_bIsFolder = bIsFolder;
373 if (m_bIsFolder && !m_strPath.empty() && !IsFileFolder())
374 URIUtils::AddSlashAtEnd(m_strPath);
375 FillInMimeType(false);
378 CFileItem::CFileItem(const std::string& strPath, bool bIsFolder)
380 Initialize();
381 m_strPath = strPath;
382 m_bIsFolder = bIsFolder;
383 if (m_bIsFolder && !m_strPath.empty() && !IsFileFolder())
384 URIUtils::AddSlashAtEnd(m_strPath);
385 FillInMimeType(false);
388 CFileItem::CFileItem(const CMediaSource& share)
390 Initialize();
391 m_bIsFolder = true;
392 m_bIsShareOrDrive = true;
393 m_strPath = share.strPath;
394 if (!IsRSS()) // no slash at end for rss feeds
395 URIUtils::AddSlashAtEnd(m_strPath);
396 std::string label = share.strName;
397 if (!share.strStatus.empty())
398 label = StringUtils::Format("{} ({})", share.strName, share.strStatus);
399 SetLabel(label);
400 m_iLockMode = share.m_iLockMode;
401 m_strLockCode = share.m_strLockCode;
402 m_iHasLock = share.m_iHasLock;
403 m_iBadPwdCount = share.m_iBadPwdCount;
404 m_iDriveType = share.m_iDriveType;
405 SetArt("thumb", share.m_strThumbnailImage);
406 SetLabelPreformatted(true);
407 if (IsDVD())
408 GetVideoInfoTag()->m_strFileNameAndPath = share.strDiskUniqueId; // share.strDiskUniqueId contains disc unique id
409 FillInMimeType(false);
412 CFileItem::CFileItem(std::shared_ptr<const ADDON::IAddon> addonInfo) : m_addonInfo(std::move(addonInfo))
414 Initialize();
417 CFileItem::CFileItem(const EventPtr& eventLogEntry)
419 Initialize();
421 m_eventLogEntry = eventLogEntry;
422 SetLabel(eventLogEntry->GetLabel());
423 m_dateTime = eventLogEntry->GetDateTime();
424 if (!eventLogEntry->GetIcon().empty())
425 SetArt("icon", eventLogEntry->GetIcon());
428 CFileItem::~CFileItem(void)
430 delete m_musicInfoTag;
431 delete m_videoInfoTag;
432 delete m_pictureInfoTag;
433 delete m_gameInfoTag;
435 m_musicInfoTag = NULL;
436 m_videoInfoTag = NULL;
437 m_pictureInfoTag = NULL;
438 m_gameInfoTag = NULL;
441 CFileItem& CFileItem::operator=(const CFileItem& item)
443 if (this == &item)
444 return *this;
446 CGUIListItem::operator=(item);
447 m_bLabelPreformatted=item.m_bLabelPreformatted;
448 FreeMemory();
449 m_strPath = item.m_strPath;
450 m_strDynPath = item.m_strDynPath;
451 m_bIsParentFolder = item.m_bIsParentFolder;
452 m_iDriveType = item.m_iDriveType;
453 m_bIsShareOrDrive = item.m_bIsShareOrDrive;
454 m_dateTime = item.m_dateTime;
455 m_dwSize = item.m_dwSize;
457 if (item.m_musicInfoTag)
459 if (m_musicInfoTag)
460 *m_musicInfoTag = *item.m_musicInfoTag;
461 else
462 m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag(*item.m_musicInfoTag);
464 else
466 delete m_musicInfoTag;
467 m_musicInfoTag = NULL;
470 if (item.m_videoInfoTag)
472 if (m_videoInfoTag)
473 *m_videoInfoTag = *item.m_videoInfoTag;
474 else
475 m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
477 else
479 delete m_videoInfoTag;
480 m_videoInfoTag = NULL;
483 if (item.m_pictureInfoTag)
485 if (m_pictureInfoTag)
486 *m_pictureInfoTag = *item.m_pictureInfoTag;
487 else
488 m_pictureInfoTag = new CPictureInfoTag(*item.m_pictureInfoTag);
490 else
492 delete m_pictureInfoTag;
493 m_pictureInfoTag = NULL;
496 if (item.m_gameInfoTag)
498 if (m_gameInfoTag)
499 *m_gameInfoTag = *item.m_gameInfoTag;
500 else
501 m_gameInfoTag = new CGameInfoTag(*item.m_gameInfoTag);
503 else
505 delete m_gameInfoTag;
506 m_gameInfoTag = NULL;
509 m_epgInfoTag = item.m_epgInfoTag;
510 m_epgSearchFilter = item.m_epgSearchFilter;
511 m_pvrChannelGroupMemberInfoTag = item.m_pvrChannelGroupMemberInfoTag;
512 m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
513 m_pvrTimerInfoTag = item.m_pvrTimerInfoTag;
514 m_addonInfo = item.m_addonInfo;
515 m_eventLogEntry = item.m_eventLogEntry;
517 m_lStartOffset = item.m_lStartOffset;
518 m_lStartPartNumber = item.m_lStartPartNumber;
519 m_lEndOffset = item.m_lEndOffset;
520 m_strDVDLabel = item.m_strDVDLabel;
521 m_strTitle = item.m_strTitle;
522 m_iprogramCount = item.m_iprogramCount;
523 m_idepth = item.m_idepth;
524 m_iLockMode = item.m_iLockMode;
525 m_strLockCode = item.m_strLockCode;
526 m_iHasLock = item.m_iHasLock;
527 m_iBadPwdCount = item.m_iBadPwdCount;
528 m_bCanQueue=item.m_bCanQueue;
529 m_mimetype = item.m_mimetype;
530 m_extrainfo = item.m_extrainfo;
531 m_specialSort = item.m_specialSort;
532 m_bIsAlbum = item.m_bIsAlbum;
533 m_doContentLookup = item.m_doContentLookup;
534 return *this;
537 void CFileItem::Initialize()
539 m_musicInfoTag = NULL;
540 m_videoInfoTag = NULL;
541 m_pictureInfoTag = NULL;
542 m_gameInfoTag = NULL;
543 m_bLabelPreformatted = false;
544 m_bIsAlbum = false;
545 m_dwSize = 0;
546 m_bIsParentFolder = false;
547 m_bIsShareOrDrive = false;
548 m_iDriveType = CMediaSource::SOURCE_TYPE_UNKNOWN;
549 m_lStartOffset = 0;
550 m_lStartPartNumber = 1;
551 m_lEndOffset = 0;
552 m_iprogramCount = 0;
553 m_idepth = 1;
554 m_iLockMode = LOCK_MODE_EVERYONE;
555 m_iBadPwdCount = 0;
556 m_iHasLock = LOCK_STATE_NO_LOCK;
557 m_bCanQueue = true;
558 m_specialSort = SortSpecialNone;
559 m_doContentLookup = true;
562 void CFileItem::Reset()
564 // CGUIListItem members...
565 m_strLabel2.clear();
566 SetLabel("");
567 FreeIcons();
568 m_overlayIcon = ICON_OVERLAY_NONE;
569 m_bSelected = false;
570 m_bIsFolder = false;
572 m_strDVDLabel.clear();
573 m_strTitle.clear();
574 m_strPath.clear();
575 m_strDynPath.clear();
576 m_dateTime.Reset();
577 m_strLockCode.clear();
578 m_mimetype.clear();
579 delete m_musicInfoTag;
580 m_musicInfoTag=NULL;
581 delete m_videoInfoTag;
582 m_videoInfoTag=NULL;
583 m_epgInfoTag.reset();
584 m_epgSearchFilter.reset();
585 m_pvrChannelGroupMemberInfoTag.reset();
586 m_pvrRecordingInfoTag.reset();
587 m_pvrTimerInfoTag.reset();
588 delete m_pictureInfoTag;
589 m_pictureInfoTag=NULL;
590 delete m_gameInfoTag;
591 m_gameInfoTag = NULL;
592 m_extrainfo.clear();
593 ClearProperties();
594 m_eventLogEntry.reset();
596 Initialize();
597 SetInvalid();
600 // do not archive dynamic path
601 void CFileItem::Archive(CArchive& ar)
603 CGUIListItem::Archive(ar);
605 if (ar.IsStoring())
607 ar << m_bIsParentFolder;
608 ar << m_bLabelPreformatted;
609 ar << m_strPath;
610 ar << m_bIsShareOrDrive;
611 ar << m_iDriveType;
612 ar << m_dateTime;
613 ar << m_dwSize;
614 ar << m_strDVDLabel;
615 ar << m_strTitle;
616 ar << m_iprogramCount;
617 ar << m_idepth;
618 ar << m_lStartOffset;
619 ar << m_lStartPartNumber;
620 ar << m_lEndOffset;
621 ar << m_iLockMode;
622 ar << m_strLockCode;
623 ar << m_iBadPwdCount;
625 ar << m_bCanQueue;
626 ar << m_mimetype;
627 ar << m_extrainfo;
628 ar << m_specialSort;
629 ar << m_doContentLookup;
631 if (m_musicInfoTag)
633 ar << 1;
634 ar << *m_musicInfoTag;
636 else
637 ar << 0;
638 if (m_videoInfoTag)
640 ar << 1;
641 ar << *m_videoInfoTag;
643 else
644 ar << 0;
645 if (m_pictureInfoTag)
647 ar << 1;
648 ar << *m_pictureInfoTag;
650 else
651 ar << 0;
652 if (m_gameInfoTag)
654 ar << 1;
655 ar << *m_gameInfoTag;
657 else
658 ar << 0;
660 else
662 ar >> m_bIsParentFolder;
663 ar >> m_bLabelPreformatted;
664 ar >> m_strPath;
665 ar >> m_bIsShareOrDrive;
666 ar >> m_iDriveType;
667 ar >> m_dateTime;
668 ar >> m_dwSize;
669 ar >> m_strDVDLabel;
670 ar >> m_strTitle;
671 ar >> m_iprogramCount;
672 ar >> m_idepth;
673 ar >> m_lStartOffset;
674 ar >> m_lStartPartNumber;
675 ar >> m_lEndOffset;
676 int temp;
677 ar >> temp;
678 m_iLockMode = (LockType)temp;
679 ar >> m_strLockCode;
680 ar >> m_iBadPwdCount;
682 ar >> m_bCanQueue;
683 ar >> m_mimetype;
684 ar >> m_extrainfo;
685 ar >> temp;
686 m_specialSort = (SortSpecial)temp;
687 ar >> m_doContentLookup;
689 int iType;
690 ar >> iType;
691 if (iType == 1)
692 ar >> *GetMusicInfoTag();
693 ar >> iType;
694 if (iType == 1)
695 ar >> *GetVideoInfoTag();
696 ar >> iType;
697 if (iType == 1)
698 ar >> *GetPictureInfoTag();
699 ar >> iType;
700 if (iType == 1)
701 ar >> *GetGameInfoTag();
703 SetInvalid();
707 void CFileItem::Serialize(CVariant& value) const
709 //CGUIListItem::Serialize(value["CGUIListItem"]);
711 value["strPath"] = m_strPath;
712 value["dateTime"] = (m_dateTime.IsValid()) ? m_dateTime.GetAsRFC1123DateTime() : "";
713 value["lastmodified"] = m_dateTime.IsValid() ? m_dateTime.GetAsDBDateTime() : "";
714 value["size"] = m_dwSize;
715 value["DVDLabel"] = m_strDVDLabel;
716 value["title"] = m_strTitle;
717 value["mimetype"] = m_mimetype;
718 value["extrainfo"] = m_extrainfo;
720 if (m_musicInfoTag)
721 (*m_musicInfoTag).Serialize(value["musicInfoTag"]);
723 if (m_videoInfoTag)
724 (*m_videoInfoTag).Serialize(value["videoInfoTag"]);
726 if (m_pictureInfoTag)
727 (*m_pictureInfoTag).Serialize(value["pictureInfoTag"]);
729 if (m_gameInfoTag)
730 (*m_gameInfoTag).Serialize(value["gameInfoTag"]);
732 if (!m_mapProperties.empty())
734 auto& customProperties = value["customproperties"];
735 for (const auto& prop : m_mapProperties)
736 customProperties[prop.first] = prop.second;
740 void CFileItem::ToSortable(SortItem &sortable, Field field) const
742 switch (field)
744 case FieldPath:
745 sortable[FieldPath] = m_strPath;
746 break;
747 case FieldDate:
748 sortable[FieldDate] = (m_dateTime.IsValid()) ? m_dateTime.GetAsDBDateTime() : "";
749 break;
750 case FieldSize:
751 sortable[FieldSize] = m_dwSize;
752 break;
753 case FieldDriveType:
754 sortable[FieldDriveType] = m_iDriveType;
755 break;
756 case FieldStartOffset:
757 sortable[FieldStartOffset] = m_lStartOffset;
758 break;
759 case FieldEndOffset:
760 sortable[FieldEndOffset] = m_lEndOffset;
761 break;
762 case FieldProgramCount:
763 sortable[FieldProgramCount] = m_iprogramCount;
764 break;
765 case FieldBitrate:
766 sortable[FieldBitrate] = m_dwSize;
767 break;
768 case FieldTitle:
769 sortable[FieldTitle] = m_strTitle;
770 break;
772 // If there's ever a need to convert more properties from CGUIListItem it might be
773 // worth to make CGUIListItem implement ISortable as well and call it from here
775 default:
776 break;
779 if (HasMusicInfoTag())
780 GetMusicInfoTag()->ToSortable(sortable, field);
782 if (HasVideoInfoTag())
783 GetVideoInfoTag()->ToSortable(sortable, field);
785 if (HasPictureInfoTag())
786 GetPictureInfoTag()->ToSortable(sortable, field);
788 if (HasPVRChannelInfoTag())
789 GetPVRChannelInfoTag()->ToSortable(sortable, field);
791 if (HasPVRChannelGroupMemberInfoTag())
792 GetPVRChannelGroupMemberInfoTag()->ToSortable(sortable, field);
794 if (HasAddonInfo())
796 switch (field)
798 case FieldInstallDate:
799 sortable[FieldInstallDate] = GetAddonInfo()->InstallDate().GetAsDBDateTime();
800 break;
801 case FieldLastUpdated:
802 sortable[FieldLastUpdated] = GetAddonInfo()->LastUpdated().GetAsDBDateTime();
803 break;
804 case FieldLastUsed:
805 sortable[FieldLastUsed] = GetAddonInfo()->LastUsed().GetAsDBDateTime();
806 break;
807 default:
808 break;
812 if (HasGameInfoTag())
813 GetGameInfoTag()->ToSortable(sortable, field);
815 if (m_eventLogEntry)
816 m_eventLogEntry->ToSortable(sortable, field);
818 if (IsFavourite())
820 if (field == FieldUserPreference)
821 sortable[FieldUserPreference] = GetProperty("favourite.index").asString();
825 void CFileItem::ToSortable(SortItem &sortable, const Fields &fields) const
827 Fields::const_iterator it;
828 for (it = fields.begin(); it != fields.end(); ++it)
829 ToSortable(sortable, *it);
831 /* FieldLabel is used as a fallback by all sorters and therefore has to be present as well */
832 sortable[FieldLabel] = GetLabel();
833 /* FieldSortSpecial and FieldFolder are required in conjunction with all other sorters as well */
834 sortable[FieldSortSpecial] = m_specialSort;
835 sortable[FieldFolder] = m_bIsFolder;
838 bool CFileItem::Exists(bool bUseCache /* = true */) const
840 if (m_strPath.empty()
841 || IsPath("add")
842 || IsInternetStream()
843 || IsParentFolder()
844 || IsVirtualDirectoryRoot()
845 || IsPlugin()
846 || IsPVR())
847 return true;
849 if (IsVideoDb() && HasVideoInfoTag())
851 CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
852 return dbItem.Exists();
855 std::string strPath = m_strPath;
857 if (URIUtils::IsMultiPath(strPath))
858 strPath = CMultiPathDirectory::GetFirstPath(strPath);
860 if (URIUtils::IsStack(strPath))
861 strPath = CStackDirectory::GetFirstStackedFile(strPath);
863 if (m_bIsFolder)
864 return CDirectory::Exists(strPath, bUseCache);
865 else
866 return CFile::Exists(strPath, bUseCache);
868 return false;
871 bool CFileItem::IsVideo() const
873 /* check preset mime type */
874 if(StringUtils::StartsWithNoCase(m_mimetype, "video/"))
875 return true;
877 if (HasVideoInfoTag())
878 return true;
880 if (HasGameInfoTag())
881 return false;
883 if (HasMusicInfoTag())
884 return false;
886 if (HasPictureInfoTag())
887 return false;
889 // only tv recordings are videos...
890 if (IsPVRRecording())
891 return !GetPVRRecordingInfoTag()->IsRadio();
893 // ... all other PVR items are not.
894 if (IsPVR())
895 return false;
897 if (URIUtils::IsDVD(m_strPath))
898 return true;
900 std::string extension;
901 if(StringUtils::StartsWithNoCase(m_mimetype, "application/"))
902 { /* check for some standard types */
903 extension = m_mimetype.substr(12);
904 if( StringUtils::EqualsNoCase(extension, "ogg")
905 || StringUtils::EqualsNoCase(extension, "mp4")
906 || StringUtils::EqualsNoCase(extension, "mxf") )
907 return true;
910 //! @todo If the file is a zip file, ask the game clients if any support this
911 // file before assuming it is video.
913 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions());
916 bool CFileItem::IsEPG() const
918 return HasEPGInfoTag();
921 bool CFileItem::IsPVRChannel() const
923 return HasPVRChannelInfoTag();
926 bool CFileItem::IsPVRChannelGroup() const
928 return URIUtils::IsPVRChannelGroup(m_strPath);
931 bool CFileItem::IsPVRRecording() const
933 return HasPVRRecordingInfoTag();
936 bool CFileItem::IsUsablePVRRecording() const
938 return (m_pvrRecordingInfoTag && !m_pvrRecordingInfoTag->IsDeleted());
941 bool CFileItem::IsDeletedPVRRecording() const
943 return (m_pvrRecordingInfoTag && m_pvrRecordingInfoTag->IsDeleted());
946 bool CFileItem::IsInProgressPVRRecording() const
948 return (m_pvrRecordingInfoTag && m_pvrRecordingInfoTag->IsInProgress());
951 bool CFileItem::IsPVRTimer() const
953 return HasPVRTimerInfoTag();
956 bool CFileItem::IsDiscStub() const
958 if (IsVideoDb() && HasVideoInfoTag())
960 CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
961 return dbItem.IsDiscStub();
964 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetDiscStubExtensions());
967 bool CFileItem::IsAudio() const
969 /* check preset mime type */
970 if(StringUtils::StartsWithNoCase(m_mimetype, "audio/"))
971 return true;
973 if (HasMusicInfoTag())
974 return true;
976 if (HasVideoInfoTag())
977 return false;
979 if (HasPictureInfoTag())
980 return false;
982 if (HasGameInfoTag())
983 return false;
985 if (IsCDDA())
986 return true;
988 if(StringUtils::StartsWithNoCase(m_mimetype, "application/"))
989 { /* check for some standard types */
990 std::string extension = m_mimetype.substr(12);
991 if( StringUtils::EqualsNoCase(extension, "ogg")
992 || StringUtils::EqualsNoCase(extension, "mp4")
993 || StringUtils::EqualsNoCase(extension, "mxf") )
994 return true;
997 //! @todo If the file is a zip file, ask the game clients if any support this
998 // file before assuming it is audio
1000 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetMusicExtensions());
1003 bool CFileItem::IsDeleted() const
1005 if (HasPVRRecordingInfoTag())
1006 return GetPVRRecordingInfoTag()->IsDeleted();
1008 return false;
1011 bool CFileItem::IsAudioBook() const
1013 return IsType(".m4b") || IsType(".mka");
1016 bool CFileItem::IsGame() const
1018 if (HasGameInfoTag())
1019 return true;
1021 if (HasVideoInfoTag())
1022 return false;
1024 if (HasMusicInfoTag())
1025 return false;
1027 if (HasPictureInfoTag())
1028 return false;
1030 if (IsPVR())
1031 return false;
1033 if (HasAddonInfo())
1034 return CGameUtils::IsStandaloneGame(std::const_pointer_cast<ADDON::IAddon>(GetAddonInfo()));
1036 return CGameUtils::HasGameExtension(m_strPath);
1039 bool CFileItem::IsPicture() const
1041 if (StringUtils::StartsWithNoCase(m_mimetype, "image/"))
1042 return true;
1044 if (HasPictureInfoTag())
1045 return true;
1047 if (HasGameInfoTag())
1048 return false;
1050 if (HasMusicInfoTag())
1051 return false;
1053 if (HasVideoInfoTag())
1054 return false;
1056 if (HasPVRTimerInfoTag() || HasPVRChannelInfoTag() || HasPVRChannelGroupMemberInfoTag() ||
1057 HasPVRRecordingInfoTag() || HasEPGInfoTag() || HasEPGSearchFilter())
1058 return false;
1060 if (!m_strPath.empty())
1061 return CUtil::IsPicture(m_strPath);
1063 return false;
1066 bool CFileItem::IsLyrics() const
1068 return URIUtils::HasExtension(m_strPath, ".cdg|.lrc");
1071 bool CFileItem::IsSubtitle() const
1073 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions());
1076 bool CFileItem::IsCUESheet() const
1078 return URIUtils::HasExtension(m_strPath, ".cue");
1081 bool CFileItem::IsInternetStream(const bool bStrictCheck /* = false */) const
1083 if (HasProperty("IsHTTPDirectory"))
1084 return bStrictCheck;
1086 if (!m_strDynPath.empty())
1087 return URIUtils::IsInternetStream(m_strDynPath, bStrictCheck);
1089 return URIUtils::IsInternetStream(m_strPath, bStrictCheck);
1092 bool CFileItem::IsStreamedFilesystem() const
1094 if (!m_strDynPath.empty())
1095 return URIUtils::IsStreamedFilesystem(m_strDynPath);
1097 return URIUtils::IsStreamedFilesystem(m_strPath);
1100 bool CFileItem::IsFileFolder(EFileFolderType types) const
1102 EFileFolderType always_type = EFILEFOLDER_TYPE_ALWAYS;
1104 /* internet streams are not directly expanded */
1105 if(IsInternetStream())
1106 always_type = EFILEFOLDER_TYPE_ONCLICK;
1108 if(types & always_type)
1110 if(IsSmartPlayList()
1111 || (IsPlayList() && CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders)
1112 || IsAPK()
1113 || IsZIP()
1114 || IsRAR()
1115 || IsRSS()
1116 || IsAudioBook()
1117 || IsType(".ogg|.oga|.xbt")
1118 #if defined(TARGET_ANDROID)
1119 || IsType(".apk")
1120 #endif
1122 return true;
1125 if (CServiceBroker::IsAddonInterfaceUp() &&
1126 IsType(CServiceBroker::GetFileExtensionProvider().GetFileFolderExtensions().c_str()) &&
1127 CServiceBroker::GetFileExtensionProvider().CanOperateExtension(m_strPath))
1128 return true;
1130 if(types & EFILEFOLDER_TYPE_ONBROWSE)
1132 if((IsPlayList() && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders)
1133 || IsDiscImage())
1134 return true;
1137 return false;
1140 bool CFileItem::IsSmartPlayList() const
1142 if (HasProperty("library.smartplaylist") && GetProperty("library.smartplaylist").asBoolean())
1143 return true;
1145 return URIUtils::HasExtension(m_strPath, ".xsp");
1148 bool CFileItem::IsLibraryFolder() const
1150 if (HasProperty("library.filter") && GetProperty("library.filter").asBoolean())
1151 return true;
1153 return URIUtils::IsLibraryFolder(m_strPath);
1156 bool CFileItem::IsPlayList() const
1158 return CPlayListFactory::IsPlaylist(*this);
1161 bool CFileItem::IsPythonScript() const
1163 return URIUtils::HasExtension(m_strPath, ".py");
1166 bool CFileItem::IsType(const char *ext) const
1168 if (!m_strDynPath.empty())
1169 return URIUtils::HasExtension(m_strDynPath, ext);
1171 return URIUtils::HasExtension(m_strPath, ext);
1174 bool CFileItem::IsNFO() const
1176 return URIUtils::HasExtension(m_strPath, ".nfo");
1179 bool CFileItem::IsDiscImage() const
1181 return URIUtils::HasExtension(GetDynPath(), ".img|.iso|.nrg|.udf");
1184 bool CFileItem::IsOpticalMediaFile() const
1186 if (IsDVDFile(false, true))
1187 return true;
1189 return IsBDFile();
1192 bool CFileItem::IsDVDFile(bool bVobs /*= true*/, bool bIfos /*= true*/) const
1194 std::string strFileName = URIUtils::GetFileName(GetDynPath());
1195 if (bIfos)
1197 if (StringUtils::EqualsNoCase(strFileName, "video_ts.ifo"))
1198 return true;
1199 if (StringUtils::StartsWithNoCase(strFileName, "vts_") && StringUtils::EndsWithNoCase(strFileName, "_0.ifo") && strFileName.length() == 12)
1200 return true;
1202 if (bVobs)
1204 if (StringUtils::EqualsNoCase(strFileName, "video_ts.vob"))
1205 return true;
1206 if (StringUtils::StartsWithNoCase(strFileName, "vts_") && StringUtils::EndsWithNoCase(strFileName, ".vob"))
1207 return true;
1210 return false;
1213 bool CFileItem::IsBDFile() const
1215 std::string strFileName = URIUtils::GetFileName(GetDynPath());
1216 return (StringUtils::EqualsNoCase(strFileName, "index.bdmv") || StringUtils::EqualsNoCase(strFileName, "MovieObject.bdmv")
1217 || StringUtils::EqualsNoCase(strFileName, "INDEX.BDM") || StringUtils::EqualsNoCase(strFileName, "MOVIEOBJ.BDM"));
1220 bool CFileItem::IsRAR() const
1222 return URIUtils::IsRAR(m_strPath);
1225 bool CFileItem::IsAPK() const
1227 return URIUtils::IsAPK(m_strPath);
1230 bool CFileItem::IsZIP() const
1232 return URIUtils::IsZIP(m_strPath);
1235 bool CFileItem::IsCBZ() const
1237 return URIUtils::HasExtension(m_strPath, ".cbz");
1240 bool CFileItem::IsCBR() const
1242 return URIUtils::HasExtension(m_strPath, ".cbr");
1245 bool CFileItem::IsRSS() const
1247 return StringUtils::StartsWithNoCase(m_strPath, "rss://") || URIUtils::HasExtension(m_strPath, ".rss")
1248 || StringUtils::StartsWithNoCase(m_strPath, "rsss://")
1249 || m_mimetype == "application/rss+xml";
1252 bool CFileItem::IsAndroidApp() const
1254 return URIUtils::IsAndroidApp(m_strPath);
1257 bool CFileItem::IsStack() const
1259 return URIUtils::IsStack(m_strPath);
1262 bool CFileItem::IsFavourite() const
1264 return URIUtils::IsFavourite(m_strPath);
1267 bool CFileItem::IsPlugin() const
1269 return URIUtils::IsPlugin(m_strPath);
1272 bool CFileItem::IsScript() const
1274 return URIUtils::IsScript(m_strPath);
1277 bool CFileItem::IsAddonsPath() const
1279 return URIUtils::IsAddonsPath(m_strPath);
1282 bool CFileItem::IsSourcesPath() const
1284 return URIUtils::IsSourcesPath(m_strPath);
1287 bool CFileItem::IsMultiPath() const
1289 return URIUtils::IsMultiPath(m_strPath);
1292 bool CFileItem::IsBluray() const
1294 if (URIUtils::IsBluray(m_strPath))
1295 return true;
1297 CFileItem item = CFileItem(GetOpticalMediaPath(), false);
1299 return item.IsBDFile();
1302 bool CFileItem::IsProtectedBlurayDisc() const
1304 std::string path;
1305 path = URIUtils::AddFileToFolder(GetPath(), "AACS", "Unit_Key_RO.inf");
1306 if (CFile::Exists(path))
1307 return true;
1309 return false;
1312 bool CFileItem::IsCDDA() const
1314 return URIUtils::IsCDDA(m_strPath);
1317 bool CFileItem::IsDVD() const
1319 return URIUtils::IsDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
1322 bool CFileItem::IsOnDVD() const
1324 return URIUtils::IsOnDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
1327 bool CFileItem::IsNfs() const
1329 return URIUtils::IsNfs(m_strPath);
1332 bool CFileItem::IsOnLAN() const
1334 return URIUtils::IsOnLAN(m_strPath);
1337 bool CFileItem::IsISO9660() const
1339 return URIUtils::IsISO9660(m_strPath);
1342 bool CFileItem::IsRemote() const
1344 return URIUtils::IsRemote(m_strPath);
1347 bool CFileItem::IsSmb() const
1349 return URIUtils::IsSmb(m_strPath);
1352 bool CFileItem::IsURL() const
1354 return URIUtils::IsURL(m_strPath);
1357 bool CFileItem::IsPVR() const
1359 return URIUtils::IsPVR(m_strPath);
1362 bool CFileItem::IsLiveTV() const
1364 return URIUtils::IsLiveTV(m_strPath);
1367 bool CFileItem::IsHD() const
1369 return URIUtils::IsHD(m_strPath);
1372 bool CFileItem::IsMusicDb() const
1374 return URIUtils::IsMusicDb(m_strPath);
1377 bool CFileItem::IsVideoDb() const
1379 return URIUtils::IsVideoDb(m_strPath);
1382 bool CFileItem::IsVirtualDirectoryRoot() const
1384 return (m_bIsFolder && m_strPath.empty());
1387 bool CFileItem::IsRemovable() const
1389 return IsOnDVD() || IsCDDA() || m_iDriveType == CMediaSource::SOURCE_TYPE_REMOVABLE;
1392 bool CFileItem::IsReadOnly() const
1394 if (IsParentFolder())
1395 return true;
1397 if (m_bIsShareOrDrive)
1398 return true;
1400 return !CUtil::SupportsWriteFileOperations(m_strPath);
1403 void CFileItem::FillInDefaultIcon()
1405 if (URIUtils::IsPVRGuideItem(m_strPath))
1407 // epg items never have a default icon. no need to execute this expensive method.
1408 // when filling epg grid window, easily tens of thousands of epg items are processed.
1409 return;
1412 //CLog::Log(LOGINFO, "FillInDefaultIcon({})", pItem->GetLabel());
1413 // find the default icon for a file or folder item
1414 // for files this can be the (depending on the file type)
1415 // default picture for photo's
1416 // default picture for songs
1417 // default picture for videos
1418 // default picture for shortcuts
1419 // default picture for playlists
1421 // for folders
1422 // for .. folders the default picture for parent folder
1423 // for other folders the defaultFolder.png
1425 if (GetArt("icon").empty())
1427 if (!m_bIsFolder)
1429 /* To reduce the average runtime of this code, this list should
1430 * be ordered with most frequently seen types first. Also bear
1431 * in mind the complexity of the code behind the check in the
1432 * case of IsWhatever() returns false.
1434 if (IsPVRChannel())
1436 if (GetPVRChannelInfoTag()->IsRadio())
1437 SetArt("icon", "DefaultMusicSongs.png");
1438 else
1439 SetArt("icon", "DefaultTVShows.png");
1441 else if ( IsLiveTV() )
1443 // Live TV Channel
1444 SetArt("icon", "DefaultTVShows.png");
1446 else if ( URIUtils::IsArchive(m_strPath) )
1447 { // archive
1448 SetArt("icon", "DefaultFile.png");
1450 else if ( IsUsablePVRRecording() )
1452 // PVR recording
1453 SetArt("icon", "DefaultVideo.png");
1455 else if ( IsDeletedPVRRecording() )
1457 // PVR deleted recording
1458 SetArt("icon", "DefaultVideoDeleted.png");
1460 else if ( IsAudio() )
1462 // audio
1463 SetArt("icon", "DefaultAudio.png");
1465 else if ( IsVideo() )
1467 // video
1468 SetArt("icon", "DefaultVideo.png");
1470 else if (IsPVRTimer())
1472 SetArt("icon", "DefaultVideo.png");
1474 else if ( IsPicture() )
1476 // picture
1477 SetArt("icon", "DefaultPicture.png");
1479 else if ( IsPlayList() || IsSmartPlayList())
1481 SetArt("icon", "DefaultPlaylist.png");
1483 else if ( IsPythonScript() )
1485 SetArt("icon", "DefaultScript.png");
1487 else if (IsFavourite())
1489 SetArt("icon", "DefaultFavourites.png");
1491 else
1493 // default icon for unknown file type
1494 SetArt("icon", "DefaultFile.png");
1497 else
1499 if ( IsPlayList() || IsSmartPlayList())
1501 SetArt("icon", "DefaultPlaylist.png");
1503 else if (IsParentFolder())
1505 SetArt("icon", "DefaultFolderBack.png");
1507 else
1509 SetArt("icon", "DefaultFolder.png");
1513 // Set the icon overlays (if applicable)
1514 if (!HasOverlay() && !HasProperty("icon_never_overlay"))
1516 if (URIUtils::IsInRAR(m_strPath))
1517 SetOverlayImage(CGUIListItem::ICON_OVERLAY_RAR);
1518 else if (URIUtils::IsInZIP(m_strPath))
1519 SetOverlayImage(CGUIListItem::ICON_OVERLAY_ZIP);
1523 void CFileItem::RemoveExtension()
1525 if (m_bIsFolder)
1526 return;
1528 std::string strLabel = GetLabel();
1529 URIUtils::RemoveExtension(strLabel);
1530 SetLabel(strLabel);
1533 void CFileItem::CleanString()
1535 if (IsLiveTV())
1536 return;
1538 std::string strLabel = GetLabel();
1539 std::string strTitle, strTitleAndYear, strYear;
1540 CUtil::CleanString(strLabel, strTitle, strTitleAndYear, strYear, true);
1541 SetLabel(strTitleAndYear);
1544 void CFileItem::SetLabel(const std::string &strLabel)
1546 if (strLabel == "..")
1548 m_bIsParentFolder = true;
1549 m_bIsFolder = true;
1550 m_specialSort = SortSpecialOnTop;
1551 SetLabelPreformatted(true);
1553 CGUIListItem::SetLabel(strLabel);
1556 void CFileItem::SetFileSizeLabel()
1558 if(m_bIsFolder && m_dwSize == 0)
1559 SetLabel2("");
1560 else
1561 SetLabel2(StringUtils::SizeToString(m_dwSize));
1564 bool CFileItem::CanQueue() const
1566 return m_bCanQueue;
1569 void CFileItem::SetCanQueue(bool bYesNo)
1571 m_bCanQueue = bYesNo;
1574 bool CFileItem::IsParentFolder() const
1576 return m_bIsParentFolder;
1579 void CFileItem::FillInMimeType(bool lookup /*= true*/)
1581 //! @todo adapt this to use CMime::GetMimeType()
1582 if (m_mimetype.empty())
1584 if (m_bIsFolder)
1585 m_mimetype = "x-directory/normal";
1586 else if (HasPVRChannelInfoTag())
1587 m_mimetype = GetPVRChannelInfoTag()->MimeType();
1588 else if (StringUtils::StartsWithNoCase(GetDynPath(), "shout://") ||
1589 StringUtils::StartsWithNoCase(GetDynPath(), "http://") ||
1590 StringUtils::StartsWithNoCase(GetDynPath(), "https://"))
1592 // If lookup is false, bail out early to leave mime type empty
1593 if (!lookup)
1594 return;
1596 CCurlFile::GetMimeType(GetDynURL(), m_mimetype);
1598 // try to get mime-type again but with an NSPlayer User-Agent
1599 // in order for server to provide correct mime-type. Allows us
1600 // to properly detect an MMS stream
1601 if (StringUtils::StartsWithNoCase(m_mimetype, "video/x-ms-"))
1602 CCurlFile::GetMimeType(GetDynURL(), m_mimetype, "NSPlayer/11.00.6001.7000");
1604 // make sure there are no options set in mime-type
1605 // mime-type can look like "video/x-ms-asf ; charset=utf8"
1606 size_t i = m_mimetype.find(';');
1607 if(i != std::string::npos)
1608 m_mimetype.erase(i, m_mimetype.length() - i);
1609 StringUtils::Trim(m_mimetype);
1611 else
1612 m_mimetype = CMime::GetMimeType(*this);
1614 // if it's still empty set to an unknown type
1615 if (m_mimetype.empty())
1616 m_mimetype = "application/octet-stream";
1619 // change protocol to mms for the following mime-type. Allows us to create proper FileMMS.
1620 if(StringUtils::StartsWithNoCase(m_mimetype, "application/vnd.ms.wms-hdr.asfv1") ||
1621 StringUtils::StartsWithNoCase(m_mimetype, "application/x-mms-framed"))
1623 if (m_strDynPath.empty())
1624 m_strDynPath = m_strPath;
1626 StringUtils::Replace(m_strDynPath, "http:", "mms:");
1630 void CFileItem::SetMimeTypeForInternetFile()
1632 if (m_doContentLookup && IsInternetStream())
1634 SetMimeType("");
1635 FillInMimeType(true);
1639 bool CFileItem::IsSamePath(const CFileItem *item) const
1641 if (!item)
1642 return false;
1644 if (!m_strPath.empty() && item->GetPath() == m_strPath)
1646 if (item->HasProperty("item_start") || HasProperty("item_start"))
1647 return (item->GetProperty("item_start") == GetProperty("item_start"));
1648 return true;
1650 if (HasMusicInfoTag() && item->HasMusicInfoTag())
1652 if (GetMusicInfoTag()->GetDatabaseId() != -1 && item->GetMusicInfoTag()->GetDatabaseId() != -1)
1653 return ((GetMusicInfoTag()->GetDatabaseId() == item->GetMusicInfoTag()->GetDatabaseId()) &&
1654 (GetMusicInfoTag()->GetType() == item->GetMusicInfoTag()->GetType()));
1656 if (HasVideoInfoTag() && item->HasVideoInfoTag())
1658 if (GetVideoInfoTag()->m_iDbId != -1 && item->GetVideoInfoTag()->m_iDbId != -1)
1659 return ((GetVideoInfoTag()->m_iDbId == item->GetVideoInfoTag()->m_iDbId) &&
1660 (GetVideoInfoTag()->m_type == item->GetVideoInfoTag()->m_type));
1662 if (IsMusicDb() && HasMusicInfoTag())
1664 CFileItem dbItem(m_musicInfoTag->GetURL(), false);
1665 if (HasProperty("item_start"))
1666 dbItem.SetProperty("item_start", GetProperty("item_start"));
1667 return dbItem.IsSamePath(item);
1669 if (IsVideoDb() && HasVideoInfoTag())
1671 CFileItem dbItem(GetVideoInfoTag()->m_strFileNameAndPath, false);
1672 if (HasProperty("item_start"))
1673 dbItem.SetProperty("item_start", GetProperty("item_start"));
1674 return dbItem.IsSamePath(item);
1676 if (item->IsMusicDb() && item->HasMusicInfoTag())
1678 CFileItem dbItem(item->m_musicInfoTag->GetURL(), false);
1679 if (item->HasProperty("item_start"))
1680 dbItem.SetProperty("item_start", item->GetProperty("item_start"));
1681 return IsSamePath(&dbItem);
1683 if (item->IsVideoDb() && item->HasVideoInfoTag())
1685 CFileItem dbItem(item->GetVideoInfoTag()->m_strFileNameAndPath, false);
1686 if (item->HasProperty("item_start"))
1687 dbItem.SetProperty("item_start", item->GetProperty("item_start"));
1688 return IsSamePath(&dbItem);
1690 if (HasProperty("original_listitem_url"))
1691 return (GetProperty("original_listitem_url") == item->GetPath());
1692 return false;
1695 bool CFileItem::IsAlbum() const
1697 return m_bIsAlbum;
1700 void CFileItem::UpdateInfo(const CFileItem &item, bool replaceLabels /*=true*/)
1702 if (item.HasVideoInfoTag())
1703 { // copy info across
1704 //! @todo premiered info is normally stored in m_dateTime by the db
1706 if (item.m_videoInfoTag)
1708 if (m_videoInfoTag)
1709 *m_videoInfoTag = *item.m_videoInfoTag;
1710 else
1711 m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
1713 else
1715 if (m_videoInfoTag)
1716 delete m_videoInfoTag;
1718 m_videoInfoTag = new CVideoInfoTag;
1721 m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
1723 SetOverlayImage(ICON_OVERLAY_UNWATCHED, GetVideoInfoTag()->GetPlayCount() > 0);
1724 SetInvalid();
1726 if (item.HasMusicInfoTag())
1728 *GetMusicInfoTag() = *item.GetMusicInfoTag();
1729 SetInvalid();
1731 if (item.HasPictureInfoTag())
1733 *GetPictureInfoTag() = *item.GetPictureInfoTag();
1734 SetInvalid();
1736 if (item.HasGameInfoTag())
1738 *GetGameInfoTag() = *item.GetGameInfoTag();
1739 SetInvalid();
1741 SetDynPath(item.GetDynPath());
1742 if (replaceLabels && !item.GetLabel().empty())
1743 SetLabel(item.GetLabel());
1744 if (replaceLabels && !item.GetLabel2().empty())
1745 SetLabel2(item.GetLabel2());
1746 if (!item.GetArt().empty())
1747 SetArt(item.GetArt());
1748 AppendProperties(item);
1751 void CFileItem::MergeInfo(const CFileItem& item)
1753 // TODO: Currently merge the metadata/art info is implemented for video case only
1754 if (item.HasVideoInfoTag())
1756 if (item.m_videoInfoTag)
1758 if (m_videoInfoTag)
1759 m_videoInfoTag->Merge(*item.m_videoInfoTag);
1760 else
1761 m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
1764 m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
1766 SetOverlayImage(ICON_OVERLAY_UNWATCHED, GetVideoInfoTag()->GetPlayCount() > 0);
1767 SetInvalid();
1769 if (item.HasMusicInfoTag())
1771 *GetMusicInfoTag() = *item.GetMusicInfoTag();
1772 SetInvalid();
1774 if (item.HasPictureInfoTag())
1776 *GetPictureInfoTag() = *item.GetPictureInfoTag();
1777 SetInvalid();
1779 if (item.HasGameInfoTag())
1781 *GetGameInfoTag() = *item.GetGameInfoTag();
1782 SetInvalid();
1784 SetDynPath(item.GetDynPath());
1785 if (!item.GetLabel().empty())
1786 SetLabel(item.GetLabel());
1787 if (!item.GetLabel2().empty())
1788 SetLabel2(item.GetLabel2());
1789 if (!item.GetArt().empty())
1791 if (item.IsVideo())
1792 AppendArt(item.GetArt());
1793 else
1794 SetArt(item.GetArt());
1796 AppendProperties(item);
1799 void CFileItem::SetFromVideoInfoTag(const CVideoInfoTag &video)
1801 if (!video.m_strTitle.empty())
1802 SetLabel(video.m_strTitle);
1803 if (video.m_strFileNameAndPath.empty())
1805 m_strPath = video.m_strPath;
1806 URIUtils::AddSlashAtEnd(m_strPath);
1807 m_bIsFolder = true;
1809 else
1811 m_strPath = video.m_strFileNameAndPath;
1812 m_bIsFolder = false;
1815 if (m_videoInfoTag)
1816 *m_videoInfoTag = video;
1817 else
1818 m_videoInfoTag = new CVideoInfoTag(video);
1820 if (video.m_iSeason == 0)
1821 SetProperty("isspecial", "true");
1822 FillInDefaultIcon();
1823 FillInMimeType(false);
1826 namespace
1828 class CPropertySaveHelper
1830 public:
1831 CPropertySaveHelper(CFileItem& item, const std::string& property, const std::string& value)
1832 : m_item(item), m_property(property), m_value(value)
1836 bool NeedsSave() const { return !m_value.empty() || m_item.HasProperty(m_property); }
1838 std::string GetValueToSave(const std::string& currentValue) const
1840 std::string value;
1842 if (!m_value.empty())
1844 // Overwrite whatever we have; remember what we had originally.
1845 if (!m_item.HasProperty(m_property))
1846 m_item.SetProperty(m_property, currentValue);
1848 value = m_value;
1850 else if (m_item.HasProperty(m_property))
1852 // Restore original value
1853 value = m_item.GetProperty(m_property).asString();
1854 m_item.ClearProperty(m_property);
1857 return value;
1860 private:
1861 CFileItem& m_item;
1862 const std::string m_property;
1863 const std::string m_value;
1865 } // unnamed namespace
1867 void CFileItem::SetFromMusicInfoTag(const MUSIC_INFO::CMusicInfoTag& music)
1869 const std::string path = GetPath();
1870 if (path.empty())
1872 SetPath(music.GetURL());
1874 else
1876 const CPropertySaveHelper dynpath(*this, "OriginalDynPath", music.GetURL());
1877 if (dynpath.NeedsSave())
1878 SetDynPath(dynpath.GetValueToSave(m_strDynPath));
1881 const CPropertySaveHelper label(*this, "OriginalLabel", music.GetTitle());
1882 if (label.NeedsSave())
1883 SetLabel(label.GetValueToSave(GetLabel()));
1885 const CPropertySaveHelper thumb(*this, "OriginalThumb", music.GetStationArt());
1886 if (thumb.NeedsSave())
1887 SetArt("thumb", thumb.GetValueToSave(GetArt("thumb")));
1889 *GetMusicInfoTag() = music;
1890 FillInDefaultIcon();
1891 FillInMimeType(false);
1894 void CFileItem::SetFromAlbum(const CAlbum &album)
1896 if (!album.strAlbum.empty())
1897 SetLabel(album.strAlbum);
1898 m_bIsFolder = true;
1899 m_strLabel2 = album.GetAlbumArtistString();
1900 GetMusicInfoTag()->SetAlbum(album);
1902 if (album.art.empty())
1903 SetArt("icon", "DefaultAlbumCover.png");
1904 else
1905 SetArt(album.art);
1907 m_bIsAlbum = true;
1908 CMusicDatabase::SetPropertiesFromAlbum(*this,album);
1909 FillInMimeType(false);
1912 void CFileItem::SetFromSong(const CSong &song)
1914 if (!song.strTitle.empty())
1915 SetLabel(song.strTitle);
1916 if (song.idSong > 0)
1918 std::string strExt = URIUtils::GetExtension(song.strFileName);
1919 m_strPath = StringUtils::Format("musicdb://songs/{}{}", song.idSong, strExt);
1921 else if (!song.strFileName.empty())
1922 m_strPath = song.strFileName;
1923 GetMusicInfoTag()->SetSong(song);
1924 m_lStartOffset = song.iStartOffset;
1925 m_lStartPartNumber = 1;
1926 SetProperty("item_start", song.iStartOffset);
1927 m_lEndOffset = song.iEndOffset;
1928 if (!song.strThumb.empty())
1929 SetArt("thumb", song.strThumb);
1930 FillInMimeType(false);
1933 std::string CFileItem::GetOpticalMediaPath() const
1935 std::string path;
1936 path = URIUtils::AddFileToFolder(GetPath(), "VIDEO_TS.IFO");
1937 if (CFile::Exists(path))
1938 return path;
1940 path = URIUtils::AddFileToFolder(GetPath(), "VIDEO_TS", "VIDEO_TS.IFO");
1941 if (CFile::Exists(path))
1942 return path;
1944 #ifdef HAVE_LIBBLURAY
1945 path = URIUtils::AddFileToFolder(GetPath(), "index.bdmv");
1946 if (CFile::Exists(path))
1947 return path;
1949 path = URIUtils::AddFileToFolder(GetPath(), "BDMV", "index.bdmv");
1950 if (CFile::Exists(path))
1951 return path;
1953 path = URIUtils::AddFileToFolder(GetPath(), "INDEX.BDM");
1954 if (CFile::Exists(path))
1955 return path;
1957 path = URIUtils::AddFileToFolder(GetPath(), "BDMV", "INDEX.BDM");
1958 if (CFile::Exists(path))
1959 return path;
1960 #endif
1961 return std::string();
1965 * @todo Ideally this (and SetPath) would not be available outside of construction
1966 * for CFileItem objects, or at least restricted to essentially be equivalent
1967 * to construction. This would require re-formulating a bunch of CFileItem
1968 * construction, and also allowing CFileItemList to have its own (public)
1969 * SetURL() function, so for now we give direct access.
1971 void CFileItem::SetURL(const CURL& url)
1973 m_strPath = url.Get();
1976 const CURL CFileItem::GetURL() const
1978 CURL url(m_strPath);
1979 return url;
1982 bool CFileItem::IsURL(const CURL& url) const
1984 return IsPath(url.Get());
1987 bool CFileItem::IsPath(const std::string& path, bool ignoreURLOptions /* = false */) const
1989 return URIUtils::PathEquals(m_strPath, path, false, ignoreURLOptions);
1992 void CFileItem::SetDynURL(const CURL& url)
1994 m_strDynPath = url.Get();
1997 const CURL CFileItem::GetDynURL() const
1999 if (!m_strDynPath.empty())
2001 CURL url(m_strDynPath);
2002 return url;
2004 else
2006 CURL url(m_strPath);
2007 return url;
2011 const std::string &CFileItem::GetDynPath() const
2013 if (!m_strDynPath.empty())
2014 return m_strDynPath;
2015 else
2016 return m_strPath;
2019 void CFileItem::SetDynPath(const std::string &path)
2021 m_strDynPath = path;
2024 void CFileItem::SetCueDocument(const CCueDocumentPtr& cuePtr)
2026 m_cueDocument = cuePtr;
2029 void CFileItem::LoadEmbeddedCue()
2031 CMusicInfoTag& tag = *GetMusicInfoTag();
2032 if (!tag.Loaded())
2033 return;
2035 const std::string embeddedCue = tag.GetCueSheet();
2036 if (!embeddedCue.empty())
2038 CCueDocumentPtr cuesheet(new CCueDocument);
2039 if (cuesheet->ParseTag(embeddedCue))
2041 std::vector<std::string> MediaFileVec;
2042 cuesheet->GetMediaFiles(MediaFileVec);
2043 for (std::vector<std::string>::iterator itMedia = MediaFileVec.begin();
2044 itMedia != MediaFileVec.end(); ++itMedia)
2045 cuesheet->UpdateMediaFile(*itMedia, GetPath());
2046 SetCueDocument(cuesheet);
2048 // Clear cuesheet tag having added it to item
2049 tag.SetCueSheet("");
2053 bool CFileItem::HasCueDocument() const
2055 return (m_cueDocument.get() != nullptr);
2058 bool CFileItem::LoadTracksFromCueDocument(CFileItemList& scannedItems)
2060 if (!m_cueDocument)
2061 return false;
2063 const CMusicInfoTag& tag = *GetMusicInfoTag();
2065 VECSONGS tracks;
2066 m_cueDocument->GetSongs(tracks);
2068 bool oneFilePerTrack = m_cueDocument->IsOneFilePerTrack();
2069 m_cueDocument.reset();
2071 int tracksFound = 0;
2072 for (VECSONGS::iterator it = tracks.begin(); it != tracks.end(); ++it)
2074 CSong& song = *it;
2075 if (song.strFileName == GetPath())
2077 if (tag.Loaded())
2079 if (song.strAlbum.empty() && !tag.GetAlbum().empty())
2080 song.strAlbum = tag.GetAlbum();
2081 //Pass album artist to final MusicInfoTag object via setting song album artist vector.
2082 if (song.GetAlbumArtist().empty() && !tag.GetAlbumArtist().empty())
2083 song.SetAlbumArtist(tag.GetAlbumArtist());
2084 if (song.genre.empty() && !tag.GetGenre().empty())
2085 song.genre = tag.GetGenre();
2086 //Pass artist to final MusicInfoTag object via setting song artist description string only.
2087 //Artist credits not used during loading from cue sheet.
2088 if (song.strArtistDesc.empty() && !tag.GetArtistString().empty())
2089 song.strArtistDesc = tag.GetArtistString();
2090 if (tag.GetDiscNumber())
2091 song.iTrack |= (tag.GetDiscNumber() << 16); // see CMusicInfoTag::GetDiscNumber()
2092 if (!tag.GetCueSheet().empty())
2093 song.strCueSheet = tag.GetCueSheet();
2095 if (tag.GetYear())
2096 song.strReleaseDate = tag.GetReleaseDate();
2097 if (song.embeddedArt.Empty() && !tag.GetCoverArtInfo().Empty())
2098 song.embeddedArt = tag.GetCoverArtInfo();
2101 if (!song.iDuration && tag.GetDuration() > 0)
2102 { // must be the last song
2103 song.iDuration = CUtil::ConvertMilliSecsToSecsIntRounded(CUtil::ConvertSecsToMilliSecs(tag.GetDuration()) - song.iStartOffset);
2105 if ( tag.Loaded() && oneFilePerTrack && ! ( tag.GetAlbum().empty() || tag.GetArtist().empty() || tag.GetTitle().empty() ) )
2107 // If there are multiple files in a cue file, the tags from the files should be preferred if they exist.
2108 scannedItems.Add(CFileItemPtr(new CFileItem(song, tag)));
2110 else
2112 scannedItems.Add(CFileItemPtr(new CFileItem(song)));
2114 ++tracksFound;
2117 return tracksFound != 0;
2120 /////////////////////////////////////////////////////////////////////////////////
2121 /////
2122 ///// CFileItemList
2123 /////
2124 //////////////////////////////////////////////////////////////////////////////////
2126 CFileItemList::CFileItemList()
2127 : CFileItem("", true)
2131 CFileItemList::CFileItemList(const std::string& strPath)
2132 : CFileItem(strPath, true)
2136 CFileItemList::~CFileItemList()
2138 Clear();
2141 CFileItemPtr CFileItemList::operator[] (int iItem)
2143 return Get(iItem);
2146 const CFileItemPtr CFileItemList::operator[] (int iItem) const
2148 return Get(iItem);
2151 CFileItemPtr CFileItemList::operator[] (const std::string& strPath)
2153 return Get(strPath);
2156 const CFileItemPtr CFileItemList::operator[] (const std::string& strPath) const
2158 return Get(strPath);
2161 void CFileItemList::SetIgnoreURLOptions(bool ignoreURLOptions)
2163 m_ignoreURLOptions = ignoreURLOptions;
2165 if (m_fastLookup)
2167 m_fastLookup = false; // Force SetFastlookup to clear map
2168 SetFastLookup(true); // and regenerate map
2172 void CFileItemList::SetFastLookup(bool fastLookup)
2174 std::unique_lock<CCriticalSection> lock(m_lock);
2176 if (fastLookup && !m_fastLookup)
2177 { // generate the map
2178 m_map.clear();
2179 for (unsigned int i=0; i < m_items.size(); i++)
2181 CFileItemPtr pItem = m_items[i];
2182 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2185 if (!fastLookup && m_fastLookup)
2186 m_map.clear();
2187 m_fastLookup = fastLookup;
2190 bool CFileItemList::Contains(const std::string& fileName) const
2192 std::unique_lock<CCriticalSection> lock(m_lock);
2194 if (m_fastLookup)
2195 return m_map.find(m_ignoreURLOptions ? CURL(fileName).GetWithoutOptions() : fileName) != m_map.end();
2197 // slow method...
2198 for (unsigned int i = 0; i < m_items.size(); i++)
2200 const CFileItemPtr pItem = m_items[i];
2201 if (pItem->IsPath(m_ignoreURLOptions ? CURL(fileName).GetWithoutOptions() : fileName))
2202 return true;
2204 return false;
2207 void CFileItemList::Clear()
2209 std::unique_lock<CCriticalSection> lock(m_lock);
2211 ClearItems();
2212 m_sortDescription.sortBy = SortByNone;
2213 m_sortDescription.sortOrder = SortOrderNone;
2214 m_sortDescription.sortAttributes = SortAttributeNone;
2215 m_sortIgnoreFolders = false;
2216 m_cacheToDisc = CACHE_IF_SLOW;
2217 m_sortDetails.clear();
2218 m_replaceListing = false;
2219 m_content.clear();
2222 void CFileItemList::ClearItems()
2224 std::unique_lock<CCriticalSection> lock(m_lock);
2225 // make sure we free the memory of the items (these are GUIControls which may have allocated resources)
2226 FreeMemory();
2227 for (unsigned int i = 0; i < m_items.size(); i++)
2229 CFileItemPtr item = m_items[i];
2230 item->FreeMemory();
2232 m_items.clear();
2233 m_map.clear();
2236 void CFileItemList::Add(CFileItemPtr pItem)
2238 std::unique_lock<CCriticalSection> lock(m_lock);
2239 if (m_fastLookup)
2240 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2241 m_items.emplace_back(std::move(pItem));
2244 void CFileItemList::Add(CFileItem&& item)
2246 std::unique_lock<CCriticalSection> lock(m_lock);
2247 auto ptr = std::make_shared<CFileItem>(std::move(item));
2248 if (m_fastLookup)
2249 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(ptr->GetPath()).GetWithoutOptions() : ptr->GetPath(), ptr));
2250 m_items.emplace_back(std::move(ptr));
2253 void CFileItemList::AddFront(const CFileItemPtr &pItem, int itemPosition)
2255 std::unique_lock<CCriticalSection> lock(m_lock);
2257 if (itemPosition >= 0)
2259 m_items.insert(m_items.begin()+itemPosition, pItem);
2261 else
2263 m_items.insert(m_items.begin()+(m_items.size()+itemPosition), pItem);
2265 if (m_fastLookup)
2267 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2271 void CFileItemList::Remove(CFileItem* pItem)
2273 std::unique_lock<CCriticalSection> lock(m_lock);
2275 for (IVECFILEITEMS it = m_items.begin(); it != m_items.end(); ++it)
2277 if (pItem == it->get())
2279 m_items.erase(it);
2280 if (m_fastLookup)
2282 m_map.erase(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath());
2284 break;
2289 VECFILEITEMS::iterator CFileItemList::erase(VECFILEITEMS::iterator first,
2290 VECFILEITEMS::iterator last)
2292 std::unique_lock<CCriticalSection> lock(m_lock);
2293 return m_items.erase(first, last);
2296 void CFileItemList::Remove(int iItem)
2298 std::unique_lock<CCriticalSection> lock(m_lock);
2300 if (iItem >= 0 && iItem < Size())
2302 CFileItemPtr pItem = *(m_items.begin() + iItem);
2303 if (m_fastLookup)
2305 m_map.erase(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath());
2307 m_items.erase(m_items.begin() + iItem);
2311 void CFileItemList::Append(const CFileItemList& itemlist)
2313 std::unique_lock<CCriticalSection> lock(m_lock);
2315 for (int i = 0; i < itemlist.Size(); ++i)
2316 Add(itemlist[i]);
2319 void CFileItemList::Assign(const CFileItemList& itemlist, bool append)
2321 std::unique_lock<CCriticalSection> lock(m_lock);
2322 if (!append)
2323 Clear();
2324 Append(itemlist);
2325 SetPath(itemlist.GetPath());
2326 SetLabel(itemlist.GetLabel());
2327 m_sortDetails = itemlist.m_sortDetails;
2328 m_sortDescription = itemlist.m_sortDescription;
2329 m_replaceListing = itemlist.m_replaceListing;
2330 m_content = itemlist.m_content;
2331 m_mapProperties = itemlist.m_mapProperties;
2332 m_cacheToDisc = itemlist.m_cacheToDisc;
2335 bool CFileItemList::Copy(const CFileItemList& items, bool copyItems /* = true */)
2337 // assign all CFileItem parts
2338 *static_cast<CFileItem*>(this) = static_cast<const CFileItem&>(items);
2340 // assign the rest of the CFileItemList properties
2341 m_replaceListing = items.m_replaceListing;
2342 m_content = items.m_content;
2343 m_mapProperties = items.m_mapProperties;
2344 m_cacheToDisc = items.m_cacheToDisc;
2345 m_sortDetails = items.m_sortDetails;
2346 m_sortDescription = items.m_sortDescription;
2347 m_sortIgnoreFolders = items.m_sortIgnoreFolders;
2349 if (copyItems)
2351 // make a copy of each item
2352 for (int i = 0; i < items.Size(); i++)
2354 CFileItemPtr newItem(new CFileItem(*items[i]));
2355 Add(newItem);
2359 return true;
2362 CFileItemPtr CFileItemList::Get(int iItem) const
2364 std::unique_lock<CCriticalSection> lock(m_lock);
2366 if (iItem > -1 && iItem < (int)m_items.size())
2367 return m_items[iItem];
2369 return CFileItemPtr();
2372 CFileItemPtr CFileItemList::Get(const std::string& strPath) const
2374 std::unique_lock<CCriticalSection> lock(m_lock);
2376 if (m_fastLookup)
2378 MAPFILEITEMS::const_iterator it =
2379 m_map.find(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath);
2380 if (it != m_map.end())
2381 return it->second;
2383 return CFileItemPtr();
2385 // slow method...
2386 for (unsigned int i = 0; i < m_items.size(); i++)
2388 CFileItemPtr pItem = m_items[i];
2389 if (pItem->IsPath(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath))
2390 return pItem;
2393 return CFileItemPtr();
2396 int CFileItemList::Size() const
2398 std::unique_lock<CCriticalSection> lock(m_lock);
2399 return (int)m_items.size();
2402 bool CFileItemList::IsEmpty() const
2404 std::unique_lock<CCriticalSection> lock(m_lock);
2405 return m_items.empty();
2408 void CFileItemList::Reserve(size_t iCount)
2410 std::unique_lock<CCriticalSection> lock(m_lock);
2411 m_items.reserve(iCount);
2414 void CFileItemList::Sort(FILEITEMLISTCOMPARISONFUNC func)
2416 std::unique_lock<CCriticalSection> lock(m_lock);
2417 std::stable_sort(m_items.begin(), m_items.end(), func);
2420 void CFileItemList::FillSortFields(FILEITEMFILLFUNC func)
2422 std::unique_lock<CCriticalSection> lock(m_lock);
2423 std::for_each(m_items.begin(), m_items.end(), func);
2426 void CFileItemList::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute sortAttributes /* = SortAttributeNone */)
2428 if (sortBy == SortByNone ||
2429 (m_sortDescription.sortBy == sortBy && m_sortDescription.sortOrder == sortOrder &&
2430 m_sortDescription.sortAttributes == sortAttributes))
2431 return;
2433 SortDescription sorting;
2434 sorting.sortBy = sortBy;
2435 sorting.sortOrder = sortOrder;
2436 sorting.sortAttributes = sortAttributes;
2438 Sort(sorting);
2439 m_sortDescription = sorting;
2442 void CFileItemList::Sort(SortDescription sortDescription)
2444 if (sortDescription.sortBy == SortByFile || sortDescription.sortBy == SortBySortTitle ||
2445 sortDescription.sortBy == SortByOriginalTitle || sortDescription.sortBy == SortByDateAdded ||
2446 sortDescription.sortBy == SortByRating || sortDescription.sortBy == SortByYear ||
2447 sortDescription.sortBy == SortByPlaylistOrder || sortDescription.sortBy == SortByLastPlayed ||
2448 sortDescription.sortBy == SortByPlaycount)
2449 sortDescription.sortAttributes = (SortAttribute)((int)sortDescription.sortAttributes | SortAttributeIgnoreFolders);
2451 if (sortDescription.sortBy == SortByNone ||
2452 (m_sortDescription.sortBy == sortDescription.sortBy && m_sortDescription.sortOrder == sortDescription.sortOrder &&
2453 m_sortDescription.sortAttributes == sortDescription.sortAttributes))
2454 return;
2456 if (m_sortIgnoreFolders)
2457 sortDescription.sortAttributes = (SortAttribute)((int)sortDescription.sortAttributes | SortAttributeIgnoreFolders);
2459 const Fields fields = SortUtils::GetFieldsForSorting(sortDescription.sortBy);
2460 SortItems sortItems((size_t)Size());
2461 for (int index = 0; index < Size(); index++)
2463 sortItems[index] = std::shared_ptr<SortItem>(new SortItem);
2464 m_items[index]->ToSortable(*sortItems[index], fields);
2465 (*sortItems[index])[FieldId] = index;
2468 // do the sorting
2469 SortUtils::Sort(sortDescription, sortItems);
2471 // apply the new order to the existing CFileItems
2472 VECFILEITEMS sortedFileItems;
2473 sortedFileItems.reserve(Size());
2474 for (SortItems::const_iterator it = sortItems.begin(); it != sortItems.end(); ++it)
2476 CFileItemPtr item = m_items[(int)(*it)->at(FieldId).asInteger()];
2477 // Set the sort label in the CFileItem
2478 item->SetSortLabel((*it)->at(FieldSort).asWideString());
2480 sortedFileItems.push_back(item);
2483 // replace the current list with the re-ordered one
2484 m_items = std::move(sortedFileItems);
2487 void CFileItemList::Randomize()
2489 std::unique_lock<CCriticalSection> lock(m_lock);
2490 KODI::UTILS::RandomShuffle(m_items.begin(), m_items.end());
2493 void CFileItemList::Archive(CArchive& ar)
2495 std::unique_lock<CCriticalSection> lock(m_lock);
2496 if (ar.IsStoring())
2498 CFileItem::Archive(ar);
2500 int i = 0;
2501 if (!m_items.empty() && m_items[0]->IsParentFolder())
2502 i = 1;
2504 ar << (int)(m_items.size() - i);
2506 ar << m_ignoreURLOptions;
2508 ar << m_fastLookup;
2510 ar << (int)m_sortDescription.sortBy;
2511 ar << (int)m_sortDescription.sortOrder;
2512 ar << (int)m_sortDescription.sortAttributes;
2513 ar << m_sortIgnoreFolders;
2514 ar << (int)m_cacheToDisc;
2516 ar << (int)m_sortDetails.size();
2517 for (unsigned int j = 0; j < m_sortDetails.size(); ++j)
2519 const GUIViewSortDetails &details = m_sortDetails[j];
2520 ar << (int)details.m_sortDescription.sortBy;
2521 ar << (int)details.m_sortDescription.sortOrder;
2522 ar << (int)details.m_sortDescription.sortAttributes;
2523 ar << details.m_buttonLabel;
2524 ar << details.m_labelMasks.m_strLabelFile;
2525 ar << details.m_labelMasks.m_strLabelFolder;
2526 ar << details.m_labelMasks.m_strLabel2File;
2527 ar << details.m_labelMasks.m_strLabel2Folder;
2530 ar << m_content;
2532 for (; i < (int)m_items.size(); ++i)
2534 CFileItemPtr pItem = m_items[i];
2535 ar << *pItem;
2538 else
2540 CFileItemPtr pParent;
2541 if (!IsEmpty())
2543 CFileItemPtr pItem=m_items[0];
2544 if (pItem->IsParentFolder())
2545 pParent.reset(new CFileItem(*pItem));
2548 SetIgnoreURLOptions(false);
2549 SetFastLookup(false);
2550 Clear();
2552 CFileItem::Archive(ar);
2554 int iSize = 0;
2555 ar >> iSize;
2556 if (iSize <= 0)
2557 return ;
2559 if (pParent)
2561 m_items.reserve(iSize + 1);
2562 m_items.push_back(pParent);
2564 else
2565 m_items.reserve(iSize);
2567 bool ignoreURLOptions = false;
2568 ar >> ignoreURLOptions;
2570 bool fastLookup = false;
2571 ar >> fastLookup;
2573 int tempint;
2574 ar >> tempint;
2575 m_sortDescription.sortBy = (SortBy)tempint;
2576 ar >> tempint;
2577 m_sortDescription.sortOrder = (SortOrder)tempint;
2578 ar >> tempint;
2579 m_sortDescription.sortAttributes = (SortAttribute)tempint;
2580 ar >> m_sortIgnoreFolders;
2581 ar >> tempint;
2582 m_cacheToDisc = CACHE_TYPE(tempint);
2584 unsigned int detailSize = 0;
2585 ar >> detailSize;
2586 for (unsigned int j = 0; j < detailSize; ++j)
2588 GUIViewSortDetails details;
2589 ar >> tempint;
2590 details.m_sortDescription.sortBy = (SortBy)tempint;
2591 ar >> tempint;
2592 details.m_sortDescription.sortOrder = (SortOrder)tempint;
2593 ar >> tempint;
2594 details.m_sortDescription.sortAttributes = (SortAttribute)tempint;
2595 ar >> details.m_buttonLabel;
2596 ar >> details.m_labelMasks.m_strLabelFile;
2597 ar >> details.m_labelMasks.m_strLabelFolder;
2598 ar >> details.m_labelMasks.m_strLabel2File;
2599 ar >> details.m_labelMasks.m_strLabel2Folder;
2600 m_sortDetails.push_back(details);
2603 ar >> m_content;
2605 for (int i = 0; i < iSize; ++i)
2607 CFileItemPtr pItem(new CFileItem);
2608 ar >> *pItem;
2609 Add(pItem);
2612 SetIgnoreURLOptions(ignoreURLOptions);
2613 SetFastLookup(fastLookup);
2617 void CFileItemList::FillInDefaultIcons()
2619 std::unique_lock<CCriticalSection> lock(m_lock);
2620 for (int i = 0; i < (int)m_items.size(); ++i)
2622 CFileItemPtr pItem = m_items[i];
2623 pItem->FillInDefaultIcon();
2627 int CFileItemList::GetFolderCount() const
2629 std::unique_lock<CCriticalSection> lock(m_lock);
2630 int nFolderCount = 0;
2631 for (int i = 0; i < (int)m_items.size(); i++)
2633 CFileItemPtr pItem = m_items[i];
2634 if (pItem->m_bIsFolder)
2635 nFolderCount++;
2638 return nFolderCount;
2641 int CFileItemList::GetObjectCount() const
2643 std::unique_lock<CCriticalSection> lock(m_lock);
2645 int numObjects = (int)m_items.size();
2646 if (numObjects && m_items[0]->IsParentFolder())
2647 numObjects--;
2649 return numObjects;
2652 int CFileItemList::GetFileCount() const
2654 std::unique_lock<CCriticalSection> lock(m_lock);
2655 int nFileCount = 0;
2656 for (int i = 0; i < (int)m_items.size(); i++)
2658 CFileItemPtr pItem = m_items[i];
2659 if (!pItem->m_bIsFolder)
2660 nFileCount++;
2663 return nFileCount;
2666 int CFileItemList::GetSelectedCount() const
2668 std::unique_lock<CCriticalSection> lock(m_lock);
2669 int count = 0;
2670 for (int i = 0; i < (int)m_items.size(); i++)
2672 CFileItemPtr pItem = m_items[i];
2673 if (pItem->IsSelected())
2674 count++;
2677 return count;
2680 void CFileItemList::FilterCueItems()
2682 std::unique_lock<CCriticalSection> lock(m_lock);
2683 // Handle .CUE sheet files...
2684 std::vector<std::string> itemstodelete;
2685 for (int i = 0; i < (int)m_items.size(); i++)
2687 CFileItemPtr pItem = m_items[i];
2688 if (!pItem->m_bIsFolder)
2689 { // see if it's a .CUE sheet
2690 if (pItem->IsCUESheet())
2692 CCueDocumentPtr cuesheet(new CCueDocument);
2693 if (cuesheet->ParseFile(pItem->GetPath()))
2695 std::vector<std::string> MediaFileVec;
2696 cuesheet->GetMediaFiles(MediaFileVec);
2698 // queue the cue sheet and the underlying media file for deletion
2699 for (std::vector<std::string>::iterator itMedia = MediaFileVec.begin();
2700 itMedia != MediaFileVec.end(); ++itMedia)
2702 std::string strMediaFile = *itMedia;
2703 std::string fileFromCue = strMediaFile; // save the file from the cue we're matching against,
2704 // as we're going to search for others here...
2705 bool bFoundMediaFile = CFile::Exists(strMediaFile);
2706 if (!bFoundMediaFile)
2708 // try file in same dir, not matching case...
2709 if (Contains(strMediaFile))
2711 bFoundMediaFile = true;
2713 else
2715 // try removing the .cue extension...
2716 strMediaFile = pItem->GetPath();
2717 URIUtils::RemoveExtension(strMediaFile);
2718 CFileItem item(strMediaFile, false);
2719 if (item.IsAudio() && Contains(strMediaFile))
2721 bFoundMediaFile = true;
2723 else
2724 { // try replacing the extension with one of our allowed ones.
2725 std::vector<std::string> extensions = StringUtils::Split(CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
2726 for (std::vector<std::string>::const_iterator i = extensions.begin(); i != extensions.end(); ++i)
2728 strMediaFile = URIUtils::ReplaceExtension(pItem->GetPath(), *i);
2729 CFileItem item(strMediaFile, false);
2730 if (!item.IsCUESheet() && !item.IsPlayList() && Contains(strMediaFile))
2732 bFoundMediaFile = true;
2733 break;
2739 if (bFoundMediaFile)
2741 cuesheet->UpdateMediaFile(fileFromCue, strMediaFile);
2742 // apply CUE for later processing
2743 for (int j = 0; j < (int)m_items.size(); j++)
2745 CFileItemPtr pItem = m_items[j];
2746 if (StringUtils::CompareNoCase(pItem->GetPath(), strMediaFile) == 0)
2747 pItem->SetCueDocument(cuesheet);
2752 itemstodelete.push_back(pItem->GetPath());
2756 // now delete the .CUE files.
2757 for (int i = 0; i < (int)itemstodelete.size(); i++)
2759 for (int j = 0; j < (int)m_items.size(); j++)
2761 CFileItemPtr pItem = m_items[j];
2762 if (StringUtils::CompareNoCase(pItem->GetPath(), itemstodelete[i]) == 0)
2763 { // delete this item
2764 m_items.erase(m_items.begin() + j);
2765 break;
2771 // Remove the extensions from the filenames
2772 void CFileItemList::RemoveExtensions()
2774 std::unique_lock<CCriticalSection> lock(m_lock);
2775 for (int i = 0; i < Size(); ++i)
2776 m_items[i]->RemoveExtension();
2779 void CFileItemList::Stack(bool stackFiles /* = true */)
2781 std::unique_lock<CCriticalSection> lock(m_lock);
2783 // not allowed here
2784 if (IsVirtualDirectoryRoot() ||
2785 IsLiveTV() ||
2786 IsSourcesPath() ||
2787 IsLibraryFolder())
2788 return;
2790 SetProperty("isstacked", true);
2792 // items needs to be sorted for stuff below to work properly
2793 Sort(SortByLabel, SortOrderAscending);
2795 StackFolders();
2797 if (stackFiles)
2798 StackFiles();
2801 void CFileItemList::StackFolders()
2803 // Precompile our REs
2804 VECCREGEXP folderRegExps;
2805 CRegExp folderRegExp(true, CRegExp::autoUtf8);
2806 const std::vector<std::string>& strFolderRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_folderStackRegExps;
2808 std::vector<std::string>::const_iterator strExpression = strFolderRegExps.begin();
2809 while (strExpression != strFolderRegExps.end())
2811 if (!folderRegExp.RegComp(*strExpression))
2812 CLog::Log(LOGERROR, "{}: Invalid folder stack RegExp:'{}'", __FUNCTION__,
2813 strExpression->c_str());
2814 else
2815 folderRegExps.push_back(folderRegExp);
2817 ++strExpression;
2820 if (!folderRegExp.IsCompiled())
2822 CLog::Log(LOGDEBUG, "{}: No stack expressions available. Skipping folder stacking",
2823 __FUNCTION__);
2824 return;
2827 // stack folders
2828 for (int i = 0; i < Size(); i++)
2830 CFileItemPtr item = Get(i);
2831 // combined the folder checks
2832 if (item->m_bIsFolder)
2834 // only check known fast sources?
2835 // NOTES:
2836 // 1. rars and zips may be on slow sources? is this supposed to be allowed?
2837 if( !item->IsRemote()
2838 || item->IsSmb()
2839 || item->IsNfs()
2840 || URIUtils::IsInRAR(item->GetPath())
2841 || URIUtils::IsInZIP(item->GetPath())
2842 || URIUtils::IsOnLAN(item->GetPath())
2845 // stack cd# folders if contains only a single video file
2847 bool bMatch(false);
2849 VECCREGEXP::iterator expr = folderRegExps.begin();
2850 while (!bMatch && expr != folderRegExps.end())
2852 //CLog::Log(LOGDEBUG,"{}: Running expression {} on {}", __FUNCTION__, expr->GetPattern(), item->GetLabel());
2853 bMatch = (expr->RegFind(item->GetLabel().c_str()) != -1);
2854 if (bMatch)
2856 CFileItemList items;
2857 CDirectory::GetDirectory(item->GetPath(), items,
2858 CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(),
2859 DIR_FLAG_DEFAULTS);
2860 // optimized to only traverse listing once by checking for filecount
2861 // and recording last file item for later use
2862 int nFiles = 0;
2863 int index = -1;
2864 for (int j = 0; j < items.Size(); j++)
2866 if (!items[j]->m_bIsFolder)
2868 nFiles++;
2869 index = j;
2872 if (nFiles > 1)
2873 break;
2876 if (nFiles == 1)
2877 *item = *items[index];
2879 ++expr;
2882 // check for dvd folders
2883 if (!bMatch)
2885 std::string dvdPath = item->GetOpticalMediaPath();
2887 if (!dvdPath.empty())
2889 // NOTE: should this be done for the CD# folders too?
2890 item->m_bIsFolder = false;
2891 item->SetPath(dvdPath);
2892 item->SetLabel2("");
2893 item->SetLabelPreformatted(true);
2894 m_sortDescription.sortBy = SortByNone; /* sorting is now broken */
2902 void CFileItemList::StackFiles()
2904 // Precompile our REs
2905 VECCREGEXP stackRegExps;
2906 CRegExp tmpRegExp(true, CRegExp::autoUtf8);
2907 const std::vector<std::string>& strStackRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoStackRegExps;
2908 std::vector<std::string>::const_iterator strRegExp = strStackRegExps.begin();
2909 while (strRegExp != strStackRegExps.end())
2911 if (tmpRegExp.RegComp(*strRegExp))
2913 if (tmpRegExp.GetCaptureTotal() == 4)
2914 stackRegExps.push_back(tmpRegExp);
2915 else
2916 CLog::Log(LOGERROR, "Invalid video stack RE ({}). Must have 4 captures.",
2917 strRegExp->c_str());
2919 ++strRegExp;
2922 // now stack the files, some of which may be from the previous stack iteration
2923 int i = 0;
2924 while (i < Size())
2926 CFileItemPtr item1 = Get(i);
2928 // skip folders, nfo files, playlists
2929 if (item1->m_bIsFolder
2930 || item1->IsParentFolder()
2931 || item1->IsNFO()
2932 || item1->IsPlayList()
2935 // increment index
2936 i++;
2937 continue;
2940 int64_t size = 0;
2941 size_t offset = 0;
2942 std::string stackName;
2943 std::string file1;
2944 std::string filePath;
2945 std::vector<int> stack;
2946 VECCREGEXP::iterator expr = stackRegExps.begin();
2948 URIUtils::Split(item1->GetPath(), filePath, file1);
2949 if (URIUtils::HasEncodedFilename(CURL(filePath)))
2950 file1 = CURL::Decode(file1);
2952 int j;
2953 while (expr != stackRegExps.end())
2955 if (expr->RegFind(file1, offset) != -1)
2957 std::string Title1 = expr->GetMatch(1),
2958 Volume1 = expr->GetMatch(2),
2959 Ignore1 = expr->GetMatch(3),
2960 Extension1 = expr->GetMatch(4);
2961 if (offset)
2962 Title1 = file1.substr(0, expr->GetSubStart(2));
2963 j = i + 1;
2964 while (j < Size())
2966 CFileItemPtr item2 = Get(j);
2968 // skip folders, nfo files, playlists
2969 if (item2->m_bIsFolder
2970 || item2->IsParentFolder()
2971 || item2->IsNFO()
2972 || item2->IsPlayList()
2975 // increment index
2976 j++;
2977 continue;
2980 std::string file2, filePath2;
2981 URIUtils::Split(item2->GetPath(), filePath2, file2);
2982 if (URIUtils::HasEncodedFilename(CURL(filePath2)) )
2983 file2 = CURL::Decode(file2);
2985 if (expr->RegFind(file2, offset) != -1)
2987 std::string Title2 = expr->GetMatch(1),
2988 Volume2 = expr->GetMatch(2),
2989 Ignore2 = expr->GetMatch(3),
2990 Extension2 = expr->GetMatch(4);
2991 if (offset)
2992 Title2 = file2.substr(0, expr->GetSubStart(2));
2993 if (StringUtils::EqualsNoCase(Title1, Title2))
2995 if (!StringUtils::EqualsNoCase(Volume1, Volume2))
2997 if (StringUtils::EqualsNoCase(Ignore1, Ignore2) &&
2998 StringUtils::EqualsNoCase(Extension1, Extension2))
3000 if (stack.empty())
3002 stackName = Title1 + Ignore1 + Extension1;
3003 stack.push_back(i);
3004 size += item1->m_dwSize;
3006 stack.push_back(j);
3007 size += item2->m_dwSize;
3009 else // Sequel
3011 offset = 0;
3012 ++expr;
3013 break;
3016 else if (!StringUtils::EqualsNoCase(Ignore1, Ignore2)) // False positive, try again with offset
3018 offset = expr->GetSubStart(3);
3019 break;
3021 else // Extension mismatch
3023 offset = 0;
3024 ++expr;
3025 break;
3028 else // Title mismatch
3030 offset = 0;
3031 ++expr;
3032 break;
3035 else // No match 2, next expression
3037 offset = 0;
3038 ++expr;
3039 break;
3041 j++;
3043 if (j == Size())
3044 expr = stackRegExps.end();
3046 else // No match 1
3048 offset = 0;
3049 ++expr;
3051 if (stack.size() > 1)
3053 // have a stack, remove the items and add the stacked item
3054 // dont actually stack a multipart rar set, just remove all items but the first
3055 std::string stackPath;
3056 if (Get(stack[0])->IsRAR())
3057 stackPath = Get(stack[0])->GetPath();
3058 else
3060 CStackDirectory dir;
3061 stackPath = dir.ConstructStackPath(*this, stack);
3063 item1->SetPath(stackPath);
3064 // clean up list
3065 for (unsigned k = 1; k < stack.size(); k++)
3066 Remove(i+1);
3067 // item->m_bIsFolder = true; // don't treat stacked files as folders
3068 // the label may be in a different char set from the filename (eg over smb
3069 // the label is converted from utf8, but the filename is not)
3070 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS))
3071 URIUtils::RemoveExtension(stackName);
3073 item1->SetLabel(stackName);
3074 item1->m_dwSize = size;
3075 break;
3078 i++;
3082 bool CFileItemList::Load(int windowID)
3084 CFile file;
3085 auto path = GetDiscFileCache(windowID);
3088 if (file.Open(path))
3090 CArchive ar(&file, CArchive::load);
3091 ar >> *this;
3092 CLog::Log(LOGDEBUG, "Loading items: {}, directory: {} sort method: {}, ascending: {}", Size(),
3093 CURL::GetRedacted(GetPath()), m_sortDescription.sortBy,
3094 m_sortDescription.sortOrder == SortOrderAscending ? "true" : "false");
3095 ar.Close();
3096 file.Close();
3097 return true;
3100 catch(const std::out_of_range&)
3102 CLog::Log(LOGERROR, "Corrupt archive: {}", CURL::GetRedacted(path));
3105 return false;
3108 bool CFileItemList::Save(int windowID)
3110 int iSize = Size();
3111 if (iSize <= 0)
3112 return false;
3114 CLog::Log(LOGDEBUG, "Saving fileitems [{}]", CURL::GetRedacted(GetPath()));
3116 CFile file;
3117 std::string cachefile = GetDiscFileCache(windowID);
3118 if (file.OpenForWrite(cachefile, true)) // overwrite always
3120 // Before caching save simplified cache file name in every item so the cache file can be
3121 // identifed and removed if the item is updated. List path and options (used for file
3122 // name when list cached) can not be accurately derived from item path.
3123 StringUtils::Replace(cachefile, "special://temp/archive_cache/", "");
3124 StringUtils::Replace(cachefile, ".fi", "");
3125 for (const auto& item : m_items)
3126 item->SetProperty("cachefilename", cachefile);
3128 CArchive ar(&file, CArchive::store);
3129 ar << *this;
3130 CLog::Log(LOGDEBUG, " -- items: {}, sort method: {}, ascending: {}", iSize,
3131 m_sortDescription.sortBy,
3132 m_sortDescription.sortOrder == SortOrderAscending ? "true" : "false");
3133 ar.Close();
3134 file.Close();
3135 return true;
3138 return false;
3141 void CFileItemList::RemoveDiscCache(int windowID) const
3143 RemoveDiscCache(GetDiscFileCache(windowID));
3146 void CFileItemList::RemoveDiscCache(const std::string& cacheFile) const
3148 if (CFile::Exists(cacheFile))
3150 CLog::Log(LOGDEBUG, "Clearing cached fileitems [{}]", CURL::GetRedacted(GetPath()));
3151 CFile::Delete(cacheFile);
3155 void CFileItemList::RemoveDiscCacheCRC(const std::string& crc) const
3157 std::string cachefile = StringUtils::Format("special://temp/archive_cache/{}.fi", crc);
3158 RemoveDiscCache(cachefile);
3161 std::string CFileItemList::GetDiscFileCache(int windowID) const
3163 std::string strPath(GetPath());
3164 URIUtils::RemoveSlashAtEnd(strPath);
3166 uint32_t crc = Crc32::ComputeFromLowerCase(strPath);
3168 if (IsCDDA() || IsOnDVD())
3169 return StringUtils::Format("special://temp/archive_cache/r-{:08x}.fi", crc);
3171 if (IsMusicDb())
3172 return StringUtils::Format("special://temp/archive_cache/mdb-{:08x}.fi", crc);
3174 if (IsVideoDb())
3175 return StringUtils::Format("special://temp/archive_cache/vdb-{:08x}.fi", crc);
3177 if (IsSmartPlayList())
3178 return StringUtils::Format("special://temp/archive_cache/sp-{:08x}.fi", crc);
3180 if (windowID)
3181 return StringUtils::Format("special://temp/archive_cache/{}-{:08x}.fi", windowID, crc);
3183 return StringUtils::Format("special://temp/archive_cache/{:08x}.fi", crc);
3186 bool CFileItemList::AlwaysCache() const
3188 // some database folders are always cached
3189 if (IsMusicDb())
3190 return CMusicDatabaseDirectory::CanCache(GetPath());
3191 if (IsVideoDb())
3192 return CVideoDatabaseDirectory::CanCache(GetPath());
3193 if (IsEPG())
3194 return true; // always cache
3195 return false;
3198 std::string CFileItem::GetUserMusicThumb(bool alwaysCheckRemote /* = false */, bool fallbackToFolder /* = false */) const
3200 if (m_strPath.empty()
3201 || StringUtils::StartsWithNoCase(m_strPath, "newsmartplaylist://")
3202 || StringUtils::StartsWithNoCase(m_strPath, "newplaylist://")
3203 || m_bIsShareOrDrive
3204 || IsInternetStream()
3205 || URIUtils::IsUPnP(m_strPath)
3206 || (URIUtils::IsFTP(m_strPath) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3207 || IsPlugin()
3208 || IsAddonsPath()
3209 || IsLibraryFolder()
3210 || IsParentFolder()
3211 || IsMusicDb())
3212 return "";
3214 // we first check for <filename>.tbn or <foldername>.tbn
3215 std::string fileThumb(GetTBNFile());
3216 if (CFile::Exists(fileThumb))
3217 return fileThumb;
3219 // Fall back to folder thumb, if requested
3220 if (!m_bIsFolder && fallbackToFolder)
3222 CFileItem item(URIUtils::GetDirectory(m_strPath), true);
3223 return item.GetUserMusicThumb(alwaysCheckRemote);
3226 // if a folder, check for folder.jpg
3227 if (m_bIsFolder && !IsFileFolder() && (!IsRemote() || alwaysCheckRemote || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICFILES_FINDREMOTETHUMBS)))
3229 std::vector<CVariant> thumbs = CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(
3230 CSettings::SETTING_MUSICLIBRARY_MUSICTHUMBS);
3231 for (const auto& i : thumbs)
3233 std::string strFileName = i.asString();
3234 std::string folderThumb(GetFolderThumb(strFileName));
3235 if (CFile::Exists(folderThumb)) // folder.jpg
3236 return folderThumb;
3237 size_t period = strFileName.find_last_of('.');
3238 if (period != std::string::npos)
3240 std::string ext;
3241 std::string name = strFileName;
3242 std::string folderThumb1 = folderThumb;
3243 name.erase(period);
3244 ext = strFileName.substr(period);
3245 StringUtils::ToUpper(ext);
3246 StringUtils::Replace(folderThumb1, strFileName, name + ext);
3247 if (CFile::Exists(folderThumb1)) // folder.JPG
3248 return folderThumb1;
3250 folderThumb1 = folderThumb;
3251 std::string firstletter = name.substr(0, 1);
3252 StringUtils::ToUpper(firstletter);
3253 name.replace(0, 1, firstletter);
3254 StringUtils::Replace(folderThumb1, strFileName, name + ext);
3255 if (CFile::Exists(folderThumb1)) // Folder.JPG
3256 return folderThumb1;
3258 folderThumb1 = folderThumb;
3259 StringUtils::ToLower(ext);
3260 StringUtils::Replace(folderThumb1, strFileName, name + ext);
3261 if (CFile::Exists(folderThumb1)) // Folder.jpg
3262 return folderThumb1;
3266 // No thumb found
3267 return "";
3270 // Gets the .tbn filename from a file or folder name.
3271 // <filename>.ext -> <filename>.tbn
3272 // <foldername>/ -> <foldername>.tbn
3273 std::string CFileItem::GetTBNFile() const
3275 std::string thumbFile;
3276 std::string strFile = m_strPath;
3278 if (IsStack())
3280 std::string strPath, strReturn;
3281 URIUtils::GetParentPath(m_strPath,strPath);
3282 CFileItem item(CStackDirectory::GetFirstStackedFile(strFile),false);
3283 std::string strTBNFile = item.GetTBNFile();
3284 strReturn = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strTBNFile));
3285 if (CFile::Exists(strReturn))
3286 return strReturn;
3288 strFile = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(strFile)));
3291 if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3293 std::string strPath = URIUtils::GetDirectory(strFile);
3294 std::string strParent;
3295 URIUtils::GetParentPath(strPath,strParent);
3296 strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(m_strPath));
3299 CURL url(strFile);
3300 strFile = url.GetFileName();
3302 if (m_bIsFolder && !IsFileFolder())
3303 URIUtils::RemoveSlashAtEnd(strFile);
3305 if (!strFile.empty())
3307 if (m_bIsFolder && !IsFileFolder())
3308 thumbFile = strFile + ".tbn"; // folder, so just add ".tbn"
3309 else
3310 thumbFile = URIUtils::ReplaceExtension(strFile, ".tbn");
3311 url.SetFileName(thumbFile);
3312 thumbFile = url.Get();
3314 return thumbFile;
3317 bool CFileItem::SkipLocalArt() const
3319 return (m_strPath.empty()
3320 || StringUtils::StartsWithNoCase(m_strPath, "newsmartplaylist://")
3321 || StringUtils::StartsWithNoCase(m_strPath, "newplaylist://")
3322 || m_bIsShareOrDrive
3323 || IsInternetStream()
3324 || URIUtils::IsUPnP(m_strPath)
3325 || (URIUtils::IsFTP(m_strPath) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3326 || IsPlugin()
3327 || IsAddonsPath()
3328 || IsLibraryFolder()
3329 || IsParentFolder()
3330 || IsLiveTV()
3331 || IsPVRRecording()
3332 || IsDVD());
3335 std::string CFileItem::GetThumbHideIfUnwatched(const CFileItem* item) const
3337 const std::shared_ptr<CSettingList> setting(std::dynamic_pointer_cast<CSettingList>(
3338 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(
3339 CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS)));
3340 if (setting && item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_type == MediaTypeEpisode &&
3341 item->GetVideoInfoTag()->GetPlayCount() == 0 &&
3342 !CSettingUtils::FindIntInList(setting,
3343 CSettings::VIDEOLIBRARY_THUMB_SHOW_UNWATCHED_EPISODE) &&
3344 item->HasArt("thumb"))
3346 std::string fanArt = item->GetArt("fanart");
3347 if (fanArt.empty())
3348 return "OverlaySpoiler.png";
3349 else
3350 return fanArt;
3353 return item->GetArt("thumb");
3356 std::string CFileItem::FindLocalArt(const std::string &artFile, bool useFolder) const
3358 if (SkipLocalArt())
3359 return "";
3361 std::string thumb;
3362 if (!m_bIsFolder)
3364 thumb = GetLocalArt(artFile, false);
3365 if (!thumb.empty() && CFile::Exists(thumb))
3366 return thumb;
3368 if ((useFolder || (m_bIsFolder && !IsFileFolder())) && !artFile.empty())
3370 std::string thumb2 = GetLocalArt(artFile, true);
3371 if (!thumb2.empty() && thumb2 != thumb && CFile::Exists(thumb2))
3372 return thumb2;
3374 return "";
3377 std::string CFileItem::GetLocalArtBaseFilename() const
3379 bool useFolder = false;
3380 return GetLocalArtBaseFilename(useFolder);
3383 std::string CFileItem::GetLocalArtBaseFilename(bool& useFolder) const
3385 std::string strFile = m_strPath;
3386 if (IsStack())
3388 std::string strPath;
3389 URIUtils::GetParentPath(m_strPath,strPath);
3390 strFile = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(strFile)));
3393 if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3395 std::string strPath = URIUtils::GetDirectory(strFile);
3396 std::string strParent;
3397 URIUtils::GetParentPath(strPath,strParent);
3398 strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(strFile));
3401 if (IsMultiPath())
3402 strFile = CMultiPathDirectory::GetFirstPath(m_strPath);
3404 if (IsOpticalMediaFile())
3405 { // optical media files should be treated like folders
3406 useFolder = true;
3407 strFile = GetLocalMetadataPath();
3409 else if (useFolder && !(m_bIsFolder && !IsFileFolder()))
3410 strFile = URIUtils::GetDirectory(strFile);
3412 return strFile;
3415 std::string CFileItem::GetLocalArt(const std::string& artFile, bool useFolder) const
3417 // no retrieving of empty art files from folders
3418 if (useFolder && artFile.empty())
3419 return "";
3421 std::string strFile = GetLocalArtBaseFilename(useFolder);
3422 if (strFile.empty()) // empty filepath -> nothing to find
3423 return "";
3425 if (useFolder)
3427 if (!artFile.empty())
3428 return URIUtils::AddFileToFolder(strFile, artFile);
3430 else
3432 if (artFile.empty()) // old thumbnail matching
3433 return URIUtils::ReplaceExtension(strFile, ".tbn");
3434 else
3435 return URIUtils::ReplaceExtension(strFile, "-" + artFile);
3437 return "";
3440 std::string CFileItem::GetFolderThumb(const std::string &folderJPG /* = "folder.jpg" */) const
3442 std::string strFolder = m_strPath;
3444 if (IsStack() ||
3445 URIUtils::IsInRAR(strFolder) ||
3446 URIUtils::IsInZIP(strFolder))
3448 URIUtils::GetParentPath(m_strPath,strFolder);
3451 if (IsMultiPath())
3452 strFolder = CMultiPathDirectory::GetFirstPath(m_strPath);
3454 if (IsPlugin())
3455 return "";
3457 return URIUtils::AddFileToFolder(strFolder, folderJPG);
3460 std::string CFileItem::GetMovieName(bool bUseFolderNames /* = false */) const
3462 if (IsPlugin() && HasVideoInfoTag() && !GetVideoInfoTag()->m_strTitle.empty())
3463 return GetVideoInfoTag()->m_strTitle;
3465 if (IsLabelPreformatted())
3466 return GetLabel();
3468 if (m_pvrRecordingInfoTag)
3469 return m_pvrRecordingInfoTag->m_strTitle;
3470 else if (URIUtils::IsPVRRecording(m_strPath))
3472 std::string title = CPVRRecording::GetTitleFromURL(m_strPath);
3473 if (!title.empty())
3474 return title;
3477 std::string strMovieName;
3478 if (URIUtils::IsStack(m_strPath))
3479 strMovieName = CStackDirectory::GetStackedTitlePath(m_strPath);
3480 else
3481 strMovieName = GetBaseMoviePath(bUseFolderNames);
3483 URIUtils::RemoveSlashAtEnd(strMovieName);
3485 return CURL::Decode(URIUtils::GetFileName(strMovieName));
3488 std::string CFileItem::GetBaseMoviePath(bool bUseFolderNames) const
3490 std::string strMovieName = m_strPath;
3492 if (IsMultiPath())
3493 strMovieName = CMultiPathDirectory::GetFirstPath(m_strPath);
3495 if (IsOpticalMediaFile())
3496 return GetLocalMetadataPath();
3498 if (bUseFolderNames &&
3499 (!m_bIsFolder || URIUtils::IsInArchive(m_strPath) ||
3500 (HasVideoInfoTag() && GetVideoInfoTag()->m_iDbId > 0 && !CMediaTypes::IsContainer(GetVideoInfoTag()->m_type))))
3502 std::string name2(strMovieName);
3503 URIUtils::GetParentPath(name2,strMovieName);
3504 if (URIUtils::IsInArchive(m_strPath))
3506 // Try to get archive itself, if empty take path before
3507 name2 = CURL(m_strPath).GetHostName();
3508 if (name2.empty())
3509 name2 = strMovieName;
3511 URIUtils::GetParentPath(name2, strMovieName);
3515 return strMovieName;
3518 std::string CFileItem::GetLocalFanart() const
3520 if (IsVideoDb())
3522 if (!HasVideoInfoTag())
3523 return ""; // nothing can be done
3524 CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
3525 return dbItem.GetLocalFanart();
3528 std::string strFile2;
3529 std::string strFile = m_strPath;
3530 if (IsStack())
3532 std::string strPath;
3533 URIUtils::GetParentPath(m_strPath,strPath);
3534 CStackDirectory dir;
3535 std::string strPath2;
3536 strPath2 = dir.GetStackedTitlePath(strFile);
3537 strFile = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strPath2));
3538 CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
3539 std::string strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-fanart"));
3540 strFile2 = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strTBNFile));
3542 if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3544 std::string strPath = URIUtils::GetDirectory(strFile);
3545 std::string strParent;
3546 URIUtils::GetParentPath(strPath,strParent);
3547 strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(m_strPath));
3550 // no local fanart available for these
3551 if (IsInternetStream()
3552 || URIUtils::IsUPnP(strFile)
3553 || URIUtils::IsBluray(strFile)
3554 || IsLiveTV()
3555 || IsPlugin()
3556 || IsAddonsPath()
3557 || IsDVD()
3558 || (URIUtils::IsFTP(strFile) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3559 || m_strPath.empty())
3560 return "";
3562 std::string strDir = URIUtils::GetDirectory(strFile);
3564 if (strDir.empty())
3565 return "";
3567 CFileItemList items;
3568 CDirectory::GetDirectory(strDir, items, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
3569 if (IsOpticalMediaFile())
3570 { // grab from the optical media parent folder as well
3571 CFileItemList moreItems;
3572 CDirectory::GetDirectory(GetLocalMetadataPath(), moreItems, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
3573 items.Append(moreItems);
3576 std::vector<std::string> fanarts = { "fanart" };
3578 strFile = URIUtils::ReplaceExtension(strFile, "-fanart");
3579 fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile));
3581 if (!strFile2.empty())
3582 fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile2));
3584 for (std::vector<std::string>::const_iterator i = fanarts.begin(); i != fanarts.end(); ++i)
3586 for (int j = 0; j < items.Size(); j++)
3588 std::string strCandidate = URIUtils::GetFileName(items[j]->m_strPath);
3589 URIUtils::RemoveExtension(strCandidate);
3590 std::string strFanart = *i;
3591 URIUtils::RemoveExtension(strFanart);
3592 if (StringUtils::EqualsNoCase(strCandidate, strFanart))
3593 return items[j]->m_strPath;
3597 return "";
3600 std::string CFileItem::GetLocalMetadataPath() const
3602 if (m_bIsFolder && !IsFileFolder())
3603 return m_strPath;
3605 std::string parent(URIUtils::GetParentPath(m_strPath));
3606 std::string parentFolder(parent);
3607 URIUtils::RemoveSlashAtEnd(parentFolder);
3608 parentFolder = URIUtils::GetFileName(parentFolder);
3609 if (StringUtils::EqualsNoCase(parentFolder, "VIDEO_TS") || StringUtils::EqualsNoCase(parentFolder, "BDMV"))
3610 { // go back up another one
3611 parent = URIUtils::GetParentPath(parent);
3613 return parent;
3616 bool CFileItem::LoadMusicTag()
3618 // not audio
3619 if (!IsAudio())
3620 return false;
3621 // already loaded?
3622 if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
3623 return true;
3624 // check db
3625 CMusicDatabase musicDatabase;
3626 if (musicDatabase.Open())
3628 CSong song;
3629 if (musicDatabase.GetSongByFileName(m_strPath, song))
3631 GetMusicInfoTag()->SetSong(song);
3632 return true;
3634 musicDatabase.Close();
3636 // load tag from file
3637 CLog::Log(LOGDEBUG, "{}: loading tag information for file: {}", __FUNCTION__, m_strPath);
3638 CMusicInfoTagLoaderFactory factory;
3639 std::unique_ptr<IMusicInfoTagLoader> pLoader (factory.CreateLoader(*this));
3640 if (pLoader)
3642 if (pLoader->Load(m_strPath, *GetMusicInfoTag()))
3643 return true;
3645 // no tag - try some other things
3646 if (IsCDDA())
3648 // we have the tracknumber...
3649 int iTrack = GetMusicInfoTag()->GetTrackNumber();
3650 if (iTrack >= 1)
3652 std::string strText = g_localizeStrings.Get(554); // "Track"
3653 if (!strText.empty() && strText[strText.size() - 1] != ' ')
3654 strText += " ";
3655 std::string strTrack = StringUtils::Format((strText + "{}"), iTrack);
3656 GetMusicInfoTag()->SetTitle(strTrack);
3657 GetMusicInfoTag()->SetLoaded(true);
3658 return true;
3661 else
3663 std::string fileName = URIUtils::GetFileName(m_strPath);
3664 URIUtils::RemoveExtension(fileName);
3665 for (const std::string& fileFilter : CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicTagsFromFileFilters)
3667 CLabelFormatter formatter(fileFilter, "");
3668 if (formatter.FillMusicTag(fileName, GetMusicInfoTag()))
3670 GetMusicInfoTag()->SetLoaded(true);
3671 return true;
3675 return false;
3678 bool CFileItem::LoadGameTag()
3680 // Already loaded?
3681 if (HasGameInfoTag() && m_gameInfoTag->IsLoaded())
3682 return true;
3684 //! @todo
3685 GetGameInfoTag();
3687 m_gameInfoTag->SetLoaded(true);
3689 return false;
3692 bool CFileItem::LoadDetails()
3694 if (IsVideoDb())
3696 if (HasVideoInfoTag())
3697 return true;
3699 CVideoDatabase db;
3700 if (!db.Open())
3701 return false;
3703 VIDEODATABASEDIRECTORY::CQueryParams params;
3704 VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(GetPath(), params);
3706 if (params.GetMovieId() >= 0)
3707 db.GetMovieInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetMovieId()));
3708 else if (params.GetMVideoId() >= 0)
3709 db.GetMusicVideoInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetMVideoId()));
3710 else if (params.GetEpisodeId() >= 0)
3711 db.GetEpisodeInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetEpisodeId()));
3712 else if (params.GetSetId() >= 0) // movie set
3713 db.GetSetInfo(static_cast<int>(params.GetSetId()), *GetVideoInfoTag(), this);
3714 else if (params.GetTvShowId() >= 0)
3716 if (params.GetSeason() >= 0)
3718 const int idSeason = db.GetSeasonId(static_cast<int>(params.GetTvShowId()),
3719 static_cast<int>(params.GetSeason()));
3720 if (idSeason >= 0)
3721 db.GetSeasonInfo(idSeason, *GetVideoInfoTag(), this);
3723 else
3724 db.GetTvShowInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetTvShowId()),
3725 this);
3727 else
3729 db.Close();
3730 return false;
3732 db.Close();
3733 return true;
3736 if (m_bIsFolder && URIUtils::IsPVRRecordingFileOrFolder(GetPath()))
3738 if (HasProperty("watchedepisodes") || HasProperty("watched"))
3739 return true;
3741 const std::string parentPath = URIUtils::GetParentPath(GetPath());
3743 //! @todo optimize, find a way to set the details of the directory without loading its content.
3744 CFileItemList items;
3745 if (CDirectory::GetDirectory(parentPath, items, "", XFILE::DIR_FLAG_DEFAULTS))
3747 const std::string path = GetPath();
3748 const auto it = std::find_if(items.cbegin(), items.cend(),
3749 [path](const auto& entry) { return entry->GetPath() == path; });
3750 if (it != items.cend())
3752 *this = *(*it);
3753 return true;
3757 CLog::LogF(LOGERROR, "Error filling item details (path={})", GetPath());
3758 return false;
3761 //! @todo add support for other types on demand.
3762 CLog::LogF(LOGDEBUG, "Unsupported item type (path={})", GetPath());
3763 return false;
3766 void CFileItemList::Swap(unsigned int item1, unsigned int item2)
3768 if (item1 != item2 && item1 < m_items.size() && item2 < m_items.size())
3769 std::swap(m_items[item1], m_items[item2]);
3772 bool CFileItemList::UpdateItem(const CFileItem *item)
3774 if (!item)
3775 return false;
3777 std::unique_lock<CCriticalSection> lock(m_lock);
3778 for (unsigned int i = 0; i < m_items.size(); i++)
3780 CFileItemPtr pItem = m_items[i];
3781 if (pItem->IsSamePath(item))
3783 pItem->UpdateInfo(*item);
3784 return true;
3787 return false;
3790 void CFileItemList::AddSortMethod(SortBy sortBy, int buttonLabel, const LABEL_MASKS &labelMasks, SortAttribute sortAttributes /* = SortAttributeNone */)
3792 AddSortMethod(sortBy, sortAttributes, buttonLabel, labelMasks);
3795 void CFileItemList::AddSortMethod(SortBy sortBy, SortAttribute sortAttributes, int buttonLabel, const LABEL_MASKS &labelMasks)
3797 SortDescription sorting;
3798 sorting.sortBy = sortBy;
3799 sorting.sortAttributes = sortAttributes;
3801 AddSortMethod(sorting, buttonLabel, labelMasks);
3804 void CFileItemList::AddSortMethod(SortDescription sortDescription, int buttonLabel, const LABEL_MASKS &labelMasks)
3806 GUIViewSortDetails sort;
3807 sort.m_sortDescription = sortDescription;
3808 sort.m_buttonLabel = buttonLabel;
3809 sort.m_labelMasks = labelMasks;
3811 m_sortDetails.push_back(sort);
3814 void CFileItemList::SetReplaceListing(bool replace)
3816 m_replaceListing = replace;
3819 void CFileItemList::ClearSortState()
3821 m_sortDescription.sortBy = SortByNone;
3822 m_sortDescription.sortOrder = SortOrderNone;
3823 m_sortDescription.sortAttributes = SortAttributeNone;
3826 bool CFileItem::HasVideoInfoTag() const
3828 // Note: CPVRRecording is derived from CVideoInfoTag
3829 return m_pvrRecordingInfoTag.get() != nullptr || m_videoInfoTag != nullptr;
3832 CVideoInfoTag* CFileItem::GetVideoInfoTag()
3834 // Note: CPVRRecording is derived from CVideoInfoTag
3835 if (m_pvrRecordingInfoTag)
3836 return m_pvrRecordingInfoTag.get();
3837 else if (!m_videoInfoTag)
3838 m_videoInfoTag = new CVideoInfoTag;
3840 return m_videoInfoTag;
3843 const CVideoInfoTag* CFileItem::GetVideoInfoTag() const
3845 // Note: CPVRRecording is derived from CVideoInfoTag
3846 return m_pvrRecordingInfoTag ? m_pvrRecordingInfoTag.get() : m_videoInfoTag;
3849 CPictureInfoTag* CFileItem::GetPictureInfoTag()
3851 if (!m_pictureInfoTag)
3852 m_pictureInfoTag = new CPictureInfoTag;
3854 return m_pictureInfoTag;
3857 MUSIC_INFO::CMusicInfoTag* CFileItem::GetMusicInfoTag()
3859 if (!m_musicInfoTag)
3860 m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag;
3862 return m_musicInfoTag;
3865 CGameInfoTag* CFileItem::GetGameInfoTag()
3867 if (!m_gameInfoTag)
3868 m_gameInfoTag = new CGameInfoTag;
3870 return m_gameInfoTag;
3873 bool CFileItem::HasPVRChannelInfoTag() const
3875 return m_pvrChannelGroupMemberInfoTag && m_pvrChannelGroupMemberInfoTag->Channel() != nullptr;
3878 const std::shared_ptr<PVR::CPVRChannel> CFileItem::GetPVRChannelInfoTag() const
3880 return m_pvrChannelGroupMemberInfoTag ? m_pvrChannelGroupMemberInfoTag->Channel()
3881 : std::shared_ptr<CPVRChannel>();
3884 std::string CFileItem::FindTrailer() const
3886 std::string strFile2;
3887 std::string strFile = m_strPath;
3888 if (IsStack())
3890 std::string strPath;
3891 URIUtils::GetParentPath(m_strPath,strPath);
3892 CStackDirectory dir;
3893 std::string strPath2;
3894 strPath2 = dir.GetStackedTitlePath(strFile);
3895 strFile = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2));
3896 CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
3897 std::string strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-trailer"));
3898 strFile2 = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile));
3900 if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3902 std::string strPath = URIUtils::GetDirectory(strFile);
3903 std::string strParent;
3904 URIUtils::GetParentPath(strPath,strParent);
3905 strFile = URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath));
3908 // no local trailer available for these
3909 if (IsInternetStream()
3910 || URIUtils::IsUPnP(strFile)
3911 || URIUtils::IsBluray(strFile)
3912 || IsLiveTV()
3913 || IsPlugin()
3914 || IsDVD())
3915 return "";
3917 std::string strDir = URIUtils::GetDirectory(strFile);
3918 CFileItemList items;
3919 CDirectory::GetDirectory(strDir, items, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO | DIR_FLAG_NO_FILE_DIRS);
3920 URIUtils::RemoveExtension(strFile);
3921 strFile += "-trailer";
3922 std::string strFile3 = URIUtils::AddFileToFolder(strDir, "movie-trailer");
3924 // Precompile our REs
3925 VECCREGEXP matchRegExps;
3926 CRegExp tmpRegExp(true, CRegExp::autoUtf8);
3927 const std::vector<std::string>& strMatchRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_trailerMatchRegExps;
3929 std::vector<std::string>::const_iterator strRegExp = strMatchRegExps.begin();
3930 while (strRegExp != strMatchRegExps.end())
3932 if (tmpRegExp.RegComp(*strRegExp))
3934 matchRegExps.push_back(tmpRegExp);
3936 ++strRegExp;
3939 std::string strTrailer;
3940 for (int i = 0; i < items.Size(); i++)
3942 std::string strCandidate = items[i]->m_strPath;
3943 URIUtils::RemoveExtension(strCandidate);
3944 if (StringUtils::EqualsNoCase(strCandidate, strFile) ||
3945 StringUtils::EqualsNoCase(strCandidate, strFile2) ||
3946 StringUtils::EqualsNoCase(strCandidate, strFile3))
3948 strTrailer = items[i]->m_strPath;
3949 break;
3951 else
3953 VECCREGEXP::iterator expr = matchRegExps.begin();
3955 while (expr != matchRegExps.end())
3957 if (expr->RegFind(strCandidate) != -1)
3959 strTrailer = items[i]->m_strPath;
3960 i = items.Size();
3961 break;
3963 ++expr;
3968 return strTrailer;
3971 VideoDbContentType CFileItem::GetVideoContentType() const
3973 VideoDbContentType type = VideoDbContentType::MOVIES;
3974 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeTvShow)
3975 type = VideoDbContentType::TVSHOWS;
3976 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeEpisode)
3977 return VideoDbContentType::EPISODES;
3978 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeMusicVideo)
3979 return VideoDbContentType::MUSICVIDEOS;
3980 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeAlbum)
3981 return VideoDbContentType::MUSICALBUMS;
3983 CVideoDatabaseDirectory dir;
3984 VIDEODATABASEDIRECTORY::CQueryParams params;
3985 dir.GetQueryParams(m_strPath, params);
3986 if (params.GetSetId() != -1 && params.GetMovieId() == -1) // movie set
3987 return VideoDbContentType::MOVIE_SETS;
3989 return type;
3992 CFileItem CFileItem::GetItemToPlay() const
3994 if (HasEPGInfoTag())
3996 const std::shared_ptr<CPVRChannelGroupMember> groupMember =
3997 CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().GetChannelGroupMember(*this);
3998 if (groupMember)
3999 return CFileItem(groupMember);
4001 return *this;
4004 CBookmark CFileItem::GetResumePoint() const
4006 if (HasVideoInfoTag())
4007 return GetVideoInfoTag()->GetResumePoint();
4008 return CBookmark();
4011 bool CFileItem::IsResumePointSet() const
4013 return GetResumePoint().IsSet();
4016 double CFileItem::GetCurrentResumeTime() const
4018 return lrint(GetResumePoint().timeInSeconds);
4021 bool CFileItem::GetCurrentResumeTimeAndPartNumber(int64_t& startOffset, int& partNumber) const
4023 CBookmark resumePoint(GetResumePoint());
4024 if (resumePoint.IsSet())
4026 startOffset = llrint(resumePoint.timeInSeconds);
4027 partNumber = resumePoint.partNumber;
4028 return true;
4030 return false;
4033 bool CFileItem::IsResumable() const
4035 return (!IsNFO() && !IsPlayList()) || IsType(".strm");