Merge remote branch 'mine/ext-python'
[vuplus_xbmc] / xbmc / FileItem.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "FileItem.h"
23 #include "guilib/LocalizeStrings.h"
24 #include "utils/StringUtils.h"
25 #include "utils/URIUtils.h"
26 #include "Util.h"
27 #include "pictures/Picture.h"
28 #include "playlists/PlayListFactory.h"
29 #include "utils/Crc32.h"
30 #include "filesystem/DirectoryCache.h"
31 #include "filesystem/StackDirectory.h"
32 #include "filesystem/FileCurl.h"
33 #include "filesystem/MultiPathDirectory.h"
34 #include "filesystem/MusicDatabaseDirectory.h"
35 #include "filesystem/VideoDatabaseDirectory.h"
36 #include "filesystem/FactoryDirectory.h"
37 #include "music/tags/MusicInfoTagLoaderFactory.h"
38 #include "CueDocument.h"
39 #include "video/VideoDatabase.h"
40 #include "music/MusicDatabase.h"
41 #include "SortFileItem.h"
42 #include "utils/TuxBoxUtil.h"
43 #include "video/VideoInfoTag.h"
44 #include "threads/SingleLock.h"
45 #include "music/tags/MusicInfoTag.h"
46 #include "pictures/PictureInfoTag.h"
47 #include "music/Artist.h"
48 #include "music/Album.h"
49 #include "music/Song.h"
50 #include "URL.h"
51 #include "settings/GUISettings.h"
52 #include "settings/AdvancedSettings.h"
53 #include "settings/Settings.h"
54 #include "utils/RegExp.h"
55 #include "utils/log.h"
56 #include "utils/Variant.h"
57 #include "music/karaoke/karaokelyricsfactory.h"
58
59 using namespace std;
60 using namespace XFILE;
61 using namespace PLAYLIST;
62 using namespace MUSIC_INFO;
63
64 CFileItem::CFileItem(const CSong& song)
65 {
66   m_musicInfoTag = NULL;
67   m_videoInfoTag = NULL;
68   m_pictureInfoTag = NULL;
69   Reset();
70   SetLabel(song.strTitle);
71   m_strPath = song.strFileName;
72   GetMusicInfoTag()->SetSong(song);
73   m_lStartOffset = song.iStartOffset;
74   m_lEndOffset = song.iEndOffset;
75   m_strThumbnailImage = song.strThumb;
76 }
77
78 CFileItem::CFileItem(const CStdString &path, const CAlbum& album)
79 {
80   m_musicInfoTag = NULL;
81   m_videoInfoTag = NULL;
82   m_pictureInfoTag = NULL;
83   Reset();
84   SetLabel(album.strAlbum);
85   m_strPath = path;
86   m_bIsFolder = true;
87   m_strLabel2 = album.strArtist;
88   URIUtils::AddSlashAtEnd(m_strPath);
89   GetMusicInfoTag()->SetAlbum(album);
90   if (album.thumbURL.m_url.size() > 0)
91     m_strThumbnailImage = album.thumbURL.m_url[0].m_url;
92   else
93     m_strThumbnailImage.clear();
94   m_bIsAlbum = true;
95   CMusicDatabase::SetPropertiesFromAlbum(*this,album);
96 }
97
98 CFileItem::CFileItem(const CVideoInfoTag& movie)
99 {
100   m_musicInfoTag = NULL;
101   m_videoInfoTag = NULL;
102   m_pictureInfoTag = NULL;
103   Reset();
104   SetLabel(movie.m_strTitle);
105   if (movie.m_strFileNameAndPath.IsEmpty())
106   {
107     m_strPath = movie.m_strPath;
108     URIUtils::AddSlashAtEnd(m_strPath);
109     m_bIsFolder = true;
110   }
111   else
112   {
113     m_strPath = movie.m_strFileNameAndPath;
114     m_bIsFolder = false;
115   }
116   *GetVideoInfoTag() = movie;
117   FillInDefaultIcon();
118   SetCachedVideoThumb();
119 }
120
121 CFileItem::CFileItem(const CArtist& artist)
122 {
123   m_musicInfoTag = NULL;
124   m_videoInfoTag = NULL;
125   m_pictureInfoTag = NULL;
126   Reset();
127   SetLabel(artist.strArtist);
128   m_strPath = artist.strArtist;
129   m_bIsFolder = true;
130   URIUtils::AddSlashAtEnd(m_strPath);
131   GetMusicInfoTag()->SetArtist(artist.strArtist);
132 }
133
134 CFileItem::CFileItem(const CGenre& genre)
135 {
136   m_musicInfoTag = NULL;
137   m_videoInfoTag = NULL;
138   m_pictureInfoTag = NULL;
139   Reset();
140   SetLabel(genre.strGenre);
141   m_strPath = genre.strGenre;
142   m_bIsFolder = true;
143   URIUtils::AddSlashAtEnd(m_strPath);
144   GetMusicInfoTag()->SetGenre(genre.strGenre);
145 }
146
147 CFileItem::CFileItem(const CFileItem& item): CGUIListItem()
148 {
149   m_musicInfoTag = NULL;
150   m_videoInfoTag = NULL;
151   m_pictureInfoTag = NULL;
152   *this = item;
153 }
154
155 CFileItem::CFileItem(const CGUIListItem& item)
156 {
157   m_musicInfoTag = NULL;
158   m_videoInfoTag = NULL;
159   m_pictureInfoTag = NULL;
160   Reset();
161   // not particularly pretty, but it gets around the issue of Reset() defaulting
162   // parameters in the CGUIListItem base class.
163   *((CGUIListItem *)this) = item;
164 }
165
166 CFileItem::CFileItem(void)
167 {
168   m_musicInfoTag = NULL;
169   m_videoInfoTag = NULL;
170   m_pictureInfoTag = NULL;
171   Reset();
172 }
173
174 CFileItem::CFileItem(const CStdString& strLabel)
175     : CGUIListItem()
176 {
177   m_musicInfoTag = NULL;
178   m_videoInfoTag = NULL;
179   m_pictureInfoTag = NULL;
180   Reset();
181   SetLabel(strLabel);
182 }
183
184 CFileItem::CFileItem(const CStdString& strPath, bool bIsFolder)
185 {
186   m_musicInfoTag = NULL;
187   m_videoInfoTag = NULL;
188   m_pictureInfoTag = NULL;
189   Reset();
190   m_strPath = strPath;
191   m_bIsFolder = bIsFolder;
192   // tuxbox urls cannot have a / at end
193   if (m_bIsFolder && !m_strPath.IsEmpty() && !IsFileFolder() && !URIUtils::IsTuxBox(m_strPath))
194     URIUtils::AddSlashAtEnd(m_strPath);
195 }
196
197 CFileItem::CFileItem(const CMediaSource& share)
198 {
199   m_musicInfoTag = NULL;
200   m_videoInfoTag = NULL;
201   m_pictureInfoTag = NULL;
202   Reset();
203   m_bIsFolder = true;
204   m_bIsShareOrDrive = true;
205   m_strPath = share.strPath;
206   URIUtils::AddSlashAtEnd(m_strPath);
207   CStdString label = share.strName;
208   if (!share.strStatus.IsEmpty())
209     label.Format("%s (%s)", share.strName.c_str(), share.strStatus.c_str());
210   SetLabel(label);
211   m_iLockMode = share.m_iLockMode;
212   m_strLockCode = share.m_strLockCode;
213   m_iHasLock = share.m_iHasLock;
214   m_iBadPwdCount = share.m_iBadPwdCount;
215   m_iDriveType = share.m_iDriveType;
216   m_strThumbnailImage = share.m_strThumbnailImage;
217   SetLabelPreformated(true);
218 }
219
220 CFileItem::~CFileItem(void)
221 {
222   delete m_musicInfoTag;
223   delete m_videoInfoTag;
224   delete m_pictureInfoTag;
225
226   m_musicInfoTag = NULL;
227   m_videoInfoTag = NULL;
228   m_pictureInfoTag = NULL;
229 }
230
231 const CFileItem& CFileItem::operator=(const CFileItem& item)
232 {
233   if (this == &item) return * this;
234   CGUIListItem::operator=(item);
235   m_bLabelPreformated=item.m_bLabelPreformated;
236   FreeMemory();
237   m_strPath = item.m_strPath;
238   m_bIsParentFolder = item.m_bIsParentFolder;
239   m_iDriveType = item.m_iDriveType;
240   m_bIsShareOrDrive = item.m_bIsShareOrDrive;
241   m_dateTime = item.m_dateTime;
242   m_dwSize = item.m_dwSize;
243   if (item.HasMusicInfoTag())
244   {
245     m_musicInfoTag = GetMusicInfoTag();
246     if (m_musicInfoTag)
247       *m_musicInfoTag = *item.m_musicInfoTag;
248   }
249   else
250   {
251     delete m_musicInfoTag;
252     m_musicInfoTag = NULL;
253   }
254
255   if (item.HasVideoInfoTag())
256   {
257     m_videoInfoTag = GetVideoInfoTag();
258     if (m_videoInfoTag)
259       *m_videoInfoTag = *item.m_videoInfoTag;
260   }
261   else
262   {
263     delete m_videoInfoTag;
264     m_videoInfoTag = NULL;
265   }
266
267   if (item.HasPictureInfoTag())
268   {
269     m_pictureInfoTag = GetPictureInfoTag();
270     if (m_pictureInfoTag)
271       *m_pictureInfoTag = *item.m_pictureInfoTag;
272   }
273   else
274   {
275     delete m_pictureInfoTag;
276     m_pictureInfoTag = NULL;
277   }
278
279   m_lStartOffset = item.m_lStartOffset;
280   m_lEndOffset = item.m_lEndOffset;
281   m_strDVDLabel = item.m_strDVDLabel;
282   m_strTitle = item.m_strTitle;
283   m_iprogramCount = item.m_iprogramCount;
284   m_idepth = item.m_idepth;
285   m_iLockMode = item.m_iLockMode;
286   m_strLockCode = item.m_strLockCode;
287   m_iHasLock = item.m_iHasLock;
288   m_iBadPwdCount = item.m_iBadPwdCount;
289   m_bCanQueue=item.m_bCanQueue;
290   m_mimetype = item.m_mimetype;
291   m_extrainfo = item.m_extrainfo;
292   m_specialSort = item.m_specialSort;
293   return *this;
294 }
295
296 void CFileItem::Reset()
297 {
298   m_strLabel2.Empty();
299   SetLabel("");
300   m_bLabelPreformated=false;
301   FreeIcons();
302   m_overlayIcon = ICON_OVERLAY_NONE;
303   m_bSelected = false;
304   m_bIsAlbum = false;
305   m_strDVDLabel.Empty();
306   m_strTitle.Empty();
307   m_strPath.Empty();
308   m_dwSize = 0;
309   m_bIsFolder = false;
310   m_bIsParentFolder=false;
311   m_bIsShareOrDrive = false;
312   m_dateTime.Reset();
313   m_iDriveType = CMediaSource::SOURCE_TYPE_UNKNOWN;
314   m_lStartOffset = 0;
315   m_lEndOffset = 0;
316   m_iprogramCount = 0;
317   m_idepth = 1;
318   m_iLockMode = LOCK_MODE_EVERYONE;
319   m_strLockCode = "";
320   m_iBadPwdCount = 0;
321   m_iHasLock = 0;
322   m_bCanQueue=true;
323   m_mimetype = "";
324   delete m_musicInfoTag;
325   m_musicInfoTag=NULL;
326   delete m_videoInfoTag;
327   m_videoInfoTag=NULL;
328   delete m_pictureInfoTag;
329   m_pictureInfoTag=NULL;
330   m_extrainfo.Empty();
331   m_specialSort = SORT_NORMALLY;
332   SetInvalid();
333 }
334
335 void CFileItem::Archive(CArchive& ar)
336 {
337   CGUIListItem::Archive(ar);
338
339   if (ar.IsStoring())
340   {
341     ar << m_bIsParentFolder;
342     ar << m_bLabelPreformated;
343     ar << m_strPath;
344     ar << m_bIsShareOrDrive;
345     ar << m_iDriveType;
346     ar << m_dateTime;
347     ar << m_dwSize;
348     ar << m_strDVDLabel;
349     ar << m_strTitle;
350     ar << m_iprogramCount;
351     ar << m_idepth;
352     ar << m_lStartOffset;
353     ar << m_lEndOffset;
354     ar << m_iLockMode;
355     ar << m_strLockCode;
356     ar << m_iBadPwdCount;
357
358     ar << m_bCanQueue;
359     ar << m_mimetype;
360     ar << m_extrainfo;
361     ar << m_specialSort;
362
363     if (m_musicInfoTag)
364     {
365       ar << 1;
366       ar << *m_musicInfoTag;
367     }
368     else
369       ar << 0;
370     if (m_videoInfoTag)
371     {
372       ar << 1;
373       ar << *m_videoInfoTag;
374     }
375     else
376       ar << 0;
377     if (m_pictureInfoTag)
378     {
379       ar << 1;
380       ar << *m_pictureInfoTag;
381     }
382     else
383       ar << 0;
384   }
385   else
386   {
387     ar >> m_bIsParentFolder;
388     ar >> m_bLabelPreformated;
389     ar >> m_strPath;
390     ar >> m_bIsShareOrDrive;
391     ar >> m_iDriveType;
392     ar >> m_dateTime;
393     ar >> m_dwSize;
394     ar >> m_strDVDLabel;
395     ar >> m_strTitle;
396     ar >> m_iprogramCount;
397     ar >> m_idepth;
398     ar >> m_lStartOffset;
399     ar >> m_lEndOffset;
400     int temp;
401     ar >> temp;
402     m_iLockMode = (LockType)temp;
403     ar >> m_strLockCode;
404     ar >> m_iBadPwdCount;
405
406     ar >> m_bCanQueue;
407     ar >> m_mimetype;
408     ar >> m_extrainfo;
409     ar >> temp;
410     m_specialSort = (SPECIAL_SORT)temp;
411
412     int iType;
413     ar >> iType;
414     if (iType == 1)
415       ar >> *GetMusicInfoTag();
416     ar >> iType;
417     if (iType == 1)
418       ar >> *GetVideoInfoTag();
419     ar >> iType;
420     if (iType == 1)
421       ar >> *GetPictureInfoTag();
422
423     SetInvalid();
424   }
425 }
426 void CFileItem::Serialize(CVariant& value)
427 {
428   //CGUIListItem::Serialize(value["CGUIListItem"]);
429
430   value["strPath"] = m_strPath;
431   value["dateTime"] = (m_dateTime.IsValid()) ? m_dateTime.GetAsRFC1123DateTime() : "";
432   value["size"] = (int) m_dwSize / 1000;
433   value["DVDLabel"] = m_strDVDLabel;
434   value["title"] = m_strTitle;
435   value["mimetype"] = m_mimetype;
436   value["extrainfo"] = m_extrainfo;
437
438   if (m_musicInfoTag)
439     (*m_musicInfoTag).Serialize(value["musicInfoTag"]);
440
441   if (m_videoInfoTag)
442     (*m_videoInfoTag).Serialize(value["videoInfoTag"]);
443
444   if (m_pictureInfoTag)
445     (*m_pictureInfoTag).Serialize(value["pictureInfoTag"]);
446 }
447 bool CFileItem::Exists(bool bUseCache /* = true */) const
448 {
449   if (m_strPath.IsEmpty()
450    || m_strPath.Equals("add")
451    || IsInternetStream()
452    || IsParentFolder()
453    || IsVirtualDirectoryRoot()
454    || IsPlugin())
455     return true;
456
457   if (IsVideoDb() && HasVideoInfoTag())
458   {
459     CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
460     return dbItem.Exists();
461   }
462
463   CStdString strPath = m_strPath;
464
465   if (URIUtils::IsMultiPath(strPath))
466     strPath = CMultiPathDirectory::GetFirstPath(strPath);
467
468   if (URIUtils::IsStack(strPath))
469     strPath = CStackDirectory::GetFirstStackedFile(strPath);
470
471   if (m_bIsFolder)
472     return CDirectory::Exists(strPath);
473   else
474     return CFile::Exists(strPath, bUseCache);
475
476   return false;
477 }
478
479 bool CFileItem::IsVideo() const
480 {
481   /* check preset mime type */
482   if( m_mimetype.Left(6).Equals("video/") )
483     return true;
484
485   if (HasVideoInfoTag()) return true;
486   if (HasMusicInfoTag()) return false;
487   if (HasPictureInfoTag()) return false;
488
489   if (IsHDHomeRun() || IsTuxBox() || URIUtils::IsDVD(m_strPath))
490     return true;
491
492   CStdString extension;
493   if( m_mimetype.Left(12).Equals("application/") )
494   { /* check for some standard types */
495     extension = m_mimetype.Mid(12);
496     if( extension.Equals("ogg")
497      || extension.Equals("mp4")
498      || extension.Equals("mxf") )
499      return true;
500   }
501
502   URIUtils::GetExtension(m_strPath, extension);
503
504   if (extension.IsEmpty())
505     return false;
506
507   extension.ToLower();
508
509   return (g_settings.m_videoExtensions.Find(extension) != -1);
510 }
511
512 bool CFileItem::IsDiscStub() const
513 {
514   CStdString strExtension;
515   URIUtils::GetExtension(m_strPath, strExtension);
516
517   if (strExtension.IsEmpty())
518     return false;
519
520   strExtension.ToLower();
521   strExtension += '|';
522
523   return (g_settings.m_discStubExtensions + '|').Find(strExtension) != -1;
524 }
525
526 bool CFileItem::IsAudio() const
527 {
528   /* check preset mime type */
529   if( m_mimetype.Left(6).Equals("audio/") )
530     return true;
531
532   if (HasMusicInfoTag()) return true;
533   if (HasVideoInfoTag()) return false;
534   if (HasPictureInfoTag()) return false;
535   if (IsCDDA()) return true;
536   if (!m_bIsFolder && IsLastFM()) return true;
537
538   CStdString extension;
539   if( m_mimetype.Left(12).Equals("application/") )
540   { /* check for some standard types */
541     extension = m_mimetype.Mid(12);
542     if( extension.Equals("ogg")
543      || extension.Equals("mp4")
544      || extension.Equals("mxf") )
545      return true;
546   }
547
548   URIUtils::GetExtension(m_strPath, extension);
549
550   if (extension.IsEmpty())
551     return false;
552
553   extension.ToLower();
554
555   return (g_settings.m_musicExtensions.Find(extension) != -1);
556 }
557
558 bool CFileItem::IsKaraoke() const
559 {
560   if ( !IsAudio() || IsLastFM())
561     return false;
562
563   return CKaraokeLyricsFactory::HasLyrics( m_strPath );
564 }
565
566 bool CFileItem::IsPicture() const
567 {
568   if( m_mimetype.Left(6).Equals("image/") )
569     return true;
570
571   if (HasPictureInfoTag()) return true;
572   if (HasMusicInfoTag()) return false;
573   if (HasVideoInfoTag()) return false;
574
575   return CUtil::IsPicture(m_strPath);
576 }
577
578 bool CFileItem::IsLyrics() const
579 {
580   return URIUtils::GetExtension(m_strPath).Equals(".cdg", false) || URIUtils::GetExtension(m_strPath).Equals(".lrc", false);
581 }
582
583 bool CFileItem::IsCUESheet() const
584 {
585   return URIUtils::GetExtension(m_strPath).Equals(".cue", false);
586 }
587
588 bool CFileItem::IsLastFM() const
589 {
590   return URIUtils::IsLastFM(m_strPath);
591 }
592
593 bool CFileItem::IsInternetStream() const
594 {
595   if (HasProperty("IsHTTPDirectory"))
596     return false;
597
598   return URIUtils::IsInternetStream(m_strPath);
599 }
600
601 bool CFileItem::IsFileFolder() const
602 {
603   return (
604     IsSmartPlayList() ||
605    (IsPlayList() && g_advancedSettings.m_playlistAsFolders) ||
606     IsZIP() ||
607     IsRAR() ||
608     IsRSS() ||
609     IsType(".ogg") ||
610     IsType(".nsf") ||
611     IsType(".sid") ||
612     IsType(".sap")
613     );
614 }
615
616
617 bool CFileItem::IsSmartPlayList() const
618 {
619   CStdString strExtension;
620   URIUtils::GetExtension(m_strPath, strExtension);
621   strExtension.ToLower();
622   return (strExtension == ".xsp");
623 }
624
625 bool CFileItem::IsPlayList() const
626 {
627   return CPlayListFactory::IsPlaylist(*this);
628 }
629
630 bool CFileItem::IsPythonScript() const
631 {
632   return URIUtils::GetExtension(m_strPath).Equals(".py", false);
633 }
634
635 bool CFileItem::IsXBE() const
636 {
637   return URIUtils::GetExtension(m_strPath).Equals(".xbe", false);
638 }
639
640 bool CFileItem::IsType(const char *ext) const
641 {
642   return URIUtils::GetExtension(m_strPath).Equals(ext, false);
643 }
644
645 bool CFileItem::IsShortCut() const
646 {
647   return URIUtils::GetExtension(m_strPath).Equals(".cut", false);
648 }
649
650 bool CFileItem::IsNFO() const
651 {
652   return URIUtils::GetExtension(m_strPath).Equals(".nfo", false);
653 }
654
655 bool CFileItem::IsDVDImage() const
656 {
657   CStdString strExtension;
658   URIUtils::GetExtension(m_strPath, strExtension);
659   return (strExtension.Equals(".img") || strExtension.Equals(".iso") || strExtension.Equals(".nrg"));
660 }
661
662 bool CFileItem::IsOpticalMediaFile() const
663 {
664   bool found = IsDVDFile(false, true);
665   if (found)
666     return true;
667   return IsBDFile();
668 }
669
670 bool CFileItem::IsDVDFile(bool bVobs /*= true*/, bool bIfos /*= true*/) const
671 {
672   CStdString strFileName = URIUtils::GetFileName(m_strPath);
673   if (bIfos)
674   {
675     if (strFileName.Equals("video_ts.ifo")) return true;
676     if (strFileName.Left(4).Equals("vts_") && strFileName.Right(6).Equals("_0.ifo") && strFileName.length() == 12) return true;
677   }
678   if (bVobs)
679   {
680     if (strFileName.Equals("video_ts.vob")) return true;
681     if (strFileName.Left(4).Equals("vts_") && strFileName.Right(4).Equals(".vob")) return true;
682   }
683
684   return false;
685 }
686
687 bool CFileItem::IsBDFile() const
688 {
689   CStdString strFileName = URIUtils::GetFileName(m_strPath);
690   return (strFileName.Equals("index.bdmv"));
691 }
692
693 bool CFileItem::IsRAR() const
694 {
695   return URIUtils::IsRAR(m_strPath);
696 }
697
698 bool CFileItem::IsZIP() const
699 {
700   return URIUtils::IsZIP(m_strPath);
701 }
702
703 bool CFileItem::IsCBZ() const
704 {
705   return URIUtils::GetExtension(m_strPath).Equals(".cbz", false);
706 }
707
708 bool CFileItem::IsCBR() const
709 {
710   return URIUtils::GetExtension(m_strPath).Equals(".cbr", false);
711 }
712
713 bool CFileItem::IsRSS() const
714 {
715   if (m_strPath.Left(6).Equals("rss://"))
716     return true;
717
718   return URIUtils::GetExtension(m_strPath).Equals(".rss")
719       || GetMimeType() == "application/rss+xml";
720 }
721
722 bool CFileItem::IsStack() const
723 {
724   return URIUtils::IsStack(m_strPath);
725 }
726
727 bool CFileItem::IsPlugin() const
728 {
729   return URIUtils::IsPlugin(m_strPath);
730 }
731
732 bool CFileItem::IsAddonsPath() const
733 {
734   return URIUtils::IsAddonsPath(m_strPath);
735 }
736
737 bool CFileItem::IsMultiPath() const
738 {
739   return URIUtils::IsMultiPath(m_strPath);
740 }
741
742 bool CFileItem::IsCDDA() const
743 {
744   return URIUtils::IsCDDA(m_strPath);
745 }
746
747 bool CFileItem::IsDVD() const
748 {
749   return URIUtils::IsDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
750 }
751
752 bool CFileItem::IsOnDVD() const
753 {
754   return URIUtils::IsOnDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
755 }
756
757 bool CFileItem::IsOnLAN() const
758 {
759   return URIUtils::IsOnLAN(m_strPath);
760 }
761
762 bool CFileItem::IsISO9660() const
763 {
764   return URIUtils::IsISO9660(m_strPath);
765 }
766
767 bool CFileItem::IsRemote() const
768 {
769   return URIUtils::IsRemote(m_strPath);
770 }
771
772 bool CFileItem::IsSmb() const
773 {
774   return URIUtils::IsSmb(m_strPath);
775 }
776
777 bool CFileItem::IsURL() const
778 {
779   return URIUtils::IsURL(m_strPath);
780 }
781
782 bool CFileItem::IsDAAP() const
783 {
784   return URIUtils::IsDAAP(m_strPath);
785 }
786
787 bool CFileItem::IsTuxBox() const
788 {
789   return URIUtils::IsTuxBox(m_strPath);
790 }
791
792 bool CFileItem::IsMythTV() const
793 {
794   return URIUtils::IsMythTV(m_strPath);
795 }
796
797 bool CFileItem::IsHDHomeRun() const
798 {
799   return URIUtils::IsHDHomeRun(m_strPath);
800 }
801
802 bool CFileItem::IsVTP() const
803 {
804   return URIUtils::IsVTP(m_strPath);
805 }
806
807 bool CFileItem::IsLiveTV() const
808 {
809   return URIUtils::IsLiveTV(m_strPath);
810 }
811
812 bool CFileItem::IsHD() const
813 {
814   return URIUtils::IsHD(m_strPath);
815 }
816
817 bool CFileItem::IsMusicDb() const
818 {
819   CURL url(m_strPath);
820   return url.GetProtocol().Equals("musicdb");
821 }
822
823 bool CFileItem::IsVideoDb() const
824 {
825   CURL url(m_strPath);
826   return url.GetProtocol().Equals("videodb");
827 }
828
829 bool CFileItem::IsVirtualDirectoryRoot() const
830 {
831   return (m_bIsFolder && m_strPath.IsEmpty());
832 }
833
834 bool CFileItem::IsRemovable() const
835 {
836   return IsOnDVD() || IsCDDA() || m_iDriveType == CMediaSource::SOURCE_TYPE_REMOVABLE;
837 }
838
839 bool CFileItem::IsReadOnly() const
840 {
841   if (IsParentFolder()) return true;
842   if (m_bIsShareOrDrive) return true;
843   return !CUtil::SupportsFileOperations(m_strPath);
844 }
845
846 void CFileItem::FillInDefaultIcon()
847 {
848   //CLog::Log(LOGINFO, "FillInDefaultIcon(%s)", pItem->GetLabel().c_str());
849   // find the default icon for a file or folder item
850   // for files this can be the (depending on the file type)
851   //   default picture for photo's
852   //   default picture for songs
853   //   default picture for videos
854   //   default picture for shortcuts
855   //   default picture for playlists
856   //   or the icon embedded in an .xbe
857   //
858   // for folders
859   //   for .. folders the default picture for parent folder
860   //   for other folders the defaultFolder.png
861
862   if (GetIconImage().IsEmpty())
863   {
864     if (!m_bIsFolder)
865     {
866       /* To reduce the average runtime of this code, this list should
867        * be ordered with most frequently seen types first.  Also bear
868        * in mind the complexity of the code behind the check in the
869        * case of IsWhatater() returns false.
870        */
871       if ( IsAudio() )
872       {
873         // audio
874         SetIconImage("DefaultAudio.png");
875       }
876       else if ( IsVideo() )
877       {
878         // video
879         SetIconImage("DefaultVideo.png");
880       }
881       else if ( IsPicture() )
882       {
883         // picture
884         SetIconImage("DefaultPicture.png");
885       }
886       else if ( IsPlayList() )
887       {
888         SetIconImage("DefaultPlaylist.png");
889       }
890       else if ( IsXBE() )
891       {
892         // xbe
893         SetIconImage("DefaultProgram.png");
894       }
895       else if ( IsShortCut() && !IsLabelPreformated() )
896       {
897         // shortcut
898         CStdString strFName = URIUtils::GetFileName(m_strPath);
899         int iPos = strFName.ReverseFind(".");
900         CStdString strDescription = strFName.Left(iPos);
901         SetLabel(strDescription);
902         SetIconImage("DefaultShortcut.png");
903       }
904       else if ( IsPythonScript() )
905       {
906         SetIconImage("DefaultScript.png");
907       }
908       else
909       {
910         // default icon for unknown file type
911         SetIconImage("DefaultFile.png");
912       }
913     }
914     else
915     {
916       if ( IsPlayList() )
917       {
918         SetIconImage("DefaultPlaylist.png");
919       }
920       else if (IsParentFolder())
921       {
922         SetIconImage("DefaultFolderBack.png");
923       }
924       else
925       {
926         SetIconImage("DefaultFolder.png");
927       }
928     }
929   }
930   // Set the icon overlays (if applicable)
931   if (!HasOverlay())
932   {
933     if (URIUtils::IsInRAR(m_strPath))
934       SetOverlayImage(CGUIListItem::ICON_OVERLAY_RAR);
935     else if (URIUtils::IsInZIP(m_strPath))
936       SetOverlayImage(CGUIListItem::ICON_OVERLAY_ZIP);
937   }
938 }
939
940 CStdString CFileItem::GetCachedArtistThumb() const
941 {
942   return GetCachedThumb("artist"+GetLabel(),g_settings.GetMusicArtistThumbFolder());
943 }
944
945 CStdString CFileItem::GetCachedSeasonThumb() const
946 {
947   CStdString seasonPath;
948   if (HasVideoInfoTag())
949     seasonPath = GetVideoInfoTag()->m_strPath;
950
951   return GetCachedThumb("season"+seasonPath+GetLabel(),g_settings.GetVideoThumbFolder(),true);
952 }
953
954 CStdString CFileItem::GetCachedActorThumb() const
955 {
956   return GetCachedThumb("actor"+GetLabel(),g_settings.GetVideoThumbFolder(),true);
957 }
958
959 void CFileItem::SetCachedArtistThumb()
960 {
961   CStdString thumb(GetCachedArtistThumb());
962   if (CFile::Exists(thumb))
963   {
964     // found it, we are finished.
965     SetThumbnailImage(thumb);
966   }
967 }
968
969 // set the album thumb for a file or folder
970 void CFileItem::SetMusicThumb(bool alwaysCheckRemote /* = true */)
971 {
972   if (HasThumbnail()) return;
973
974   SetCachedMusicThumb();
975   if (!HasThumbnail())
976     SetUserMusicThumb(alwaysCheckRemote);
977 }
978
979 void CFileItem::SetCachedSeasonThumb()
980 {
981   CStdString thumb(GetCachedSeasonThumb());
982   if (CFile::Exists(thumb))
983   {
984     // found it, we are finished.
985     SetThumbnailImage(thumb);
986   }
987 }
988
989 void CFileItem::RemoveExtension()
990 {
991   if (m_bIsFolder)
992     return;
993   CStdString strLabel = GetLabel();
994   URIUtils::RemoveExtension(strLabel);
995   SetLabel(strLabel);
996 }
997
998 void CFileItem::CleanString()
999 {
1000   if (IsLiveTV())
1001     return;
1002
1003   bool bIsFolder = m_bIsFolder;
1004
1005   // make sure we don't append the extension to stacked dvd folders
1006   if (HasProperty("isstacked") && IsOpticalMediaFile())
1007     bIsFolder = true;
1008
1009   CStdString strLabel = GetLabel();
1010   CStdString strTitle, strTitleAndYear, strYear;
1011   CUtil::CleanString(strLabel, strTitle, strTitleAndYear, strYear, (bIsFolder || !g_guiSettings.GetBool("filelists.showextensions") ) );
1012   SetLabel(strTitleAndYear);
1013 }
1014
1015 void CFileItem::SetLabel(const CStdString &strLabel)
1016 {
1017   if (strLabel=="..")
1018   {
1019     m_bIsParentFolder=true;
1020     m_bIsFolder=true;
1021     m_specialSort = SORT_ON_TOP;
1022     SetLabelPreformated(true);
1023   }
1024   CGUIListItem::SetLabel(strLabel);
1025 }
1026
1027 void CFileItem::SetFileSizeLabel()
1028 {
1029   if( m_bIsFolder && m_dwSize == 0 )
1030     SetLabel2("");
1031   else
1032     SetLabel2(StringUtils::SizeToString(m_dwSize));
1033 }
1034
1035 CURL CFileItem::GetAsUrl() const
1036 {
1037   return CURL(m_strPath);
1038 }
1039
1040 bool CFileItem::CanQueue() const
1041 {
1042   return m_bCanQueue;
1043 }
1044
1045 void CFileItem::SetCanQueue(bool bYesNo)
1046 {
1047   m_bCanQueue=bYesNo;
1048 }
1049
1050 bool CFileItem::IsParentFolder() const
1051 {
1052   return m_bIsParentFolder;
1053 }
1054
1055 const CStdString& CFileItem::GetMimeType(bool lookup /*= true*/) const
1056 {
1057   if( m_mimetype.IsEmpty() && lookup)
1058   {
1059     // discard const qualifyier
1060     CStdString& m_ref = (CStdString&)m_mimetype;
1061
1062     if( m_bIsFolder )
1063       m_ref = "x-directory/normal";
1064     else if( m_strPath.Left(8).Equals("shout://")
1065           || m_strPath.Left(7).Equals("http://")
1066           || m_strPath.Left(8).Equals("https://"))
1067     {
1068       CFileCurl::GetMimeType(GetAsUrl(), m_ref);
1069
1070       // try to get mime-type again but with an NSPlayer User-Agent
1071       // in order for server to provide correct mime-type.  Allows us
1072       // to properly detect an MMS stream
1073       if (m_ref.Left(11).Equals("video/x-ms-"))
1074         CFileCurl::GetMimeType(GetAsUrl(), m_ref, "NSPlayer/11.00.6001.7000");
1075
1076       // make sure there are no options set in mime-type
1077       // mime-type can look like "video/x-ms-asf ; charset=utf8"
1078       int i = m_ref.Find(';');
1079       if(i>=0)
1080         m_ref.Delete(i,m_ref.length()-i);
1081       m_ref.Trim();
1082     }
1083
1084     // if it's still empty set to an unknown type
1085     if( m_ref.IsEmpty() )
1086       m_ref = "application/octet-stream";
1087   }
1088
1089   // change protocol to mms for the following mome-type.  Allows us to create proper FileMMS.
1090   if( m_mimetype.Left(32).Equals("application/vnd.ms.wms-hdr.asfv1") || m_mimetype.Left(24).Equals("application/x-mms-framed") )
1091   {
1092     CStdString& m_path = (CStdString&)m_strPath;
1093     m_path.Replace("http:", "mms:");
1094   }
1095
1096   return m_mimetype;
1097 }
1098
1099 bool CFileItem::IsSamePath(const CFileItem *item) const
1100 {
1101   if (!item)
1102     return false;
1103
1104   if (item->m_strPath == m_strPath && item->m_lStartOffset == m_lStartOffset) return true;
1105   if (IsMusicDb() && HasMusicInfoTag())
1106   {
1107     CFileItem dbItem(m_musicInfoTag->GetURL(), false);
1108     dbItem.m_lStartOffset = m_lStartOffset;
1109     return dbItem.IsSamePath(item);
1110   }
1111   if (IsVideoDb() && HasVideoInfoTag())
1112   {
1113     CFileItem dbItem(m_videoInfoTag->m_strFileNameAndPath, false);
1114     dbItem.m_lStartOffset = m_lStartOffset;
1115     return dbItem.IsSamePath(item);
1116   }
1117   if (item->IsMusicDb() && item->HasMusicInfoTag())
1118   {
1119     CFileItem dbItem(item->m_musicInfoTag->GetURL(), false);
1120     dbItem.m_lStartOffset = item->m_lStartOffset;
1121     return IsSamePath(&dbItem);
1122   }
1123   if (item->IsVideoDb() && item->HasVideoInfoTag())
1124   {
1125     CFileItem dbItem(item->m_videoInfoTag->m_strFileNameAndPath, false);
1126     dbItem.m_lStartOffset = item->m_lStartOffset;
1127     return IsSamePath(&dbItem);
1128   }
1129   if (HasProperty("original_listitem_url"))
1130     return (GetProperty("original_listitem_url") == item->m_strPath);
1131   return false;
1132 }
1133
1134 bool CFileItem::IsAlbum() const
1135 {
1136   return m_bIsAlbum;
1137 }
1138
1139 void CFileItem::UpdateInfo(const CFileItem &item)
1140 {
1141   if (item.HasVideoInfoTag())
1142   { // copy info across (TODO: premiered info is normally stored in m_dateTime by the db)
1143     *GetVideoInfoTag() = *item.GetVideoInfoTag();
1144     SetOverlayImage(ICON_OVERLAY_UNWATCHED, GetVideoInfoTag()->m_playCount > 0);
1145   }
1146   if (item.HasMusicInfoTag())
1147     *GetMusicInfoTag() = *item.GetMusicInfoTag();
1148   if (item.HasPictureInfoTag())
1149     *GetPictureInfoTag() = *item.GetPictureInfoTag();
1150
1151   if (!item.GetLabel().IsEmpty())
1152     SetLabel(item.GetLabel());
1153   if (!item.GetLabel2().IsEmpty())
1154     SetLabel2(item.GetLabel2());
1155   if (!item.GetThumbnailImage().IsEmpty())
1156     SetThumbnailImage(item.GetThumbnailImage());
1157   if (!item.GetIconImage().IsEmpty())
1158     SetIconImage(item.GetIconImage());
1159   AppendProperties(item);
1160 }
1161
1162 /////////////////////////////////////////////////////////////////////////////////
1163 /////
1164 ///// CFileItemList
1165 /////
1166 //////////////////////////////////////////////////////////////////////////////////
1167
1168 CFileItemList::CFileItemList()
1169 {
1170   m_fastLookup = false;
1171   m_bIsFolder=true;
1172   m_cacheToDisc=CACHE_IF_SLOW;
1173   m_sortMethod=SORT_METHOD_NONE;
1174   m_sortOrder=SORT_ORDER_NONE;
1175   m_sortIgnoreFolders = false;
1176   m_replaceListing = false;
1177 }
1178
1179 CFileItemList::CFileItemList(const CStdString& strPath)
1180 {
1181   m_strPath=strPath;
1182   m_fastLookup = false;
1183   m_bIsFolder=true;
1184   m_cacheToDisc=CACHE_IF_SLOW;
1185   m_sortMethod=SORT_METHOD_NONE;
1186   m_sortOrder=SORT_ORDER_NONE;
1187   m_sortIgnoreFolders = false;
1188   m_replaceListing = false;
1189 }
1190
1191 CFileItemList::~CFileItemList()
1192 {
1193   Clear();
1194 }
1195
1196 CFileItemPtr CFileItemList::operator[] (int iItem)
1197 {
1198   return Get(iItem);
1199 }
1200
1201 const CFileItemPtr CFileItemList::operator[] (int iItem) const
1202 {
1203   return Get(iItem);
1204 }
1205
1206 CFileItemPtr CFileItemList::operator[] (const CStdString& strPath)
1207 {
1208   return Get(strPath);
1209 }
1210
1211 const CFileItemPtr CFileItemList::operator[] (const CStdString& strPath) const
1212 {
1213   return Get(strPath);
1214 }
1215
1216 void CFileItemList::SetFastLookup(bool fastLookup)
1217 {
1218   CSingleLock lock(m_lock);
1219
1220   if (fastLookup && !m_fastLookup)
1221   { // generate the map
1222     m_map.clear();
1223     for (unsigned int i=0; i < m_items.size(); i++)
1224     {
1225       CFileItemPtr pItem = m_items[i];
1226       CStdString path(pItem->m_strPath); path.ToLower();
1227       m_map.insert(MAPFILEITEMSPAIR(path, pItem));
1228     }
1229   }
1230   if (!fastLookup && m_fastLookup)
1231     m_map.clear();
1232   m_fastLookup = fastLookup;
1233 }
1234
1235 bool CFileItemList::Contains(const CStdString& fileName) const
1236 {
1237   CSingleLock lock(m_lock);
1238
1239   // checks case insensitive
1240   CStdString checkPath(fileName); checkPath.ToLower();
1241   if (m_fastLookup)
1242     return m_map.find(checkPath) != m_map.end();
1243   // slow method...
1244   for (unsigned int i = 0; i < m_items.size(); i++)
1245   {
1246     const CFileItemPtr pItem = m_items[i];
1247     if (pItem->m_strPath.Equals(checkPath))
1248       return true;
1249   }
1250   return false;
1251 }
1252
1253 void CFileItemList::Clear()
1254 {
1255   CSingleLock lock(m_lock);
1256
1257   ClearItems();
1258   m_sortMethod=SORT_METHOD_NONE;
1259   m_sortOrder=SORT_ORDER_NONE;
1260   m_sortIgnoreFolders = false;
1261   m_cacheToDisc=CACHE_IF_SLOW;
1262   m_sortDetails.clear();
1263   m_replaceListing = false;
1264   m_content.Empty();
1265 }
1266
1267 void CFileItemList::ClearItems()
1268 {
1269   CSingleLock lock(m_lock);
1270   // make sure we free the memory of the items (these are GUIControls which may have allocated resources)
1271   FreeMemory();
1272   for (unsigned int i = 0; i < m_items.size(); i++)
1273   {
1274     CFileItemPtr item = m_items[i];
1275     item->FreeMemory();
1276   }
1277   m_items.clear();
1278   m_map.clear();
1279 }
1280
1281 void CFileItemList::Add(const CFileItemPtr &pItem)
1282 {
1283   CSingleLock lock(m_lock);
1284
1285   m_items.push_back(pItem);
1286   if (m_fastLookup)
1287   {
1288     CStdString path(pItem->m_strPath); 
1289     path.ToLower();
1290     m_map.insert(MAPFILEITEMSPAIR(path, pItem));
1291   }
1292 }
1293
1294 void CFileItemList::AddFront(const CFileItemPtr &pItem, int itemPosition)
1295 {
1296   CSingleLock lock(m_lock);
1297
1298   if (itemPosition >= 0)
1299   {
1300     m_items.insert(m_items.begin()+itemPosition, pItem);
1301   }
1302   else
1303   {
1304     m_items.insert(m_items.begin()+(m_items.size()+itemPosition), pItem);
1305   }
1306   if (m_fastLookup)
1307   {
1308     CStdString path(pItem->m_strPath); path.ToLower();
1309     m_map.insert(MAPFILEITEMSPAIR(path, pItem));
1310   }
1311 }
1312
1313 void CFileItemList::Remove(CFileItem* pItem)
1314 {
1315   CSingleLock lock(m_lock);
1316
1317   for (IVECFILEITEMS it = m_items.begin(); it != m_items.end(); ++it)
1318   {
1319     if (pItem == it->get())
1320     {
1321       m_items.erase(it);
1322       if (m_fastLookup)
1323       {
1324         CStdString path(pItem->m_strPath); path.ToLower();
1325         m_map.erase(path);
1326       }
1327       break;
1328     }
1329   }
1330 }
1331
1332 void CFileItemList::Remove(int iItem)
1333 {
1334   CSingleLock lock(m_lock);
1335
1336   if (iItem >= 0 && iItem < (int)Size())
1337   {
1338     CFileItemPtr pItem = *(m_items.begin() + iItem);
1339     if (m_fastLookup)
1340     {
1341       CStdString path(pItem->m_strPath); path.ToLower();
1342       m_map.erase(path);
1343     }
1344     m_items.erase(m_items.begin() + iItem);
1345   }
1346 }
1347
1348 void CFileItemList::Append(const CFileItemList& itemlist)
1349 {
1350   CSingleLock lock(m_lock);
1351
1352   for (int i = 0; i < itemlist.Size(); ++i)
1353     Add(itemlist[i]);
1354 }
1355
1356 void CFileItemList::Assign(const CFileItemList& itemlist, bool append)
1357 {
1358   CSingleLock lock(m_lock);
1359   if (!append)
1360     Clear();
1361   Append(itemlist);
1362   m_strPath = itemlist.m_strPath;
1363   m_sortDetails = itemlist.m_sortDetails;
1364   m_replaceListing = itemlist.m_replaceListing;
1365   m_content = itemlist.m_content;
1366   m_mapProperties = itemlist.m_mapProperties;
1367   m_cacheToDisc = itemlist.m_cacheToDisc;
1368 }
1369
1370 bool CFileItemList::Copy(const CFileItemList& items)
1371 {
1372   // assign all CFileItem parts
1373   *(CFileItem*)this = *(CFileItem*)&items;
1374
1375   // assign the rest of the CFileItemList properties
1376   m_replaceListing = items.m_replaceListing;
1377   m_content        = items.m_content;
1378   m_mapProperties  = items.m_mapProperties;
1379   m_cacheToDisc    = items.m_cacheToDisc;
1380   m_sortDetails    = items.m_sortDetails;
1381   m_sortMethod     = items.m_sortMethod;
1382   m_sortOrder      = items.m_sortOrder;
1383   m_sortIgnoreFolders = items.m_sortIgnoreFolders;
1384
1385   // make a copy of each item
1386   for (int i = 0; i < items.Size(); i++)
1387   {
1388     CFileItemPtr newItem(new CFileItem(*items[i]));
1389     Add(newItem);
1390   }
1391
1392   return true;
1393 }
1394
1395 CFileItemPtr CFileItemList::Get(int iItem)
1396 {
1397   CSingleLock lock(m_lock);
1398
1399   if (iItem > -1 && iItem < (int)m_items.size())
1400     return m_items[iItem];
1401
1402   return CFileItemPtr();
1403 }
1404
1405 const CFileItemPtr CFileItemList::Get(int iItem) const
1406 {
1407   CSingleLock lock(m_lock);
1408
1409   if (iItem > -1 && iItem < (int)m_items.size())
1410     return m_items[iItem];
1411
1412   return CFileItemPtr();
1413 }
1414
1415 CFileItemPtr CFileItemList::Get(const CStdString& strPath)
1416 {
1417   CSingleLock lock(m_lock);
1418
1419   CStdString pathToCheck(strPath); pathToCheck.ToLower();
1420   if (m_fastLookup)
1421   {
1422     IMAPFILEITEMS it=m_map.find(pathToCheck);
1423     if (it != m_map.end())
1424       return it->second;
1425
1426     return CFileItemPtr();
1427   }
1428   // slow method...
1429   for (unsigned int i = 0; i < m_items.size(); i++)
1430   {
1431     CFileItemPtr pItem = m_items[i];
1432     if (pItem->m_strPath.Equals(pathToCheck))
1433       return pItem;
1434   }
1435
1436   return CFileItemPtr();
1437 }
1438
1439 const CFileItemPtr CFileItemList::Get(const CStdString& strPath) const
1440 {
1441   CSingleLock lock(m_lock);
1442
1443   CStdString pathToCheck(strPath); pathToCheck.ToLower();
1444   if (m_fastLookup)
1445   {
1446     map<CStdString, CFileItemPtr>::const_iterator it=m_map.find(pathToCheck);
1447     if (it != m_map.end())
1448       return it->second;
1449
1450     return CFileItemPtr();
1451   }
1452   // slow method...
1453   for (unsigned int i = 0; i < m_items.size(); i++)
1454   {
1455     CFileItemPtr pItem = m_items[i];
1456     if (pItem->m_strPath.Equals(pathToCheck))
1457       return pItem;
1458   }
1459
1460   return CFileItemPtr();
1461 }
1462
1463 int CFileItemList::Size() const
1464 {
1465   CSingleLock lock(m_lock);
1466   return (int)m_items.size();
1467 }
1468
1469 bool CFileItemList::IsEmpty() const
1470 {
1471   CSingleLock lock(m_lock);
1472   return (m_items.size() <= 0);
1473 }
1474
1475 void CFileItemList::Reserve(int iCount)
1476 {
1477   CSingleLock lock(m_lock);
1478   m_items.reserve(iCount);
1479 }
1480
1481 void CFileItemList::Sort(FILEITEMLISTCOMPARISONFUNC func)
1482 {
1483   CSingleLock lock(m_lock);
1484   std::stable_sort(m_items.begin(), m_items.end(), func);
1485 }
1486
1487 void CFileItemList::FillSortFields(FILEITEMFILLFUNC func)
1488 {
1489   CSingleLock lock(m_lock);
1490   std::for_each(m_items.begin(), m_items.end(), func);
1491 }
1492
1493 void CFileItemList::Sort(SORT_METHOD sortMethod, SORT_ORDER sortOrder)
1494 {
1495   //  Already sorted?
1496   if (sortMethod==m_sortMethod && m_sortOrder==sortOrder)
1497     return;
1498
1499   switch (sortMethod)
1500   {
1501   case SORT_METHOD_LABEL:
1502   case SORT_METHOD_LABEL_IGNORE_FOLDERS:
1503     FillSortFields(SSortFileItem::ByLabel);
1504     break;
1505   case SORT_METHOD_LABEL_IGNORE_THE:
1506     FillSortFields(SSortFileItem::ByLabelNoThe);
1507     break;
1508   case SORT_METHOD_DATE:
1509     FillSortFields(SSortFileItem::ByDate);
1510     break;
1511   case SORT_METHOD_SIZE:
1512     FillSortFields(SSortFileItem::BySize);
1513     break;
1514   case SORT_METHOD_BITRATE:
1515     FillSortFields(SSortFileItem::ByBitrate);
1516     break;      
1517   case SORT_METHOD_DRIVE_TYPE:
1518     FillSortFields(SSortFileItem::ByDriveType);
1519     break;
1520   case SORT_METHOD_TRACKNUM:
1521     FillSortFields(SSortFileItem::BySongTrackNum);
1522     break;
1523   case SORT_METHOD_EPISODE:
1524     FillSortFields(SSortFileItem::ByEpisodeNum);
1525     break;
1526   case SORT_METHOD_DURATION:
1527     FillSortFields(SSortFileItem::BySongDuration);
1528     break;
1529   case SORT_METHOD_TITLE_IGNORE_THE:
1530     FillSortFields(SSortFileItem::BySongTitleNoThe);
1531     break;
1532   case SORT_METHOD_TITLE:
1533     FillSortFields(SSortFileItem::BySongTitle);
1534     break;
1535   case SORT_METHOD_ARTIST:
1536     FillSortFields(SSortFileItem::BySongArtist);
1537     break;
1538   case SORT_METHOD_ARTIST_IGNORE_THE:
1539     FillSortFields(SSortFileItem::BySongArtistNoThe);
1540     break;
1541   case SORT_METHOD_ALBUM:
1542     FillSortFields(SSortFileItem::BySongAlbum);
1543     break;
1544   case SORT_METHOD_ALBUM_IGNORE_THE:
1545     FillSortFields(SSortFileItem::BySongAlbumNoThe);
1546     break;
1547   case SORT_METHOD_GENRE:
1548     FillSortFields(SSortFileItem::ByGenre);
1549     break;
1550   case SORT_METHOD_COUNTRY:
1551     FillSortFields(SSortFileItem::ByCountry);
1552     break;
1553   case SORT_METHOD_DATEADDED:
1554     FillSortFields(SSortFileItem::ByDateAdded);
1555     break;
1556   case SORT_METHOD_FILE:
1557     FillSortFields(SSortFileItem::ByFile);
1558     break;
1559   case SORT_METHOD_VIDEO_RATING:
1560     FillSortFields(SSortFileItem::ByMovieRating);
1561     break;
1562   case SORT_METHOD_VIDEO_TITLE:
1563     FillSortFields(SSortFileItem::ByMovieTitle);
1564     break;
1565   case SORT_METHOD_VIDEO_SORT_TITLE:
1566     FillSortFields(SSortFileItem::ByMovieSortTitle);
1567     break;
1568   case SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE:
1569     FillSortFields(SSortFileItem::ByMovieSortTitleNoThe);
1570     break;
1571   case SORT_METHOD_YEAR:
1572     FillSortFields(SSortFileItem::ByYear);
1573     break;
1574   case SORT_METHOD_PRODUCTIONCODE:
1575     FillSortFields(SSortFileItem::ByProductionCode);
1576     break;
1577   case SORT_METHOD_PROGRAM_COUNT:
1578   case SORT_METHOD_PLAYLIST_ORDER:
1579     // TODO: Playlist order is hacked into program count variable (not nice, but ok until 2.0)
1580     FillSortFields(SSortFileItem::ByProgramCount);
1581     break;
1582   case SORT_METHOD_SONG_RATING:
1583     FillSortFields(SSortFileItem::BySongRating);
1584     break;
1585   case SORT_METHOD_MPAA_RATING:
1586     FillSortFields(SSortFileItem::ByMPAARating);
1587     break;
1588   case SORT_METHOD_VIDEO_RUNTIME:
1589     FillSortFields(SSortFileItem::ByMovieRuntime);
1590     break;
1591   case SORT_METHOD_STUDIO:
1592     FillSortFields(SSortFileItem::ByStudio);
1593     break;
1594   case SORT_METHOD_STUDIO_IGNORE_THE:
1595     FillSortFields(SSortFileItem::ByStudioNoThe);
1596     break;
1597   case SORT_METHOD_FULLPATH:
1598     FillSortFields(SSortFileItem::ByFullPath);
1599     break;
1600   case SORT_METHOD_LASTPLAYED:
1601     FillSortFields(SSortFileItem::ByLastPlayed);
1602     break;
1603   case SORT_METHOD_PLAYCOUNT:
1604     FillSortFields(SSortFileItem::ByPlayCount);
1605     break;
1606   case SORT_METHOD_LISTENERS:
1607     FillSortFields(SSortFileItem::ByListeners);
1608     break;    
1609   default:
1610     break;
1611   }
1612   if (sortMethod == SORT_METHOD_FILE        ||
1613       sortMethod == SORT_METHOD_VIDEO_SORT_TITLE ||
1614       sortMethod == SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE ||
1615       sortMethod == SORT_METHOD_LABEL_IGNORE_FOLDERS ||
1616       m_sortIgnoreFolders)
1617     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::IgnoreFoldersAscending : SSortFileItem::IgnoreFoldersDescending);
1618   else if (sortMethod != SORT_METHOD_NONE && sortMethod != SORT_METHOD_UNSORTED)
1619     Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::Ascending : SSortFileItem::Descending);
1620
1621   m_sortMethod=sortMethod;
1622   m_sortOrder=sortOrder;
1623 }
1624
1625 void CFileItemList::Randomize()
1626 {
1627   CSingleLock lock(m_lock);
1628   random_shuffle(m_items.begin(), m_items.end());
1629 }
1630
1631 void CFileItemList::Archive(CArchive& ar)
1632 {
1633   CSingleLock lock(m_lock);
1634   if (ar.IsStoring())
1635   {
1636     CFileItem::Archive(ar);
1637
1638     int i = 0;
1639     if (m_items.size() > 0 && m_items[0]->IsParentFolder())
1640       i = 1;
1641
1642     ar << (int)(m_items.size() - i);
1643
1644     ar << m_fastLookup;
1645
1646     ar << (int)m_sortMethod;
1647     ar << (int)m_sortOrder;
1648     ar << m_sortIgnoreFolders;
1649     ar << (int)m_cacheToDisc;
1650
1651     ar << (int)m_sortDetails.size();
1652     for (unsigned int j = 0; j < m_sortDetails.size(); ++j)
1653     {
1654       const SORT_METHOD_DETAILS &details = m_sortDetails[j];
1655       ar << (int)details.m_sortMethod;
1656       ar << details.m_buttonLabel;
1657       ar << details.m_labelMasks.m_strLabelFile;
1658       ar << details.m_labelMasks.m_strLabelFolder;
1659       ar << details.m_labelMasks.m_strLabel2File;
1660       ar << details.m_labelMasks.m_strLabel2Folder;
1661     }
1662
1663     ar << m_content;
1664
1665     for (; i < (int)m_items.size(); ++i)
1666     {
1667       CFileItemPtr pItem = m_items[i];
1668       ar << *pItem;
1669     }
1670   }
1671   else
1672   {
1673     CFileItemPtr pParent;
1674     if (!IsEmpty())
1675     {
1676       CFileItemPtr pItem=m_items[0];
1677       if (pItem->IsParentFolder())
1678         pParent.reset(new CFileItem(*pItem));
1679     }
1680
1681     SetFastLookup(false);
1682     Clear();
1683
1684
1685     CFileItem::Archive(ar);
1686
1687     int iSize = 0;
1688     ar >> iSize;
1689     if (iSize <= 0)
1690       return ;
1691
1692     if (pParent)
1693     {
1694       m_items.reserve(iSize + 1);
1695       m_items.push_back(pParent);
1696     }
1697     else
1698       m_items.reserve(iSize);
1699
1700     bool fastLookup=false;
1701     ar >> fastLookup;
1702
1703     int tempint;
1704     ar >> (int&)tempint;
1705     m_sortMethod = SORT_METHOD(tempint);
1706     ar >> (int&)tempint;
1707     m_sortOrder = SORT_ORDER(tempint);
1708     ar >> m_sortIgnoreFolders;
1709     ar >> (int&)tempint;
1710     m_cacheToDisc = CACHE_TYPE(tempint);
1711
1712     unsigned int detailSize = 0;
1713     ar >> detailSize;
1714     for (unsigned int j = 0; j < detailSize; ++j)
1715     {
1716       SORT_METHOD_DETAILS details;
1717       ar >> (int&)tempint;
1718       details.m_sortMethod = SORT_METHOD(tempint);
1719       ar >> details.m_buttonLabel;
1720       ar >> details.m_labelMasks.m_strLabelFile;
1721       ar >> details.m_labelMasks.m_strLabelFolder;
1722       ar >> details.m_labelMasks.m_strLabel2File;
1723       ar >> details.m_labelMasks.m_strLabel2Folder;
1724       m_sortDetails.push_back(details);
1725     }
1726
1727     ar >> m_content;
1728
1729     for (int i = 0; i < iSize; ++i)
1730     {
1731       CFileItemPtr pItem(new CFileItem);
1732       ar >> *pItem;
1733       Add(pItem);
1734     }
1735
1736     SetFastLookup(fastLookup);
1737   }
1738 }
1739
1740 void CFileItemList::FillInDefaultIcons()
1741 {
1742   CSingleLock lock(m_lock);
1743   for (int i = 0; i < (int)m_items.size(); ++i)
1744   {
1745     CFileItemPtr pItem = m_items[i];
1746     pItem->FillInDefaultIcon();
1747   }
1748 }
1749
1750 void CFileItemList::SetMusicThumbs()
1751 {
1752   CSingleLock lock(m_lock);
1753   //cache thumbnails directory
1754   g_directoryCache.InitMusicThumbCache();
1755
1756   for (int i = 0; i < (int)m_items.size(); ++i)
1757   {
1758     CFileItemPtr pItem = m_items[i];
1759     pItem->SetMusicThumb();
1760   }
1761
1762   g_directoryCache.ClearMusicThumbCache();
1763 }
1764
1765 int CFileItemList::GetFolderCount() const
1766 {
1767   CSingleLock lock(m_lock);
1768   int nFolderCount = 0;
1769   for (int i = 0; i < (int)m_items.size(); i++)
1770   {
1771     CFileItemPtr pItem = m_items[i];
1772     if (pItem->m_bIsFolder)
1773       nFolderCount++;
1774   }
1775
1776   return nFolderCount;
1777 }
1778
1779 int CFileItemList::GetObjectCount() const
1780 {
1781   CSingleLock lock(m_lock);
1782
1783   int numObjects = (int)m_items.size();
1784   if (numObjects && m_items[0]->IsParentFolder())
1785     numObjects--;
1786
1787   return numObjects;
1788 }
1789
1790 int CFileItemList::GetFileCount() const
1791 {
1792   CSingleLock lock(m_lock);
1793   int nFileCount = 0;
1794   for (int i = 0; i < (int)m_items.size(); i++)
1795   {
1796     CFileItemPtr pItem = m_items[i];
1797     if (!pItem->m_bIsFolder)
1798       nFileCount++;
1799   }
1800
1801   return nFileCount;
1802 }
1803
1804 int CFileItemList::GetSelectedCount() const
1805 {
1806   CSingleLock lock(m_lock);
1807   int count = 0;
1808   for (int i = 0; i < (int)m_items.size(); i++)
1809   {
1810     CFileItemPtr pItem = m_items[i];
1811     if (pItem->IsSelected())
1812       count++;
1813   }
1814
1815   return count;
1816 }
1817
1818 void CFileItemList::FilterCueItems()
1819 {
1820   CSingleLock lock(m_lock);
1821   // Handle .CUE sheet files...
1822   VECSONGS itemstoadd;
1823   CStdStringArray itemstodelete;
1824   for (int i = 0; i < (int)m_items.size(); i++)
1825   {
1826     CFileItemPtr pItem = m_items[i];
1827     if (!pItem->m_bIsFolder)
1828     { // see if it's a .CUE sheet
1829       if (pItem->IsCUESheet())
1830       {
1831         CCueDocument cuesheet;
1832         if (cuesheet.Parse(pItem->m_strPath))
1833         {
1834           VECSONGS newitems;
1835           cuesheet.GetSongs(newitems);
1836
1837           std::vector<CStdString> MediaFileVec;
1838           cuesheet.GetMediaFiles(MediaFileVec);
1839
1840           // queue the cue sheet and the underlying media file for deletion
1841           for(std::vector<CStdString>::iterator itMedia = MediaFileVec.begin(); itMedia != MediaFileVec.end(); itMedia++)
1842           {
1843             CStdString strMediaFile = *itMedia;
1844             CStdString fileFromCue = strMediaFile; // save the file from the cue we're matching against,
1845                                                    // as we're going to search for others here...
1846             bool bFoundMediaFile = CFile::Exists(strMediaFile);
1847             // queue the cue sheet and the underlying media file for deletion
1848             if (!bFoundMediaFile)
1849             {
1850               // try file in same dir, not matching case...
1851               if (Contains(strMediaFile))
1852               {
1853                 bFoundMediaFile = true;
1854               }
1855               else
1856               {
1857                 // try removing the .cue extension...
1858                 strMediaFile = pItem->m_strPath;
1859                 URIUtils::RemoveExtension(strMediaFile);
1860                 CFileItem item(strMediaFile, false);
1861                 if (item.IsAudio() && Contains(strMediaFile))
1862                 {
1863                   bFoundMediaFile = true;
1864                 }
1865                 else
1866                 { // try replacing the extension with one of our allowed ones.
1867                   CStdStringArray extensions;
1868                   StringUtils::SplitString(g_settings.m_musicExtensions, "|", extensions);
1869                   for (unsigned int i = 0; i < extensions.size(); i++)
1870                   {
1871                     strMediaFile = URIUtils::ReplaceExtension(pItem->m_strPath, extensions[i]);
1872                     CFileItem item(strMediaFile, false);
1873                     if (!item.IsCUESheet() && !item.IsPlayList() && Contains(strMediaFile))
1874                     {
1875                       bFoundMediaFile = true;
1876                       break;
1877                     }
1878                   }
1879                 }
1880               }
1881             }
1882             if (bFoundMediaFile)
1883             {
1884               itemstodelete.push_back(pItem->m_strPath);
1885               itemstodelete.push_back(strMediaFile);
1886               // get the additional stuff (year, genre etc.) from the underlying media files tag.
1887               CMusicInfoTag tag;
1888               auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(strMediaFile));
1889               if (NULL != pLoader.get())
1890               {
1891                 // get id3tag
1892                 pLoader->Load(strMediaFile, tag);
1893               }
1894               // fill in any missing entries from underlying media file
1895               for (int j = 0; j < (int)newitems.size(); j++)
1896               {
1897                 CSong song = newitems[j];
1898                 // only for songs that actually match the current media file
1899                 if (song.strFileName == fileFromCue)
1900                 {
1901                   // we might have a new media file from the above matching code
1902                   song.strFileName = strMediaFile;
1903                   if (tag.Loaded())
1904                   {
1905                     if (song.strAlbum.empty() && !tag.GetAlbum().empty()) song.strAlbum = tag.GetAlbum();
1906                     if (song.strAlbumArtist.empty() && !tag.GetAlbumArtist().empty()) song.strAlbumArtist = tag.GetAlbumArtist();
1907                     if (song.strGenre.empty() && !tag.GetGenre().empty()) song.strGenre = tag.GetGenre();
1908                     if (song.strArtist.empty() && !tag.GetArtist().empty()) song.strArtist = tag.GetArtist();
1909                     if (tag.GetDiscNumber()) song.iTrack |= (tag.GetDiscNumber() << 16); // see CMusicInfoTag::GetDiscNumber()
1910                     SYSTEMTIME dateTime;
1911                     tag.GetReleaseDate(dateTime);
1912                     if (dateTime.wYear) song.iYear = dateTime.wYear;
1913                   }
1914                   if (!song.iDuration && tag.GetDuration() > 0)
1915                   { // must be the last song
1916                     song.iDuration = (tag.GetDuration() * 75 - song.iStartOffset + 37) / 75;
1917                   }
1918                   // add this item to the list
1919                   itemstoadd.push_back(song);
1920                 }
1921               }
1922             }
1923             else
1924             { // remove the .cue sheet from the directory
1925               itemstodelete.push_back(pItem->m_strPath);
1926             }
1927           }
1928         }
1929         else
1930         { // remove the .cue sheet from the directory (can't parse it - no point listing it)
1931           itemstodelete.push_back(pItem->m_strPath);
1932         }
1933       }
1934     }
1935   }
1936   // now delete the .CUE files and underlying media files.
1937   for (int i = 0; i < (int)itemstodelete.size(); i++)
1938   {
1939     for (int j = 0; j < (int)m_items.size(); j++)
1940     {
1941       CFileItemPtr pItem = m_items[j];
1942       if (stricmp(pItem->m_strPath.c_str(), itemstodelete[i].c_str()) == 0)
1943       { // delete this item
1944         m_items.erase(m_items.begin() + j);
1945         break;
1946       }
1947     }
1948   }
1949   // and add the files from the .CUE sheet
1950   for (int i = 0; i < (int)itemstoadd.size(); i++)
1951   {
1952     // now create the file item, and add to the item list.
1953     CFileItemPtr pItem(new CFileItem(itemstoadd[i]));
1954     m_items.push_back(pItem);
1955   }
1956 }
1957
1958 // Remove the extensions from the filenames
1959 void CFileItemList::RemoveExtensions()
1960 {
1961   CSingleLock lock(m_lock);
1962   for (int i = 0; i < Size(); ++i)
1963     m_items[i]->RemoveExtension();
1964 }
1965
1966 void CFileItemList::Stack()
1967 {
1968   CSingleLock lock(m_lock);
1969
1970   // not allowed here
1971   if (IsVirtualDirectoryRoot() || IsLiveTV() || m_strPath.Left(10).Equals("sources://"))
1972     return;
1973
1974   SetProperty("isstacked", "1");
1975
1976   // items needs to be sorted for stuff below to work properly
1977   Sort(SORT_METHOD_LABEL, SORT_ORDER_ASC);
1978
1979   // stack folders
1980   int i = 0;
1981   for (i = 0; i < Size(); ++i)
1982   {
1983     CFileItemPtr item = Get(i);
1984     // combined the folder checks
1985     if (item->m_bIsFolder)
1986     {
1987       // only check known fast sources?
1988       // NOTES:
1989       // 1. rars and zips may be on slow sources? is this supposed to be allowed?
1990       if( !item->IsRemote()
1991         || item->IsSmb()
1992         || URIUtils::IsInRAR(item->m_strPath)
1993         || URIUtils::IsInZIP(item->m_strPath)
1994         )
1995       {
1996         // stack cd# folders if contains only a single video file
1997         // NOTE: if we're doing this anyway, why not collapse *all* folders with just a single video file?
1998         CStdString folderName = item->GetLabel();
1999         if (folderName.Left(2).Equals("CD") && StringUtils::IsNaturalNumber(folderName.Mid(2)))
2000         {
2001           CFileItemList items;
2002           CDirectory::GetDirectory(item->m_strPath,items,g_settings.m_videoExtensions,true);
2003           // optimized to only traverse listing once by checking for filecount
2004           // and recording last file item for later use
2005           int nFiles = 0;
2006           int index = -1;
2007           for (int j = 0; j < items.Size(); j++)
2008           {
2009             if (!items[j]->m_bIsFolder)
2010             {
2011               nFiles++;
2012               index = j;
2013             }
2014             if (nFiles > 1)
2015               break;
2016           }
2017           if (nFiles == 1)
2018           {
2019             *item = *items[index];
2020           }
2021         }
2022
2023         // check for dvd folders
2024         else
2025         {
2026           CStdString path;
2027           CStdString dvdPath;
2028           URIUtils::AddFileToFolder(item->m_strPath, "VIDEO_TS.IFO", path);
2029           if (CFile::Exists(path))
2030             dvdPath = path;
2031           else
2032           {
2033             URIUtils::AddFileToFolder(item->m_strPath, "VIDEO_TS", dvdPath);
2034             URIUtils::AddFileToFolder(dvdPath, "VIDEO_TS.IFO", path);
2035             dvdPath.Empty();
2036             if (CFile::Exists(path))
2037               dvdPath = path;
2038           }
2039 #ifdef HAVE_LIBBLURAY
2040           if (dvdPath.IsEmpty())
2041           {
2042             URIUtils::AddFileToFolder(item->m_strPath, "index.bdmv", path);
2043             if (CFile::Exists(path))
2044               dvdPath = path;
2045             else
2046             {
2047               URIUtils::AddFileToFolder(item->m_strPath, "BDMV", dvdPath);
2048               URIUtils::AddFileToFolder(dvdPath, "index.bdmv", path);
2049               dvdPath.Empty();
2050               if (CFile::Exists(path))
2051                 dvdPath = path;
2052             }
2053           }
2054 #endif
2055           if (!dvdPath.IsEmpty())
2056           {
2057             // NOTE: should this be done for the CD# folders too?
2058             /* set the thumbnail based on folder */
2059             item->SetCachedVideoThumb();
2060             if (!item->HasThumbnail())
2061               item->SetUserVideoThumb();
2062
2063             item->m_bIsFolder = false;
2064             item->m_strPath = dvdPath;
2065             item->SetLabel2("");
2066             item->SetLabelPreformated(true);
2067             m_sortMethod = SORT_METHOD_NONE; /* sorting is now broken */
2068
2069             /* override the previously set thumb if video_ts.ifo has any */
2070             /* otherwise user can't set icon on the stacked file as that */
2071             /* will allways be set on the video_ts.ifo file */
2072             CStdString thumb(item->GetCachedVideoThumb());
2073             if(CFile::Exists(thumb))
2074               item->SetThumbnailImage(thumb);
2075             else
2076               item->SetUserVideoThumb();
2077           }
2078         }
2079       }
2080     }
2081   }
2082
2083   // Precompile our REs
2084   VECCREGEXP stackRegExps;
2085   CRegExp tmpRegExp(true);
2086   const CStdStringArray& strStackRegExps = g_advancedSettings.m_videoStackRegExps;
2087   CStdStringArray::const_iterator strRegExp = strStackRegExps.begin();
2088   while (strRegExp != strStackRegExps.end())
2089   {
2090     if (tmpRegExp.RegComp(*strRegExp))
2091     {
2092       if (tmpRegExp.GetCaptureTotal() == 4)
2093         stackRegExps.push_back(tmpRegExp);
2094       else
2095         CLog::Log(LOGERROR, "Invalid video stack RE (%s). Must have 4 captures.", strRegExp->c_str());
2096     }
2097     strRegExp++;
2098   }
2099
2100   // now stack the files, some of which may be from the previous stack iteration
2101   i = 0;
2102   while (i < Size())
2103   {
2104     CFileItemPtr item1 = Get(i);
2105
2106     // set property
2107     item1->SetProperty("isstacked", "1");
2108
2109     // skip folders, nfo files, playlists
2110     if (item1->m_bIsFolder
2111       || item1->IsParentFolder()
2112       || item1->IsNFO()
2113       || item1->IsPlayList()
2114       )
2115     {
2116       // increment index
2117       i++;
2118       continue;
2119     }
2120
2121     int64_t               size        = 0;
2122     size_t                offset      = 0;
2123     CStdString            stackName;
2124     CStdString            file1;
2125     CStdString            filePath;
2126     vector<int>           stack;
2127     VECCREGEXP::iterator  expr        = stackRegExps.begin();
2128
2129     URIUtils::Split(item1->m_strPath, filePath, file1);
2130     int j;
2131     while (expr != stackRegExps.end())
2132     {
2133       if (expr->RegFind(file1, offset) != -1)
2134       {
2135         CStdString  Title1      = expr->GetMatch(1),
2136                     Volume1     = expr->GetMatch(2),
2137                     Ignore1     = expr->GetMatch(3),
2138                     Extension1  = expr->GetMatch(4);
2139         if (offset)
2140           Title1 = file1.substr(0, expr->GetSubStart(2));
2141         j = i + 1;
2142         while (j < Size())
2143         {
2144           CFileItemPtr item2 = Get(j);
2145
2146           // skip folders, nfo files, playlists
2147           if (item2->m_bIsFolder
2148             || item2->IsParentFolder()
2149             || item2->IsNFO()
2150             || item2->IsPlayList()
2151             )
2152           {
2153             // increment index
2154             j++;
2155             continue;
2156           }
2157
2158           CStdString file2, filePath2;
2159           URIUtils::Split(item2->m_strPath, filePath2, file2);
2160
2161           if (expr->RegFind(file2, offset) != -1)
2162           {
2163             CStdString  Title2      = expr->GetMatch(1),
2164                         Volume2     = expr->GetMatch(2),
2165                         Ignore2     = expr->GetMatch(3),
2166                         Extension2  = expr->GetMatch(4);
2167             if (offset)
2168               Title2 = file2.substr(0, expr->GetSubStart(2));
2169             if (Title1.Equals(Title2))
2170             {
2171               if (!Volume1.Equals(Volume2))
2172               {
2173                 if (Ignore1.Equals(Ignore2) && Extension1.Equals(Extension2))
2174                 {
2175                   if (stack.size() == 0)
2176                   {
2177                     stackName = Title1 + Ignore1 + Extension1;
2178                     stack.push_back(i);
2179                     size += item1->m_dwSize;
2180                   }
2181                   stack.push_back(j);
2182                   size += item2->m_dwSize;
2183                 }
2184                 else // Sequel
2185                 {
2186                   offset = 0;
2187                   expr++;
2188                   break;
2189                 }
2190               }
2191               else if (!Ignore1.Equals(Ignore2)) // False positive, try again with offset
2192               {
2193                 offset = expr->GetSubStart(3);
2194                 break;
2195               }
2196               else // Extension mismatch
2197               {
2198                 offset = 0;
2199                 expr++;
2200                 break;
2201               }
2202             }
2203             else // Title mismatch
2204             {
2205               offset = 0;
2206               expr++;
2207               break;
2208             }
2209           }
2210           else // No match 2, next expression
2211           {
2212             offset = 0;
2213             expr++;
2214             break;
2215           }
2216           j++;
2217         }
2218         if (j == Size())
2219           expr = stackRegExps.end();
2220       }
2221       else // No match 1
2222       {
2223         offset = 0;
2224         expr++;
2225       }
2226       if (stack.size() > 1)
2227       {
2228         // have a stack, remove the items and add the stacked item
2229         // dont actually stack a multipart rar set, just remove all items but the first
2230         CStdString stackPath;
2231         if (Get(stack[0])->IsRAR())
2232           stackPath = Get(stack[0])->m_strPath;
2233         else
2234         {
2235           CStackDirectory dir;
2236           stackPath = dir.ConstructStackPath(*this, stack);
2237         }
2238         item1->m_strPath = stackPath;
2239         // clean up list
2240         for (unsigned k = 1; k < stack.size(); k++)
2241           Remove(i+1);
2242         // item->m_bIsFolder = true;  // don't treat stacked files as folders
2243         // the label may be in a different char set from the filename (eg over smb
2244         // the label is converted from utf8, but the filename is not)
2245         if (!g_guiSettings.GetBool("filelists.showextensions"))
2246           URIUtils::RemoveExtension(stackName);
2247         CURL::Decode(stackName);
2248         item1->SetLabel(stackName);
2249         item1->m_dwSize = size;
2250         break;
2251       }
2252     }
2253     i++;
2254   }
2255 }
2256
2257 bool CFileItemList::Load(int windowID)
2258 {
2259   CFile file;
2260   if (file.Open(GetDiscCacheFile(windowID)))
2261   {
2262     CLog::Log(LOGDEBUG,"Loading fileitems [%s]",m_strPath.c_str());
2263     CArchive ar(&file, CArchive::load);
2264     ar >> *this;
2265     CLog::Log(LOGDEBUG,"  -- items: %i, directory: %s sort method: %i, ascending: %s",Size(),m_strPath.c_str(), m_sortMethod, m_sortOrder ? "true" : "false");
2266     ar.Close();
2267     file.Close();
2268     return true;
2269   }
2270
2271   return false;
2272 }
2273
2274 bool CFileItemList::Save(int windowID)
2275 {
2276   int iSize = Size();
2277   if (iSize <= 0)
2278     return false;
2279
2280   CLog::Log(LOGDEBUG,"Saving fileitems [%s]",m_strPath.c_str());
2281
2282   CFile file;
2283   if (file.OpenForWrite(GetDiscCacheFile(windowID), true)) // overwrite always
2284   {
2285     CArchive ar(&file, CArchive::store);
2286     ar << *this;
2287     CLog::Log(LOGDEBUG,"  -- items: %i, sort method: %i, ascending: %s",iSize,m_sortMethod, m_sortOrder ? "true" : "false");
2288     ar.Close();
2289     file.Close();
2290     return true;
2291   }
2292
2293   return false;
2294 }
2295
2296 void CFileItemList::RemoveDiscCache(int windowID) const
2297 {
2298   CStdString cacheFile(GetDiscCacheFile(windowID));
2299   if (CFile::Exists(cacheFile))
2300   {
2301     CLog::Log(LOGDEBUG,"Clearing cached fileitems [%s]",m_strPath.c_str());
2302     CFile::Delete(cacheFile);
2303   }
2304 }
2305
2306 CStdString CFileItemList::GetDiscCacheFile(int windowID) const
2307 {
2308   CStdString strPath=m_strPath;
2309   URIUtils::RemoveSlashAtEnd(strPath);
2310
2311   Crc32 crc;
2312   crc.ComputeFromLowerCase(strPath);
2313
2314   CStdString cacheFile;
2315   if (IsCDDA() || IsOnDVD())
2316     cacheFile.Format("special://temp/r-%08x.fi", (unsigned __int32)crc);
2317   else if (IsMusicDb())
2318     cacheFile.Format("special://temp/mdb-%08x.fi", (unsigned __int32)crc);
2319   else if (IsVideoDb())
2320     cacheFile.Format("special://temp/vdb-%08x.fi", (unsigned __int32)crc);
2321   else if (windowID)
2322     cacheFile.Format("special://temp/%i-%08x.fi", windowID, (unsigned __int32)crc);
2323   else
2324     cacheFile.Format("special://temp/%08x.fi", (unsigned __int32)crc);
2325   return cacheFile;
2326 }
2327
2328 bool CFileItemList::AlwaysCache() const
2329 {
2330   // some database folders are always cached
2331   if (IsMusicDb())
2332     return CMusicDatabaseDirectory::CanCache(m_strPath);
2333   if (IsVideoDb())
2334     return CVideoDatabaseDirectory::CanCache(m_strPath);
2335   return false;
2336 }
2337
2338 void CFileItemList::SetCachedVideoThumbs()
2339 {
2340   CSingleLock lock(m_lock);
2341   // TODO: Investigate caching time to see if it speeds things up
2342   for (unsigned int i = 0; i < m_items.size(); ++i)
2343   {
2344     CFileItemPtr pItem = m_items[i];
2345     pItem->SetCachedVideoThumb();
2346   }
2347 }
2348
2349 void CFileItemList::SetCachedMusicThumbs()
2350 {
2351   CSingleLock lock(m_lock);
2352   // TODO: Investigate caching time to see if it speeds things up
2353   for (unsigned int i = 0; i < m_items.size(); ++i)
2354   {
2355     CFileItemPtr pItem = m_items[i];
2356     pItem->SetCachedMusicThumb();
2357   }
2358 }
2359
2360 void CFileItem::SetCachedMusicThumb()
2361 {
2362   // if it already has a thumbnail, then return
2363   if (HasThumbnail() || m_bIsShareOrDrive) return ;
2364
2365   // streams do not have thumbnails
2366   if (IsInternetStream()) return ;
2367
2368   //  music db items already have thumbs or there is no thumb available
2369   if (IsMusicDb()) return;
2370
2371   // ignore the parent dir items
2372   if (IsParentFolder()) return;
2373
2374   CStdString cachedThumb(GetPreviouslyCachedMusicThumb());
2375   if (!cachedThumb.IsEmpty())
2376     SetThumbnailImage(cachedThumb);
2377     // SetIconImage(cachedThumb);
2378 }
2379
2380 CStdString CFileItem::GetPreviouslyCachedMusicThumb() const
2381 {
2382   // look if an album thumb is available,
2383   // could be any file with tags loaded or
2384   // a directory in album window
2385   CStdString strAlbum, strArtist;
2386   if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
2387   {
2388     strAlbum = m_musicInfoTag->GetAlbum();
2389     if (!m_musicInfoTag->GetAlbumArtist().IsEmpty())
2390       strArtist = m_musicInfoTag->GetAlbumArtist();
2391     else
2392       strArtist = m_musicInfoTag->GetArtist();
2393   }
2394   if (!strAlbum.IsEmpty() && !strArtist.IsEmpty())
2395   {
2396     // try permanent album thumb using "album name + artist name"
2397     CStdString thumb(CUtil::GetCachedAlbumThumb(strAlbum, strArtist));
2398     if (CFile::Exists(thumb))
2399       return thumb;
2400   }
2401
2402   // if a file, try to find a cached filename.tbn
2403   if (!m_bIsFolder)
2404   {
2405     // look for locally cached tbn
2406     CStdString thumb(CUtil::GetCachedMusicThumb(m_strPath));
2407     if (CFile::Exists(thumb))
2408       return thumb;
2409   }
2410
2411   // try and find a cached folder thumb (folder.jpg or folder.tbn)
2412   CStdString strPath;
2413   if (!m_bIsFolder)
2414     URIUtils::GetDirectory(m_strPath, strPath);
2415   else
2416     strPath = m_strPath;
2417   // music thumbs are cached without slash at end
2418   URIUtils::RemoveSlashAtEnd(strPath);
2419
2420   CStdString thumb(CUtil::GetCachedMusicThumb(strPath));
2421   if (CFile::Exists(thumb))
2422     return thumb;
2423
2424   return "";
2425 }
2426
2427 CStdString CFileItem::GetUserMusicThumb(bool alwaysCheckRemote /* = false */) const
2428 {
2429   if (m_strPath.IsEmpty()
2430    || m_bIsShareOrDrive
2431    || IsInternetStream()
2432    || URIUtils::IsUPnP(m_strPath)
2433    || (URIUtils::IsFTP(m_strPath) && !g_advancedSettings.m_bFTPThumbs)
2434    || IsPlugin()
2435    || IsAddonsPath()
2436    || IsParentFolder()
2437    || IsMusicDb())
2438     return "";
2439
2440   // we first check for <filename>.tbn or <foldername>.tbn
2441   CStdString fileThumb(GetTBNFile());
2442   if (CFile::Exists(fileThumb))
2443     return fileThumb;
2444
2445   // if a folder, check for folder.jpg
2446   if (m_bIsFolder && !IsFileFolder() && (!IsRemote() || alwaysCheckRemote || g_guiSettings.GetBool("musicfiles.findremotethumbs")))
2447   {
2448     CStdStringArray thumbs;
2449     StringUtils::SplitString(g_advancedSettings.m_musicThumbs, "|", thumbs);
2450     for (unsigned int i = 0; i < thumbs.size(); ++i)
2451     {
2452       CStdString folderThumb(GetFolderThumb(thumbs[i]));
2453       if (CFile::Exists(folderThumb))
2454       {
2455         return folderThumb;
2456       }
2457     }
2458   }
2459   // this adds support for files which inherit a folder.jpg icon which has not been cached yet.
2460   // this occurs when queueing a top-level folder which has not been traversed yet.
2461   else if (!IsRemote() || alwaysCheckRemote || g_guiSettings.GetBool("musicfiles.findremotethumbs"))
2462   {
2463     CStdString strFolder, strFile;
2464     URIUtils::Split(m_strPath, strFolder, strFile);
2465     CFileItem folderItem(strFolder, true);
2466     folderItem.SetMusicThumb(alwaysCheckRemote);
2467     if (folderItem.HasThumbnail())
2468       return folderItem.GetThumbnailImage();
2469   }
2470   // No thumb found
2471   return "";
2472 }
2473
2474 void CFileItem::SetUserMusicThumb(bool alwaysCheckRemote /* = false */)
2475 {
2476   // caches as the local thumb
2477   CStdString thumb(GetUserMusicThumb(alwaysCheckRemote));
2478   if (!thumb.IsEmpty())
2479   {
2480     CStdString cachedThumb(CUtil::GetCachedMusicThumb(m_strPath));
2481     CPicture::CreateThumbnail(thumb, cachedThumb);
2482   }
2483
2484   SetCachedMusicThumb();
2485 }
2486
2487 CStdString CFileItem::GetCachedVideoThumb() const
2488 {
2489   if (IsStack())
2490     return GetCachedThumb(CStackDirectory::GetFirstStackedFile(m_strPath),g_settings.GetVideoThumbFolder(),true);
2491   else if (IsVideoDb() && HasVideoInfoTag())
2492   {
2493     if (m_bIsFolder && !GetVideoInfoTag()->m_strPath.IsEmpty())
2494       return GetCachedThumb(GetVideoInfoTag()->m_strPath, g_settings.GetVideoThumbFolder(), true);
2495     else if (!GetVideoInfoTag()->m_strFileNameAndPath.IsEmpty())
2496       return GetCachedThumb(GetVideoInfoTag()->m_strFileNameAndPath, g_settings.GetVideoThumbFolder(), true);
2497   }
2498   return GetCachedThumb(m_strPath,g_settings.GetVideoThumbFolder(),true);
2499 }
2500
2501 CStdString CFileItem::GetCachedEpisodeThumb() const
2502 {
2503   // get the locally cached thumb
2504   CStdString strCRC;
2505   strCRC.Format("%sepisode%i",GetVideoInfoTag()->m_strFileNameAndPath.c_str(),GetVideoInfoTag()->m_iEpisode);
2506   return GetCachedThumb(strCRC,g_settings.GetVideoThumbFolder(),true);
2507 }
2508
2509 void CFileItem::SetCachedVideoThumb()
2510 {
2511   if (IsParentFolder()) return;
2512   if (HasThumbnail()) return;
2513   CStdString cachedThumb(GetCachedVideoThumb());
2514   if (HasVideoInfoTag() && !m_bIsFolder  &&
2515       GetVideoInfoTag()->m_iEpisode > -1 &&
2516       CFile::Exists(GetCachedEpisodeThumb()))
2517   {
2518     SetThumbnailImage(GetCachedEpisodeThumb());
2519   }
2520   else if (CFile::Exists(cachedThumb))
2521     SetThumbnailImage(cachedThumb);
2522 }
2523
2524 // Gets the .tbn filename from a file or folder name.
2525 // <filename>.ext -> <filename>.tbn
2526 // <foldername>/ -> <foldername>.tbn
2527 CStdString CFileItem::GetTBNFile() const
2528 {
2529   CStdString thumbFile;
2530   CStdString strFile = m_strPath;
2531
2532   if (IsStack())
2533   {
2534     CStdString strPath, strReturn;
2535     URIUtils::GetParentPath(m_strPath,strPath);
2536     CFileItem item(CStackDirectory::GetFirstStackedFile(strFile),false);
2537     CStdString strTBNFile = item.GetTBNFile();
2538     URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile),strReturn);
2539     if (CFile::Exists(strReturn))
2540       return strReturn;
2541
2542     URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(strFile)),strFile);
2543   }
2544
2545   if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
2546   {
2547     CStdString strPath, strParent;
2548     URIUtils::GetDirectory(strFile,strPath);
2549     URIUtils::GetParentPath(strPath,strParent);
2550     URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath),strFile);
2551   }
2552
2553   CURL url(strFile);
2554   strFile = url.GetFileName();
2555
2556   if (m_bIsFolder && !IsFileFolder())
2557     URIUtils::RemoveSlashAtEnd(strFile);
2558
2559   if (!strFile.IsEmpty())
2560   {
2561     if (m_bIsFolder && !IsFileFolder())
2562       thumbFile = strFile + ".tbn"; // folder, so just add ".tbn"
2563     else
2564       thumbFile = URIUtils::ReplaceExtension(strFile, ".tbn");
2565     url.SetFileName(thumbFile);
2566     thumbFile = url.Get();
2567   }
2568   return thumbFile;
2569 }
2570
2571 CStdString CFileItem::GetUserVideoThumb() const
2572 {
2573   if (IsTuxBox())
2574   {
2575     if (!m_bIsFolder)
2576       return g_tuxbox.GetPicon(GetLabel());
2577     else return "";
2578   }
2579
2580   if (m_strPath.IsEmpty()
2581    || m_bIsShareOrDrive
2582    || IsInternetStream()
2583    || URIUtils::IsUPnP(m_strPath)
2584    || (URIUtils::IsFTP(m_strPath) && !g_advancedSettings.m_bFTPThumbs)
2585    || IsPlugin()
2586    || IsAddonsPath()
2587    || IsParentFolder()
2588    || IsLiveTV())
2589     return "";
2590
2591
2592   // 1. check <filename>.tbn or <foldername>.tbn
2593   CStdString fileThumb(GetTBNFile());
2594   if (CFile::Exists(fileThumb))
2595     return fileThumb;
2596
2597   // 2. - check movie.tbn, as long as it's not a folder
2598   if (!m_bIsFolder)
2599   {
2600     CStdString strPath, movietbnFile;
2601     URIUtils::GetParentPath(m_strPath, strPath);
2602     URIUtils::AddFileToFolder(strPath, "movie.tbn", movietbnFile);
2603     if (CFile::Exists(movietbnFile))
2604       return movietbnFile;
2605   }
2606
2607   // 3. check folder image in_m_dvdThumbs (folder.jpg)
2608   if (m_bIsFolder && !IsFileFolder())
2609   {
2610     CStdStringArray thumbs;
2611     StringUtils::SplitString(g_advancedSettings.m_dvdThumbs, "|", thumbs);
2612     for (unsigned int i = 0; i < thumbs.size(); ++i)
2613     {
2614       CStdString folderThumb(GetFolderThumb(thumbs[i]));
2615       if (CFile::Exists(folderThumb))
2616       {
2617         return folderThumb;
2618       }
2619     }
2620   }
2621   // No thumb found
2622   return "";
2623 }
2624
2625 CStdString CFileItem::GetFolderThumb(const CStdString &folderJPG /* = "folder.jpg" */) const
2626 {
2627   CStdString folderThumb;
2628   CStdString strFolder = m_strPath;
2629
2630   if (IsStack() ||
2631       URIUtils::IsInRAR(strFolder) ||
2632       URIUtils::IsInZIP(strFolder))
2633   {
2634     URIUtils::GetParentPath(m_strPath,strFolder);
2635   }
2636
2637   if (IsMultiPath())
2638     strFolder = CMultiPathDirectory::GetFirstPath(m_strPath);
2639
2640   URIUtils::AddFileToFolder(strFolder, folderJPG, folderThumb);
2641   return folderThumb;
2642 }
2643
2644 CStdString CFileItem::GetMovieName(bool bUseFolderNames /* = false */) const
2645 {
2646   if (IsLabelPreformated())
2647     return GetLabel();
2648
2649   CStdString strMovieName = GetBaseMoviePath(bUseFolderNames);
2650
2651   URIUtils::RemoveSlashAtEnd(strMovieName);
2652   strMovieName = URIUtils::GetFileName(strMovieName);
2653   CURL::Decode(strMovieName);
2654
2655   return strMovieName;
2656 }
2657
2658 CStdString CFileItem::GetBaseMoviePath(bool bUseFolderNames) const
2659 {
2660   CStdString strMovieName = m_strPath;
2661
2662   if (IsMultiPath())
2663     strMovieName = CMultiPathDirectory::GetFirstPath(m_strPath);
2664
2665   if (URIUtils::IsStack(strMovieName))
2666     strMovieName = CStackDirectory::GetStackedTitlePath(strMovieName);
2667
2668   int pos;
2669   if ((pos=strMovieName.Find("BDMV/")) != -1 ||
2670       (pos=strMovieName.Find("BDMV\\")) != -1)
2671     strMovieName = strMovieName.Mid(0,pos+5);
2672
2673   if ((!m_bIsFolder || IsOpticalMediaFile() || URIUtils::IsInArchive(m_strPath)) && bUseFolderNames)
2674   {
2675     CStdString name2(strMovieName);
2676     URIUtils::GetParentPath(name2,strMovieName);
2677     if (URIUtils::IsInArchive(m_strPath) || strMovieName.Find( "VIDEO_TS" ) != -1)
2678     {
2679       CStdString strArchivePath;
2680       URIUtils::GetParentPath(strMovieName, strArchivePath);
2681       strMovieName = strArchivePath;
2682     }
2683   }
2684
2685   return strMovieName;
2686 }
2687
2688 void CFileItem::SetVideoThumb()
2689 {
2690   if (HasThumbnail()) return;
2691   SetCachedVideoThumb();
2692   if (!HasThumbnail())
2693     SetUserVideoThumb();
2694 }
2695
2696 void CFileItem::SetUserVideoThumb()
2697 {
2698   if (m_bIsShareOrDrive) return;
2699   if (IsParentFolder()) return;
2700
2701   // caches as the local thumb
2702   CStdString thumb(GetUserVideoThumb());
2703   if (!thumb.IsEmpty())
2704   {
2705     CStdString cachedThumb(GetCachedVideoThumb());
2706     CPicture::CreateThumbnail(thumb, cachedThumb);
2707   }
2708   SetCachedVideoThumb();
2709 }
2710
2711 bool CFileItem::CacheLocalFanart() const
2712 {
2713   // first check for an already cached fanart image
2714   CStdString cachedFanart(GetCachedFanart());
2715   if (CFile::Exists(cachedFanart))
2716     return true;
2717
2718   // we don't have a cached image, so let's see if the user has a local image, and cache it if so
2719   CStdString localFanart(GetLocalFanart());
2720   if (!localFanart.IsEmpty())
2721     return CPicture::CacheFanart(localFanart, cachedFanart);
2722   return false;
2723 }
2724
2725 CStdString CFileItem::GetLocalFanart() const
2726 {
2727   if (IsVideoDb())
2728   {
2729     if (!HasVideoInfoTag())
2730       return ""; // nothing can be done
2731     CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
2732     return dbItem.GetLocalFanart();
2733   }
2734
2735   CStdString strFile2;
2736   CStdString strFile = m_strPath;
2737   if (IsStack())
2738   {
2739     CStdString strPath;
2740     URIUtils::GetParentPath(m_strPath,strPath);
2741     CStackDirectory dir;
2742     CStdString strPath2;
2743     strPath2 = dir.GetStackedTitlePath(strFile);
2744     URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2),strFile);
2745     CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
2746     CStdString strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-fanart"));
2747     URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile),strFile2);
2748   }
2749   if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
2750   {
2751     CStdString strPath, strParent;
2752     URIUtils::GetDirectory(strFile,strPath);
2753     URIUtils::GetParentPath(strPath,strParent);
2754     URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath),strFile);
2755   }
2756
2757   // no local fanart available for these
2758   if (IsInternetStream()
2759    || URIUtils::IsUPnP(strFile)
2760    || IsLiveTV()
2761    || IsPlugin()
2762    || IsAddonsPath()
2763    || (URIUtils::IsFTP(strFile) && !g_advancedSettings.m_bFTPThumbs)
2764    || m_strPath.IsEmpty())
2765     return "";
2766
2767   CStdString strDir;
2768   URIUtils::GetDirectory(strFile, strDir);
2769
2770   if (strDir.IsEmpty())
2771     return "";
2772
2773   CFileItemList items;
2774   CDirectory::GetDirectory(strDir, items, g_settings.m_pictureExtensions, false, false, DIR_CACHE_ALWAYS, false, true);
2775
2776   CStdStringArray fanarts;
2777   StringUtils::SplitString(g_advancedSettings.m_fanartImages, "|", fanarts);
2778
2779   strFile = URIUtils::ReplaceExtension(strFile, "-fanart");
2780   fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile));
2781
2782   if (!strFile2.IsEmpty())
2783     fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile2));
2784
2785   for (unsigned int i = 0; i < fanarts.size(); ++i)
2786   {
2787     for (int j = 0; j < items.Size(); j++)
2788     {
2789       CStdString strCandidate = URIUtils::GetFileName(items[j]->m_strPath);
2790       URIUtils::RemoveExtension(strCandidate);
2791       CStdString strFanart = fanarts[i];
2792       URIUtils::RemoveExtension(strFanart);
2793       if (strCandidate.CompareNoCase(strFanart) == 0)
2794         return items[j]->m_strPath;
2795     }
2796   }
2797
2798   return "";
2799 }
2800
2801 CStdString CFileItem::GetCachedFanart() const
2802 {
2803   // get the locally cached thumb
2804   if (IsVideoDb())
2805   {
2806     if (!HasVideoInfoTag())
2807       return "";
2808     if (!GetVideoInfoTag()->m_strArtist.IsEmpty())
2809       return GetCachedThumb(GetVideoInfoTag()->m_strArtist,g_settings.GetMusicFanartFolder());
2810     if (!m_bIsFolder && !GetVideoInfoTag()->m_strShowTitle.IsEmpty())
2811     {
2812       CVideoDatabase database;
2813       database.Open();
2814       int iShowId = database.GetTvShowId(GetVideoInfoTag()->m_strPath);
2815       CStdString showPath;
2816       database.GetFilePathById(iShowId,showPath,VIDEODB_CONTENT_TVSHOWS);
2817       return GetCachedThumb(showPath,g_settings.GetVideoFanartFolder());
2818     }
2819     return GetCachedThumb(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath,g_settings.GetVideoFanartFolder());
2820   }
2821   if (HasMusicInfoTag())
2822     return GetCachedThumb(GetMusicInfoTag()->GetArtist(),g_settings.GetMusicFanartFolder());
2823
2824   return GetCachedThumb(m_strPath,g_settings.GetVideoFanartFolder());
2825 }
2826
2827 CStdString CFileItem::GetCachedThumb(const CStdString &path, const CStdString &path2, bool split)
2828 {
2829   // get the locally cached thumb
2830   Crc32 crc;
2831   crc.ComputeFromLowerCase(path);
2832
2833   CStdString thumb;
2834   if (split)
2835   {
2836     CStdString hex;
2837     hex.Format("%08x", (__int32)crc);
2838     thumb.Format("%c\\%08x.tbn", hex[0], (unsigned __int32)crc);
2839   }
2840   else
2841     thumb.Format("%08x.tbn", (unsigned __int32)crc);
2842
2843   return URIUtils::AddFileToFolder(path2, thumb);
2844 }
2845
2846 /*void CFileItem::SetThumb()
2847 {
2848   // we need to know the type of file at this point
2849   // as differing views have differing inheritance rules for thumbs:
2850
2851   // Videos:
2852   // Folders only use <foldername>/folder.jpg or <foldername>.tbn
2853   // Files use <filename>.tbn
2854   //  * Thumbs are cached from here using file or folder path
2855
2856   // Music:
2857   // Folders only use <foldername>/folder.jpg or <foldername>.tbn
2858   // Files use <filename>.tbn or the album/path cached thumb or inherit from the folder
2859   //  * Thumbs are cached from here using file or folder path
2860
2861   // Programs:
2862   // Folders only use <foldername>/folder.jpg or <foldername>.tbn
2863   // Files use <filename>.tbn or the embedded xbe.  Shortcuts have the additional step of the <thumbnail> tag to check
2864   //  * Thumbs are cached from here using file or folder path
2865
2866   // Pictures:
2867   // Folders use <foldername>/folder.jpg or <foldername>.tbn, or auto-generated from 4 images in the folder
2868   // Files use <filename>.tbn or a resized version of the picture
2869   //  * Thumbs are cached from here using file or folder path
2870
2871 }*/
2872
2873 bool CFileItem::LoadMusicTag()
2874 {
2875   // not audio
2876   if (!IsAudio())
2877     return false;
2878   // already loaded?
2879   if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
2880     return true;
2881   // check db
2882   CMusicDatabase musicDatabase;
2883   if (musicDatabase.Open())
2884   {
2885     CSong song;
2886     if (musicDatabase.GetSongByFileName(m_strPath, song))
2887     {
2888       GetMusicInfoTag()->SetSong(song);
2889       SetThumbnailImage(song.strThumb);
2890       return true;
2891     }
2892     musicDatabase.Close();
2893   }
2894   // load tag from file
2895   CLog::Log(LOGDEBUG, "%s: loading tag information for file: %s", __FUNCTION__, m_strPath.c_str());
2896   CMusicInfoTagLoaderFactory factory;
2897   auto_ptr<IMusicInfoTagLoader> pLoader (factory.CreateLoader(m_strPath));
2898   if (NULL != pLoader.get())
2899   {
2900     if (pLoader->Load(m_strPath, *GetMusicInfoTag()))
2901       return true;
2902   }
2903   // no tag - try some other things
2904   if (IsCDDA())
2905   {
2906     // we have the tracknumber...
2907     int iTrack = GetMusicInfoTag()->GetTrackNumber();
2908     if (iTrack >= 1)
2909     {
2910       CStdString strText = g_localizeStrings.Get(554); // "Track"
2911       if (strText.GetAt(strText.size() - 1) != ' ')
2912         strText += " ";
2913       CStdString strTrack;
2914       strTrack.Format(strText + "%i", iTrack);
2915       GetMusicInfoTag()->SetTitle(strTrack);
2916       GetMusicInfoTag()->SetLoaded(true);
2917       return true;
2918     }
2919   }
2920   else
2921   {
2922     CStdString fileName = URIUtils::GetFileName(m_strPath);
2923     URIUtils::RemoveExtension(fileName);
2924     for (unsigned int i = 0; i < g_advancedSettings.m_musicTagsFromFileFilters.size(); i++)
2925     {
2926       CLabelFormatter formatter(g_advancedSettings.m_musicTagsFromFileFilters[i], "");
2927       if (formatter.FillMusicTag(fileName, GetMusicInfoTag()))
2928       {
2929         GetMusicInfoTag()->SetLoaded(true);
2930         return true;
2931       }
2932     }
2933   }
2934   return false;
2935 }
2936
2937 void CFileItemList::Swap(unsigned int item1, unsigned int item2)
2938 {
2939   if (item1 != item2 && item1 < m_items.size() && item2 < m_items.size())
2940     std::swap(m_items[item1], m_items[item2]);
2941 }
2942
2943 bool CFileItemList::UpdateItem(const CFileItem *item)
2944 {
2945   if (!item) return false;
2946   CFileItemPtr oldItem = Get(item->m_strPath);
2947   if (oldItem)
2948     *oldItem = *item;
2949   return oldItem;
2950 }
2951
2952 void CFileItemList::AddSortMethod(SORT_METHOD sortMethod, int buttonLabel, const LABEL_MASKS &labelMasks)
2953 {
2954   SORT_METHOD_DETAILS sort;
2955   sort.m_sortMethod=sortMethod;
2956   sort.m_buttonLabel=buttonLabel;
2957   sort.m_labelMasks=labelMasks;
2958
2959   m_sortDetails.push_back(sort);
2960 }
2961
2962 void CFileItemList::SetReplaceListing(bool replace)
2963 {
2964   m_replaceListing = replace;
2965 }
2966
2967 void CFileItemList::ClearSortState()
2968 {
2969   m_sortMethod=SORT_METHOD_NONE;
2970   m_sortOrder=SORT_ORDER_NONE;
2971 }
2972
2973 CVideoInfoTag* CFileItem::GetVideoInfoTag()
2974 {
2975   if (!m_videoInfoTag)
2976     m_videoInfoTag = new CVideoInfoTag;
2977
2978   return m_videoInfoTag;
2979 }
2980
2981 CPictureInfoTag* CFileItem::GetPictureInfoTag()
2982 {
2983   if (!m_pictureInfoTag)
2984     m_pictureInfoTag = new CPictureInfoTag;
2985
2986   return m_pictureInfoTag;
2987 }
2988
2989 MUSIC_INFO::CMusicInfoTag* CFileItem::GetMusicInfoTag()
2990 {
2991   if (!m_musicInfoTag)
2992     m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag;
2993
2994   return m_musicInfoTag;
2995 }
2996
2997 CStdString CFileItem::FindTrailer() const
2998 {
2999   CStdString strFile2, strTrailer;
3000   CStdString strFile = m_strPath;
3001   if (IsStack())
3002   {
3003     CStdString strPath;
3004     URIUtils::GetParentPath(m_strPath,strPath);
3005     CStackDirectory dir;
3006     CStdString strPath2;
3007     strPath2 = dir.GetStackedTitlePath(strFile);
3008     URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2),strFile);
3009     CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
3010     CStdString strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-trailer"));
3011     URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile),strFile2);
3012   }
3013   if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
3014   {
3015     CStdString strPath, strParent;
3016     URIUtils::GetDirectory(strFile,strPath);
3017     URIUtils::GetParentPath(strPath,strParent);
3018     URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath),strFile);
3019   }
3020
3021   // no local trailer available for these
3022   if (IsInternetStream()
3023    || URIUtils::IsUPnP(strFile)
3024    || IsLiveTV()
3025    || IsPlugin())
3026     return strTrailer;
3027
3028   CStdString strDir;
3029   URIUtils::GetDirectory(strFile, strDir);
3030   CFileItemList items;
3031   CDirectory::GetDirectory(strDir, items, g_settings.m_videoExtensions, true, false, DIR_CACHE_ALWAYS, false, true);
3032   URIUtils::RemoveExtension(strFile);
3033   strFile += "-trailer";
3034   CStdString strFile3 = URIUtils::AddFileToFolder(strDir, "movie-trailer");
3035
3036   // Precompile our REs
3037   VECCREGEXP matchRegExps;
3038   CRegExp tmpRegExp(true);
3039   const CStdStringArray& strMatchRegExps = g_advancedSettings.m_trailerMatchRegExps;
3040
3041   CStdStringArray::const_iterator strRegExp = strMatchRegExps.begin();
3042   while (strRegExp != strMatchRegExps.end())
3043   {
3044     if (tmpRegExp.RegComp(*strRegExp))
3045     {
3046       matchRegExps.push_back(tmpRegExp);
3047     }
3048     strRegExp++;
3049   }
3050
3051   for (int i = 0; i < items.Size(); i++)
3052   {
3053     CStdString strCandidate = items[i]->m_strPath;
3054     URIUtils::RemoveExtension(strCandidate);
3055     if (strCandidate.CompareNoCase(strFile) == 0 ||
3056         strCandidate.CompareNoCase(strFile2) == 0 ||
3057         strCandidate.CompareNoCase(strFile3) == 0)
3058     {
3059       strTrailer = items[i]->m_strPath;
3060       break;
3061     }
3062     else
3063     {
3064       VECCREGEXP::iterator expr = matchRegExps.begin();
3065
3066       while (expr != matchRegExps.end())
3067       {
3068         if (expr->RegFind(strCandidate) != -1)
3069         {
3070           strTrailer = items[i]->m_strPath;
3071           i = items.Size();
3072           break;
3073         }
3074         expr++;
3075       }
3076     }
3077   }
3078
3079   return strTrailer;
3080 }
3081
3082 int CFileItem::GetVideoContentType() const
3083 {
3084   VIDEODB_CONTENT_TYPE type = VIDEODB_CONTENT_MOVIES;
3085   if (HasVideoInfoTag() && !GetVideoInfoTag()->m_strShowTitle.IsEmpty()) // tvshow
3086     type = VIDEODB_CONTENT_TVSHOWS;
3087   if (HasVideoInfoTag() && GetVideoInfoTag()->m_iSeason > -1 && !m_bIsFolder) // episode
3088     type = VIDEODB_CONTENT_EPISODES;
3089   if (HasVideoInfoTag() && !GetVideoInfoTag()->m_strArtist.IsEmpty())
3090     type = VIDEODB_CONTENT_MUSICVIDEOS;
3091   return type;
3092 }
3093