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