2 * Copyright (C) 2005-2020 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "FileItemList.h"
11 #include "CueDocument.h"
12 #include "ServiceBroker.h"
14 #include "filesystem/Directory.h"
15 #include "filesystem/File.h"
16 #include "filesystem/MusicDatabaseDirectory.h"
17 #include "filesystem/StackDirectory.h"
18 #include "filesystem/VideoDatabaseDirectory.h"
19 #include "music/MusicFileItemClassify.h"
20 #include "network/NetworkFileItemClassify.h"
21 #include "playlists/PlayListFileItemClassify.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "utils/Archive.h"
26 #include "utils/ArtUtils.h"
27 #include "utils/Crc32.h"
28 #include "utils/FileExtensionProvider.h"
29 #include "utils/Random.h"
30 #include "utils/RegExp.h"
31 #include "utils/URIUtils.h"
32 #include "utils/log.h"
33 #include "video/VideoFileItemClassify.h"
34 #include "video/VideoUtils.h"
39 using namespace XFILE
;
41 CFileItemList::CFileItemList() : CFileItem("", true)
45 CFileItemList::CFileItemList(const std::string
& strPath
) : CFileItem(strPath
, true)
49 CFileItemList::~CFileItemList()
54 CFileItemPtr
CFileItemList::operator[](int iItem
)
59 const CFileItemPtr
CFileItemList::operator[](int iItem
) const
64 CFileItemPtr
CFileItemList::operator[](const std::string
& strPath
)
69 const CFileItemPtr
CFileItemList::operator[](const std::string
& strPath
) const
74 void CFileItemList::SetIgnoreURLOptions(bool ignoreURLOptions
)
76 m_ignoreURLOptions
= ignoreURLOptions
;
80 m_fastLookup
= false; // Force SetFastlookup to clear map
81 SetFastLookup(true); // and regenerate map
85 void CFileItemList::SetFastLookup(bool fastLookup
)
87 std::unique_lock
<CCriticalSection
> lock(m_lock
);
89 if (fastLookup
&& !m_fastLookup
)
92 for (unsigned int i
= 0; i
< m_items
.size(); i
++)
94 CFileItemPtr pItem
= m_items
[i
];
95 m_map
.insert(MAPFILEITEMSPAIR(m_ignoreURLOptions
? CURL(pItem
->GetPath()).GetWithoutOptions()
100 if (!fastLookup
&& m_fastLookup
)
102 m_fastLookup
= fastLookup
;
105 bool CFileItemList::Contains(const std::string
& fileName
) const
107 std::unique_lock
<CCriticalSection
> lock(m_lock
);
110 return m_map
.find(m_ignoreURLOptions
? CURL(fileName
).GetWithoutOptions() : fileName
) !=
114 for (unsigned int i
= 0; i
< m_items
.size(); i
++)
116 const CFileItemPtr pItem
= m_items
[i
];
117 if (pItem
->IsPath(m_ignoreURLOptions
? CURL(fileName
).GetWithoutOptions() : fileName
))
123 void CFileItemList::Clear()
125 std::unique_lock
<CCriticalSection
> lock(m_lock
);
128 m_sortDescription
.sortBy
= SortByNone
;
129 m_sortDescription
.sortOrder
= SortOrderNone
;
130 m_sortDescription
.sortAttributes
= SortAttributeNone
;
131 m_sortIgnoreFolders
= false;
132 m_cacheToDisc
= CacheType::IF_SLOW
;
133 m_sortDetails
.clear();
134 m_replaceListing
= false;
138 void CFileItemList::ClearItems()
140 std::unique_lock
<CCriticalSection
> lock(m_lock
);
141 // make sure we free the memory of the items (these are GUIControls which may have allocated resources)
143 for (unsigned int i
= 0; i
< m_items
.size(); i
++)
145 CFileItemPtr item
= m_items
[i
];
152 void CFileItemList::Add(CFileItemPtr pItem
)
154 std::unique_lock
<CCriticalSection
> lock(m_lock
);
156 m_map
.insert(MAPFILEITEMSPAIR(
157 m_ignoreURLOptions
? CURL(pItem
->GetPath()).GetWithoutOptions() : pItem
->GetPath(), pItem
));
158 m_items
.emplace_back(std::move(pItem
));
161 void CFileItemList::Add(CFileItem
&& item
)
163 std::unique_lock
<CCriticalSection
> lock(m_lock
);
164 auto ptr
= std::make_shared
<CFileItem
>(std::move(item
));
166 m_map
.insert(MAPFILEITEMSPAIR(
167 m_ignoreURLOptions
? CURL(ptr
->GetPath()).GetWithoutOptions() : ptr
->GetPath(), ptr
));
168 m_items
.emplace_back(std::move(ptr
));
171 void CFileItemList::AddFront(const CFileItemPtr
& pItem
, int itemPosition
)
173 std::unique_lock
<CCriticalSection
> lock(m_lock
);
175 if (itemPosition
>= 0)
177 m_items
.insert(m_items
.begin() + itemPosition
, pItem
);
181 m_items
.insert(m_items
.begin() + (m_items
.size() + itemPosition
), pItem
);
185 m_map
.insert(MAPFILEITEMSPAIR(
186 m_ignoreURLOptions
? CURL(pItem
->GetPath()).GetWithoutOptions() : pItem
->GetPath(), pItem
));
190 void CFileItemList::Remove(CFileItem
* pItem
)
192 std::unique_lock
<CCriticalSection
> lock(m_lock
);
194 for (IVECFILEITEMS it
= m_items
.begin(); it
!= m_items
.end(); ++it
)
196 if (pItem
== it
->get())
201 m_map
.erase(m_ignoreURLOptions
? CURL(pItem
->GetPath()).GetWithoutOptions()
209 VECFILEITEMS::iterator
CFileItemList::erase(VECFILEITEMS::iterator first
,
210 VECFILEITEMS::iterator last
)
212 std::unique_lock
<CCriticalSection
> lock(m_lock
);
213 return m_items
.erase(first
, last
);
216 void CFileItemList::Remove(int iItem
)
218 std::unique_lock
<CCriticalSection
> lock(m_lock
);
220 if (iItem
>= 0 && iItem
< Size())
222 CFileItemPtr pItem
= *(m_items
.begin() + iItem
);
225 m_map
.erase(m_ignoreURLOptions
? CURL(pItem
->GetPath()).GetWithoutOptions()
228 m_items
.erase(m_items
.begin() + iItem
);
232 void CFileItemList::Append(const CFileItemList
& itemlist
)
234 std::unique_lock
<CCriticalSection
> lock(m_lock
);
236 for (int i
= 0; i
< itemlist
.Size(); ++i
)
240 void CFileItemList::Assign(const CFileItemList
& itemlist
, bool append
)
242 std::unique_lock
<CCriticalSection
> lock(m_lock
);
246 SetPath(itemlist
.GetPath());
247 SetLabel(itemlist
.GetLabel());
248 m_sortDetails
= itemlist
.m_sortDetails
;
249 m_sortDescription
= itemlist
.m_sortDescription
;
250 m_replaceListing
= itemlist
.m_replaceListing
;
251 m_content
= itemlist
.m_content
;
252 m_mapProperties
= itemlist
.m_mapProperties
;
253 m_cacheToDisc
= itemlist
.m_cacheToDisc
;
256 bool CFileItemList::Copy(const CFileItemList
& items
, bool copyItems
/* = true */)
258 // assign all CFileItem parts
259 *static_cast<CFileItem
*>(this) = static_cast<const CFileItem
&>(items
);
261 // assign the rest of the CFileItemList properties
262 m_replaceListing
= items
.m_replaceListing
;
263 m_content
= items
.m_content
;
264 m_mapProperties
= items
.m_mapProperties
;
265 m_cacheToDisc
= items
.m_cacheToDisc
;
266 m_sortDetails
= items
.m_sortDetails
;
267 m_sortDescription
= items
.m_sortDescription
;
268 m_sortIgnoreFolders
= items
.m_sortIgnoreFolders
;
272 // make a copy of each item
273 for (int i
= 0; i
< items
.Size(); i
++)
275 CFileItemPtr
newItem(new CFileItem(*items
[i
]));
283 CFileItemPtr
CFileItemList::Get(int iItem
) const
285 std::unique_lock
<CCriticalSection
> lock(m_lock
);
287 if (iItem
> -1 && iItem
< (int)m_items
.size())
288 return m_items
[iItem
];
290 return CFileItemPtr();
293 CFileItemPtr
CFileItemList::Get(const std::string
& strPath
) const
295 std::unique_lock
<CCriticalSection
> lock(m_lock
);
299 MAPFILEITEMS::const_iterator it
=
300 m_map
.find(m_ignoreURLOptions
? CURL(strPath
).GetWithoutOptions() : strPath
);
301 if (it
!= m_map
.end())
304 return CFileItemPtr();
307 for (unsigned int i
= 0; i
< m_items
.size(); i
++)
309 CFileItemPtr pItem
= m_items
[i
];
310 if (pItem
->IsPath(m_ignoreURLOptions
? CURL(strPath
).GetWithoutOptions() : strPath
))
314 return CFileItemPtr();
317 int CFileItemList::Size() const
319 std::unique_lock
<CCriticalSection
> lock(m_lock
);
320 return (int)m_items
.size();
323 bool CFileItemList::IsEmpty() const
325 std::unique_lock
<CCriticalSection
> lock(m_lock
);
326 return m_items
.empty();
329 void CFileItemList::Reserve(size_t iCount
)
331 std::unique_lock
<CCriticalSection
> lock(m_lock
);
332 m_items
.reserve(iCount
);
335 void CFileItemList::Sort(FILEITEMLISTCOMPARISONFUNC func
)
337 std::unique_lock
<CCriticalSection
> lock(m_lock
);
338 std::stable_sort(m_items
.begin(), m_items
.end(), func
);
341 void CFileItemList::FillSortFields(FILEITEMFILLFUNC func
)
343 std::unique_lock
<CCriticalSection
> lock(m_lock
);
344 std::for_each(m_items
.begin(), m_items
.end(), func
);
347 void CFileItemList::Sort(SortBy sortBy
,
349 SortAttribute sortAttributes
/* = SortAttributeNone */)
351 if (sortBy
== SortByNone
||
352 (m_sortDescription
.sortBy
== sortBy
&& m_sortDescription
.sortOrder
== sortOrder
&&
353 m_sortDescription
.sortAttributes
== sortAttributes
))
356 SortDescription sorting
;
357 sorting
.sortBy
= sortBy
;
358 sorting
.sortOrder
= sortOrder
;
359 sorting
.sortAttributes
= sortAttributes
;
362 m_sortDescription
= sorting
;
365 void CFileItemList::Sort(SortDescription sortDescription
)
367 if (sortDescription
.sortBy
== SortByFile
|| sortDescription
.sortBy
== SortBySortTitle
||
368 sortDescription
.sortBy
== SortByOriginalTitle
|| sortDescription
.sortBy
== SortByDateAdded
||
369 sortDescription
.sortBy
== SortByRating
|| sortDescription
.sortBy
== SortByYear
||
370 sortDescription
.sortBy
== SortByPlaylistOrder
|| sortDescription
.sortBy
== SortByLastPlayed
||
371 sortDescription
.sortBy
== SortByPlaycount
)
372 sortDescription
.sortAttributes
=
373 (SortAttribute
)((int)sortDescription
.sortAttributes
| SortAttributeIgnoreFolders
);
375 if (sortDescription
.sortBy
== SortByNone
||
376 (m_sortDescription
.sortBy
== sortDescription
.sortBy
&&
377 m_sortDescription
.sortOrder
== sortDescription
.sortOrder
&&
378 m_sortDescription
.sortAttributes
== sortDescription
.sortAttributes
))
381 if (m_sortIgnoreFolders
)
382 sortDescription
.sortAttributes
=
383 (SortAttribute
)((int)sortDescription
.sortAttributes
| SortAttributeIgnoreFolders
);
385 const Fields fields
= SortUtils::GetFieldsForSorting(sortDescription
.sortBy
);
386 SortItems
sortItems((size_t)Size());
387 for (int index
= 0; index
< Size(); index
++)
389 sortItems
[index
] = std::make_shared
<SortItem
>();
390 m_items
[index
]->ToSortable(*sortItems
[index
], fields
);
391 (*sortItems
[index
])[FieldId
] = index
;
395 SortUtils::Sort(sortDescription
, sortItems
);
397 // apply the new order to the existing CFileItems
398 VECFILEITEMS sortedFileItems
;
399 sortedFileItems
.reserve(Size());
400 for (SortItems::const_iterator it
= sortItems
.begin(); it
!= sortItems
.end(); ++it
)
402 CFileItemPtr item
= m_items
[(int)(*it
)->at(FieldId
).asInteger()];
403 // Set the sort label in the CFileItem
404 item
->SetSortLabel((*it
)->at(FieldSort
).asWideString());
406 sortedFileItems
.push_back(item
);
409 // replace the current list with the re-ordered one
410 m_items
= std::move(sortedFileItems
);
413 void CFileItemList::Randomize()
415 std::unique_lock
<CCriticalSection
> lock(m_lock
);
416 KODI::UTILS::RandomShuffle(m_items
.begin(), m_items
.end());
419 void CFileItemList::Archive(CArchive
& ar
)
421 std::unique_lock
<CCriticalSection
> lock(m_lock
);
424 CFileItem::Archive(ar
);
427 if (!m_items
.empty() && m_items
[0]->IsParentFolder())
430 ar
<< (int)(m_items
.size() - i
);
432 ar
<< m_ignoreURLOptions
;
436 ar
<< (int)m_sortDescription
.sortBy
;
437 ar
<< (int)m_sortDescription
.sortOrder
;
438 ar
<< (int)m_sortDescription
.sortAttributes
;
439 ar
<< m_sortIgnoreFolders
;
440 ar
<< (int)m_cacheToDisc
;
442 ar
<< (int)m_sortDetails
.size();
443 for (unsigned int j
= 0; j
< m_sortDetails
.size(); ++j
)
445 const GUIViewSortDetails
& details
= m_sortDetails
[j
];
446 ar
<< (int)details
.m_sortDescription
.sortBy
;
447 ar
<< (int)details
.m_sortDescription
.sortOrder
;
448 ar
<< (int)details
.m_sortDescription
.sortAttributes
;
449 ar
<< details
.m_buttonLabel
;
450 ar
<< details
.m_labelMasks
.m_strLabelFile
;
451 ar
<< details
.m_labelMasks
.m_strLabelFolder
;
452 ar
<< details
.m_labelMasks
.m_strLabel2File
;
453 ar
<< details
.m_labelMasks
.m_strLabel2Folder
;
458 for (; i
< (int)m_items
.size(); ++i
)
460 CFileItemPtr pItem
= m_items
[i
];
466 CFileItemPtr pParent
;
469 CFileItemPtr pItem
= m_items
[0];
470 if (pItem
->IsParentFolder())
471 pParent
= std::make_shared
<CFileItem
>(*pItem
);
474 SetIgnoreURLOptions(false);
475 SetFastLookup(false);
478 CFileItem::Archive(ar
);
487 m_items
.reserve(iSize
+ 1);
488 m_items
.push_back(pParent
);
491 m_items
.reserve(iSize
);
493 bool ignoreURLOptions
= false;
494 ar
>> ignoreURLOptions
;
496 bool fastLookup
= false;
501 m_sortDescription
.sortBy
= (SortBy
)tempint
;
503 m_sortDescription
.sortOrder
= (SortOrder
)tempint
;
505 m_sortDescription
.sortAttributes
= (SortAttribute
)tempint
;
506 ar
>> m_sortIgnoreFolders
;
508 m_cacheToDisc
= CacheType(tempint
);
510 unsigned int detailSize
= 0;
512 for (unsigned int j
= 0; j
< detailSize
; ++j
)
514 GUIViewSortDetails details
;
516 details
.m_sortDescription
.sortBy
= (SortBy
)tempint
;
518 details
.m_sortDescription
.sortOrder
= (SortOrder
)tempint
;
520 details
.m_sortDescription
.sortAttributes
= (SortAttribute
)tempint
;
521 ar
>> details
.m_buttonLabel
;
522 ar
>> details
.m_labelMasks
.m_strLabelFile
;
523 ar
>> details
.m_labelMasks
.m_strLabelFolder
;
524 ar
>> details
.m_labelMasks
.m_strLabel2File
;
525 ar
>> details
.m_labelMasks
.m_strLabel2Folder
;
526 m_sortDetails
.push_back(details
);
531 for (int i
= 0; i
< iSize
; ++i
)
533 CFileItemPtr
pItem(new CFileItem
);
538 SetIgnoreURLOptions(ignoreURLOptions
);
539 SetFastLookup(fastLookup
);
543 void CFileItemList::FillInDefaultIcons()
545 std::unique_lock
<CCriticalSection
> lock(m_lock
);
546 for (int i
= 0; i
< (int)m_items
.size(); ++i
)
548 CFileItemPtr pItem
= m_items
[i
];
549 ART::FillInDefaultIcon(*pItem
);
553 int CFileItemList::GetFolderCount() const
555 std::unique_lock
<CCriticalSection
> lock(m_lock
);
556 int nFolderCount
= 0;
557 for (int i
= 0; i
< (int)m_items
.size(); i
++)
559 CFileItemPtr pItem
= m_items
[i
];
560 if (pItem
->m_bIsFolder
)
567 int CFileItemList::GetObjectCount() const
569 std::unique_lock
<CCriticalSection
> lock(m_lock
);
571 int numObjects
= (int)m_items
.size();
572 if (numObjects
&& m_items
[0]->IsParentFolder())
578 int CFileItemList::GetFileCount() const
580 std::unique_lock
<CCriticalSection
> lock(m_lock
);
582 for (int i
= 0; i
< (int)m_items
.size(); i
++)
584 CFileItemPtr pItem
= m_items
[i
];
585 if (!pItem
->m_bIsFolder
)
592 int CFileItemList::GetSelectedCount() const
594 std::unique_lock
<CCriticalSection
> lock(m_lock
);
596 for (int i
= 0; i
< (int)m_items
.size(); i
++)
598 CFileItemPtr pItem
= m_items
[i
];
599 if (pItem
->IsSelected())
606 void CFileItemList::FilterCueItems()
608 std::unique_lock
<CCriticalSection
> lock(m_lock
);
609 // Handle .CUE sheet files...
610 std::vector
<std::string
> itemstodelete
;
611 for (int i
= 0; i
< (int)m_items
.size(); i
++)
613 CFileItemPtr pItem
= m_items
[i
];
614 if (!pItem
->m_bIsFolder
)
615 { // see if it's a .CUE sheet
616 if (MUSIC::IsCUESheet(*pItem
))
618 CCueDocumentPtr
cuesheet(new CCueDocument
);
619 if (cuesheet
->ParseFile(pItem
->GetPath()))
621 std::vector
<std::string
> MediaFileVec
;
622 cuesheet
->GetMediaFiles(MediaFileVec
);
624 // queue the cue sheet and the underlying media file for deletion
625 for (std::vector
<std::string
>::iterator itMedia
= MediaFileVec
.begin();
626 itMedia
!= MediaFileVec
.end(); ++itMedia
)
628 std::string strMediaFile
= *itMedia
;
629 std::string fileFromCue
=
630 strMediaFile
; // save the file from the cue we're matching against,
631 // as we're going to search for others here...
632 bool bFoundMediaFile
= CFile::Exists(strMediaFile
);
633 if (!bFoundMediaFile
)
635 // try file in same dir, not matching case...
636 if (Contains(strMediaFile
))
638 bFoundMediaFile
= true;
642 // try removing the .cue extension...
643 strMediaFile
= pItem
->GetPath();
644 URIUtils::RemoveExtension(strMediaFile
);
645 CFileItem
item(strMediaFile
, false);
646 if (MUSIC::IsAudio(item
) && Contains(strMediaFile
))
648 bFoundMediaFile
= true;
651 { // try replacing the extension with one of our allowed ones.
652 std::vector
<std::string
> extensions
= StringUtils::Split(
653 CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
654 for (std::vector
<std::string
>::const_iterator i
= extensions
.begin();
655 i
!= extensions
.end(); ++i
)
657 strMediaFile
= URIUtils::ReplaceExtension(pItem
->GetPath(), *i
);
658 CFileItem
item(strMediaFile
, false);
659 if (!MUSIC::IsCUESheet(item
) && !PLAYLIST::IsPlayList(item
) &&
660 Contains(strMediaFile
))
662 bFoundMediaFile
= true;
671 cuesheet
->UpdateMediaFile(fileFromCue
, strMediaFile
);
672 // apply CUE for later processing
673 for (int j
= 0; j
< (int)m_items
.size(); j
++)
675 CFileItemPtr pItem
= m_items
[j
];
676 if (StringUtils::CompareNoCase(pItem
->GetPath(), strMediaFile
) == 0)
677 pItem
->SetCueDocument(cuesheet
);
682 itemstodelete
.push_back(pItem
->GetPath());
686 // now delete the .CUE files.
687 for (int i
= 0; i
< (int)itemstodelete
.size(); i
++)
689 for (int j
= 0; j
< (int)m_items
.size(); j
++)
691 CFileItemPtr pItem
= m_items
[j
];
692 if (StringUtils::CompareNoCase(pItem
->GetPath(), itemstodelete
[i
]) == 0)
693 { // delete this item
694 m_items
.erase(m_items
.begin() + j
);
701 // Remove the extensions from the filenames
702 void CFileItemList::RemoveExtensions()
704 std::unique_lock
<CCriticalSection
> lock(m_lock
);
705 for (int i
= 0; i
< Size(); ++i
)
706 m_items
[i
]->RemoveExtension();
709 void CFileItemList::Stack(bool stackFiles
/* = true */)
711 std::unique_lock
<CCriticalSection
> lock(m_lock
);
714 if (IsVirtualDirectoryRoot() || IsLiveTV() || IsSourcesPath() || IsLibraryFolder())
717 SetProperty("isstacked", true);
719 // items needs to be sorted for stuff below to work properly
720 Sort(SortByLabel
, SortOrderAscending
);
728 void CFileItemList::StackFolders()
730 // Precompile our REs
731 VECCREGEXP folderRegExps
;
732 CRegExp
folderRegExp(true, CRegExp::autoUtf8
);
733 const std::vector
<std::string
>& strFolderRegExps
=
734 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_folderStackRegExps
;
736 std::vector
<std::string
>::const_iterator strExpression
= strFolderRegExps
.begin();
737 while (strExpression
!= strFolderRegExps
.end())
739 if (!folderRegExp
.RegComp(*strExpression
))
740 CLog::Log(LOGERROR
, "{}: Invalid folder stack RegExp:'{}'", __FUNCTION__
,
741 strExpression
->c_str());
743 folderRegExps
.push_back(folderRegExp
);
748 if (!folderRegExp
.IsCompiled())
750 CLog::Log(LOGDEBUG
, "{}: No stack expressions available. Skipping folder stacking",
756 for (int i
= 0; i
< Size(); i
++)
758 CFileItemPtr item
= Get(i
);
759 // combined the folder checks
760 if (item
->m_bIsFolder
)
762 // only check known fast sources?
764 // 1. rars and zips may be on slow sources? is this supposed to be allowed?
765 if (!NETWORK::IsRemote(*item
) || item
->IsSmb() || item
->IsNfs() ||
766 URIUtils::IsInRAR(item
->GetPath()) || URIUtils::IsInZIP(item
->GetPath()) ||
767 URIUtils::IsOnLAN(item
->GetPath()))
769 // stack cd# folders if contains only a single video file
773 VECCREGEXP::iterator expr
= folderRegExps
.begin();
774 while (!bMatch
&& expr
!= folderRegExps
.end())
776 //CLog::Log(LOGDEBUG,"{}: Running expression {} on {}", __FUNCTION__, expr->GetPattern(), item->GetLabel());
777 bMatch
= (expr
->RegFind(item
->GetLabel().c_str()) != -1);
781 CDirectory::GetDirectory(
782 item
->GetPath(), items
,
783 CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), DIR_FLAG_DEFAULTS
);
784 // optimized to only traverse listing once by checking for filecount
785 // and recording last file item for later use
788 for (int j
= 0; j
< items
.Size(); j
++)
790 if (!items
[j
]->m_bIsFolder
)
801 *item
= *items
[index
];
806 // check for dvd folders
809 std::string dvdPath
= VIDEO::UTILS::GetOpticalMediaPath(*item
);
811 if (!dvdPath
.empty())
813 // NOTE: should this be done for the CD# folders too?
814 item
->m_bIsFolder
= false;
815 item
->SetPath(dvdPath
);
817 item
->SetLabelPreformatted(true);
818 m_sortDescription
.sortBy
= SortByNone
; /* sorting is now broken */
826 void CFileItemList::StackFiles()
828 // Precompile our REs
829 VECCREGEXP stackRegExps
;
830 CRegExp
tmpRegExp(true, CRegExp::autoUtf8
);
831 const std::vector
<std::string
>& strStackRegExps
=
832 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoStackRegExps
;
833 std::vector
<std::string
>::const_iterator strRegExp
= strStackRegExps
.begin();
834 while (strRegExp
!= strStackRegExps
.end())
836 if (tmpRegExp
.RegComp(*strRegExp
))
838 if (tmpRegExp
.GetCaptureTotal() == 4)
839 stackRegExps
.push_back(tmpRegExp
);
841 CLog::Log(LOGERROR
, "Invalid video stack RE ({}). Must have 4 captures.", *strRegExp
);
846 // now stack the files, some of which may be from the previous stack iteration
850 CFileItemPtr item1
= Get(i
);
852 // skip folders, nfo files, playlists
853 if (item1
->m_bIsFolder
|| item1
->IsParentFolder() || item1
->IsNFO() ||
854 PLAYLIST::IsPlayList(*item1
))
863 std::string stackName
;
865 std::string filePath
;
866 std::vector
<int> stack
;
867 VECCREGEXP::iterator expr
= stackRegExps
.begin();
869 URIUtils::Split(item1
->GetPath(), filePath
, file1
);
870 if (URIUtils::HasEncodedFilename(CURL(filePath
)))
871 file1
= CURL::Decode(file1
);
874 while (expr
!= stackRegExps
.end())
876 if (expr
->RegFind(file1
, offset
) != -1)
878 std::string Title1
= expr
->GetMatch(1), Volume1
= expr
->GetMatch(2),
879 Ignore1
= expr
->GetMatch(3), Extension1
= expr
->GetMatch(4);
881 Title1
= file1
.substr(0, expr
->GetSubStart(2));
885 CFileItemPtr item2
= Get(j
);
887 // skip folders, nfo files, playlists
888 if (item2
->m_bIsFolder
|| item2
->IsParentFolder() || item2
->IsNFO() ||
889 PLAYLIST::IsPlayList(*item2
))
896 std::string file2
, filePath2
;
897 URIUtils::Split(item2
->GetPath(), filePath2
, file2
);
898 if (URIUtils::HasEncodedFilename(CURL(filePath2
)))
899 file2
= CURL::Decode(file2
);
901 if (expr
->RegFind(file2
, offset
) != -1)
903 std::string Title2
= expr
->GetMatch(1), Volume2
= expr
->GetMatch(2),
904 Ignore2
= expr
->GetMatch(3), Extension2
= expr
->GetMatch(4);
906 Title2
= file2
.substr(0, expr
->GetSubStart(2));
907 if (StringUtils::EqualsNoCase(Title1
, Title2
))
909 if (!StringUtils::EqualsNoCase(Volume1
, Volume2
))
911 if (StringUtils::EqualsNoCase(Ignore1
, Ignore2
) &&
912 StringUtils::EqualsNoCase(Extension1
, Extension2
))
916 stackName
= Title1
+ Ignore1
+ Extension1
;
918 size
+= item1
->m_dwSize
;
921 size
+= item2
->m_dwSize
;
930 else if (!StringUtils::EqualsNoCase(Ignore1
,
931 Ignore2
)) // False positive, try again with offset
933 offset
= expr
->GetSubStart(3);
936 else // Extension mismatch
943 else // Title mismatch
950 else // No match 2, next expression
959 expr
= stackRegExps
.end();
966 if (stack
.size() > 1)
968 // have a stack, remove the items and add the stacked item
969 // dont actually stack a multipart rar set, just remove all items but the first
970 std::string stackPath
;
971 if (Get(stack
[0])->IsRAR())
972 stackPath
= Get(stack
[0])->GetPath();
976 stackPath
= dir
.ConstructStackPath(*this, stack
);
978 item1
->SetPath(stackPath
);
980 for (unsigned k
= 1; k
< stack
.size(); k
++)
982 // item->m_bIsFolder = true; // don't treat stacked files as folders
983 // the label may be in a different char set from the filename (eg over smb
984 // the label is converted from utf8, but the filename is not)
985 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
986 CSettings::SETTING_FILELISTS_SHOWEXTENSIONS
))
987 URIUtils::RemoveExtension(stackName
);
989 item1
->SetLabel(stackName
);
990 item1
->m_dwSize
= size
;
998 bool CFileItemList::Load(int windowID
)
1001 auto path
= GetDiscFileCache(windowID
);
1004 if (file
.Open(path
))
1006 CArchive
ar(&file
, CArchive::load
);
1008 CLog::Log(LOGDEBUG
, "Loading items: {}, directory: {} sort method: {}, ascending: {}", Size(),
1009 CURL::GetRedacted(GetPath()), m_sortDescription
.sortBy
,
1010 m_sortDescription
.sortOrder
== SortOrderAscending
? "true" : "false");
1016 catch (const std::out_of_range
&)
1018 CLog::Log(LOGERROR
, "Corrupt archive: {}", CURL::GetRedacted(path
));
1024 bool CFileItemList::Save(int windowID
)
1030 CLog::Log(LOGDEBUG
, "Saving fileitems [{}]", CURL::GetRedacted(GetPath()));
1033 std::string cachefile
= GetDiscFileCache(windowID
);
1034 if (file
.OpenForWrite(cachefile
, true)) // overwrite always
1036 // Before caching save simplified cache file name in every item so the cache file can be
1037 // identifed and removed if the item is updated. List path and options (used for file
1038 // name when list cached) can not be accurately derived from item path.
1039 StringUtils::Replace(cachefile
, "special://temp/archive_cache/", "");
1040 StringUtils::Replace(cachefile
, ".fi", "");
1041 for (const auto& item
: m_items
)
1042 item
->SetProperty("cachefilename", cachefile
);
1044 CArchive
ar(&file
, CArchive::store
);
1046 CLog::Log(LOGDEBUG
, " -- items: {}, sort method: {}, ascending: {}", iSize
,
1047 m_sortDescription
.sortBy
,
1048 m_sortDescription
.sortOrder
== SortOrderAscending
? "true" : "false");
1057 void CFileItemList::RemoveDiscCache(int windowID
) const
1059 RemoveDiscCache(GetDiscFileCache(windowID
));
1062 void CFileItemList::RemoveDiscCache(const std::string
& cacheFile
) const
1064 if (CFile::Exists(cacheFile
))
1066 CLog::Log(LOGDEBUG
, "Clearing cached fileitems [{}]", CURL::GetRedacted(GetPath()));
1067 CFile::Delete(cacheFile
);
1071 void CFileItemList::RemoveDiscCacheCRC(const std::string
& crc
) const
1073 std::string cachefile
= StringUtils::Format("special://temp/archive_cache/{}.fi", crc
);
1074 RemoveDiscCache(cachefile
);
1077 std::string
CFileItemList::GetDiscFileCache(int windowID
) const
1079 std::string
strPath(GetPath());
1080 URIUtils::RemoveSlashAtEnd(strPath
);
1082 uint32_t crc
= Crc32::ComputeFromLowerCase(strPath
);
1084 if (MUSIC::IsCDDA(*this) || IsOnDVD())
1085 return StringUtils::Format("special://temp/archive_cache/r-{:08x}.fi", crc
);
1087 if (MUSIC::IsMusicDb(*this))
1088 return StringUtils::Format("special://temp/archive_cache/mdb-{:08x}.fi", crc
);
1090 if (VIDEO::IsVideoDb(*this))
1091 return StringUtils::Format("special://temp/archive_cache/vdb-{:08x}.fi", crc
);
1093 if (PLAYLIST::IsSmartPlayList(*this))
1094 return StringUtils::Format("special://temp/archive_cache/sp-{:08x}.fi", crc
);
1097 return StringUtils::Format("special://temp/archive_cache/{}-{:08x}.fi", windowID
, crc
);
1099 return StringUtils::Format("special://temp/archive_cache/{:08x}.fi", crc
);
1102 bool CFileItemList::AlwaysCache() const
1104 // some database folders are always cached
1105 if (MUSIC::IsMusicDb(*this))
1106 return CMusicDatabaseDirectory::CanCache(GetPath());
1107 if (VIDEO::IsVideoDb(*this))
1108 return CVideoDatabaseDirectory::CanCache(GetPath());
1110 return true; // always cache
1114 void CFileItemList::Swap(unsigned int item1
, unsigned int item2
)
1116 if (item1
!= item2
&& item1
< m_items
.size() && item2
< m_items
.size())
1117 std::swap(m_items
[item1
], m_items
[item2
]);
1120 bool CFileItemList::UpdateItem(const CFileItem
* item
)
1125 std::unique_lock
<CCriticalSection
> lock(m_lock
);
1126 for (unsigned int i
= 0; i
< m_items
.size(); i
++)
1128 CFileItemPtr pItem
= m_items
[i
];
1129 if (pItem
->IsSamePath(item
))
1131 pItem
->UpdateInfo(*item
);
1138 void CFileItemList::AddSortMethod(SortBy sortBy
,
1140 const LABEL_MASKS
& labelMasks
,
1141 SortAttribute sortAttributes
/* = SortAttributeNone */)
1143 AddSortMethod(sortBy
, sortAttributes
, buttonLabel
, labelMasks
);
1146 void CFileItemList::AddSortMethod(SortBy sortBy
,
1147 SortAttribute sortAttributes
,
1149 const LABEL_MASKS
& labelMasks
)
1151 SortDescription sorting
;
1152 sorting
.sortBy
= sortBy
;
1153 sorting
.sortAttributes
= sortAttributes
;
1155 AddSortMethod(sorting
, buttonLabel
, labelMasks
);
1158 void CFileItemList::AddSortMethod(const SortDescription
& sortDescription
,
1160 const LABEL_MASKS
& labelMasks
)
1162 GUIViewSortDetails sort
;
1163 sort
.m_sortDescription
= sortDescription
;
1164 sort
.m_buttonLabel
= buttonLabel
;
1165 sort
.m_labelMasks
= labelMasks
;
1167 m_sortDetails
.push_back(sort
);
1170 void CFileItemList::SetReplaceListing(bool replace
)
1172 m_replaceListing
= replace
;
1175 void CFileItemList::ClearSortState()
1177 m_sortDescription
.sortBy
= SortByNone
;
1178 m_sortDescription
.sortOrder
= SortOrderNone
;
1179 m_sortDescription
.sortAttributes
= SortAttributeNone
;