[video] Fix the refresh of movies with additional versions or extras
[xbmc.git] / xbmc / FileItem.cpp
blobfcbacd8bfedf982fe997abf404c321c557445347
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/PlayList.h"
35 #include "playlists/PlayListFactory.h"
36 #include "pvr/PVRManager.h"
37 #include "pvr/channels/PVRChannel.h"
38 #include "pvr/channels/PVRChannelGroupMember.h"
39 #include "pvr/epg/EpgInfoTag.h"
40 #include "pvr/epg/EpgSearchFilter.h"
41 #include "pvr/guilib/PVRGUIActionsChannels.h"
42 #include "pvr/guilib/PVRGUIActionsUtils.h"
43 #include "pvr/recordings/PVRRecording.h"
44 #include "pvr/timers/PVRTimerInfoTag.h"
45 #include "settings/AdvancedSettings.h"
46 #include "settings/SettingUtils.h"
47 #include "settings/Settings.h"
48 #include "settings/SettingsComponent.h"
49 #include "settings/lib/Setting.h"
50 #include "utils/Archive.h"
51 #include "utils/Crc32.h"
52 #include "utils/FileExtensionProvider.h"
53 #include "utils/Mime.h"
54 #include "utils/Random.h"
55 #include "utils/RegExp.h"
56 #include "utils/StringUtils.h"
57 #include "utils/URIUtils.h"
58 #include "utils/Variant.h"
59 #include "utils/log.h"
60 #include "video/Bookmark.h"
61 #include "video/VideoDatabase.h"
62 #include "video/VideoInfoTag.h"
64 #include <algorithm>
65 #include <cstdlib>
66 #include <memory>
67 #include <mutex>
69 using namespace KODI;
70 using namespace XFILE;
71 using namespace PLAYLIST;
72 using namespace MUSIC_INFO;
73 using namespace PVR;
74 using namespace GAME;
76 CFileItem::CFileItem(const CSong& song)
78 Initialize();
79 SetFromSong(song);
82 CFileItem::CFileItem(const CSong& song, const CMusicInfoTag& music)
84 Initialize();
85 SetFromSong(song);
86 *GetMusicInfoTag() = music;
89 CFileItem::CFileItem(const CURL &url, const CAlbum& album)
91 Initialize();
93 m_strPath = url.Get();
94 URIUtils::AddSlashAtEnd(m_strPath);
95 SetFromAlbum(album);
98 CFileItem::CFileItem(const std::string &path, const CAlbum& album)
100 Initialize();
102 m_strPath = path;
103 URIUtils::AddSlashAtEnd(m_strPath);
104 SetFromAlbum(album);
107 CFileItem::CFileItem(const CMusicInfoTag& music)
109 Initialize();
110 SetLabel(music.GetTitle());
111 m_strPath = music.GetURL();
112 m_bIsFolder = URIUtils::HasSlashAtEnd(m_strPath);
113 *GetMusicInfoTag() = music;
114 FillInDefaultIcon();
115 FillInMimeType(false);
118 CFileItem::CFileItem(const CVideoInfoTag& movie)
120 Initialize();
121 SetFromVideoInfoTag(movie);
124 namespace
126 std::string GetEpgTagTitle(const std::shared_ptr<const CPVREpgInfoTag>& epgTag)
128 if (CServiceBroker::GetPVRManager().IsParentalLocked(epgTag))
129 return g_localizeStrings.Get(19266); // Parental locked
130 else if (epgTag->Title().empty() &&
131 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
132 CSettings::SETTING_EPG_HIDENOINFOAVAILABLE))
133 return g_localizeStrings.Get(19055); // no information available
134 else
135 return epgTag->Title();
137 } // unnamed namespace
139 void CFileItem::FillMusicInfoTag(const std::shared_ptr<const CPVREpgInfoTag>& tag)
141 CMusicInfoTag* musictag = GetMusicInfoTag(); // create (!) the music tag.
143 if (tag)
145 musictag->SetTitle(GetEpgTagTitle(tag));
146 musictag->SetGenre(tag->Genre());
147 musictag->SetDuration(tag->GetDuration());
148 musictag->SetURL(tag->Path());
150 else if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
151 CSettings::SETTING_EPG_HIDENOINFOAVAILABLE))
153 musictag->SetTitle(g_localizeStrings.Get(19055)); // no information available
156 musictag->SetLoaded(true);
159 CFileItem::CFileItem(const std::shared_ptr<CPVREpgInfoTag>& tag)
161 Initialize();
163 m_bIsFolder = false;
164 m_epgInfoTag = tag;
165 m_strPath = tag->Path();
166 m_bCanQueue = false;
167 SetLabel(GetEpgTagTitle(tag));
168 m_dateTime = tag->StartAsLocalTime();
170 if (!tag->IconPath().empty())
172 SetArt("icon", tag->IconPath());
174 else
176 const std::string iconPath = tag->ChannelIconPath();
177 if (!iconPath.empty())
178 SetArt("icon", iconPath);
179 else if (tag->IsRadio())
180 SetArt("icon", "DefaultMusicSongs.png");
181 else
182 SetArt("icon", "DefaultTVShows.png");
185 // Speedup FillInDefaultIcon()
186 SetProperty("icon_never_overlay", true);
188 if (tag->IsRadio() && !HasMusicInfoTag())
189 FillMusicInfoTag(tag);
191 FillInMimeType(false);
194 CFileItem::CFileItem(const std::shared_ptr<PVR::CPVREpgSearchFilter>& filter)
196 Initialize();
198 m_bIsFolder = true;
199 m_epgSearchFilter = filter;
200 m_strPath = filter->GetPath();
201 m_bCanQueue = false;
202 SetLabel(filter->GetTitle());
204 const CDateTime lastExec = filter->GetLastExecutedDateTime();
205 if (lastExec.IsValid())
206 m_dateTime.SetFromUTCDateTime(lastExec);
208 SetArt("icon", "DefaultPVRSearch.png");
210 // Speedup FillInDefaultIcon()
211 SetProperty("icon_never_overlay", true);
213 FillInMimeType(false);
216 CFileItem::CFileItem(const std::shared_ptr<CPVRChannelGroupMember>& channelGroupMember)
218 Initialize();
220 const std::shared_ptr<const CPVRChannel> channel = channelGroupMember->Channel();
222 m_pvrChannelGroupMemberInfoTag = channelGroupMember;
224 m_strPath = channelGroupMember->Path();
225 m_bIsFolder = false;
226 m_bCanQueue = false;
227 SetLabel(channel->ChannelName());
229 if (!channel->IconPath().empty())
230 SetArt("icon", channel->IconPath());
231 else if (channel->IsRadio())
232 SetArt("icon", "DefaultMusicSongs.png");
233 else
234 SetArt("icon", "DefaultTVShows.png");
236 SetProperty("channelid", channel->ChannelID());
237 SetProperty("path", channelGroupMember->Path());
238 SetArt("thumb", channel->IconPath());
240 // Speedup FillInDefaultIcon()
241 SetProperty("icon_never_overlay", true);
243 if (channel->IsRadio() && !HasMusicInfoTag())
245 const std::shared_ptr<const CPVREpgInfoTag> epgNow = channel->GetEPGNow();
246 FillMusicInfoTag(epgNow);
248 FillInMimeType(false);
251 CFileItem::CFileItem(const std::shared_ptr<CPVRRecording>& record)
253 Initialize();
255 m_bIsFolder = false;
256 m_pvrRecordingInfoTag = record;
257 m_strPath = record->m_strFileNameAndPath;
258 SetLabel(record->m_strTitle);
259 m_dateTime = record->RecordingTimeAsLocalTime();
260 m_dwSize = record->GetSizeInBytes();
261 m_bCanQueue = true;
263 // Set art
264 if (!record->IconPath().empty())
265 SetArt("icon", record->IconPath());
266 else
268 const std::shared_ptr<const CPVRChannel> channel = record->Channel();
269 if (channel && !channel->IconPath().empty())
270 SetArt("icon", channel->IconPath());
271 else if (record->IsRadio())
272 SetArt("icon", "DefaultMusicSongs.png");
273 else
274 SetArt("icon", "DefaultTVShows.png");
277 if (!record->ThumbnailPath().empty())
278 SetArt("thumb", record->ThumbnailPath());
280 if (!record->FanartPath().empty())
281 SetArt("fanart", record->FanartPath());
283 // Speedup FillInDefaultIcon()
284 SetProperty("icon_never_overlay", true);
286 FillInMimeType(false);
289 CFileItem::CFileItem(const std::shared_ptr<CPVRTimerInfoTag>& timer)
291 Initialize();
293 m_bIsFolder = timer->IsTimerRule();
294 m_pvrTimerInfoTag = timer;
295 m_strPath = timer->Path();
296 SetLabel(timer->Title());
297 m_dateTime = timer->StartAsLocalTime();
298 m_bCanQueue = false;
300 if (!timer->ChannelIcon().empty())
301 SetArt("icon", timer->ChannelIcon());
302 else if (timer->IsRadio())
303 SetArt("icon", "DefaultMusicSongs.png");
304 else
305 SetArt("icon", "DefaultTVShows.png");
307 // Speedup FillInDefaultIcon()
308 SetProperty("icon_never_overlay", true);
310 FillInMimeType(false);
313 CFileItem::CFileItem(const CArtist& artist)
315 Initialize();
316 SetLabel(artist.strArtist);
317 m_strPath = artist.strArtist;
318 m_bIsFolder = true;
319 URIUtils::AddSlashAtEnd(m_strPath);
320 GetMusicInfoTag()->SetArtist(artist);
321 FillInMimeType(false);
324 CFileItem::CFileItem(const CGenre& genre)
326 Initialize();
327 SetLabel(genre.strGenre);
328 m_strPath = genre.strGenre;
329 m_bIsFolder = true;
330 URIUtils::AddSlashAtEnd(m_strPath);
331 GetMusicInfoTag()->SetGenre(genre.strGenre);
332 FillInMimeType(false);
335 CFileItem::CFileItem(const CFileItem& item)
336 : CGUIListItem(item),
337 m_musicInfoTag(NULL),
338 m_videoInfoTag(NULL),
339 m_pictureInfoTag(NULL),
340 m_gameInfoTag(NULL)
342 *this = item;
345 CFileItem::CFileItem(const CGUIListItem& item)
347 Initialize();
348 // not particularly pretty, but it gets around the issue of Initialize() defaulting
349 // parameters in the CGUIListItem base class.
350 *static_cast<CGUIListItem*>(this) = item;
352 FillInMimeType(false);
355 CFileItem::CFileItem(void)
357 Initialize();
360 CFileItem::CFileItem(const std::string& strLabel)
362 Initialize();
363 SetLabel(strLabel);
366 CFileItem::CFileItem(const char* strLabel)
368 Initialize();
369 SetLabel(std::string(strLabel));
372 CFileItem::CFileItem(const CURL& path, bool bIsFolder)
374 Initialize();
375 m_strPath = path.Get();
376 m_bIsFolder = bIsFolder;
377 if (m_bIsFolder && !m_strPath.empty() && !IsFileFolder())
378 URIUtils::AddSlashAtEnd(m_strPath);
379 FillInMimeType(false);
382 CFileItem::CFileItem(const std::string& strPath, bool bIsFolder)
384 Initialize();
385 m_strPath = strPath;
386 m_bIsFolder = bIsFolder;
387 if (m_bIsFolder && !m_strPath.empty() && !IsFileFolder())
388 URIUtils::AddSlashAtEnd(m_strPath);
389 FillInMimeType(false);
392 CFileItem::CFileItem(const CMediaSource& share)
394 Initialize();
395 m_bIsFolder = true;
396 m_bIsShareOrDrive = true;
397 m_strPath = share.strPath;
398 if (!IsRSS()) // no slash at end for rss feeds
399 URIUtils::AddSlashAtEnd(m_strPath);
400 std::string label = share.strName;
401 if (!share.strStatus.empty())
402 label = StringUtils::Format("{} ({})", share.strName, share.strStatus);
403 SetLabel(label);
404 m_iLockMode = share.m_iLockMode;
405 m_strLockCode = share.m_strLockCode;
406 m_iHasLock = share.m_iHasLock;
407 m_iBadPwdCount = share.m_iBadPwdCount;
408 m_iDriveType = share.m_iDriveType;
409 SetArt("thumb", share.m_strThumbnailImage);
410 SetLabelPreformatted(true);
411 if (IsDVD())
412 GetVideoInfoTag()->m_strFileNameAndPath = share.strDiskUniqueId; // share.strDiskUniqueId contains disc unique id
413 FillInMimeType(false);
416 CFileItem::CFileItem(std::shared_ptr<const ADDON::IAddon> addonInfo) : m_addonInfo(std::move(addonInfo))
418 Initialize();
421 CFileItem::CFileItem(const EventPtr& eventLogEntry)
423 Initialize();
425 m_eventLogEntry = eventLogEntry;
426 SetLabel(eventLogEntry->GetLabel());
427 m_dateTime = eventLogEntry->GetDateTime();
428 if (!eventLogEntry->GetIcon().empty())
429 SetArt("icon", eventLogEntry->GetIcon());
432 CFileItem::~CFileItem(void)
434 delete m_musicInfoTag;
435 delete m_videoInfoTag;
436 delete m_pictureInfoTag;
437 delete m_gameInfoTag;
439 m_musicInfoTag = NULL;
440 m_videoInfoTag = NULL;
441 m_pictureInfoTag = NULL;
442 m_gameInfoTag = NULL;
445 CFileItem& CFileItem::operator=(const CFileItem& item)
447 if (this == &item)
448 return *this;
450 CGUIListItem::operator=(item);
451 m_bLabelPreformatted=item.m_bLabelPreformatted;
452 FreeMemory();
453 m_strPath = item.m_strPath;
454 m_strDynPath = item.m_strDynPath;
455 m_bIsParentFolder = item.m_bIsParentFolder;
456 m_iDriveType = item.m_iDriveType;
457 m_bIsShareOrDrive = item.m_bIsShareOrDrive;
458 m_dateTime = item.m_dateTime;
459 m_dwSize = item.m_dwSize;
461 if (item.m_musicInfoTag)
463 if (m_musicInfoTag)
464 *m_musicInfoTag = *item.m_musicInfoTag;
465 else
466 m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag(*item.m_musicInfoTag);
468 else
470 delete m_musicInfoTag;
471 m_musicInfoTag = NULL;
474 if (item.m_videoInfoTag)
476 if (m_videoInfoTag)
477 *m_videoInfoTag = *item.m_videoInfoTag;
478 else
479 m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
481 else
483 delete m_videoInfoTag;
484 m_videoInfoTag = NULL;
487 if (item.m_pictureInfoTag)
489 if (m_pictureInfoTag)
490 *m_pictureInfoTag = *item.m_pictureInfoTag;
491 else
492 m_pictureInfoTag = new CPictureInfoTag(*item.m_pictureInfoTag);
494 else
496 delete m_pictureInfoTag;
497 m_pictureInfoTag = NULL;
500 if (item.m_gameInfoTag)
502 if (m_gameInfoTag)
503 *m_gameInfoTag = *item.m_gameInfoTag;
504 else
505 m_gameInfoTag = new CGameInfoTag(*item.m_gameInfoTag);
507 else
509 delete m_gameInfoTag;
510 m_gameInfoTag = NULL;
513 m_epgInfoTag = item.m_epgInfoTag;
514 m_epgSearchFilter = item.m_epgSearchFilter;
515 m_pvrChannelGroupMemberInfoTag = item.m_pvrChannelGroupMemberInfoTag;
516 m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
517 m_pvrTimerInfoTag = item.m_pvrTimerInfoTag;
518 m_addonInfo = item.m_addonInfo;
519 m_eventLogEntry = item.m_eventLogEntry;
521 m_lStartOffset = item.m_lStartOffset;
522 m_lStartPartNumber = item.m_lStartPartNumber;
523 m_lEndOffset = item.m_lEndOffset;
524 m_strDVDLabel = item.m_strDVDLabel;
525 m_strTitle = item.m_strTitle;
526 m_iprogramCount = item.m_iprogramCount;
527 m_idepth = item.m_idepth;
528 m_iLockMode = item.m_iLockMode;
529 m_strLockCode = item.m_strLockCode;
530 m_iHasLock = item.m_iHasLock;
531 m_iBadPwdCount = item.m_iBadPwdCount;
532 m_bCanQueue=item.m_bCanQueue;
533 m_mimetype = item.m_mimetype;
534 m_extrainfo = item.m_extrainfo;
535 m_specialSort = item.m_specialSort;
536 m_bIsAlbum = item.m_bIsAlbum;
537 m_doContentLookup = item.m_doContentLookup;
538 return *this;
541 void CFileItem::Initialize()
543 m_musicInfoTag = NULL;
544 m_videoInfoTag = NULL;
545 m_pictureInfoTag = NULL;
546 m_gameInfoTag = NULL;
547 m_bLabelPreformatted = false;
548 m_bIsAlbum = false;
549 m_dwSize = 0;
550 m_bIsParentFolder = false;
551 m_bIsShareOrDrive = false;
552 m_iDriveType = CMediaSource::SOURCE_TYPE_UNKNOWN;
553 m_lStartOffset = 0;
554 m_lStartPartNumber = 1;
555 m_lEndOffset = 0;
556 m_iprogramCount = 0;
557 m_idepth = 1;
558 m_iLockMode = LOCK_MODE_EVERYONE;
559 m_iBadPwdCount = 0;
560 m_iHasLock = LOCK_STATE_NO_LOCK;
561 m_bCanQueue = true;
562 m_specialSort = SortSpecialNone;
563 m_doContentLookup = true;
566 void CFileItem::Reset()
568 // CGUIListItem members...
569 m_strLabel2.clear();
570 SetLabel("");
571 FreeIcons();
572 m_overlayIcon = ICON_OVERLAY_NONE;
573 m_bSelected = false;
574 m_bIsFolder = false;
576 m_strDVDLabel.clear();
577 m_strTitle.clear();
578 m_strPath.clear();
579 m_strDynPath.clear();
580 m_dateTime.Reset();
581 m_strLockCode.clear();
582 m_mimetype.clear();
583 delete m_musicInfoTag;
584 m_musicInfoTag=NULL;
585 delete m_videoInfoTag;
586 m_videoInfoTag=NULL;
587 m_epgInfoTag.reset();
588 m_epgSearchFilter.reset();
589 m_pvrChannelGroupMemberInfoTag.reset();
590 m_pvrRecordingInfoTag.reset();
591 m_pvrTimerInfoTag.reset();
592 delete m_pictureInfoTag;
593 m_pictureInfoTag=NULL;
594 delete m_gameInfoTag;
595 m_gameInfoTag = NULL;
596 m_extrainfo.clear();
597 ClearProperties();
598 m_eventLogEntry.reset();
600 Initialize();
601 SetInvalid();
604 // do not archive dynamic path
605 void CFileItem::Archive(CArchive& ar)
607 CGUIListItem::Archive(ar);
609 if (ar.IsStoring())
611 ar << m_bIsParentFolder;
612 ar << m_bLabelPreformatted;
613 ar << m_strPath;
614 ar << m_bIsShareOrDrive;
615 ar << m_iDriveType;
616 ar << m_dateTime;
617 ar << m_dwSize;
618 ar << m_strDVDLabel;
619 ar << m_strTitle;
620 ar << m_iprogramCount;
621 ar << m_idepth;
622 ar << m_lStartOffset;
623 ar << m_lStartPartNumber;
624 ar << m_lEndOffset;
625 ar << m_iLockMode;
626 ar << m_strLockCode;
627 ar << m_iBadPwdCount;
629 ar << m_bCanQueue;
630 ar << m_mimetype;
631 ar << m_extrainfo;
632 ar << m_specialSort;
633 ar << m_doContentLookup;
635 if (m_musicInfoTag)
637 ar << 1;
638 ar << *m_musicInfoTag;
640 else
641 ar << 0;
642 if (m_videoInfoTag)
644 ar << 1;
645 ar << *m_videoInfoTag;
647 else
648 ar << 0;
649 if (m_pictureInfoTag)
651 ar << 1;
652 ar << *m_pictureInfoTag;
654 else
655 ar << 0;
656 if (m_gameInfoTag)
658 ar << 1;
659 ar << *m_gameInfoTag;
661 else
662 ar << 0;
664 else
666 ar >> m_bIsParentFolder;
667 ar >> m_bLabelPreformatted;
668 ar >> m_strPath;
669 ar >> m_bIsShareOrDrive;
670 ar >> m_iDriveType;
671 ar >> m_dateTime;
672 ar >> m_dwSize;
673 ar >> m_strDVDLabel;
674 ar >> m_strTitle;
675 ar >> m_iprogramCount;
676 ar >> m_idepth;
677 ar >> m_lStartOffset;
678 ar >> m_lStartPartNumber;
679 ar >> m_lEndOffset;
680 int temp;
681 ar >> temp;
682 m_iLockMode = (LockType)temp;
683 ar >> m_strLockCode;
684 ar >> m_iBadPwdCount;
686 ar >> m_bCanQueue;
687 ar >> m_mimetype;
688 ar >> m_extrainfo;
689 ar >> temp;
690 m_specialSort = (SortSpecial)temp;
691 ar >> m_doContentLookup;
693 int iType;
694 ar >> iType;
695 if (iType == 1)
696 ar >> *GetMusicInfoTag();
697 ar >> iType;
698 if (iType == 1)
699 ar >> *GetVideoInfoTag();
700 ar >> iType;
701 if (iType == 1)
702 ar >> *GetPictureInfoTag();
703 ar >> iType;
704 if (iType == 1)
705 ar >> *GetGameInfoTag();
707 SetInvalid();
711 void CFileItem::Serialize(CVariant& value) const
713 //CGUIListItem::Serialize(value["CGUIListItem"]);
715 value["strPath"] = m_strPath;
716 value["dateTime"] = (m_dateTime.IsValid()) ? m_dateTime.GetAsRFC1123DateTime() : "";
717 value["lastmodified"] = m_dateTime.IsValid() ? m_dateTime.GetAsDBDateTime() : "";
718 value["size"] = m_dwSize;
719 value["DVDLabel"] = m_strDVDLabel;
720 value["title"] = m_strTitle;
721 value["mimetype"] = m_mimetype;
722 value["extrainfo"] = m_extrainfo;
724 if (m_musicInfoTag)
725 (*m_musicInfoTag).Serialize(value["musicInfoTag"]);
727 if (m_videoInfoTag)
728 (*m_videoInfoTag).Serialize(value["videoInfoTag"]);
730 if (m_pictureInfoTag)
731 (*m_pictureInfoTag).Serialize(value["pictureInfoTag"]);
733 if (m_gameInfoTag)
734 (*m_gameInfoTag).Serialize(value["gameInfoTag"]);
736 if (!m_mapProperties.empty())
738 auto& customProperties = value["customproperties"];
739 for (const auto& prop : m_mapProperties)
740 customProperties[prop.first] = prop.second;
744 void CFileItem::ToSortable(SortItem &sortable, Field field) const
746 switch (field)
748 case FieldPath:
749 sortable[FieldPath] = m_strPath;
750 break;
751 case FieldDate:
752 sortable[FieldDate] = (m_dateTime.IsValid()) ? m_dateTime.GetAsDBDateTime() : "";
753 break;
754 case FieldSize:
755 sortable[FieldSize] = m_dwSize;
756 break;
757 case FieldDriveType:
758 sortable[FieldDriveType] = m_iDriveType;
759 break;
760 case FieldStartOffset:
761 sortable[FieldStartOffset] = m_lStartOffset;
762 break;
763 case FieldEndOffset:
764 sortable[FieldEndOffset] = m_lEndOffset;
765 break;
766 case FieldProgramCount:
767 sortable[FieldProgramCount] = m_iprogramCount;
768 break;
769 case FieldBitrate:
770 sortable[FieldBitrate] = m_dwSize;
771 break;
772 case FieldTitle:
773 sortable[FieldTitle] = m_strTitle;
774 break;
776 // If there's ever a need to convert more properties from CGUIListItem it might be
777 // worth to make CGUIListItem implement ISortable as well and call it from here
779 default:
780 break;
783 if (HasMusicInfoTag())
784 GetMusicInfoTag()->ToSortable(sortable, field);
786 if (HasVideoInfoTag())
787 GetVideoInfoTag()->ToSortable(sortable, field);
789 if (HasPictureInfoTag())
790 GetPictureInfoTag()->ToSortable(sortable, field);
792 if (HasPVRChannelInfoTag())
793 GetPVRChannelInfoTag()->ToSortable(sortable, field);
795 if (HasPVRChannelGroupMemberInfoTag())
796 GetPVRChannelGroupMemberInfoTag()->ToSortable(sortable, field);
798 if (HasAddonInfo())
800 switch (field)
802 case FieldInstallDate:
803 sortable[FieldInstallDate] = GetAddonInfo()->InstallDate().GetAsDBDateTime();
804 break;
805 case FieldLastUpdated:
806 sortable[FieldLastUpdated] = GetAddonInfo()->LastUpdated().GetAsDBDateTime();
807 break;
808 case FieldLastUsed:
809 sortable[FieldLastUsed] = GetAddonInfo()->LastUsed().GetAsDBDateTime();
810 break;
811 default:
812 break;
816 if (HasGameInfoTag())
817 GetGameInfoTag()->ToSortable(sortable, field);
819 if (m_eventLogEntry)
820 m_eventLogEntry->ToSortable(sortable, field);
822 if (IsFavourite())
824 if (field == FieldUserPreference)
825 sortable[FieldUserPreference] = GetProperty("favourite.index").asString();
829 void CFileItem::ToSortable(SortItem &sortable, const Fields &fields) const
831 Fields::const_iterator it;
832 for (it = fields.begin(); it != fields.end(); ++it)
833 ToSortable(sortable, *it);
835 /* FieldLabel is used as a fallback by all sorters and therefore has to be present as well */
836 sortable[FieldLabel] = GetLabel();
837 /* FieldSortSpecial and FieldFolder are required in conjunction with all other sorters as well */
838 sortable[FieldSortSpecial] = m_specialSort;
839 sortable[FieldFolder] = m_bIsFolder;
842 bool CFileItem::Exists(bool bUseCache /* = true */) const
844 if (m_strPath.empty()
845 || IsPath("add")
846 || IsInternetStream()
847 || IsParentFolder()
848 || IsVirtualDirectoryRoot()
849 || IsPlugin()
850 || IsPVR())
851 return true;
853 if (IsVideoDb() && HasVideoInfoTag())
855 CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
856 return dbItem.Exists();
859 std::string strPath = m_strPath;
861 if (URIUtils::IsMultiPath(strPath))
862 strPath = CMultiPathDirectory::GetFirstPath(strPath);
864 if (URIUtils::IsStack(strPath))
865 strPath = CStackDirectory::GetFirstStackedFile(strPath);
867 if (m_bIsFolder)
868 return CDirectory::Exists(strPath, bUseCache);
869 else
870 return CFile::Exists(strPath, bUseCache);
872 return false;
875 bool CFileItem::IsVideo() const
877 /* check preset mime type */
878 if(StringUtils::StartsWithNoCase(m_mimetype, "video/"))
879 return true;
881 if (HasVideoInfoTag())
882 return true;
884 if (HasGameInfoTag())
885 return false;
887 if (HasMusicInfoTag())
888 return false;
890 if (HasPictureInfoTag())
891 return false;
893 // TV recordings are videos...
894 if (!m_bIsFolder && URIUtils::IsPVRTVRecordingFileOrFolder(GetPath()))
895 return true;
897 // ... all other PVR items are not.
898 if (IsPVR())
899 return false;
901 if (URIUtils::IsDVD(m_strPath))
902 return true;
904 std::string extension;
905 if(StringUtils::StartsWithNoCase(m_mimetype, "application/"))
906 { /* check for some standard types */
907 extension = m_mimetype.substr(12);
908 if( StringUtils::EqualsNoCase(extension, "ogg")
909 || StringUtils::EqualsNoCase(extension, "mp4")
910 || StringUtils::EqualsNoCase(extension, "mxf") )
911 return true;
914 //! @todo If the file is a zip file, ask the game clients if any support this
915 // file before assuming it is video.
917 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions());
920 bool CFileItem::IsEPG() const
922 return HasEPGInfoTag();
925 bool CFileItem::IsPVRChannel() const
927 return HasPVRChannelInfoTag();
930 bool CFileItem::IsPVRChannelGroup() const
932 return URIUtils::IsPVRChannelGroup(m_strPath);
935 bool CFileItem::IsPVRRecording() const
937 return HasPVRRecordingInfoTag();
940 bool CFileItem::IsUsablePVRRecording() const
942 return (m_pvrRecordingInfoTag && !m_pvrRecordingInfoTag->IsDeleted());
945 bool CFileItem::IsDeletedPVRRecording() const
947 return (m_pvrRecordingInfoTag && m_pvrRecordingInfoTag->IsDeleted());
950 bool CFileItem::IsInProgressPVRRecording() const
952 return (m_pvrRecordingInfoTag && m_pvrRecordingInfoTag->IsInProgress());
955 bool CFileItem::IsPVRTimer() const
957 return HasPVRTimerInfoTag();
960 bool CFileItem::IsDiscStub() const
962 if (IsVideoDb() && HasVideoInfoTag())
964 CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
965 return dbItem.IsDiscStub();
968 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetDiscStubExtensions());
971 bool CFileItem::IsAudio() const
973 /* check preset mime type */
974 if(StringUtils::StartsWithNoCase(m_mimetype, "audio/"))
975 return true;
977 if (HasMusicInfoTag())
978 return true;
980 if (HasVideoInfoTag())
981 return false;
983 if (HasPictureInfoTag())
984 return false;
986 if (HasGameInfoTag())
987 return false;
989 if (IsCDDA())
990 return true;
992 if(StringUtils::StartsWithNoCase(m_mimetype, "application/"))
993 { /* check for some standard types */
994 std::string extension = m_mimetype.substr(12);
995 if( StringUtils::EqualsNoCase(extension, "ogg")
996 || StringUtils::EqualsNoCase(extension, "mp4")
997 || StringUtils::EqualsNoCase(extension, "mxf") )
998 return true;
1001 //! @todo If the file is a zip file, ask the game clients if any support this
1002 // file before assuming it is audio
1004 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetMusicExtensions());
1007 bool CFileItem::IsDeleted() const
1009 if (HasPVRRecordingInfoTag())
1010 return GetPVRRecordingInfoTag()->IsDeleted();
1012 return false;
1015 bool CFileItem::IsAudioBook() const
1017 return IsType(".m4b") || IsType(".mka");
1020 bool CFileItem::IsGame() const
1022 if (HasGameInfoTag())
1023 return true;
1025 if (HasVideoInfoTag())
1026 return false;
1028 if (HasMusicInfoTag())
1029 return false;
1031 if (HasPictureInfoTag())
1032 return false;
1034 if (IsPVR())
1035 return false;
1037 if (HasAddonInfo())
1038 return CGameUtils::IsStandaloneGame(std::const_pointer_cast<ADDON::IAddon>(GetAddonInfo()));
1040 return CGameUtils::HasGameExtension(m_strPath);
1043 bool CFileItem::IsPicture() const
1045 if (StringUtils::StartsWithNoCase(m_mimetype, "image/"))
1046 return true;
1048 if (HasPictureInfoTag())
1049 return true;
1051 if (HasGameInfoTag())
1052 return false;
1054 if (HasMusicInfoTag())
1055 return false;
1057 if (HasVideoInfoTag())
1058 return false;
1060 if (HasPVRTimerInfoTag() || HasPVRChannelInfoTag() || HasPVRChannelGroupMemberInfoTag() ||
1061 HasPVRRecordingInfoTag() || HasEPGInfoTag() || HasEPGSearchFilter())
1062 return false;
1064 if (!m_strPath.empty())
1065 return CUtil::IsPicture(m_strPath);
1067 return false;
1070 bool CFileItem::IsLyrics() const
1072 return URIUtils::HasExtension(m_strPath, ".cdg|.lrc");
1075 bool CFileItem::IsSubtitle() const
1077 return URIUtils::HasExtension(m_strPath, CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions());
1080 bool CFileItem::IsCUESheet() const
1082 return URIUtils::HasExtension(m_strPath, ".cue");
1085 bool CFileItem::IsInternetStream(const bool bStrictCheck /* = false */) const
1087 if (HasProperty("IsHTTPDirectory"))
1088 return bStrictCheck;
1090 if (!m_strDynPath.empty())
1091 return URIUtils::IsInternetStream(m_strDynPath, bStrictCheck);
1093 return URIUtils::IsInternetStream(m_strPath, bStrictCheck);
1096 bool CFileItem::IsStreamedFilesystem() const
1098 if (!m_strDynPath.empty())
1099 return URIUtils::IsStreamedFilesystem(m_strDynPath);
1101 return URIUtils::IsStreamedFilesystem(m_strPath);
1104 bool CFileItem::IsFileFolder(EFileFolderType types) const
1106 EFileFolderType always_type = EFILEFOLDER_TYPE_ALWAYS;
1108 /* internet streams are not directly expanded */
1109 if(IsInternetStream())
1110 always_type = EFILEFOLDER_TYPE_ONCLICK;
1112 // strm files are not browsable
1113 if (IsType(".strm") && (types & EFILEFOLDER_TYPE_ONBROWSE))
1114 return false;
1116 if(types & always_type)
1118 if(IsSmartPlayList()
1119 || (IsPlayList() && CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders)
1120 || IsAPK()
1121 || IsZIP()
1122 || IsRAR()
1123 || IsRSS()
1124 || IsAudioBook()
1125 || IsType(".ogg|.oga|.xbt")
1126 #if defined(TARGET_ANDROID)
1127 || IsType(".apk")
1128 #endif
1130 return true;
1133 if (CServiceBroker::IsAddonInterfaceUp() &&
1134 IsType(CServiceBroker::GetFileExtensionProvider().GetFileFolderExtensions().c_str()) &&
1135 CServiceBroker::GetFileExtensionProvider().CanOperateExtension(m_strPath))
1136 return true;
1138 if(types & EFILEFOLDER_TYPE_ONBROWSE)
1140 if((IsPlayList() && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders)
1141 || IsDiscImage())
1142 return true;
1145 return false;
1148 bool CFileItem::IsSmartPlayList() const
1150 if (HasProperty("library.smartplaylist") && GetProperty("library.smartplaylist").asBoolean())
1151 return true;
1153 return URIUtils::HasExtension(m_strPath, ".xsp");
1156 bool CFileItem::IsLibraryFolder() const
1158 if (HasProperty("library.filter") && GetProperty("library.filter").asBoolean())
1159 return true;
1161 return URIUtils::IsLibraryFolder(m_strPath);
1164 bool CFileItem::IsPlayList() const
1166 return CPlayListFactory::IsPlaylist(*this);
1169 bool CFileItem::IsPythonScript() const
1171 return URIUtils::HasExtension(m_strPath, ".py");
1174 bool CFileItem::IsType(const char *ext) const
1176 if (!m_strDynPath.empty())
1177 return URIUtils::HasExtension(m_strDynPath, ext);
1179 return URIUtils::HasExtension(m_strPath, ext);
1182 bool CFileItem::IsNFO() const
1184 return URIUtils::HasExtension(m_strPath, ".nfo");
1187 bool CFileItem::IsVideoExtras() const
1189 return m_bIsFolder &&
1190 StringUtils::EqualsNoCase(URIUtils::GetFileOrFolderName(m_strPath), "extras");
1193 bool CFileItem::IsDiscImage() const
1195 return URIUtils::IsDiscImage(GetDynPath());
1198 bool CFileItem::IsOpticalMediaFile() const
1200 if (IsDVDFile(false, true))
1201 return true;
1203 return IsBDFile();
1206 bool CFileItem::IsDVDFile(bool bVobs /*= true*/, bool bIfos /*= true*/) const
1208 std::string strFileName = URIUtils::GetFileName(GetDynPath());
1209 if (bIfos)
1211 if (StringUtils::EqualsNoCase(strFileName, "video_ts.ifo"))
1212 return true;
1213 if (StringUtils::StartsWithNoCase(strFileName, "vts_") && StringUtils::EndsWithNoCase(strFileName, "_0.ifo") && strFileName.length() == 12)
1214 return true;
1216 if (bVobs)
1218 if (StringUtils::EqualsNoCase(strFileName, "video_ts.vob"))
1219 return true;
1220 if (StringUtils::StartsWithNoCase(strFileName, "vts_") && StringUtils::EndsWithNoCase(strFileName, ".vob"))
1221 return true;
1224 return false;
1227 bool CFileItem::IsBDFile() const
1229 std::string strFileName = URIUtils::GetFileName(GetDynPath());
1230 return (StringUtils::EqualsNoCase(strFileName, "index.bdmv") || StringUtils::EqualsNoCase(strFileName, "MovieObject.bdmv")
1231 || StringUtils::EqualsNoCase(strFileName, "INDEX.BDM") || StringUtils::EqualsNoCase(strFileName, "MOVIEOBJ.BDM"));
1234 bool CFileItem::IsRAR() const
1236 return URIUtils::IsRAR(m_strPath);
1239 bool CFileItem::IsAPK() const
1241 return URIUtils::IsAPK(m_strPath);
1244 bool CFileItem::IsZIP() const
1246 return URIUtils::IsZIP(m_strPath);
1249 bool CFileItem::IsCBZ() const
1251 return URIUtils::HasExtension(m_strPath, ".cbz");
1254 bool CFileItem::IsCBR() const
1256 return URIUtils::HasExtension(m_strPath, ".cbr");
1259 bool CFileItem::IsRSS() const
1261 return StringUtils::StartsWithNoCase(m_strPath, "rss://") || URIUtils::HasExtension(m_strPath, ".rss")
1262 || StringUtils::StartsWithNoCase(m_strPath, "rsss://")
1263 || m_mimetype == "application/rss+xml";
1266 bool CFileItem::IsAndroidApp() const
1268 return URIUtils::IsAndroidApp(m_strPath);
1271 bool CFileItem::IsStack() const
1273 return URIUtils::IsStack(m_strPath);
1276 bool CFileItem::IsFavourite() const
1278 return URIUtils::IsFavourite(m_strPath);
1281 bool CFileItem::IsPlugin() const
1283 return URIUtils::IsPlugin(m_strPath);
1286 bool CFileItem::IsScript() const
1288 return URIUtils::IsScript(m_strPath);
1291 bool CFileItem::IsAddonsPath() const
1293 return URIUtils::IsAddonsPath(m_strPath);
1296 bool CFileItem::IsSourcesPath() const
1298 return URIUtils::IsSourcesPath(m_strPath);
1301 bool CFileItem::IsMultiPath() const
1303 return URIUtils::IsMultiPath(m_strPath);
1306 bool CFileItem::IsBluray() const
1308 if (URIUtils::IsBluray(m_strPath))
1309 return true;
1311 CFileItem item = CFileItem(GetOpticalMediaPath(), false);
1313 return item.IsBDFile();
1316 bool CFileItem::IsProtectedBlurayDisc() const
1318 std::string path;
1319 path = URIUtils::AddFileToFolder(GetPath(), "AACS", "Unit_Key_RO.inf");
1320 if (CFile::Exists(path))
1321 return true;
1323 return false;
1326 bool CFileItem::IsCDDA() const
1328 return URIUtils::IsCDDA(m_strPath);
1331 bool CFileItem::IsDVD() const
1333 return URIUtils::IsDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
1336 bool CFileItem::IsOnDVD() const
1338 return URIUtils::IsOnDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
1341 bool CFileItem::IsNfs() const
1343 return URIUtils::IsNfs(m_strPath);
1346 bool CFileItem::IsOnLAN() const
1348 return URIUtils::IsOnLAN(m_strPath);
1351 bool CFileItem::IsISO9660() const
1353 return URIUtils::IsISO9660(m_strPath);
1356 bool CFileItem::IsRemote() const
1358 return URIUtils::IsRemote(m_strPath);
1361 bool CFileItem::IsSmb() const
1363 return URIUtils::IsSmb(m_strPath);
1366 bool CFileItem::IsURL() const
1368 return URIUtils::IsURL(m_strPath);
1371 bool CFileItem::IsPVR() const
1373 return URIUtils::IsPVR(m_strPath);
1376 bool CFileItem::IsLiveTV() const
1378 return URIUtils::IsLiveTV(m_strPath);
1381 bool CFileItem::IsHD() const
1383 return URIUtils::IsHD(m_strPath);
1386 bool CFileItem::IsMusicDb() const
1388 return URIUtils::IsMusicDb(m_strPath);
1391 bool CFileItem::IsVideoDb() const
1393 return URIUtils::IsVideoDb(m_strPath);
1396 bool CFileItem::IsVirtualDirectoryRoot() const
1398 return (m_bIsFolder && m_strPath.empty());
1401 bool CFileItem::IsRemovable() const
1403 return IsOnDVD() || IsCDDA() || m_iDriveType == CMediaSource::SOURCE_TYPE_REMOVABLE;
1406 bool CFileItem::IsReadOnly() const
1408 if (IsParentFolder())
1409 return true;
1411 if (m_bIsShareOrDrive)
1412 return true;
1414 return !CUtil::SupportsWriteFileOperations(m_strPath);
1417 void CFileItem::FillInDefaultIcon()
1419 if (URIUtils::IsPVRGuideItem(m_strPath))
1421 // epg items never have a default icon. no need to execute this expensive method.
1422 // when filling epg grid window, easily tens of thousands of epg items are processed.
1423 return;
1426 //CLog::Log(LOGINFO, "FillInDefaultIcon({})", pItem->GetLabel());
1427 // find the default icon for a file or folder item
1428 // for files this can be the (depending on the file type)
1429 // default picture for photo's
1430 // default picture for songs
1431 // default picture for videos
1432 // default picture for shortcuts
1433 // default picture for playlists
1435 // for folders
1436 // for .. folders the default picture for parent folder
1437 // for other folders the defaultFolder.png
1439 if (GetArt("icon").empty())
1441 if (!m_bIsFolder)
1443 /* To reduce the average runtime of this code, this list should
1444 * be ordered with most frequently seen types first. Also bear
1445 * in mind the complexity of the code behind the check in the
1446 * case of IsWhatever() returns false.
1448 if (IsPVRChannel())
1450 if (GetPVRChannelInfoTag()->IsRadio())
1451 SetArt("icon", "DefaultMusicSongs.png");
1452 else
1453 SetArt("icon", "DefaultTVShows.png");
1455 else if ( IsLiveTV() )
1457 // Live TV Channel
1458 SetArt("icon", "DefaultTVShows.png");
1460 else if ( URIUtils::IsArchive(m_strPath) )
1461 { // archive
1462 SetArt("icon", "DefaultFile.png");
1464 else if ( IsUsablePVRRecording() )
1466 // PVR recording
1467 SetArt("icon", "DefaultVideo.png");
1469 else if ( IsDeletedPVRRecording() )
1471 // PVR deleted recording
1472 SetArt("icon", "DefaultVideoDeleted.png");
1474 else if ( IsAudio() )
1476 // audio
1477 SetArt("icon", "DefaultAudio.png");
1479 else if ( IsVideo() )
1481 // video
1482 SetArt("icon", "DefaultVideo.png");
1484 else if (IsPVRTimer())
1486 SetArt("icon", "DefaultVideo.png");
1488 else if ( IsPicture() )
1490 // picture
1491 SetArt("icon", "DefaultPicture.png");
1493 else if ( IsPlayList() || IsSmartPlayList())
1495 SetArt("icon", "DefaultPlaylist.png");
1497 else if ( IsPythonScript() )
1499 SetArt("icon", "DefaultScript.png");
1501 else if (IsFavourite())
1503 SetArt("icon", "DefaultFavourites.png");
1505 else
1507 // default icon for unknown file type
1508 SetArt("icon", "DefaultFile.png");
1511 else
1513 if ( IsPlayList() || IsSmartPlayList())
1515 SetArt("icon", "DefaultPlaylist.png");
1517 else if (IsParentFolder())
1519 SetArt("icon", "DefaultFolderBack.png");
1521 else
1523 SetArt("icon", "DefaultFolder.png");
1527 // Set the icon overlays (if applicable)
1528 if (!HasOverlay() && !HasProperty("icon_never_overlay"))
1530 if (URIUtils::IsInRAR(m_strPath))
1531 SetOverlayImage(CGUIListItem::ICON_OVERLAY_RAR);
1532 else if (URIUtils::IsInZIP(m_strPath))
1533 SetOverlayImage(CGUIListItem::ICON_OVERLAY_ZIP);
1537 void CFileItem::RemoveExtension()
1539 if (m_bIsFolder)
1540 return;
1542 std::string strLabel = GetLabel();
1543 URIUtils::RemoveExtension(strLabel);
1544 SetLabel(strLabel);
1547 void CFileItem::CleanString()
1549 if (IsLiveTV())
1550 return;
1552 std::string strLabel = GetLabel();
1553 std::string strTitle, strTitleAndYear, strYear;
1554 CUtil::CleanString(strLabel, strTitle, strTitleAndYear, strYear, true);
1555 SetLabel(strTitleAndYear);
1558 void CFileItem::SetLabel(const std::string &strLabel)
1560 if (strLabel == "..")
1562 m_bIsParentFolder = true;
1563 m_bIsFolder = true;
1564 m_specialSort = SortSpecialOnTop;
1565 SetLabelPreformatted(true);
1567 CGUIListItem::SetLabel(strLabel);
1570 void CFileItem::SetFileSizeLabel()
1572 if(m_bIsFolder && m_dwSize == 0)
1573 SetLabel2("");
1574 else
1575 SetLabel2(StringUtils::SizeToString(m_dwSize));
1578 bool CFileItem::CanQueue() const
1580 return m_bCanQueue;
1583 void CFileItem::SetCanQueue(bool bYesNo)
1585 m_bCanQueue = bYesNo;
1588 bool CFileItem::IsParentFolder() const
1590 return m_bIsParentFolder;
1593 void CFileItem::FillInMimeType(bool lookup /*= true*/)
1595 //! @todo adapt this to use CMime::GetMimeType()
1596 if (m_mimetype.empty())
1598 if (m_bIsFolder)
1599 m_mimetype = "x-directory/normal";
1600 else if (HasPVRChannelInfoTag())
1601 m_mimetype = GetPVRChannelInfoTag()->MimeType();
1602 else if (StringUtils::StartsWithNoCase(GetDynPath(), "shout://") ||
1603 StringUtils::StartsWithNoCase(GetDynPath(), "http://") ||
1604 StringUtils::StartsWithNoCase(GetDynPath(), "https://"))
1606 // If lookup is false, bail out early to leave mime type empty
1607 if (!lookup)
1608 return;
1610 CCurlFile::GetMimeType(GetDynURL(), m_mimetype);
1612 // try to get mime-type again but with an NSPlayer User-Agent
1613 // in order for server to provide correct mime-type. Allows us
1614 // to properly detect an MMS stream
1615 if (StringUtils::StartsWithNoCase(m_mimetype, "video/x-ms-"))
1616 CCurlFile::GetMimeType(GetDynURL(), m_mimetype, "NSPlayer/11.00.6001.7000");
1618 // make sure there are no options set in mime-type
1619 // mime-type can look like "video/x-ms-asf ; charset=utf8"
1620 size_t i = m_mimetype.find(';');
1621 if(i != std::string::npos)
1622 m_mimetype.erase(i, m_mimetype.length() - i);
1623 StringUtils::Trim(m_mimetype);
1625 else
1626 m_mimetype = CMime::GetMimeType(*this);
1628 // if it's still empty set to an unknown type
1629 if (m_mimetype.empty())
1630 m_mimetype = "application/octet-stream";
1633 // change protocol to mms for the following mime-type. Allows us to create proper FileMMS.
1634 if(StringUtils::StartsWithNoCase(m_mimetype, "application/vnd.ms.wms-hdr.asfv1") ||
1635 StringUtils::StartsWithNoCase(m_mimetype, "application/x-mms-framed"))
1637 if (m_strDynPath.empty())
1638 m_strDynPath = m_strPath;
1640 StringUtils::Replace(m_strDynPath, "http:", "mms:");
1644 void CFileItem::UpdateMimeType(bool lookup /*= true*/)
1646 //! @todo application/octet-stream might actually have been set by a web lookup. Currently we
1647 //! cannot distinguish between set as fallback only (see FillInMimeType) or as an actual value.
1648 if (m_mimetype == "application/octet-stream")
1649 m_mimetype.clear();
1651 FillInMimeType(lookup);
1654 void CFileItem::SetMimeTypeForInternetFile()
1656 if (m_doContentLookup && IsInternetStream())
1658 SetMimeType("");
1659 FillInMimeType(true);
1663 bool CFileItem::IsSamePath(const CFileItem *item) const
1665 if (!item)
1666 return false;
1668 if (!m_strPath.empty() && item->GetPath() == m_strPath)
1670 if (item->HasProperty("item_start") || HasProperty("item_start"))
1671 return (item->GetProperty("item_start") == GetProperty("item_start"));
1672 return true;
1674 if (HasMusicInfoTag() && item->HasMusicInfoTag())
1676 if (GetMusicInfoTag()->GetDatabaseId() != -1 && item->GetMusicInfoTag()->GetDatabaseId() != -1)
1677 return ((GetMusicInfoTag()->GetDatabaseId() == item->GetMusicInfoTag()->GetDatabaseId()) &&
1678 (GetMusicInfoTag()->GetType() == item->GetMusicInfoTag()->GetType()));
1680 if (HasVideoInfoTag() && item->HasVideoInfoTag())
1682 const CVideoInfoTag* myTag{GetVideoInfoTag()};
1683 const CVideoInfoTag* otherTag{item->GetVideoInfoTag()};
1684 if (myTag->m_iDbId != -1 && otherTag->m_iDbId != -1)
1686 if ((myTag->m_iDbId == otherTag->m_iDbId) && (myTag->m_type == otherTag->m_type))
1688 // for movies with multiple versions, wie need also to check the file id
1689 if (HasVideoVersions() && item->HasVideoVersions() && myTag->m_iFileId != -1 &&
1690 otherTag->m_iFileId != -1)
1691 return myTag->m_iFileId == otherTag->m_iFileId;
1692 return true;
1696 if (IsMusicDb() && HasMusicInfoTag())
1698 CFileItem dbItem(m_musicInfoTag->GetURL(), false);
1699 if (HasProperty("item_start"))
1700 dbItem.SetProperty("item_start", GetProperty("item_start"));
1701 return dbItem.IsSamePath(item);
1703 if (IsVideoDb() && HasVideoInfoTag())
1705 CFileItem dbItem(GetVideoInfoTag()->m_strFileNameAndPath, false);
1706 if (HasProperty("item_start"))
1707 dbItem.SetProperty("item_start", GetProperty("item_start"));
1708 return dbItem.IsSamePath(item);
1710 if (item->IsMusicDb() && item->HasMusicInfoTag())
1712 CFileItem dbItem(item->m_musicInfoTag->GetURL(), false);
1713 if (item->HasProperty("item_start"))
1714 dbItem.SetProperty("item_start", item->GetProperty("item_start"));
1715 return IsSamePath(&dbItem);
1717 if (item->IsVideoDb() && item->HasVideoInfoTag())
1719 CFileItem dbItem(item->GetVideoInfoTag()->m_strFileNameAndPath, false);
1720 if (item->HasProperty("item_start"))
1721 dbItem.SetProperty("item_start", item->GetProperty("item_start"));
1722 return IsSamePath(&dbItem);
1724 if (HasProperty("original_listitem_url"))
1725 return (GetProperty("original_listitem_url") == item->GetPath());
1726 return false;
1729 bool CFileItem::IsAlbum() const
1731 return m_bIsAlbum;
1734 void CFileItem::UpdateInfo(const CFileItem &item, bool replaceLabels /*=true*/)
1736 if (item.HasVideoInfoTag())
1737 { // copy info across
1738 //! @todo premiered info is normally stored in m_dateTime by the db
1740 if (item.m_videoInfoTag)
1742 if (m_videoInfoTag)
1743 *m_videoInfoTag = *item.m_videoInfoTag;
1744 else
1745 m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
1747 else
1749 if (m_videoInfoTag)
1750 delete m_videoInfoTag;
1752 m_videoInfoTag = new CVideoInfoTag;
1755 m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
1757 SetOverlayImage(GetVideoInfoTag()->GetPlayCount() > 0 ? CGUIListItem::ICON_OVERLAY_WATCHED
1758 : CGUIListItem::ICON_OVERLAY_UNWATCHED);
1759 SetInvalid();
1761 if (item.HasMusicInfoTag())
1763 *GetMusicInfoTag() = *item.GetMusicInfoTag();
1764 SetInvalid();
1766 if (item.HasPictureInfoTag())
1768 *GetPictureInfoTag() = *item.GetPictureInfoTag();
1769 SetInvalid();
1771 if (item.HasGameInfoTag())
1773 *GetGameInfoTag() = *item.GetGameInfoTag();
1774 SetInvalid();
1776 if (item.HasPVRChannelGroupMemberInfoTag())
1778 m_pvrChannelGroupMemberInfoTag = item.GetPVRChannelGroupMemberInfoTag();
1779 SetInvalid();
1781 if (item.HasPVRTimerInfoTag())
1783 m_pvrTimerInfoTag = item.m_pvrTimerInfoTag;
1784 SetInvalid();
1786 if (item.HasEPGInfoTag())
1788 m_epgInfoTag = item.m_epgInfoTag;
1789 SetInvalid();
1791 if (item.HasEPGSearchFilter())
1793 m_epgSearchFilter = item.m_epgSearchFilter;
1794 SetInvalid();
1796 SetDynPath(item.GetDynPath());
1797 if (replaceLabels && !item.GetLabel().empty())
1798 SetLabel(item.GetLabel());
1799 if (replaceLabels && !item.GetLabel2().empty())
1800 SetLabel2(item.GetLabel2());
1801 if (!item.GetArt().empty())
1802 SetArt(item.GetArt());
1803 AppendProperties(item);
1804 UpdateMimeType();
1807 void CFileItem::MergeInfo(const CFileItem& item)
1809 // TODO: Currently merge the metadata/art info is implemented for video case only
1810 if (item.HasVideoInfoTag())
1812 if (item.m_videoInfoTag)
1814 if (m_videoInfoTag)
1815 m_videoInfoTag->Merge(*item.m_videoInfoTag);
1816 else
1817 m_videoInfoTag = new CVideoInfoTag(*item.m_videoInfoTag);
1820 m_pvrRecordingInfoTag = item.m_pvrRecordingInfoTag;
1822 SetOverlayImage(GetVideoInfoTag()->GetPlayCount() > 0 ? CGUIListItem::ICON_OVERLAY_WATCHED
1823 : CGUIListItem::ICON_OVERLAY_UNWATCHED);
1824 SetInvalid();
1826 if (item.HasMusicInfoTag())
1828 *GetMusicInfoTag() = *item.GetMusicInfoTag();
1829 SetInvalid();
1831 if (item.HasPictureInfoTag())
1833 *GetPictureInfoTag() = *item.GetPictureInfoTag();
1834 SetInvalid();
1836 if (item.HasGameInfoTag())
1838 *GetGameInfoTag() = *item.GetGameInfoTag();
1839 SetInvalid();
1841 if (item.HasPVRChannelGroupMemberInfoTag())
1843 m_pvrChannelGroupMemberInfoTag = item.GetPVRChannelGroupMemberInfoTag();
1844 SetInvalid();
1846 if (item.HasPVRTimerInfoTag())
1848 m_pvrTimerInfoTag = item.m_pvrTimerInfoTag;
1849 SetInvalid();
1851 if (item.HasEPGInfoTag())
1853 m_epgInfoTag = item.m_epgInfoTag;
1854 SetInvalid();
1856 if (item.HasEPGSearchFilter())
1858 m_epgSearchFilter = item.m_epgSearchFilter;
1859 SetInvalid();
1861 SetDynPath(item.GetDynPath());
1862 if (!item.GetLabel().empty())
1863 SetLabel(item.GetLabel());
1864 if (!item.GetLabel2().empty())
1865 SetLabel2(item.GetLabel2());
1866 if (!item.GetArt().empty())
1868 if (item.IsVideo())
1869 AppendArt(item.GetArt());
1870 else
1871 SetArt(item.GetArt());
1873 AppendProperties(item);
1874 UpdateMimeType();
1877 void CFileItem::SetFromVideoInfoTag(const CVideoInfoTag &video)
1879 if (!video.m_strTitle.empty())
1880 SetLabel(video.m_strTitle);
1881 if (video.m_strFileNameAndPath.empty())
1883 m_strPath = video.m_strPath;
1884 URIUtils::AddSlashAtEnd(m_strPath);
1885 m_bIsFolder = true;
1887 else
1889 m_strPath = video.m_strFileNameAndPath;
1890 m_bIsFolder = false;
1893 if (m_videoInfoTag)
1894 *m_videoInfoTag = video;
1895 else
1896 m_videoInfoTag = new CVideoInfoTag(video);
1898 if (video.m_iSeason == 0)
1899 SetProperty("isspecial", "true");
1900 FillInDefaultIcon();
1901 FillInMimeType(false);
1904 namespace
1906 class CPropertySaveHelper
1908 public:
1909 CPropertySaveHelper(CFileItem& item, const std::string& property, const std::string& value)
1910 : m_item(item), m_property(property), m_value(value)
1914 bool NeedsSave() const { return !m_value.empty() || m_item.HasProperty(m_property); }
1916 std::string GetValueToSave(const std::string& currentValue) const
1918 std::string value;
1920 if (!m_value.empty())
1922 // Overwrite whatever we have; remember what we had originally.
1923 if (!m_item.HasProperty(m_property))
1924 m_item.SetProperty(m_property, currentValue);
1926 value = m_value;
1928 else if (m_item.HasProperty(m_property))
1930 // Restore original value
1931 value = m_item.GetProperty(m_property).asString();
1932 m_item.ClearProperty(m_property);
1935 return value;
1938 private:
1939 CFileItem& m_item;
1940 const std::string m_property;
1941 const std::string m_value;
1943 } // unnamed namespace
1945 void CFileItem::SetFromMusicInfoTag(const MUSIC_INFO::CMusicInfoTag& music)
1947 const std::string path = GetPath();
1948 if (path.empty())
1950 SetPath(music.GetURL());
1952 else
1954 const CPropertySaveHelper dynpath(*this, "OriginalDynPath", music.GetURL());
1955 if (dynpath.NeedsSave())
1956 SetDynPath(dynpath.GetValueToSave(m_strDynPath));
1959 const CPropertySaveHelper label(*this, "OriginalLabel", music.GetTitle());
1960 if (label.NeedsSave())
1961 SetLabel(label.GetValueToSave(GetLabel()));
1963 const CPropertySaveHelper thumb(*this, "OriginalThumb", music.GetStationArt());
1964 if (thumb.NeedsSave())
1965 SetArt("thumb", thumb.GetValueToSave(GetArt("thumb")));
1967 *GetMusicInfoTag() = music;
1968 FillInDefaultIcon();
1969 FillInMimeType(false);
1972 void CFileItem::SetFromAlbum(const CAlbum &album)
1974 if (!album.strAlbum.empty())
1975 SetLabel(album.strAlbum);
1976 m_bIsFolder = true;
1977 m_strLabel2 = album.GetAlbumArtistString();
1978 GetMusicInfoTag()->SetAlbum(album);
1980 if (album.art.empty())
1981 SetArt("icon", "DefaultAlbumCover.png");
1982 else
1983 SetArt(album.art);
1985 m_bIsAlbum = true;
1986 CMusicDatabase::SetPropertiesFromAlbum(*this,album);
1987 FillInMimeType(false);
1990 void CFileItem::SetFromSong(const CSong &song)
1992 if (!song.strTitle.empty())
1993 SetLabel(song.strTitle);
1994 if (song.idSong > 0)
1996 std::string strExt = URIUtils::GetExtension(song.strFileName);
1997 m_strPath = StringUtils::Format("musicdb://songs/{}{}", song.idSong, strExt);
1999 else if (!song.strFileName.empty())
2000 m_strPath = song.strFileName;
2001 GetMusicInfoTag()->SetSong(song);
2002 m_lStartOffset = song.iStartOffset;
2003 m_lStartPartNumber = 1;
2004 SetProperty("item_start", song.iStartOffset);
2005 m_lEndOffset = song.iEndOffset;
2006 if (!song.strThumb.empty())
2007 SetArt("thumb", song.strThumb);
2008 FillInMimeType(false);
2011 std::string CFileItem::GetOpticalMediaPath() const
2013 std::string path;
2014 path = URIUtils::AddFileToFolder(GetPath(), "VIDEO_TS.IFO");
2015 if (CFile::Exists(path))
2016 return path;
2018 path = URIUtils::AddFileToFolder(GetPath(), "VIDEO_TS", "VIDEO_TS.IFO");
2019 if (CFile::Exists(path))
2020 return path;
2022 #ifdef HAVE_LIBBLURAY
2023 path = URIUtils::AddFileToFolder(GetPath(), "index.bdmv");
2024 if (CFile::Exists(path))
2025 return path;
2027 path = URIUtils::AddFileToFolder(GetPath(), "BDMV", "index.bdmv");
2028 if (CFile::Exists(path))
2029 return path;
2031 path = URIUtils::AddFileToFolder(GetPath(), "INDEX.BDM");
2032 if (CFile::Exists(path))
2033 return path;
2035 path = URIUtils::AddFileToFolder(GetPath(), "BDMV", "INDEX.BDM");
2036 if (CFile::Exists(path))
2037 return path;
2038 #endif
2039 return std::string();
2043 * @todo Ideally this (and SetPath) would not be available outside of construction
2044 * for CFileItem objects, or at least restricted to essentially be equivalent
2045 * to construction. This would require re-formulating a bunch of CFileItem
2046 * construction, and also allowing CFileItemList to have its own (public)
2047 * SetURL() function, so for now we give direct access.
2049 void CFileItem::SetURL(const CURL& url)
2051 m_strPath = url.Get();
2054 const CURL CFileItem::GetURL() const
2056 CURL url(m_strPath);
2057 return url;
2060 bool CFileItem::IsURL(const CURL& url) const
2062 return IsPath(url.Get());
2065 bool CFileItem::IsPath(const std::string& path, bool ignoreURLOptions /* = false */) const
2067 return URIUtils::PathEquals(m_strPath, path, false, ignoreURLOptions);
2070 void CFileItem::SetDynURL(const CURL& url)
2072 m_strDynPath = url.Get();
2075 const CURL CFileItem::GetDynURL() const
2077 if (!m_strDynPath.empty())
2079 CURL url(m_strDynPath);
2080 return url;
2082 else
2084 CURL url(m_strPath);
2085 return url;
2089 const std::string &CFileItem::GetDynPath() const
2091 if (!m_strDynPath.empty())
2092 return m_strDynPath;
2093 else
2094 return m_strPath;
2097 void CFileItem::SetDynPath(const std::string &path)
2099 m_strDynPath = path;
2102 void CFileItem::SetCueDocument(const CCueDocumentPtr& cuePtr)
2104 m_cueDocument = cuePtr;
2107 void CFileItem::LoadEmbeddedCue()
2109 CMusicInfoTag& tag = *GetMusicInfoTag();
2110 if (!tag.Loaded())
2111 return;
2113 const std::string embeddedCue = tag.GetCueSheet();
2114 if (!embeddedCue.empty())
2116 CCueDocumentPtr cuesheet(new CCueDocument);
2117 if (cuesheet->ParseTag(embeddedCue))
2119 std::vector<std::string> MediaFileVec;
2120 cuesheet->GetMediaFiles(MediaFileVec);
2121 for (std::vector<std::string>::iterator itMedia = MediaFileVec.begin();
2122 itMedia != MediaFileVec.end(); ++itMedia)
2123 cuesheet->UpdateMediaFile(*itMedia, GetPath());
2124 SetCueDocument(cuesheet);
2126 // Clear cuesheet tag having added it to item
2127 tag.SetCueSheet("");
2131 bool CFileItem::HasCueDocument() const
2133 return (m_cueDocument.get() != nullptr);
2136 bool CFileItem::LoadTracksFromCueDocument(CFileItemList& scannedItems)
2138 if (!m_cueDocument)
2139 return false;
2141 const CMusicInfoTag& tag = *GetMusicInfoTag();
2143 VECSONGS tracks;
2144 m_cueDocument->GetSongs(tracks);
2146 bool oneFilePerTrack = m_cueDocument->IsOneFilePerTrack();
2147 m_cueDocument.reset();
2149 int tracksFound = 0;
2150 for (VECSONGS::iterator it = tracks.begin(); it != tracks.end(); ++it)
2152 CSong& song = *it;
2153 if (song.strFileName == GetPath())
2155 if (tag.Loaded())
2157 if (song.strAlbum.empty() && !tag.GetAlbum().empty())
2158 song.strAlbum = tag.GetAlbum();
2159 //Pass album artist to final MusicInfoTag object via setting song album artist vector.
2160 if (song.GetAlbumArtist().empty() && !tag.GetAlbumArtist().empty())
2161 song.SetAlbumArtist(tag.GetAlbumArtist());
2162 if (song.genre.empty() && !tag.GetGenre().empty())
2163 song.genre = tag.GetGenre();
2164 //Pass artist to final MusicInfoTag object via setting song artist description string only.
2165 //Artist credits not used during loading from cue sheet.
2166 if (song.strArtistDesc.empty() && !tag.GetArtistString().empty())
2167 song.strArtistDesc = tag.GetArtistString();
2168 if (tag.GetDiscNumber())
2169 song.iTrack |= (tag.GetDiscNumber() << 16); // see CMusicInfoTag::GetDiscNumber()
2170 if (!tag.GetCueSheet().empty())
2171 song.strCueSheet = tag.GetCueSheet();
2173 if (tag.GetYear())
2174 song.strReleaseDate = tag.GetReleaseDate();
2175 if (song.embeddedArt.Empty() && !tag.GetCoverArtInfo().Empty())
2176 song.embeddedArt = tag.GetCoverArtInfo();
2179 if (!song.iDuration && tag.GetDuration() > 0)
2180 { // must be the last song
2181 song.iDuration = CUtil::ConvertMilliSecsToSecsIntRounded(CUtil::ConvertSecsToMilliSecs(tag.GetDuration()) - song.iStartOffset);
2183 if ( tag.Loaded() && oneFilePerTrack && ! ( tag.GetAlbum().empty() || tag.GetArtist().empty() || tag.GetTitle().empty() ) )
2185 // If there are multiple files in a cue file, the tags from the files should be preferred if they exist.
2186 scannedItems.Add(std::make_shared<CFileItem>(song, tag));
2188 else
2190 scannedItems.Add(std::make_shared<CFileItem>(song));
2192 ++tracksFound;
2195 return tracksFound != 0;
2198 /////////////////////////////////////////////////////////////////////////////////
2199 /////
2200 ///// CFileItemList
2201 /////
2202 //////////////////////////////////////////////////////////////////////////////////
2204 CFileItemList::CFileItemList()
2205 : CFileItem("", true)
2209 CFileItemList::CFileItemList(const std::string& strPath)
2210 : CFileItem(strPath, true)
2214 CFileItemList::~CFileItemList()
2216 Clear();
2219 CFileItemPtr CFileItemList::operator[] (int iItem)
2221 return Get(iItem);
2224 const CFileItemPtr CFileItemList::operator[] (int iItem) const
2226 return Get(iItem);
2229 CFileItemPtr CFileItemList::operator[] (const std::string& strPath)
2231 return Get(strPath);
2234 const CFileItemPtr CFileItemList::operator[] (const std::string& strPath) const
2236 return Get(strPath);
2239 void CFileItemList::SetIgnoreURLOptions(bool ignoreURLOptions)
2241 m_ignoreURLOptions = ignoreURLOptions;
2243 if (m_fastLookup)
2245 m_fastLookup = false; // Force SetFastlookup to clear map
2246 SetFastLookup(true); // and regenerate map
2250 void CFileItemList::SetFastLookup(bool fastLookup)
2252 std::unique_lock<CCriticalSection> lock(m_lock);
2254 if (fastLookup && !m_fastLookup)
2255 { // generate the map
2256 m_map.clear();
2257 for (unsigned int i=0; i < m_items.size(); i++)
2259 CFileItemPtr pItem = m_items[i];
2260 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2263 if (!fastLookup && m_fastLookup)
2264 m_map.clear();
2265 m_fastLookup = fastLookup;
2268 bool CFileItemList::Contains(const std::string& fileName) const
2270 std::unique_lock<CCriticalSection> lock(m_lock);
2272 if (m_fastLookup)
2273 return m_map.find(m_ignoreURLOptions ? CURL(fileName).GetWithoutOptions() : fileName) != m_map.end();
2275 // slow method...
2276 for (unsigned int i = 0; i < m_items.size(); i++)
2278 const CFileItemPtr pItem = m_items[i];
2279 if (pItem->IsPath(m_ignoreURLOptions ? CURL(fileName).GetWithoutOptions() : fileName))
2280 return true;
2282 return false;
2285 void CFileItemList::Clear()
2287 std::unique_lock<CCriticalSection> lock(m_lock);
2289 ClearItems();
2290 m_sortDescription.sortBy = SortByNone;
2291 m_sortDescription.sortOrder = SortOrderNone;
2292 m_sortDescription.sortAttributes = SortAttributeNone;
2293 m_sortIgnoreFolders = false;
2294 m_cacheToDisc = CACHE_IF_SLOW;
2295 m_sortDetails.clear();
2296 m_replaceListing = false;
2297 m_content.clear();
2300 void CFileItemList::ClearItems()
2302 std::unique_lock<CCriticalSection> lock(m_lock);
2303 // make sure we free the memory of the items (these are GUIControls which may have allocated resources)
2304 FreeMemory();
2305 for (unsigned int i = 0; i < m_items.size(); i++)
2307 CFileItemPtr item = m_items[i];
2308 item->FreeMemory();
2310 m_items.clear();
2311 m_map.clear();
2314 void CFileItemList::Add(CFileItemPtr pItem)
2316 std::unique_lock<CCriticalSection> lock(m_lock);
2317 if (m_fastLookup)
2318 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2319 m_items.emplace_back(std::move(pItem));
2322 void CFileItemList::Add(CFileItem&& item)
2324 std::unique_lock<CCriticalSection> lock(m_lock);
2325 auto ptr = std::make_shared<CFileItem>(std::move(item));
2326 if (m_fastLookup)
2327 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(ptr->GetPath()).GetWithoutOptions() : ptr->GetPath(), ptr));
2328 m_items.emplace_back(std::move(ptr));
2331 void CFileItemList::AddFront(const CFileItemPtr &pItem, int itemPosition)
2333 std::unique_lock<CCriticalSection> lock(m_lock);
2335 if (itemPosition >= 0)
2337 m_items.insert(m_items.begin()+itemPosition, pItem);
2339 else
2341 m_items.insert(m_items.begin()+(m_items.size()+itemPosition), pItem);
2343 if (m_fastLookup)
2345 m_map.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath(), pItem));
2349 void CFileItemList::Remove(CFileItem* pItem)
2351 std::unique_lock<CCriticalSection> lock(m_lock);
2353 for (IVECFILEITEMS it = m_items.begin(); it != m_items.end(); ++it)
2355 if (pItem == it->get())
2357 m_items.erase(it);
2358 if (m_fastLookup)
2360 m_map.erase(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath());
2362 break;
2367 VECFILEITEMS::iterator CFileItemList::erase(VECFILEITEMS::iterator first,
2368 VECFILEITEMS::iterator last)
2370 std::unique_lock<CCriticalSection> lock(m_lock);
2371 return m_items.erase(first, last);
2374 void CFileItemList::Remove(int iItem)
2376 std::unique_lock<CCriticalSection> lock(m_lock);
2378 if (iItem >= 0 && iItem < Size())
2380 CFileItemPtr pItem = *(m_items.begin() + iItem);
2381 if (m_fastLookup)
2383 m_map.erase(m_ignoreURLOptions ? CURL(pItem->GetPath()).GetWithoutOptions() : pItem->GetPath());
2385 m_items.erase(m_items.begin() + iItem);
2389 void CFileItemList::Append(const CFileItemList& itemlist)
2391 std::unique_lock<CCriticalSection> lock(m_lock);
2393 for (int i = 0; i < itemlist.Size(); ++i)
2394 Add(itemlist[i]);
2397 void CFileItemList::Assign(const CFileItemList& itemlist, bool append)
2399 std::unique_lock<CCriticalSection> lock(m_lock);
2400 if (!append)
2401 Clear();
2402 Append(itemlist);
2403 SetPath(itemlist.GetPath());
2404 SetLabel(itemlist.GetLabel());
2405 m_sortDetails = itemlist.m_sortDetails;
2406 m_sortDescription = itemlist.m_sortDescription;
2407 m_replaceListing = itemlist.m_replaceListing;
2408 m_content = itemlist.m_content;
2409 m_mapProperties = itemlist.m_mapProperties;
2410 m_cacheToDisc = itemlist.m_cacheToDisc;
2413 bool CFileItemList::Copy(const CFileItemList& items, bool copyItems /* = true */)
2415 // assign all CFileItem parts
2416 *static_cast<CFileItem*>(this) = static_cast<const CFileItem&>(items);
2418 // assign the rest of the CFileItemList properties
2419 m_replaceListing = items.m_replaceListing;
2420 m_content = items.m_content;
2421 m_mapProperties = items.m_mapProperties;
2422 m_cacheToDisc = items.m_cacheToDisc;
2423 m_sortDetails = items.m_sortDetails;
2424 m_sortDescription = items.m_sortDescription;
2425 m_sortIgnoreFolders = items.m_sortIgnoreFolders;
2427 if (copyItems)
2429 // make a copy of each item
2430 for (int i = 0; i < items.Size(); i++)
2432 CFileItemPtr newItem(new CFileItem(*items[i]));
2433 Add(newItem);
2437 return true;
2440 CFileItemPtr CFileItemList::Get(int iItem) const
2442 std::unique_lock<CCriticalSection> lock(m_lock);
2444 if (iItem > -1 && iItem < (int)m_items.size())
2445 return m_items[iItem];
2447 return CFileItemPtr();
2450 CFileItemPtr CFileItemList::Get(const std::string& strPath) const
2452 std::unique_lock<CCriticalSection> lock(m_lock);
2454 if (m_fastLookup)
2456 MAPFILEITEMS::const_iterator it =
2457 m_map.find(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath);
2458 if (it != m_map.end())
2459 return it->second;
2461 return CFileItemPtr();
2463 // slow method...
2464 for (unsigned int i = 0; i < m_items.size(); i++)
2466 CFileItemPtr pItem = m_items[i];
2467 if (pItem->IsPath(m_ignoreURLOptions ? CURL(strPath).GetWithoutOptions() : strPath))
2468 return pItem;
2471 return CFileItemPtr();
2474 int CFileItemList::Size() const
2476 std::unique_lock<CCriticalSection> lock(m_lock);
2477 return (int)m_items.size();
2480 bool CFileItemList::IsEmpty() const
2482 std::unique_lock<CCriticalSection> lock(m_lock);
2483 return m_items.empty();
2486 void CFileItemList::Reserve(size_t iCount)
2488 std::unique_lock<CCriticalSection> lock(m_lock);
2489 m_items.reserve(iCount);
2492 void CFileItemList::Sort(FILEITEMLISTCOMPARISONFUNC func)
2494 std::unique_lock<CCriticalSection> lock(m_lock);
2495 std::stable_sort(m_items.begin(), m_items.end(), func);
2498 void CFileItemList::FillSortFields(FILEITEMFILLFUNC func)
2500 std::unique_lock<CCriticalSection> lock(m_lock);
2501 std::for_each(m_items.begin(), m_items.end(), func);
2504 void CFileItemList::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute sortAttributes /* = SortAttributeNone */)
2506 if (sortBy == SortByNone ||
2507 (m_sortDescription.sortBy == sortBy && m_sortDescription.sortOrder == sortOrder &&
2508 m_sortDescription.sortAttributes == sortAttributes))
2509 return;
2511 SortDescription sorting;
2512 sorting.sortBy = sortBy;
2513 sorting.sortOrder = sortOrder;
2514 sorting.sortAttributes = sortAttributes;
2516 Sort(sorting);
2517 m_sortDescription = sorting;
2520 void CFileItemList::Sort(SortDescription sortDescription)
2522 if (sortDescription.sortBy == SortByFile || sortDescription.sortBy == SortBySortTitle ||
2523 sortDescription.sortBy == SortByOriginalTitle || sortDescription.sortBy == SortByDateAdded ||
2524 sortDescription.sortBy == SortByRating || sortDescription.sortBy == SortByYear ||
2525 sortDescription.sortBy == SortByPlaylistOrder || sortDescription.sortBy == SortByLastPlayed ||
2526 sortDescription.sortBy == SortByPlaycount)
2527 sortDescription.sortAttributes = (SortAttribute)((int)sortDescription.sortAttributes | SortAttributeIgnoreFolders);
2529 if (sortDescription.sortBy == SortByNone ||
2530 (m_sortDescription.sortBy == sortDescription.sortBy && m_sortDescription.sortOrder == sortDescription.sortOrder &&
2531 m_sortDescription.sortAttributes == sortDescription.sortAttributes))
2532 return;
2534 if (m_sortIgnoreFolders)
2535 sortDescription.sortAttributes = (SortAttribute)((int)sortDescription.sortAttributes | SortAttributeIgnoreFolders);
2537 const Fields fields = SortUtils::GetFieldsForSorting(sortDescription.sortBy);
2538 SortItems sortItems((size_t)Size());
2539 for (int index = 0; index < Size(); index++)
2541 sortItems[index] = std::make_shared<SortItem>();
2542 m_items[index]->ToSortable(*sortItems[index], fields);
2543 (*sortItems[index])[FieldId] = index;
2546 // do the sorting
2547 SortUtils::Sort(sortDescription, sortItems);
2549 // apply the new order to the existing CFileItems
2550 VECFILEITEMS sortedFileItems;
2551 sortedFileItems.reserve(Size());
2552 for (SortItems::const_iterator it = sortItems.begin(); it != sortItems.end(); ++it)
2554 CFileItemPtr item = m_items[(int)(*it)->at(FieldId).asInteger()];
2555 // Set the sort label in the CFileItem
2556 item->SetSortLabel((*it)->at(FieldSort).asWideString());
2558 sortedFileItems.push_back(item);
2561 // replace the current list with the re-ordered one
2562 m_items = std::move(sortedFileItems);
2565 void CFileItemList::Randomize()
2567 std::unique_lock<CCriticalSection> lock(m_lock);
2568 KODI::UTILS::RandomShuffle(m_items.begin(), m_items.end());
2571 void CFileItemList::Archive(CArchive& ar)
2573 std::unique_lock<CCriticalSection> lock(m_lock);
2574 if (ar.IsStoring())
2576 CFileItem::Archive(ar);
2578 int i = 0;
2579 if (!m_items.empty() && m_items[0]->IsParentFolder())
2580 i = 1;
2582 ar << (int)(m_items.size() - i);
2584 ar << m_ignoreURLOptions;
2586 ar << m_fastLookup;
2588 ar << (int)m_sortDescription.sortBy;
2589 ar << (int)m_sortDescription.sortOrder;
2590 ar << (int)m_sortDescription.sortAttributes;
2591 ar << m_sortIgnoreFolders;
2592 ar << (int)m_cacheToDisc;
2594 ar << (int)m_sortDetails.size();
2595 for (unsigned int j = 0; j < m_sortDetails.size(); ++j)
2597 const GUIViewSortDetails &details = m_sortDetails[j];
2598 ar << (int)details.m_sortDescription.sortBy;
2599 ar << (int)details.m_sortDescription.sortOrder;
2600 ar << (int)details.m_sortDescription.sortAttributes;
2601 ar << details.m_buttonLabel;
2602 ar << details.m_labelMasks.m_strLabelFile;
2603 ar << details.m_labelMasks.m_strLabelFolder;
2604 ar << details.m_labelMasks.m_strLabel2File;
2605 ar << details.m_labelMasks.m_strLabel2Folder;
2608 ar << m_content;
2610 for (; i < (int)m_items.size(); ++i)
2612 CFileItemPtr pItem = m_items[i];
2613 ar << *pItem;
2616 else
2618 CFileItemPtr pParent;
2619 if (!IsEmpty())
2621 CFileItemPtr pItem=m_items[0];
2622 if (pItem->IsParentFolder())
2623 pParent = std::make_shared<CFileItem>(*pItem);
2626 SetIgnoreURLOptions(false);
2627 SetFastLookup(false);
2628 Clear();
2630 CFileItem::Archive(ar);
2632 int iSize = 0;
2633 ar >> iSize;
2634 if (iSize <= 0)
2635 return ;
2637 if (pParent)
2639 m_items.reserve(iSize + 1);
2640 m_items.push_back(pParent);
2642 else
2643 m_items.reserve(iSize);
2645 bool ignoreURLOptions = false;
2646 ar >> ignoreURLOptions;
2648 bool fastLookup = false;
2649 ar >> fastLookup;
2651 int tempint;
2652 ar >> tempint;
2653 m_sortDescription.sortBy = (SortBy)tempint;
2654 ar >> tempint;
2655 m_sortDescription.sortOrder = (SortOrder)tempint;
2656 ar >> tempint;
2657 m_sortDescription.sortAttributes = (SortAttribute)tempint;
2658 ar >> m_sortIgnoreFolders;
2659 ar >> tempint;
2660 m_cacheToDisc = CACHE_TYPE(tempint);
2662 unsigned int detailSize = 0;
2663 ar >> detailSize;
2664 for (unsigned int j = 0; j < detailSize; ++j)
2666 GUIViewSortDetails details;
2667 ar >> tempint;
2668 details.m_sortDescription.sortBy = (SortBy)tempint;
2669 ar >> tempint;
2670 details.m_sortDescription.sortOrder = (SortOrder)tempint;
2671 ar >> tempint;
2672 details.m_sortDescription.sortAttributes = (SortAttribute)tempint;
2673 ar >> details.m_buttonLabel;
2674 ar >> details.m_labelMasks.m_strLabelFile;
2675 ar >> details.m_labelMasks.m_strLabelFolder;
2676 ar >> details.m_labelMasks.m_strLabel2File;
2677 ar >> details.m_labelMasks.m_strLabel2Folder;
2678 m_sortDetails.push_back(details);
2681 ar >> m_content;
2683 for (int i = 0; i < iSize; ++i)
2685 CFileItemPtr pItem(new CFileItem);
2686 ar >> *pItem;
2687 Add(pItem);
2690 SetIgnoreURLOptions(ignoreURLOptions);
2691 SetFastLookup(fastLookup);
2695 void CFileItemList::FillInDefaultIcons()
2697 std::unique_lock<CCriticalSection> lock(m_lock);
2698 for (int i = 0; i < (int)m_items.size(); ++i)
2700 CFileItemPtr pItem = m_items[i];
2701 pItem->FillInDefaultIcon();
2705 int CFileItemList::GetFolderCount() const
2707 std::unique_lock<CCriticalSection> lock(m_lock);
2708 int nFolderCount = 0;
2709 for (int i = 0; i < (int)m_items.size(); i++)
2711 CFileItemPtr pItem = m_items[i];
2712 if (pItem->m_bIsFolder)
2713 nFolderCount++;
2716 return nFolderCount;
2719 int CFileItemList::GetObjectCount() const
2721 std::unique_lock<CCriticalSection> lock(m_lock);
2723 int numObjects = (int)m_items.size();
2724 if (numObjects && m_items[0]->IsParentFolder())
2725 numObjects--;
2727 return numObjects;
2730 int CFileItemList::GetFileCount() const
2732 std::unique_lock<CCriticalSection> lock(m_lock);
2733 int nFileCount = 0;
2734 for (int i = 0; i < (int)m_items.size(); i++)
2736 CFileItemPtr pItem = m_items[i];
2737 if (!pItem->m_bIsFolder)
2738 nFileCount++;
2741 return nFileCount;
2744 int CFileItemList::GetSelectedCount() const
2746 std::unique_lock<CCriticalSection> lock(m_lock);
2747 int count = 0;
2748 for (int i = 0; i < (int)m_items.size(); i++)
2750 CFileItemPtr pItem = m_items[i];
2751 if (pItem->IsSelected())
2752 count++;
2755 return count;
2758 void CFileItemList::FilterCueItems()
2760 std::unique_lock<CCriticalSection> lock(m_lock);
2761 // Handle .CUE sheet files...
2762 std::vector<std::string> itemstodelete;
2763 for (int i = 0; i < (int)m_items.size(); i++)
2765 CFileItemPtr pItem = m_items[i];
2766 if (!pItem->m_bIsFolder)
2767 { // see if it's a .CUE sheet
2768 if (pItem->IsCUESheet())
2770 CCueDocumentPtr cuesheet(new CCueDocument);
2771 if (cuesheet->ParseFile(pItem->GetPath()))
2773 std::vector<std::string> MediaFileVec;
2774 cuesheet->GetMediaFiles(MediaFileVec);
2776 // queue the cue sheet and the underlying media file for deletion
2777 for (std::vector<std::string>::iterator itMedia = MediaFileVec.begin();
2778 itMedia != MediaFileVec.end(); ++itMedia)
2780 std::string strMediaFile = *itMedia;
2781 std::string fileFromCue = strMediaFile; // save the file from the cue we're matching against,
2782 // as we're going to search for others here...
2783 bool bFoundMediaFile = CFile::Exists(strMediaFile);
2784 if (!bFoundMediaFile)
2786 // try file in same dir, not matching case...
2787 if (Contains(strMediaFile))
2789 bFoundMediaFile = true;
2791 else
2793 // try removing the .cue extension...
2794 strMediaFile = pItem->GetPath();
2795 URIUtils::RemoveExtension(strMediaFile);
2796 CFileItem item(strMediaFile, false);
2797 if (item.IsAudio() && Contains(strMediaFile))
2799 bFoundMediaFile = true;
2801 else
2802 { // try replacing the extension with one of our allowed ones.
2803 std::vector<std::string> extensions = StringUtils::Split(CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
2804 for (std::vector<std::string>::const_iterator i = extensions.begin(); i != extensions.end(); ++i)
2806 strMediaFile = URIUtils::ReplaceExtension(pItem->GetPath(), *i);
2807 CFileItem item(strMediaFile, false);
2808 if (!item.IsCUESheet() && !item.IsPlayList() && Contains(strMediaFile))
2810 bFoundMediaFile = true;
2811 break;
2817 if (bFoundMediaFile)
2819 cuesheet->UpdateMediaFile(fileFromCue, strMediaFile);
2820 // apply CUE for later processing
2821 for (int j = 0; j < (int)m_items.size(); j++)
2823 CFileItemPtr pItem = m_items[j];
2824 if (StringUtils::CompareNoCase(pItem->GetPath(), strMediaFile) == 0)
2825 pItem->SetCueDocument(cuesheet);
2830 itemstodelete.push_back(pItem->GetPath());
2834 // now delete the .CUE files.
2835 for (int i = 0; i < (int)itemstodelete.size(); i++)
2837 for (int j = 0; j < (int)m_items.size(); j++)
2839 CFileItemPtr pItem = m_items[j];
2840 if (StringUtils::CompareNoCase(pItem->GetPath(), itemstodelete[i]) == 0)
2841 { // delete this item
2842 m_items.erase(m_items.begin() + j);
2843 break;
2849 // Remove the extensions from the filenames
2850 void CFileItemList::RemoveExtensions()
2852 std::unique_lock<CCriticalSection> lock(m_lock);
2853 for (int i = 0; i < Size(); ++i)
2854 m_items[i]->RemoveExtension();
2857 void CFileItemList::Stack(bool stackFiles /* = true */)
2859 std::unique_lock<CCriticalSection> lock(m_lock);
2861 // not allowed here
2862 if (IsVirtualDirectoryRoot() ||
2863 IsLiveTV() ||
2864 IsSourcesPath() ||
2865 IsLibraryFolder())
2866 return;
2868 SetProperty("isstacked", true);
2870 // items needs to be sorted for stuff below to work properly
2871 Sort(SortByLabel, SortOrderAscending);
2873 StackFolders();
2875 if (stackFiles)
2876 StackFiles();
2879 void CFileItemList::StackFolders()
2881 // Precompile our REs
2882 VECCREGEXP folderRegExps;
2883 CRegExp folderRegExp(true, CRegExp::autoUtf8);
2884 const std::vector<std::string>& strFolderRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_folderStackRegExps;
2886 std::vector<std::string>::const_iterator strExpression = strFolderRegExps.begin();
2887 while (strExpression != strFolderRegExps.end())
2889 if (!folderRegExp.RegComp(*strExpression))
2890 CLog::Log(LOGERROR, "{}: Invalid folder stack RegExp:'{}'", __FUNCTION__,
2891 strExpression->c_str());
2892 else
2893 folderRegExps.push_back(folderRegExp);
2895 ++strExpression;
2898 if (!folderRegExp.IsCompiled())
2900 CLog::Log(LOGDEBUG, "{}: No stack expressions available. Skipping folder stacking",
2901 __FUNCTION__);
2902 return;
2905 // stack folders
2906 for (int i = 0; i < Size(); i++)
2908 CFileItemPtr item = Get(i);
2909 // combined the folder checks
2910 if (item->m_bIsFolder)
2912 // only check known fast sources?
2913 // NOTES:
2914 // 1. rars and zips may be on slow sources? is this supposed to be allowed?
2915 if( !item->IsRemote()
2916 || item->IsSmb()
2917 || item->IsNfs()
2918 || URIUtils::IsInRAR(item->GetPath())
2919 || URIUtils::IsInZIP(item->GetPath())
2920 || URIUtils::IsOnLAN(item->GetPath())
2923 // stack cd# folders if contains only a single video file
2925 bool bMatch(false);
2927 VECCREGEXP::iterator expr = folderRegExps.begin();
2928 while (!bMatch && expr != folderRegExps.end())
2930 //CLog::Log(LOGDEBUG,"{}: Running expression {} on {}", __FUNCTION__, expr->GetPattern(), item->GetLabel());
2931 bMatch = (expr->RegFind(item->GetLabel().c_str()) != -1);
2932 if (bMatch)
2934 CFileItemList items;
2935 CDirectory::GetDirectory(item->GetPath(), items,
2936 CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(),
2937 DIR_FLAG_DEFAULTS);
2938 // optimized to only traverse listing once by checking for filecount
2939 // and recording last file item for later use
2940 int nFiles = 0;
2941 int index = -1;
2942 for (int j = 0; j < items.Size(); j++)
2944 if (!items[j]->m_bIsFolder)
2946 nFiles++;
2947 index = j;
2950 if (nFiles > 1)
2951 break;
2954 if (nFiles == 1)
2955 *item = *items[index];
2957 ++expr;
2960 // check for dvd folders
2961 if (!bMatch)
2963 std::string dvdPath = item->GetOpticalMediaPath();
2965 if (!dvdPath.empty())
2967 // NOTE: should this be done for the CD# folders too?
2968 item->m_bIsFolder = false;
2969 item->SetPath(dvdPath);
2970 item->SetLabel2("");
2971 item->SetLabelPreformatted(true);
2972 m_sortDescription.sortBy = SortByNone; /* sorting is now broken */
2980 void CFileItemList::StackFiles()
2982 // Precompile our REs
2983 VECCREGEXP stackRegExps;
2984 CRegExp tmpRegExp(true, CRegExp::autoUtf8);
2985 const std::vector<std::string>& strStackRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoStackRegExps;
2986 std::vector<std::string>::const_iterator strRegExp = strStackRegExps.begin();
2987 while (strRegExp != strStackRegExps.end())
2989 if (tmpRegExp.RegComp(*strRegExp))
2991 if (tmpRegExp.GetCaptureTotal() == 4)
2992 stackRegExps.push_back(tmpRegExp);
2993 else
2994 CLog::Log(LOGERROR, "Invalid video stack RE ({}). Must have 4 captures.", *strRegExp);
2996 ++strRegExp;
2999 // now stack the files, some of which may be from the previous stack iteration
3000 int i = 0;
3001 while (i < Size())
3003 CFileItemPtr item1 = Get(i);
3005 // skip folders, nfo files, playlists
3006 if (item1->m_bIsFolder
3007 || item1->IsParentFolder()
3008 || item1->IsNFO()
3009 || item1->IsPlayList()
3012 // increment index
3013 i++;
3014 continue;
3017 int64_t size = 0;
3018 size_t offset = 0;
3019 std::string stackName;
3020 std::string file1;
3021 std::string filePath;
3022 std::vector<int> stack;
3023 VECCREGEXP::iterator expr = stackRegExps.begin();
3025 URIUtils::Split(item1->GetPath(), filePath, file1);
3026 if (URIUtils::HasEncodedFilename(CURL(filePath)))
3027 file1 = CURL::Decode(file1);
3029 int j;
3030 while (expr != stackRegExps.end())
3032 if (expr->RegFind(file1, offset) != -1)
3034 std::string Title1 = expr->GetMatch(1),
3035 Volume1 = expr->GetMatch(2),
3036 Ignore1 = expr->GetMatch(3),
3037 Extension1 = expr->GetMatch(4);
3038 if (offset)
3039 Title1 = file1.substr(0, expr->GetSubStart(2));
3040 j = i + 1;
3041 while (j < Size())
3043 CFileItemPtr item2 = Get(j);
3045 // skip folders, nfo files, playlists
3046 if (item2->m_bIsFolder
3047 || item2->IsParentFolder()
3048 || item2->IsNFO()
3049 || item2->IsPlayList()
3052 // increment index
3053 j++;
3054 continue;
3057 std::string file2, filePath2;
3058 URIUtils::Split(item2->GetPath(), filePath2, file2);
3059 if (URIUtils::HasEncodedFilename(CURL(filePath2)) )
3060 file2 = CURL::Decode(file2);
3062 if (expr->RegFind(file2, offset) != -1)
3064 std::string Title2 = expr->GetMatch(1),
3065 Volume2 = expr->GetMatch(2),
3066 Ignore2 = expr->GetMatch(3),
3067 Extension2 = expr->GetMatch(4);
3068 if (offset)
3069 Title2 = file2.substr(0, expr->GetSubStart(2));
3070 if (StringUtils::EqualsNoCase(Title1, Title2))
3072 if (!StringUtils::EqualsNoCase(Volume1, Volume2))
3074 if (StringUtils::EqualsNoCase(Ignore1, Ignore2) &&
3075 StringUtils::EqualsNoCase(Extension1, Extension2))
3077 if (stack.empty())
3079 stackName = Title1 + Ignore1 + Extension1;
3080 stack.push_back(i);
3081 size += item1->m_dwSize;
3083 stack.push_back(j);
3084 size += item2->m_dwSize;
3086 else // Sequel
3088 offset = 0;
3089 ++expr;
3090 break;
3093 else if (!StringUtils::EqualsNoCase(Ignore1, Ignore2)) // False positive, try again with offset
3095 offset = expr->GetSubStart(3);
3096 break;
3098 else // Extension mismatch
3100 offset = 0;
3101 ++expr;
3102 break;
3105 else // Title mismatch
3107 offset = 0;
3108 ++expr;
3109 break;
3112 else // No match 2, next expression
3114 offset = 0;
3115 ++expr;
3116 break;
3118 j++;
3120 if (j == Size())
3121 expr = stackRegExps.end();
3123 else // No match 1
3125 offset = 0;
3126 ++expr;
3128 if (stack.size() > 1)
3130 // have a stack, remove the items and add the stacked item
3131 // dont actually stack a multipart rar set, just remove all items but the first
3132 std::string stackPath;
3133 if (Get(stack[0])->IsRAR())
3134 stackPath = Get(stack[0])->GetPath();
3135 else
3137 CStackDirectory dir;
3138 stackPath = dir.ConstructStackPath(*this, stack);
3140 item1->SetPath(stackPath);
3141 // clean up list
3142 for (unsigned k = 1; k < stack.size(); k++)
3143 Remove(i+1);
3144 // item->m_bIsFolder = true; // don't treat stacked files as folders
3145 // the label may be in a different char set from the filename (eg over smb
3146 // the label is converted from utf8, but the filename is not)
3147 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS))
3148 URIUtils::RemoveExtension(stackName);
3150 item1->SetLabel(stackName);
3151 item1->m_dwSize = size;
3152 break;
3155 i++;
3159 bool CFileItemList::Load(int windowID)
3161 CFile file;
3162 auto path = GetDiscFileCache(windowID);
3165 if (file.Open(path))
3167 CArchive ar(&file, CArchive::load);
3168 ar >> *this;
3169 CLog::Log(LOGDEBUG, "Loading items: {}, directory: {} sort method: {}, ascending: {}", Size(),
3170 CURL::GetRedacted(GetPath()), m_sortDescription.sortBy,
3171 m_sortDescription.sortOrder == SortOrderAscending ? "true" : "false");
3172 ar.Close();
3173 file.Close();
3174 return true;
3177 catch(const std::out_of_range&)
3179 CLog::Log(LOGERROR, "Corrupt archive: {}", CURL::GetRedacted(path));
3182 return false;
3185 bool CFileItemList::Save(int windowID)
3187 int iSize = Size();
3188 if (iSize <= 0)
3189 return false;
3191 CLog::Log(LOGDEBUG, "Saving fileitems [{}]", CURL::GetRedacted(GetPath()));
3193 CFile file;
3194 std::string cachefile = GetDiscFileCache(windowID);
3195 if (file.OpenForWrite(cachefile, true)) // overwrite always
3197 // Before caching save simplified cache file name in every item so the cache file can be
3198 // identifed and removed if the item is updated. List path and options (used for file
3199 // name when list cached) can not be accurately derived from item path.
3200 StringUtils::Replace(cachefile, "special://temp/archive_cache/", "");
3201 StringUtils::Replace(cachefile, ".fi", "");
3202 for (const auto& item : m_items)
3203 item->SetProperty("cachefilename", cachefile);
3205 CArchive ar(&file, CArchive::store);
3206 ar << *this;
3207 CLog::Log(LOGDEBUG, " -- items: {}, sort method: {}, ascending: {}", iSize,
3208 m_sortDescription.sortBy,
3209 m_sortDescription.sortOrder == SortOrderAscending ? "true" : "false");
3210 ar.Close();
3211 file.Close();
3212 return true;
3215 return false;
3218 void CFileItemList::RemoveDiscCache(int windowID) const
3220 RemoveDiscCache(GetDiscFileCache(windowID));
3223 void CFileItemList::RemoveDiscCache(const std::string& cacheFile) const
3225 if (CFile::Exists(cacheFile))
3227 CLog::Log(LOGDEBUG, "Clearing cached fileitems [{}]", CURL::GetRedacted(GetPath()));
3228 CFile::Delete(cacheFile);
3232 void CFileItemList::RemoveDiscCacheCRC(const std::string& crc) const
3234 std::string cachefile = StringUtils::Format("special://temp/archive_cache/{}.fi", crc);
3235 RemoveDiscCache(cachefile);
3238 std::string CFileItemList::GetDiscFileCache(int windowID) const
3240 std::string strPath(GetPath());
3241 URIUtils::RemoveSlashAtEnd(strPath);
3243 uint32_t crc = Crc32::ComputeFromLowerCase(strPath);
3245 if (IsCDDA() || IsOnDVD())
3246 return StringUtils::Format("special://temp/archive_cache/r-{:08x}.fi", crc);
3248 if (IsMusicDb())
3249 return StringUtils::Format("special://temp/archive_cache/mdb-{:08x}.fi", crc);
3251 if (IsVideoDb())
3252 return StringUtils::Format("special://temp/archive_cache/vdb-{:08x}.fi", crc);
3254 if (IsSmartPlayList())
3255 return StringUtils::Format("special://temp/archive_cache/sp-{:08x}.fi", crc);
3257 if (windowID)
3258 return StringUtils::Format("special://temp/archive_cache/{}-{:08x}.fi", windowID, crc);
3260 return StringUtils::Format("special://temp/archive_cache/{:08x}.fi", crc);
3263 bool CFileItemList::AlwaysCache() const
3265 // some database folders are always cached
3266 if (IsMusicDb())
3267 return CMusicDatabaseDirectory::CanCache(GetPath());
3268 if (IsVideoDb())
3269 return CVideoDatabaseDirectory::CanCache(GetPath());
3270 if (IsEPG())
3271 return true; // always cache
3272 return false;
3275 std::string CFileItem::GetUserMusicThumb(bool alwaysCheckRemote /* = false */, bool fallbackToFolder /* = false */) const
3277 if (m_strPath.empty()
3278 || StringUtils::StartsWithNoCase(m_strPath, "newsmartplaylist://")
3279 || StringUtils::StartsWithNoCase(m_strPath, "newplaylist://")
3280 || m_bIsShareOrDrive
3281 || IsInternetStream()
3282 || URIUtils::IsUPnP(m_strPath)
3283 || (URIUtils::IsFTP(m_strPath) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3284 || IsPlugin()
3285 || IsAddonsPath()
3286 || IsLibraryFolder()
3287 || IsParentFolder()
3288 || IsMusicDb())
3289 return "";
3291 // we first check for <filename>.tbn or <foldername>.tbn
3292 std::string fileThumb(GetTBNFile());
3293 if (CFile::Exists(fileThumb))
3294 return fileThumb;
3296 // Fall back to folder thumb, if requested
3297 if (!m_bIsFolder && fallbackToFolder)
3299 CFileItem item(URIUtils::GetDirectory(m_strPath), true);
3300 return item.GetUserMusicThumb(alwaysCheckRemote);
3303 // if a folder, check for folder.jpg
3304 if (m_bIsFolder && !IsFileFolder() && (!IsRemote() || alwaysCheckRemote || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICFILES_FINDREMOTETHUMBS)))
3306 std::vector<CVariant> thumbs = CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(
3307 CSettings::SETTING_MUSICLIBRARY_MUSICTHUMBS);
3308 for (const auto& i : thumbs)
3310 std::string strFileName = i.asString();
3311 std::string folderThumb(GetFolderThumb(strFileName));
3312 if (CFile::Exists(folderThumb)) // folder.jpg
3313 return folderThumb;
3314 size_t period = strFileName.find_last_of('.');
3315 if (period != std::string::npos)
3317 std::string ext;
3318 std::string name = strFileName;
3319 std::string folderThumb1 = folderThumb;
3320 name.erase(period);
3321 ext = strFileName.substr(period);
3322 StringUtils::ToUpper(ext);
3323 StringUtils::Replace(folderThumb1, strFileName, name + ext);
3324 if (CFile::Exists(folderThumb1)) // folder.JPG
3325 return folderThumb1;
3327 folderThumb1 = folderThumb;
3328 std::string firstletter = name.substr(0, 1);
3329 StringUtils::ToUpper(firstletter);
3330 name.replace(0, 1, firstletter);
3331 StringUtils::Replace(folderThumb1, strFileName, name + ext);
3332 if (CFile::Exists(folderThumb1)) // Folder.JPG
3333 return folderThumb1;
3335 folderThumb1 = folderThumb;
3336 StringUtils::ToLower(ext);
3337 StringUtils::Replace(folderThumb1, strFileName, name + ext);
3338 if (CFile::Exists(folderThumb1)) // Folder.jpg
3339 return folderThumb1;
3343 // No thumb found
3344 return "";
3347 // Gets the .tbn filename from a file or folder name.
3348 // <filename>.ext -> <filename>.tbn
3349 // <foldername>/ -> <foldername>.tbn
3350 std::string CFileItem::GetTBNFile() const
3352 std::string thumbFile;
3353 std::string strFile = m_strPath;
3355 if (IsStack())
3357 std::string strPath, strReturn;
3358 URIUtils::GetParentPath(m_strPath,strPath);
3359 CFileItem item(CStackDirectory::GetFirstStackedFile(strFile),false);
3360 std::string strTBNFile = item.GetTBNFile();
3361 strReturn = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strTBNFile));
3362 if (CFile::Exists(strReturn))
3363 return strReturn;
3365 strFile = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(strFile)));
3368 if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3370 std::string strPath = URIUtils::GetDirectory(strFile);
3371 std::string strParent;
3372 URIUtils::GetParentPath(strPath,strParent);
3373 strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(m_strPath));
3376 CURL url(strFile);
3377 strFile = url.GetFileName();
3379 if (m_bIsFolder && !IsFileFolder())
3380 URIUtils::RemoveSlashAtEnd(strFile);
3382 if (!strFile.empty())
3384 if (m_bIsFolder && !IsFileFolder())
3385 thumbFile = strFile + ".tbn"; // folder, so just add ".tbn"
3386 else
3387 thumbFile = URIUtils::ReplaceExtension(strFile, ".tbn");
3388 url.SetFileName(thumbFile);
3389 thumbFile = url.Get();
3391 return thumbFile;
3394 bool CFileItem::SkipLocalArt() const
3396 return (m_strPath.empty()
3397 || StringUtils::StartsWithNoCase(m_strPath, "newsmartplaylist://")
3398 || StringUtils::StartsWithNoCase(m_strPath, "newplaylist://")
3399 || m_bIsShareOrDrive
3400 || IsInternetStream()
3401 || URIUtils::IsUPnP(m_strPath)
3402 || (URIUtils::IsFTP(m_strPath) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3403 || IsPlugin()
3404 || IsAddonsPath()
3405 || IsLibraryFolder()
3406 || IsParentFolder()
3407 || IsLiveTV()
3408 || IsPVRRecording()
3409 || IsDVD());
3412 std::string CFileItem::GetThumbHideIfUnwatched(const CFileItem* item) const
3414 const std::shared_ptr<CSettingList> setting(std::dynamic_pointer_cast<CSettingList>(
3415 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(
3416 CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS)));
3417 if (setting && item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_type == MediaTypeEpisode &&
3418 item->GetVideoInfoTag()->GetPlayCount() == 0 &&
3419 !CSettingUtils::FindIntInList(setting,
3420 CSettings::VIDEOLIBRARY_THUMB_SHOW_UNWATCHED_EPISODE) &&
3421 item->HasArt("thumb"))
3423 std::string fanArt = item->GetArt("fanart");
3424 if (fanArt.empty())
3425 return "OverlaySpoiler.png";
3426 else
3427 return fanArt;
3430 return item->GetArt("thumb");
3433 std::string CFileItem::FindLocalArt(const std::string &artFile, bool useFolder) const
3435 if (SkipLocalArt())
3436 return "";
3438 std::string thumb;
3439 if (!m_bIsFolder)
3441 thumb = GetLocalArt(artFile, false);
3442 if (!thumb.empty() && CFile::Exists(thumb))
3443 return thumb;
3445 if ((useFolder || (m_bIsFolder && !IsFileFolder())) && !artFile.empty())
3447 std::string thumb2 = GetLocalArt(artFile, true);
3448 if (!thumb2.empty() && thumb2 != thumb && CFile::Exists(thumb2))
3449 return thumb2;
3451 return "";
3454 std::string CFileItem::GetLocalArtBaseFilename() const
3456 bool useFolder = false;
3457 return GetLocalArtBaseFilename(useFolder);
3460 std::string CFileItem::GetLocalArtBaseFilename(bool& useFolder) const
3462 std::string strFile;
3463 if (IsStack())
3465 std::string strPath;
3466 URIUtils::GetParentPath(m_strPath,strPath);
3467 strFile = URIUtils::AddFileToFolder(
3468 strPath, URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(m_strPath)));
3471 std::string file = strFile.empty() ? m_strPath : strFile;
3472 if (URIUtils::IsInRAR(file) || URIUtils::IsInZIP(file))
3474 std::string strPath = URIUtils::GetDirectory(file);
3475 std::string strParent;
3476 URIUtils::GetParentPath(strPath,strParent);
3477 strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(file));
3480 if (IsMultiPath())
3481 strFile = CMultiPathDirectory::GetFirstPath(m_strPath);
3483 if (IsOpticalMediaFile())
3484 { // optical media files should be treated like folders
3485 useFolder = true;
3486 strFile = GetLocalMetadataPath();
3488 else if (useFolder && !(m_bIsFolder && !IsFileFolder()))
3490 file = strFile.empty() ? m_strPath : strFile;
3491 strFile = URIUtils::GetDirectory(file);
3494 if (strFile.empty())
3495 strFile = GetDynPath();
3497 return strFile;
3500 std::string CFileItem::GetLocalArt(const std::string& artFile, bool useFolder) const
3502 // no retrieving of empty art files from folders
3503 if (useFolder && artFile.empty())
3504 return "";
3506 std::string strFile = GetLocalArtBaseFilename(useFolder);
3507 if (strFile.empty()) // empty filepath -> nothing to find
3508 return "";
3510 if (useFolder)
3512 if (!artFile.empty())
3513 return URIUtils::AddFileToFolder(strFile, artFile);
3515 else
3517 if (artFile.empty()) // old thumbnail matching
3518 return URIUtils::ReplaceExtension(strFile, ".tbn");
3519 else
3520 return URIUtils::ReplaceExtension(strFile, "-" + artFile);
3522 return "";
3525 std::string CFileItem::GetFolderThumb(const std::string &folderJPG /* = "folder.jpg" */) const
3527 std::string strFolder = m_strPath;
3529 if (IsStack() ||
3530 URIUtils::IsInRAR(strFolder) ||
3531 URIUtils::IsInZIP(strFolder))
3533 URIUtils::GetParentPath(m_strPath,strFolder);
3536 if (IsMultiPath())
3537 strFolder = CMultiPathDirectory::GetFirstPath(m_strPath);
3539 if (IsPlugin())
3540 return "";
3542 return URIUtils::AddFileToFolder(strFolder, folderJPG);
3545 std::string CFileItem::GetMovieName(bool bUseFolderNames /* = false */) const
3547 if (IsPlugin() && HasVideoInfoTag() && !GetVideoInfoTag()->m_strTitle.empty())
3548 return GetVideoInfoTag()->m_strTitle;
3550 if (IsLabelPreformatted())
3551 return GetLabel();
3553 if (m_pvrRecordingInfoTag)
3554 return m_pvrRecordingInfoTag->m_strTitle;
3555 else if (URIUtils::IsPVRRecording(m_strPath))
3557 std::string title = CPVRRecording::GetTitleFromURL(m_strPath);
3558 if (!title.empty())
3559 return title;
3562 std::string strMovieName;
3563 if (URIUtils::IsStack(m_strPath))
3564 strMovieName = CStackDirectory::GetStackedTitlePath(m_strPath);
3565 else
3566 strMovieName = GetBaseMoviePath(bUseFolderNames);
3568 URIUtils::RemoveSlashAtEnd(strMovieName);
3570 return CURL::Decode(URIUtils::GetFileName(strMovieName));
3573 std::string CFileItem::GetBaseMoviePath(bool bUseFolderNames) const
3575 std::string strMovieName = m_strPath;
3577 if (IsMultiPath())
3578 strMovieName = CMultiPathDirectory::GetFirstPath(m_strPath);
3580 if (IsOpticalMediaFile())
3581 return GetLocalMetadataPath();
3583 if (bUseFolderNames &&
3584 (!m_bIsFolder || URIUtils::IsInArchive(m_strPath) ||
3585 (HasVideoInfoTag() && GetVideoInfoTag()->m_iDbId > 0 && !CMediaTypes::IsContainer(GetVideoInfoTag()->m_type))))
3587 std::string name2(strMovieName);
3588 URIUtils::GetParentPath(name2,strMovieName);
3589 if (URIUtils::IsInArchive(m_strPath))
3591 // Try to get archive itself, if empty take path before
3592 name2 = CURL(m_strPath).GetHostName();
3593 if (name2.empty())
3594 name2 = strMovieName;
3596 URIUtils::GetParentPath(name2, strMovieName);
3599 // Remove trailing 'Disc n' path segment to get actual movie title
3600 strMovieName = CUtil::RemoveTrailingDiscNumberSegmentFromPath(strMovieName);
3603 return strMovieName;
3606 std::string CFileItem::GetLocalFanart() const
3608 if (IsVideoDb())
3610 if (!HasVideoInfoTag())
3611 return ""; // nothing can be done
3612 CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
3613 return dbItem.GetLocalFanart();
3616 std::string strFile2;
3617 std::string strFile = m_strPath;
3618 if (IsStack())
3620 std::string strPath;
3621 URIUtils::GetParentPath(m_strPath,strPath);
3622 CStackDirectory dir;
3623 std::string strPath2;
3624 strPath2 = dir.GetStackedTitlePath(strFile);
3625 strFile = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strPath2));
3626 CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
3627 std::string strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-fanart"));
3628 strFile2 = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strTBNFile));
3630 if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3632 std::string strPath = URIUtils::GetDirectory(strFile);
3633 std::string strParent;
3634 URIUtils::GetParentPath(strPath,strParent);
3635 strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(m_strPath));
3638 // no local fanart available for these
3639 if (IsInternetStream()
3640 || URIUtils::IsUPnP(strFile)
3641 || URIUtils::IsBluray(strFile)
3642 || IsLiveTV()
3643 || IsPlugin()
3644 || IsAddonsPath()
3645 || IsDVD()
3646 || (URIUtils::IsFTP(strFile) && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bFTPThumbs)
3647 || m_strPath.empty())
3648 return "";
3650 std::string strDir = URIUtils::GetDirectory(strFile);
3652 if (strDir.empty())
3653 return "";
3655 CFileItemList items;
3656 CDirectory::GetDirectory(strDir, items, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
3657 if (IsOpticalMediaFile())
3658 { // grab from the optical media parent folder as well
3659 CFileItemList moreItems;
3660 CDirectory::GetDirectory(GetLocalMetadataPath(), moreItems, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
3661 items.Append(moreItems);
3664 std::vector<std::string> fanarts = { "fanart" };
3666 strFile = URIUtils::ReplaceExtension(strFile, "-fanart");
3667 fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile));
3669 if (!strFile2.empty())
3670 fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile2));
3672 for (std::vector<std::string>::const_iterator i = fanarts.begin(); i != fanarts.end(); ++i)
3674 for (int j = 0; j < items.Size(); j++)
3676 std::string strCandidate = URIUtils::GetFileName(items[j]->m_strPath);
3677 URIUtils::RemoveExtension(strCandidate);
3678 std::string strFanart = *i;
3679 URIUtils::RemoveExtension(strFanart);
3680 if (StringUtils::EqualsNoCase(strCandidate, strFanart))
3681 return items[j]->m_strPath;
3685 return "";
3688 std::string CFileItem::GetLocalMetadataPath() const
3690 if (m_bIsFolder && !IsFileFolder())
3691 return m_strPath;
3693 std::string parent(URIUtils::GetParentPath(m_strPath));
3694 std::string parentFolder(parent);
3695 URIUtils::RemoveSlashAtEnd(parentFolder);
3696 parentFolder = URIUtils::GetFileName(parentFolder);
3697 if (StringUtils::EqualsNoCase(parentFolder, "VIDEO_TS") || StringUtils::EqualsNoCase(parentFolder, "BDMV"))
3698 { // go back up another one
3699 parent = URIUtils::GetParentPath(parent);
3701 return parent;
3704 bool CFileItem::LoadMusicTag()
3706 // not audio
3707 if (!IsAudio())
3708 return false;
3709 // already loaded?
3710 if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
3711 return true;
3712 // check db
3713 CMusicDatabase musicDatabase;
3714 if (musicDatabase.Open())
3716 CSong song;
3717 if (musicDatabase.GetSongByFileName(m_strPath, song))
3719 GetMusicInfoTag()->SetSong(song);
3720 return true;
3722 musicDatabase.Close();
3724 // load tag from file
3725 CLog::Log(LOGDEBUG, "{}: loading tag information for file: {}", __FUNCTION__, m_strPath);
3726 CMusicInfoTagLoaderFactory factory;
3727 std::unique_ptr<IMusicInfoTagLoader> pLoader (factory.CreateLoader(*this));
3728 if (pLoader)
3730 if (pLoader->Load(m_strPath, *GetMusicInfoTag()))
3731 return true;
3733 // no tag - try some other things
3734 if (IsCDDA())
3736 // we have the tracknumber...
3737 int iTrack = GetMusicInfoTag()->GetTrackNumber();
3738 if (iTrack >= 1)
3740 std::string strText = g_localizeStrings.Get(554); // "Track"
3741 if (!strText.empty() && strText[strText.size() - 1] != ' ')
3742 strText += " ";
3743 std::string strTrack = StringUtils::Format((strText + "{}"), iTrack);
3744 GetMusicInfoTag()->SetTitle(strTrack);
3745 GetMusicInfoTag()->SetLoaded(true);
3746 return true;
3749 else
3751 std::string fileName = URIUtils::GetFileName(m_strPath);
3752 URIUtils::RemoveExtension(fileName);
3753 for (const std::string& fileFilter : CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicTagsFromFileFilters)
3755 CLabelFormatter formatter(fileFilter, "");
3756 if (formatter.FillMusicTag(fileName, GetMusicInfoTag()))
3758 GetMusicInfoTag()->SetLoaded(true);
3759 return true;
3763 return false;
3766 bool CFileItem::LoadGameTag()
3768 // Already loaded?
3769 if (HasGameInfoTag() && m_gameInfoTag->IsLoaded())
3770 return true;
3772 //! @todo
3773 GetGameInfoTag();
3775 m_gameInfoTag->SetLoaded(true);
3777 return false;
3780 bool CFileItem::LoadDetails()
3782 if (IsVideoDb())
3784 if (HasVideoInfoTag())
3785 return true;
3787 CVideoDatabase db;
3788 if (!db.Open())
3790 CLog::LogF(LOGERROR, "Error opening video database");
3791 return false;
3794 VIDEODATABASEDIRECTORY::CQueryParams params;
3795 VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(GetPath(), params);
3797 bool ret{false};
3798 auto tag{std::make_unique<CVideoInfoTag>()};
3799 if (params.GetMovieId() >= 0)
3800 ret = db.GetMovieInfo(GetPath(), *tag, static_cast<int>(params.GetMovieId()),
3801 static_cast<int>(params.GetVideoVersionId()));
3802 else if (params.GetMVideoId() >= 0)
3803 ret = db.GetMusicVideoInfo(GetPath(), *tag, static_cast<int>(params.GetMVideoId()));
3804 else if (params.GetEpisodeId() >= 0)
3805 ret = db.GetEpisodeInfo(GetPath(), *tag, static_cast<int>(params.GetEpisodeId()));
3806 else if (params.GetSetId() >= 0) // movie set
3807 ret = db.GetSetInfo(static_cast<int>(params.GetSetId()), *tag, this);
3808 else if (params.GetTvShowId() >= 0)
3810 if (params.GetSeason() >= 0)
3812 const int idSeason = db.GetSeasonId(static_cast<int>(params.GetTvShowId()),
3813 static_cast<int>(params.GetSeason()));
3814 if (idSeason >= 0)
3815 ret = db.GetSeasonInfo(idSeason, *tag, this);
3817 else
3818 ret = db.GetTvShowInfo(GetPath(), *tag, static_cast<int>(params.GetTvShowId()), this);
3821 if (ret)
3823 const CFileItem loadedItem{*tag};
3824 UpdateInfo(loadedItem);
3826 return ret;
3829 if (IsPVR())
3831 const std::shared_ptr<CFileItem> loadedItem{
3832 CServiceBroker::GetPVRManager().Get<PVR::GUI::Utils>().LoadItem(*this)};
3833 if (loadedItem)
3835 UpdateInfo(*loadedItem);
3836 return true;
3838 CLog::LogF(LOGERROR, "Error filling PVR item details (path={})", GetPath());
3839 return false;
3842 if (!IsPlayList() && IsVideo())
3844 if (HasVideoInfoTag())
3845 return true;
3847 CVideoDatabase db;
3848 if (!db.Open())
3850 CLog::LogF(LOGERROR, "Error opening video database");
3851 return false;
3854 auto tag{std::make_unique<CVideoInfoTag>()};
3855 if (db.LoadVideoInfo(GetDynPath(), *tag))
3857 const CFileItem loadedItem{*tag};
3858 UpdateInfo(loadedItem);
3859 return true;
3862 CLog::LogF(LOGERROR, "Error filling item details (path={})", GetPath());
3863 return false;
3866 if (IsPlayList() && IsType(".strm"))
3868 const std::unique_ptr<PLAYLIST::CPlayList> playlist(PLAYLIST::CPlayListFactory::Create(*this));
3869 if (playlist)
3871 if (playlist->Load(GetPath()) && playlist->size() == 1)
3873 const auto item{(*playlist)[0]};
3874 if (item->IsVideo())
3876 CVideoDatabase db;
3877 if (!db.Open())
3879 CLog::LogF(LOGERROR, "Error opening video database");
3880 return false;
3883 CVideoInfoTag tag;
3884 if (db.LoadVideoInfo(GetDynPath(), tag))
3886 UpdateInfo(*item);
3887 *GetVideoInfoTag() = tag;
3888 return true;
3891 else if (item->IsAudio())
3893 if (item->LoadMusicTag())
3895 UpdateInfo(*item);
3896 return true;
3901 CLog::LogF(LOGERROR, "Error loading strm file details (path={})", GetPath());
3902 return false;
3905 if (IsAudio())
3907 return LoadMusicTag();
3910 if (IsMusicDb())
3912 if (HasMusicInfoTag())
3913 return true;
3915 CMusicDatabase db;
3916 if (!db.Open())
3918 CLog::LogF(LOGERROR, "Error opening music database");
3919 return false;
3922 MUSICDATABASEDIRECTORY::CQueryParams params;
3923 MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(GetPath(), params);
3925 if (params.GetSongId() >= 0)
3927 CSong song;
3928 if (db.GetSong(params.GetSongId(), song))
3930 GetMusicInfoTag()->SetSong(song);
3931 return true;
3934 else if (params.GetAlbumId() >= 0)
3936 m_bIsFolder = true;
3937 CAlbum album;
3938 if (db.GetAlbum(params.GetAlbumId(), album, false))
3940 GetMusicInfoTag()->SetAlbum(album);
3941 return true;
3944 else if (params.GetArtistId() >= 0)
3946 m_bIsFolder = true;
3947 CArtist artist;
3948 if (db.GetArtist(params.GetArtistId(), artist, false))
3950 GetMusicInfoTag()->SetArtist(artist);
3951 return true;
3954 return false;
3957 //! @todo add support for other types on demand.
3958 CLog::LogF(LOGDEBUG, "Unsupported item type (path={})", GetPath());
3959 return false;
3962 void CFileItemList::Swap(unsigned int item1, unsigned int item2)
3964 if (item1 != item2 && item1 < m_items.size() && item2 < m_items.size())
3965 std::swap(m_items[item1], m_items[item2]);
3968 bool CFileItemList::UpdateItem(const CFileItem *item)
3970 if (!item)
3971 return false;
3973 std::unique_lock<CCriticalSection> lock(m_lock);
3974 for (unsigned int i = 0; i < m_items.size(); i++)
3976 CFileItemPtr pItem = m_items[i];
3977 if (pItem->IsSamePath(item))
3979 pItem->UpdateInfo(*item);
3980 return true;
3983 return false;
3986 void CFileItemList::AddSortMethod(SortBy sortBy, int buttonLabel, const LABEL_MASKS &labelMasks, SortAttribute sortAttributes /* = SortAttributeNone */)
3988 AddSortMethod(sortBy, sortAttributes, buttonLabel, labelMasks);
3991 void CFileItemList::AddSortMethod(SortBy sortBy, SortAttribute sortAttributes, int buttonLabel, const LABEL_MASKS &labelMasks)
3993 SortDescription sorting;
3994 sorting.sortBy = sortBy;
3995 sorting.sortAttributes = sortAttributes;
3997 AddSortMethod(sorting, buttonLabel, labelMasks);
4000 void CFileItemList::AddSortMethod(SortDescription sortDescription, int buttonLabel, const LABEL_MASKS &labelMasks)
4002 GUIViewSortDetails sort;
4003 sort.m_sortDescription = sortDescription;
4004 sort.m_buttonLabel = buttonLabel;
4005 sort.m_labelMasks = labelMasks;
4007 m_sortDetails.push_back(sort);
4010 void CFileItemList::SetReplaceListing(bool replace)
4012 m_replaceListing = replace;
4015 void CFileItemList::ClearSortState()
4017 m_sortDescription.sortBy = SortByNone;
4018 m_sortDescription.sortOrder = SortOrderNone;
4019 m_sortDescription.sortAttributes = SortAttributeNone;
4022 bool CFileItem::HasVideoInfoTag() const
4024 // Note: CPVRRecording is derived from CVideoInfoTag
4025 return m_pvrRecordingInfoTag.get() != nullptr || m_videoInfoTag != nullptr;
4028 CVideoInfoTag* CFileItem::GetVideoInfoTag()
4030 // Note: CPVRRecording is derived from CVideoInfoTag
4031 if (m_pvrRecordingInfoTag)
4032 return m_pvrRecordingInfoTag.get();
4033 else if (!m_videoInfoTag)
4034 m_videoInfoTag = new CVideoInfoTag;
4036 return m_videoInfoTag;
4039 const CVideoInfoTag* CFileItem::GetVideoInfoTag() const
4041 // Note: CPVRRecording is derived from CVideoInfoTag
4042 return m_pvrRecordingInfoTag ? m_pvrRecordingInfoTag.get() : m_videoInfoTag;
4045 CPictureInfoTag* CFileItem::GetPictureInfoTag()
4047 if (!m_pictureInfoTag)
4048 m_pictureInfoTag = new CPictureInfoTag;
4050 return m_pictureInfoTag;
4053 MUSIC_INFO::CMusicInfoTag* CFileItem::GetMusicInfoTag()
4055 if (!m_musicInfoTag)
4056 m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag;
4058 return m_musicInfoTag;
4061 CGameInfoTag* CFileItem::GetGameInfoTag()
4063 if (!m_gameInfoTag)
4064 m_gameInfoTag = new CGameInfoTag;
4066 return m_gameInfoTag;
4069 bool CFileItem::HasPVRChannelInfoTag() const
4071 return m_pvrChannelGroupMemberInfoTag && m_pvrChannelGroupMemberInfoTag->Channel() != nullptr;
4074 const std::shared_ptr<PVR::CPVRChannel> CFileItem::GetPVRChannelInfoTag() const
4076 return m_pvrChannelGroupMemberInfoTag ? m_pvrChannelGroupMemberInfoTag->Channel()
4077 : std::shared_ptr<CPVRChannel>();
4080 std::string CFileItem::FindTrailer() const
4082 std::string strFile2;
4083 std::string strFile = m_strPath;
4084 if (IsStack())
4086 std::string strPath;
4087 URIUtils::GetParentPath(m_strPath,strPath);
4088 CStackDirectory dir;
4089 std::string strPath2;
4090 strPath2 = dir.GetStackedTitlePath(strFile);
4091 strFile = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2));
4092 CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
4093 std::string strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-trailer"));
4094 strFile2 = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile));
4096 if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
4098 std::string strPath = URIUtils::GetDirectory(strFile);
4099 std::string strParent;
4100 URIUtils::GetParentPath(strPath,strParent);
4101 strFile = URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath));
4104 // no local trailer available for these
4105 if (IsInternetStream()
4106 || URIUtils::IsUPnP(strFile)
4107 || URIUtils::IsBluray(strFile)
4108 || IsLiveTV()
4109 || IsPlugin()
4110 || IsDVD())
4111 return "";
4113 std::string strDir = URIUtils::GetDirectory(strFile);
4114 CFileItemList items;
4115 CDirectory::GetDirectory(strDir, items, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO | DIR_FLAG_NO_FILE_DIRS);
4116 URIUtils::RemoveExtension(strFile);
4117 strFile += "-trailer";
4118 std::string strFile3 = URIUtils::AddFileToFolder(strDir, "movie-trailer");
4120 // Precompile our REs
4121 VECCREGEXP matchRegExps;
4122 CRegExp tmpRegExp(true, CRegExp::autoUtf8);
4123 const std::vector<std::string>& strMatchRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_trailerMatchRegExps;
4125 std::vector<std::string>::const_iterator strRegExp = strMatchRegExps.begin();
4126 while (strRegExp != strMatchRegExps.end())
4128 if (tmpRegExp.RegComp(*strRegExp))
4130 matchRegExps.push_back(tmpRegExp);
4132 ++strRegExp;
4135 std::string strTrailer;
4136 for (int i = 0; i < items.Size(); i++)
4138 std::string strCandidate = items[i]->m_strPath;
4139 URIUtils::RemoveExtension(strCandidate);
4140 if (StringUtils::EqualsNoCase(strCandidate, strFile) ||
4141 StringUtils::EqualsNoCase(strCandidate, strFile2) ||
4142 StringUtils::EqualsNoCase(strCandidate, strFile3))
4144 strTrailer = items[i]->m_strPath;
4145 break;
4147 else
4149 VECCREGEXP::iterator expr = matchRegExps.begin();
4151 while (expr != matchRegExps.end())
4153 if (expr->RegFind(strCandidate) != -1)
4155 strTrailer = items[i]->m_strPath;
4156 i = items.Size();
4157 break;
4159 ++expr;
4164 return strTrailer;
4167 VideoDbContentType CFileItem::GetVideoContentType() const
4169 VideoDbContentType type = VideoDbContentType::MOVIES;
4170 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeTvShow)
4171 type = VideoDbContentType::TVSHOWS;
4172 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeEpisode)
4173 return VideoDbContentType::EPISODES;
4174 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeMusicVideo)
4175 return VideoDbContentType::MUSICVIDEOS;
4176 if (HasVideoInfoTag() && GetVideoInfoTag()->m_type == MediaTypeAlbum)
4177 return VideoDbContentType::MUSICALBUMS;
4179 CVideoDatabaseDirectory dir;
4180 VIDEODATABASEDIRECTORY::CQueryParams params;
4181 dir.GetQueryParams(m_strPath, params);
4182 if (params.GetSetId() != -1 && params.GetMovieId() == -1) // movie set
4183 return VideoDbContentType::MOVIE_SETS;
4185 return type;
4188 CFileItem CFileItem::GetItemToPlay() const
4190 if (HasEPGInfoTag())
4192 const std::shared_ptr<CPVRChannelGroupMember> groupMember =
4193 CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().GetChannelGroupMember(*this);
4194 if (groupMember)
4195 return CFileItem(groupMember);
4197 return *this;
4200 CBookmark CFileItem::GetResumePoint() const
4202 if (HasVideoInfoTag())
4203 return GetVideoInfoTag()->GetResumePoint();
4204 return CBookmark();
4207 bool CFileItem::IsResumePointSet() const
4209 return GetResumePoint().IsSet();
4212 double CFileItem::GetCurrentResumeTime() const
4214 return lrint(GetResumePoint().timeInSeconds);
4217 bool CFileItem::GetCurrentResumeTimeAndPartNumber(int64_t& startOffset, int& partNumber) const
4219 CBookmark resumePoint(GetResumePoint());
4220 if (resumePoint.IsSet())
4222 startOffset = llrint(resumePoint.timeInSeconds);
4223 partNumber = resumePoint.partNumber;
4224 return true;
4226 return false;
4229 bool CFileItem::IsResumable() const
4231 if (m_bIsFolder)
4233 int64_t watched = 0;
4234 int64_t inprogress = 0;
4235 int64_t total = 0;
4236 if (HasProperty("inprogressepisodes"))
4238 // show/season
4239 watched = GetProperty("watchedepisodes").asInteger();
4240 inprogress = GetProperty("inprogressepisodes").asInteger();
4241 total = GetProperty("totalepisodes").asInteger();
4243 else if (HasProperty("inprogress"))
4245 // movie set
4246 watched = GetProperty("watched").asInteger();
4247 inprogress = GetProperty("inprogress").asInteger();
4248 total = GetProperty("total").asInteger();
4251 return ((total != watched) && (inprogress > 0 || watched != 0));
4253 else
4255 return HasVideoInfoTag() && GetVideoInfoTag()->GetResumePoint().IsPartWay();
4259 bool CFileItem::HasVideoVersions() const
4261 if (HasVideoInfoTag())
4263 return GetVideoInfoTag()->HasVideoVersions();
4265 return false;
4268 bool CFileItem::HasVideoExtras() const
4270 if (HasVideoInfoTag())
4272 return GetVideoInfoTag()->HasVideoExtras();
4274 return false;