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