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