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