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