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