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