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