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