/*
* Copyright (C) 2005-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* .
*
*/
#include "system.h"
#include "utils/log.h"
#include "FileItem.h"
#include "Util.h"
#include "utils/URIUtils.h"
#include "pictures/Picture.h"
#include "playlists/PlayListFactory.h"
#include "Shortcut.h"
#include "utils/Crc32.h"
#include "FileSystem/DirectoryCache.h"
#include "FileSystem/StackDirectory.h"
#include "FileSystem/CurlFile.h"
#include "FileSystem/MultiPathDirectory.h"
#include "FileSystem/MusicDatabaseDirectory.h"
#include "FileSystem/VideoDatabaseDirectory.h"
#include "FileSystem/DirectoryFactory.h"
#include "music/tags/MusicInfoTagLoaderFactory.h"
#include "CueDocument.h"
#include "video/VideoDatabase.h"
#include "music/MusicDatabase.h"
#include "SortFileItem.h"
#include "utils/TuxBoxUtil.h"
#include "video/VideoInfoTag.h"
#include "utils/SingleLock.h"
#include "music/tags/MusicInfoTag.h"
#include "pictures/PictureInfoTag.h"
#include "music/Artist.h"
#include "music/Album.h"
#include "music/Song.h"
#include "URL.h"
#include "settings/Settings.h"
#include "settings/AdvancedSettings.h"
#include "LocalizeStrings.h"
#include "utils/RegExp.h"
#include "utils/Variant.h"
using namespace std;
using namespace XFILE;
using namespace PLAYLIST;
using namespace MUSIC_INFO;
CFileItem::CFileItem(const CSong& song)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
SetLabel(song.strTitle);
m_strPath = song.strFileName;
GetMusicInfoTag()->SetSong(song);
m_lStartOffset = song.iStartOffset;
m_lEndOffset = song.iEndOffset;
m_strThumbnailImage = song.strThumb;
}
CFileItem::CFileItem(const CStdString &path, const CAlbum& album)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
SetLabel(album.strAlbum);
m_strPath = path;
m_bIsFolder = true;
m_strLabel2 = album.strArtist;
URIUtils::AddSlashAtEnd(m_strPath);
GetMusicInfoTag()->SetAlbum(album);
if (album.thumbURL.m_url.size() > 0)
m_strThumbnailImage = album.thumbURL.m_url[0].m_url;
else
m_strThumbnailImage.clear();
/* TODO: remove when we remove old properties */
SetProperty("description", album.strReview);
SetProperty("theme", album.strThemes);
SetProperty("mood", album.strMoods);
SetProperty("style", album.strStyles);
SetProperty("type", album.strType);
SetProperty("label", album.strLabel);
if (album.iRating > 0)
SetProperty("rating", album.iRating);
CMusicDatabase::SetPropertiesFromAlbum(*this,album);
}
CFileItem::CFileItem(const CVideoInfoTag& movie)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
SetLabel(movie.m_strTitle);
if (movie.m_strFileNameAndPath.IsEmpty())
{
m_strPath = movie.m_strPath;
URIUtils::AddSlashAtEnd(m_strPath);
m_bIsFolder = true;
}
else
{
m_strPath = movie.m_strFileNameAndPath;
m_bIsFolder = false;
}
*GetVideoInfoTag() = movie;
FillInDefaultIcon();
SetCachedVideoThumb();
}
CFileItem::CFileItem(const CArtist& artist)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
SetLabel(artist.strArtist);
m_strPath = artist.strArtist;
m_bIsFolder = true;
URIUtils::AddSlashAtEnd(m_strPath);
GetMusicInfoTag()->SetArtist(artist.strArtist);
}
CFileItem::CFileItem(const CGenre& genre)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
SetLabel(genre.strGenre);
m_strPath = genre.strGenre;
m_bIsFolder = true;
URIUtils::AddSlashAtEnd(m_strPath);
GetMusicInfoTag()->SetGenre(genre.strGenre);
}
CFileItem::CFileItem(const CFileItem& item): CGUIListItem()
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
*this = item;
}
CFileItem::CFileItem(const CGUIListItem& item)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
// not particularly pretty, but it gets around the issue of Reset() defaulting
// parameters in the CGUIListItem base class.
*((CGUIListItem *)this) = item;
}
CFileItem::CFileItem(void)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
}
CFileItem::CFileItem(const CStdString& strLabel)
: CGUIListItem()
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
SetLabel(strLabel);
}
CFileItem::CFileItem(const CStdString& strPath, bool bIsFolder)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
m_strPath = strPath;
m_bIsFolder = bIsFolder;
// tuxbox urls cannot have a / at end
if (m_bIsFolder && !m_strPath.IsEmpty() && !IsFileFolder() && !URIUtils::IsTuxBox(m_strPath))
{
#ifdef DEBUG
ASSERT(URIUtils::HasSlashAtEnd(m_strPath));
#endif
URIUtils::AddSlashAtEnd(m_strPath);
}
}
CFileItem::CFileItem(const CMediaSource& share)
{
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
Reset();
m_bIsFolder = true;
m_bIsShareOrDrive = true;
m_strPath = share.strPath;
URIUtils::AddSlashAtEnd(m_strPath);
CStdString label = share.strName;
if (!share.strStatus.IsEmpty())
label.Format("%s (%s)", share.strName.c_str(), share.strStatus.c_str());
SetLabel(label);
m_iLockMode = share.m_iLockMode;
m_strLockCode = share.m_strLockCode;
m_iHasLock = share.m_iHasLock;
m_iBadPwdCount = share.m_iBadPwdCount;
m_iDriveType = share.m_iDriveType;
m_strThumbnailImage = share.m_strThumbnailImage;
SetLabelPreformated(true);
}
CFileItem::~CFileItem(void)
{
delete m_musicInfoTag;
delete m_videoInfoTag;
delete m_pictureInfoTag;
m_musicInfoTag = NULL;
m_videoInfoTag = NULL;
m_pictureInfoTag = NULL;
}
const CFileItem& CFileItem::operator=(const CFileItem& item)
{
if (this == &item) return * this;
CGUIListItem::operator=(item);
m_bLabelPreformated=item.m_bLabelPreformated;
FreeMemory();
m_strPath = item.GetPath();
m_bIsParentFolder = item.m_bIsParentFolder;
m_iDriveType = item.m_iDriveType;
m_bIsShareOrDrive = item.m_bIsShareOrDrive;
m_dateTime = item.m_dateTime;
m_dwSize = item.m_dwSize;
if (item.HasMusicInfoTag())
{
m_musicInfoTag = GetMusicInfoTag();
if (m_musicInfoTag)
*m_musicInfoTag = *item.m_musicInfoTag;
}
else
{
delete m_musicInfoTag;
m_musicInfoTag = NULL;
}
if (item.HasVideoInfoTag())
{
m_videoInfoTag = GetVideoInfoTag();
if (m_videoInfoTag)
*m_videoInfoTag = *item.m_videoInfoTag;
}
else
{
delete m_videoInfoTag;
m_videoInfoTag = NULL;
}
if (item.HasPictureInfoTag())
{
m_pictureInfoTag = GetPictureInfoTag();
if (m_pictureInfoTag)
*m_pictureInfoTag = *item.m_pictureInfoTag;
}
else
{
delete m_pictureInfoTag;
m_pictureInfoTag = NULL;
}
m_lStartOffset = item.m_lStartOffset;
m_lEndOffset = item.m_lEndOffset;
m_strDVDLabel = item.m_strDVDLabel;
m_strTitle = item.m_strTitle;
m_iprogramCount = item.m_iprogramCount;
m_idepth = item.m_idepth;
m_iLockMode = item.m_iLockMode;
m_strLockCode = item.m_strLockCode;
m_iHasLock = item.m_iHasLock;
m_iBadPwdCount = item.m_iBadPwdCount;
m_bCanQueue=item.m_bCanQueue;
m_mimetype = item.m_mimetype;
m_extrainfo = item.m_extrainfo;
m_specialSort = item.m_specialSort;
return *this;
}
void CFileItem::Reset()
{
m_strLabel2.Empty();
SetLabel("");
m_bLabelPreformated=false;
FreeIcons();
m_overlayIcon = ICON_OVERLAY_NONE;
m_bSelected = false;
m_strDVDLabel.Empty();
m_strTitle.Empty();
m_strPath.Empty();
m_dwSize = 0;
m_bIsFolder = false;
m_bIsParentFolder=false;
m_bIsShareOrDrive = false;
m_dateTime.Reset();
m_iDriveType = CMediaSource::SOURCE_TYPE_UNKNOWN;
m_lStartOffset = 0;
m_lEndOffset = 0;
m_iprogramCount = 0;
m_idepth = 1;
m_iLockMode = LOCK_MODE_EVERYONE;
m_strLockCode = "";
m_iBadPwdCount = 0;
m_iHasLock = 0;
m_bCanQueue=true;
m_mimetype = "";
delete m_musicInfoTag;
m_musicInfoTag=NULL;
delete m_videoInfoTag;
m_videoInfoTag=NULL;
delete m_pictureInfoTag;
m_pictureInfoTag=NULL;
m_extrainfo.Empty();
m_specialSort = SORT_NORMALLY;
SetInvalid();
}
void CFileItem::Archive(CArchive& ar)
{
CGUIListItem::Archive(ar);
if (ar.IsStoring())
{
ar << m_bIsParentFolder;
ar << m_bLabelPreformated;
ar << m_strPath;
ar << m_bIsShareOrDrive;
ar << m_iDriveType;
ar << m_dateTime;
ar << m_dwSize;
ar << m_strDVDLabel;
ar << m_strTitle;
ar << m_iprogramCount;
ar << m_idepth;
ar << m_lStartOffset;
ar << m_lEndOffset;
ar << m_iLockMode;
ar << m_strLockCode;
ar << m_iBadPwdCount;
ar << m_bCanQueue;
ar << m_mimetype;
ar << m_extrainfo;
ar << m_specialSort;
if (m_musicInfoTag)
{
ar << 1;
ar << *m_musicInfoTag;
}
else
ar << 0;
if (m_videoInfoTag)
{
ar << 1;
ar << *m_videoInfoTag;
}
else
ar << 0;
if (m_pictureInfoTag)
{
ar << 1;
ar << *m_pictureInfoTag;
}
else
ar << 0;
}
else
{
ar >> m_bIsParentFolder;
ar >> m_bLabelPreformated;
ar >> m_strPath;
ar >> m_bIsShareOrDrive;
ar >> m_iDriveType;
ar >> m_dateTime;
ar >> m_dwSize;
ar >> m_strDVDLabel;
ar >> m_strTitle;
ar >> m_iprogramCount;
ar >> m_idepth;
ar >> m_lStartOffset;
ar >> m_lEndOffset;
int temp;
ar >> temp;
m_iLockMode = (LockType)temp;
ar >> m_strLockCode;
ar >> m_iBadPwdCount;
ar >> m_bCanQueue;
ar >> m_mimetype;
ar >> m_extrainfo;
ar >> temp;
m_specialSort = (SPECIAL_SORT)temp;
int iType;
ar >> iType;
if (iType == 1)
ar >> *GetMusicInfoTag();
ar >> iType;
if (iType == 1)
ar >> *GetVideoInfoTag();
ar >> iType;
if (iType == 1)
ar >> *GetPictureInfoTag();
SetInvalid();
}
}
void CFileItem::Serialize(CVariant& value)
{
//CGUIListItem::Serialize(value["CGUIListItem"]);
value["strPath"] = m_strPath;
value["dateTime"] = (m_dateTime.IsValid()) ? m_dateTime.GetAsRFC1123DateTime() : "";
value["size"] = (int) m_dwSize / 1000;
value["DVDLabel"] = m_strDVDLabel;
value["title"] = m_strTitle;
value["mimetype"] = m_mimetype;
value["extrainfo"] = m_extrainfo;
if (m_musicInfoTag)
(*m_musicInfoTag).Serialize(value["musicInfoTag"]);
if (m_videoInfoTag)
(*m_videoInfoTag).Serialize(value["videoInfoTag"]);
if (m_pictureInfoTag)
(*m_pictureInfoTag).Serialize(value["pictureInfoTag"]);
}
bool CFileItem::Exists(bool bUseCache /* = true */) const
{
if (m_strPath.IsEmpty()
|| m_strPath.Equals("add")
|| IsInternetStream()
|| IsParentFolder()
|| IsVirtualDirectoryRoot()
|| IsPlugin())
return true;
if (IsVideoDb() && HasVideoInfoTag())
{
CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
return dbItem.Exists();
}
CStdString strPath = m_strPath;
if (URIUtils::IsMultiPath(strPath))
strPath = CMultiPathDirectory::GetFirstPath(strPath);
if (URIUtils::IsStack(strPath))
strPath = CStackDirectory::GetFirstStackedFile(strPath);
if (m_bIsFolder)
return CDirectory::Exists(strPath);
else
return CFile::Exists(strPath, bUseCache);
return false;
}
bool CFileItem::IsVideo() const
{
/* check preset mime type */
if( m_mimetype.Left(6).Equals("video/") )
return true;
if (HasVideoInfoTag()) return true;
if (HasMusicInfoTag()) return false;
if (HasPictureInfoTag()) return false;
if (IsHDHomeRun() || IsTuxBox() || URIUtils::IsDVD(m_strPath) || IsSlingbox())
return true;
CStdString extension;
if( m_mimetype.Left(12).Equals("application/") )
{ /* check for some standard types */
extension = m_mimetype.Mid(12);
if( extension.Equals("ogg")
|| extension.Equals("mp4")
|| extension.Equals("mxf") )
return true;
}
URIUtils::GetExtension(m_strPath, extension);
if (extension.IsEmpty())
return false;
extension.ToLower();
if (g_settings.m_videoExtensions.Find(extension) != -1)
return true;
return false;
}
bool CFileItem::IsAudio() const
{
/* check preset mime type */
if( m_mimetype.Left(6).Equals("audio/") )
return true;
if (HasMusicInfoTag()) return true;
if (HasVideoInfoTag()) return false;
if (HasPictureInfoTag()) return false;
if (IsCDDA()) return true;
if (!m_bIsFolder && IsShoutCast()) return true;
if (!m_bIsFolder && IsLastFM()) return true;
CStdString extension;
if( m_mimetype.Left(12).Equals("application/") )
{ /* check for some standard types */
extension = m_mimetype.Mid(12);
if( extension.Equals("ogg")
|| extension.Equals("mp4")
|| extension.Equals("mxf") )
return true;
}
URIUtils::GetExtension(m_strPath, extension);
if (extension.IsEmpty())
return false;
extension.ToLower();
if (g_settings.m_musicExtensions.Find(extension) != -1)
return true;
return false;
}
bool CFileItem::IsPicture() const
{
if( m_mimetype.Left(6).Equals("image/") )
return true;
if (HasPictureInfoTag()) return true;
if (HasMusicInfoTag()) return false;
if (HasVideoInfoTag()) return false;
return CUtil::IsPicture(m_strPath);
}
bool CFileItem::IsLyrics() const
{
return URIUtils::GetExtension(m_strPath).Equals(".cdg", false) || URIUtils::GetExtension(m_strPath).Equals(".lrc", false);
}
bool CFileItem::IsCUESheet() const
{
return URIUtils::GetExtension(m_strPath).Equals(".cue", false);
}
bool CFileItem::IsShoutCast() const
{
return URIUtils::IsShoutCast(m_strPath);
}
bool CFileItem::IsLastFM() const
{
return URIUtils::IsLastFM(m_strPath);
}
bool CFileItem::IsInternetStream() const
{
if (HasProperty("IsHTTPDirectory"))
return false;
return URIUtils::IsInternetStream(m_strPath);
}
bool CFileItem::IsFileFolder() const
{
return (
IsSmartPlayList() ||
(IsPlayList() && g_advancedSettings.m_playlistAsFolders) ||
IsZIP() ||
IsRAR() ||
IsRSS() ||
IsType(".ogg") ||
IsType(".nsf") ||
IsType(".sid") ||
IsType(".sap") ||
IsShoutCast()
);
}
bool CFileItem::IsSmartPlayList() const
{
CStdString strExtension;
URIUtils::GetExtension(m_strPath, strExtension);
strExtension.ToLower();
return (strExtension == ".xsp");
}
bool CFileItem::IsPlayList() const
{
return CPlayListFactory::IsPlaylist(*this);
}
bool CFileItem::IsPythonScript() const
{
return URIUtils::GetExtension(m_strPath).Equals(".py", false);
}
bool CFileItem::IsXBE() const
{
return URIUtils::GetExtension(m_strPath).Equals(".xbe", false);
}
bool CFileItem::IsType(const char *ext) const
{
return URIUtils::GetExtension(m_strPath).Equals(ext, false);
}
bool CFileItem::IsDefaultXBE() const
{
CStdString filename = URIUtils::GetFileName(m_strPath);
if (filename.Equals("default.xbe")) return true;
return false;
}
bool CFileItem::IsShortCut() const
{
return URIUtils::GetExtension(m_strPath).Equals(".cut", false);
}
bool CFileItem::IsNFO() const
{
return URIUtils::GetExtension(m_strPath).Equals(".nfo", false);
}
bool CFileItem::IsDVDImage() const
{
CStdString strExtension;
URIUtils::GetExtension(m_strPath, strExtension);
if (strExtension.Equals(".img") || strExtension.Equals(".iso") || strExtension.Equals(".nrg")) return true;
return false;
}
bool CFileItem::IsDVDFile(bool bVobs /*= true*/, bool bIfos /*= true*/) const
{
CStdString strFileName = URIUtils::GetFileName(m_strPath);
if (bIfos)
{
if (strFileName.Equals("video_ts.ifo")) return true;
if (strFileName.Left(4).Equals("vts_") && strFileName.Right(6).Equals("_0.ifo") && strFileName.length() == 12) return true;
}
if (bVobs)
{
if (strFileName.Equals("video_ts.vob")) return true;
if (strFileName.Left(4).Equals("vts_") && strFileName.Right(4).Equals(".vob")) return true;
}
return false;
}
bool CFileItem::IsRAR() const
{
return URIUtils::IsRAR(m_strPath);
}
bool CFileItem::IsZIP() const
{
return URIUtils::IsZIP(m_strPath);
}
bool CFileItem::IsCBZ() const
{
return URIUtils::GetExtension(m_strPath).Equals(".cbz", false);
}
bool CFileItem::IsCBR() const
{
return URIUtils::GetExtension(m_strPath).Equals(".cbr", false);
}
bool CFileItem::IsRSS() const
{
if (m_strPath.Left(6).Equals("rss://"))
return true;
return URIUtils::GetExtension(m_strPath).Equals(".rss")
|| GetMimeType() == "application/rss+xml";
}
bool CFileItem::IsStack() const
{
return URIUtils::IsStack(m_strPath);
}
bool CFileItem::IsPlugin() const
{
return URIUtils::IsPlugin(m_strPath);
}
bool CFileItem::IsPluginRoot() const
{
return URIUtils::IsPluginRoot(m_strPath);
}
bool CFileItem::IsMultiPath() const
{
return URIUtils::IsMultiPath(m_strPath);
}
bool CFileItem::IsCDDA() const
{
return URIUtils::IsCDDA(m_strPath);
}
bool CFileItem::IsDVD() const
{
return URIUtils::IsDVD(m_strPath);
}
bool CFileItem::IsOnDVD() const
{
return URIUtils::IsOnDVD(m_strPath);
}
bool CFileItem::IsOnLAN() const
{
return URIUtils::IsOnLAN(m_strPath);
}
bool CFileItem::IsISO9660() const
{
return URIUtils::IsISO9660(m_strPath);
}
bool CFileItem::IsRemote() const
{
return URIUtils::IsRemote(m_strPath);
}
bool CFileItem::IsSmb() const
{
return URIUtils::IsSmb(m_strPath);
}
bool CFileItem::IsXBMS() const
{
return URIUtils::IsXBMS(m_strPath);
}
bool CFileItem::IsURL() const
{
return URIUtils::IsURL(m_strPath);
}
bool CFileItem::IsDAAP() const
{
return URIUtils::IsDAAP(m_strPath);
}
bool CFileItem::IsTuxBox() const
{
return URIUtils::IsTuxBox(m_strPath);
}
bool CFileItem::IsMythTV() const
{
return URIUtils::IsMythTV(m_strPath);
}
bool CFileItem::IsHDHomeRun() const
{
return URIUtils::IsHDHomeRun(m_strPath);
}
bool CFileItem::IsSlingbox() const
{
return URIUtils::IsSlingbox(m_strPath);
}
bool CFileItem::IsVTP() const
{
return URIUtils::IsVTP(m_strPath);
}
bool CFileItem::IsLiveTV() const
{
return URIUtils::IsLiveTV(m_strPath);
}
bool CFileItem::IsHD() const
{
return URIUtils::IsHD(m_strPath);
}
bool CFileItem::IsMusicDb() const
{
CURL url(m_strPath);
return url.GetProtocol().Equals("musicdb");
}
bool CFileItem::IsVideoDb() const
{
CURL url(m_strPath);
return url.GetProtocol().Equals("videodb");
}
bool CFileItem::IsVirtualDirectoryRoot() const
{
return (m_bIsFolder && m_strPath.IsEmpty());
}
bool CFileItem::IsMemoryUnit() const
{
CURL url(m_strPath);
return url.GetProtocol().Left(3).Equals("mem");
}
bool CFileItem::IsRemovable() const
{
return IsOnDVD() || IsCDDA() || IsMemoryUnit();
}
bool CFileItem::IsReadOnly() const
{
if (IsParentFolder()) return true;
if (m_bIsShareOrDrive) return true;
return !CUtil::SupportsWriteFileOperations(m_strPath);
}
void CFileItem::FillInDefaultIcon()
{
//CLog::Log(LOGINFO, "FillInDefaultIcon(%s)", pItem->GetLabel().c_str());
// find the default icon for a file or folder item
// for files this can be the (depending on the file type)
// default picture for photo's
// default picture for songs
// default picture for videos
// default picture for shortcuts
// default picture for playlists
// or the icon embedded in an .xbe
//
// for folders
// for .. folders the default picture for parent folder
// for other folders the defaultFolder.png
if (GetIconImage().IsEmpty())
{
if (!m_bIsFolder)
{
/* To reduce the average runtime of this code, this list should
* be ordered with most frequently seen types first. Also bear
* in mind the complexity of the code behind the check in the
* case of IsWhatater() returns false.
*/
if ( IsAudio() )
{
// audio
SetIconImage("DefaultAudio.png");
}
else if ( IsVideo() )
{
// video
SetIconImage("DefaultVideo.png");
}
else if ( IsPicture() )
{
// picture
SetIconImage("DefaultPicture.png");
}
else if ( IsPlayList() )
{
SetIconImage("DefaultPlaylist.png");
}
else if ( IsXBE() )
{
// xbe
SetIconImage("DefaultProgram.png");
}
else if ( IsShortCut() && !IsLabelPreformated() )
{
// shortcut
CStdString strFName = URIUtils::GetFileName(m_strPath);
int iPos = strFName.ReverseFind(".");
CStdString strDescription = strFName.Left(iPos);
SetLabel(strDescription);
SetIconImage("DefaultShortcut.png");
}
else if ( IsPythonScript() )
{
SetIconImage("DefaultScript.png");
}
else
{
// default icon for unknown file type
SetIconImage("DefaultFile.png");
}
}
else
{
if ( IsPlayList() )
{
SetIconImage("DefaultPlaylist.png");
}
else if (IsParentFolder())
{
SetIconImage("DefaultFolderBack.png");
}
else
{
SetIconImage("DefaultFolder.png");
}
}
}
// Set the icon overlays (if applicable)
if (!HasOverlay())
{
if (URIUtils::IsInRAR(m_strPath))
SetOverlayImage(CGUIListItem::ICON_OVERLAY_RAR);
else if (URIUtils::IsInZIP(m_strPath))
SetOverlayImage(CGUIListItem::ICON_OVERLAY_ZIP);
}
}
CStdString CFileItem::GetCachedArtistThumb() const
{
return GetCachedThumb("artist"+GetLabel(),g_settings.GetMusicArtistThumbFolder());
}
CStdString CFileItem::GetCachedProfileThumb() const
{
return GetCachedThumb("profile"+m_strPath,URIUtils::AddFileToFolder(g_settings.GetUserDataFolder(),"Thumbnails\\Profiles"));
}
CStdString CFileItem::GetCachedSeasonThumb() const
{
CStdString seasonPath;
if (HasVideoInfoTag())
seasonPath = GetVideoInfoTag()->m_strPath;
return GetCachedThumb("season"+seasonPath+GetLabel(),g_settings.GetVideoThumbFolder(),true);
}
CStdString CFileItem::GetCachedActorThumb() const
{
return GetCachedThumb("actor"+GetLabel(),g_settings.GetVideoThumbFolder(),true);
}
void CFileItem::SetCachedArtistThumb()
{
CStdString thumb(GetCachedArtistThumb());
if (CFile::Exists(thumb))
{
// found it, we are finished.
SetThumbnailImage(thumb);
}
}
// set the album thumb for a file or folder
void CFileItem::SetMusicThumb(bool alwaysCheckRemote /* = true */)
{
if (HasThumbnail()) return;
SetCachedMusicThumb();
if (!HasThumbnail())
SetUserMusicThumb(alwaysCheckRemote);
}
void CFileItem::SetCachedSeasonThumb()
{
CStdString thumb(GetCachedSeasonThumb());
if (CFile::Exists(thumb))
{
// found it, we are finished.
SetThumbnailImage(thumb);
}
}
void CFileItem::RemoveExtension()
{
if (m_bIsFolder)
return;
CStdString strLabel = GetLabel();
URIUtils::RemoveExtension(strLabel);
SetLabel(strLabel);
}
void CFileItem::CleanString()
{
if (IsLiveTV())
return;
bool bIsFolder = m_bIsFolder;
// make sure we don't append the extension to stacked dvd folders
if (HasProperty("isstacked") && IsDVDFile(false, true))
bIsFolder = true;
CStdString strLabel = GetLabel();
CStdString strTitle, strTitleAndYear, strYear;
CUtil::CleanString(strLabel, strTitle, strTitleAndYear, strYear, (bIsFolder || !g_guiSettings.GetBool("filelists.showextensions") ) );
SetLabel(strTitleAndYear);
}
void CFileItem::SetLabel(const CStdString &strLabel)
{
if (strLabel=="..")
{
m_bIsParentFolder=true;
m_bIsFolder=true;
m_specialSort = SORT_ON_TOP;
SetLabelPreformated(true);
}
CGUIListItem::SetLabel(strLabel);
}
void CFileItem::SetFileSizeLabel()
{
if( m_bIsFolder && m_dwSize == 0 )
SetLabel2("");
else
SetLabel2(StringUtils::SizeToString(m_dwSize));
}
CURL CFileItem::GetAsUrl() const
{
return CURL(m_strPath);
}
bool CFileItem::CanQueue() const
{
return m_bCanQueue;
}
void CFileItem::SetCanQueue(bool bYesNo)
{
m_bCanQueue=bYesNo;
}
bool CFileItem::IsParentFolder() const
{
return m_bIsParentFolder;
}
const CStdString& CFileItem::GetMimeType(bool lookup /*= true*/) const
{
if( m_mimetype.IsEmpty() && lookup)
{
// discard const qualifyier
CStdString& m_ref = (CStdString&)m_mimetype;
if( m_bIsFolder )
m_ref = "x-directory/normal";
else if( m_strPath.Left(8).Equals("shout://")
|| m_strPath.Left(7).Equals("http://")
|| m_strPath.Left(8).Equals("https://"))
{
CCurlFile::GetMimeType(GetAsUrl(), m_ref);
// try to get mime-type again but with an NSPlayer User-Agent
// in order for server to provide correct mime-type. Allows us
// to properly detect an MMS stream
if (m_ref.Left(11).Equals("video/x-ms-"))
CCurlFile::GetMimeType(GetAsUrl(), m_ref, "NSPlayer/11.00.6001.7000");
// make sure there are no options set in mime-type
// mime-type can look like "video/x-ms-asf ; charset=utf8"
int i = m_ref.Find(';');
if(i>=0)
m_ref.Delete(i,m_ref.length()-i);
m_ref.Trim();
}
// if it's still empty set to an unknown type
if( m_ref.IsEmpty() )
m_ref = "application/octet-stream";
}
// change protocol to mms for the following mome-type. Allows us to create proper FileMMS.
if( m_mimetype.Left(32).Equals("application/vnd.ms.wms-hdr.asfv1") || m_mimetype.Left(24).Equals("application/x-mms-framed") )
{
CStdString& m_path = (CStdString&)m_strPath;
m_path.Replace("http:", "mms:");
}
return m_mimetype;
}
bool CFileItem::IsSamePath(const CFileItem *item) const
{
if (!item)
return false;
if (item->GetPath() == m_strPath && item->m_lStartOffset == m_lStartOffset) return true;
if (IsMusicDb() && HasMusicInfoTag())
{
CFileItem dbItem(m_musicInfoTag->GetURL(), false);
dbItem.m_lStartOffset = m_lStartOffset;
return dbItem.IsSamePath(item);
}
if (IsVideoDb() && HasVideoInfoTag())
{
CFileItem dbItem(m_videoInfoTag->m_strFileNameAndPath, false);
dbItem.m_lStartOffset = m_lStartOffset;
return dbItem.IsSamePath(item);
}
if (item->IsMusicDb() && item->HasMusicInfoTag())
{
CFileItem dbItem(item->m_musicInfoTag->GetURL(), false);
dbItem.m_lStartOffset = item->m_lStartOffset;
return IsSamePath(&dbItem);
}
if (item->IsVideoDb() && item->HasVideoInfoTag())
{
CFileItem dbItem(item->m_videoInfoTag->m_strFileNameAndPath, false);
dbItem.m_lStartOffset = item->m_lStartOffset;
return IsSamePath(&dbItem);
}
if (HasProperty("original_listitem_url"))
return (GetProperty("original_listitem_url") == item->GetPath());
return false;
}
/////////////////////////////////////////////////////////////////////////////////
/////
///// CFileItemList
/////
//////////////////////////////////////////////////////////////////////////////////
CFileItemList::CFileItemList()
{
m_fastLookup = false;
m_bIsFolder=true;
m_cacheToDisc=CACHE_IF_SLOW;
m_sortMethod=SORT_METHOD_NONE;
m_sortOrder=SORT_ORDER_NONE;
m_replaceListing = false;
}
CFileItemList::CFileItemList(const CStdString& strPath) : CFileItem(strPath, true)
{
m_fastLookup = false;
m_cacheToDisc=CACHE_IF_SLOW;
m_sortMethod=SORT_METHOD_NONE;
m_sortOrder=SORT_ORDER_NONE;
m_replaceListing = false;
}
CFileItemList::~CFileItemList()
{
Clear();
}
CFileItemPtr CFileItemList::operator[] (int iItem)
{
return Get(iItem);
}
const CFileItemPtr CFileItemList::operator[] (int iItem) const
{
return Get(iItem);
}
CFileItemPtr CFileItemList::operator[] (const CStdString& strPath)
{
return Get(strPath);
}
const CFileItemPtr CFileItemList::operator[] (const CStdString& strPath) const
{
return Get(strPath);
}
void CFileItemList::SetFastLookup(bool fastLookup)
{
CSingleLock lock(m_lock);
if (fastLookup && !m_fastLookup)
{ // generate the map
m_map.clear();
for (unsigned int i=0; i < m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
CStdString path(pItem->GetPath());
path.ToLower();
m_map.insert(MAPFILEITEMSPAIR(path, pItem));
}
}
if (!fastLookup && m_fastLookup)
m_map.clear();
m_fastLookup = fastLookup;
}
bool CFileItemList::Contains(const CStdString& fileName) const
{
CSingleLock lock(m_lock);
// checks case insensitive
CStdString checkPath(fileName);
checkPath.ToLower();
if (m_fastLookup)
return m_map.find(checkPath) != m_map.end();
// slow method...
for (unsigned int i = 0; i < m_items.size(); i++)
{
const CFileItemPtr pItem = m_items[i];
if (pItem->GetPath().Equals(checkPath))
return true;
}
return false;
}
void CFileItemList::Clear()
{
CSingleLock lock(m_lock);
ClearItems();
m_sortMethod=SORT_METHOD_NONE;
m_sortOrder=SORT_ORDER_NONE;
m_cacheToDisc=CACHE_IF_SLOW;
m_sortDetails.clear();
m_replaceListing = false;
m_content.Empty();
}
void CFileItemList::ClearItems()
{
CSingleLock lock(m_lock);
// make sure we free the memory of the items (these are GUIControls which may have allocated resources)
FreeMemory();
for (unsigned int i = 0; i < m_items.size(); i++)
{
CFileItemPtr item = m_items[i];
item->FreeMemory();
}
m_items.clear();
m_map.clear();
}
void CFileItemList::Add(const CFileItemPtr &pItem)
{
CSingleLock lock(m_lock);
m_items.push_back(pItem);
if (m_fastLookup)
{
CStdString path(pItem->GetPath());
path.ToLower();
m_map.insert(MAPFILEITEMSPAIR(path, pItem));
}
}
void CFileItemList::AddFront(const CFileItemPtr &pItem, int itemPosition)
{
CSingleLock lock(m_lock);
if (itemPosition >= 0)
{
m_items.insert(m_items.begin()+itemPosition, pItem);
}
else
{
m_items.insert(m_items.begin()+(m_items.size()+itemPosition), pItem);
}
if (m_fastLookup)
{
CStdString path(pItem->GetPath());
path.ToLower();
m_map.insert(MAPFILEITEMSPAIR(path, pItem));
}
}
void CFileItemList::Remove(CFileItem* pItem)
{
CSingleLock lock(m_lock);
for (IVECFILEITEMS it = m_items.begin(); it != m_items.end(); ++it)
{
if (pItem == it->get())
{
m_items.erase(it);
if (m_fastLookup)
{
CStdString path(pItem->GetPath());
path.ToLower();
m_map.erase(path);
}
break;
}
}
}
void CFileItemList::Remove(int iItem)
{
CSingleLock lock(m_lock);
if (iItem >= 0 && iItem < (int)Size())
{
CFileItemPtr pItem = *(m_items.begin() + iItem);
if (m_fastLookup)
{
CStdString path(pItem->GetPath());
path.ToLower();
m_map.erase(path);
}
m_items.erase(m_items.begin() + iItem);
}
}
void CFileItemList::Append(const CFileItemList& itemlist)
{
CSingleLock lock(m_lock);
for (int i = 0; i < itemlist.Size(); ++i)
Add(itemlist[i]);
}
void CFileItemList::Assign(const CFileItemList& itemlist, bool append)
{
CSingleLock lock(m_lock);
if (!append)
Clear();
Append(itemlist);
SetPath(itemlist.GetPath());
m_sortDetails = itemlist.m_sortDetails;
m_replaceListing = itemlist.m_replaceListing;
m_content = itemlist.m_content;
m_mapProperties = itemlist.m_mapProperties;
m_cacheToDisc = itemlist.m_cacheToDisc;
}
bool CFileItemList::Copy(const CFileItemList& items)
{
// assign all CFileItem parts
*(CFileItem*)this = *(CFileItem*)&items;
// assign the rest of the CFileItemList properties
m_replaceListing = items.m_replaceListing;
m_content = items.m_content;
m_mapProperties = items.m_mapProperties;
m_cacheToDisc = items.m_cacheToDisc;
m_sortDetails = items.m_sortDetails;
m_sortMethod = items.m_sortMethod;
m_sortOrder = items.m_sortOrder;
// make a copy of each item
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr newItem(new CFileItem(*items[i]));
Add(newItem);
}
return true;
}
CFileItemPtr CFileItemList::Get(int iItem)
{
CSingleLock lock(m_lock);
if (iItem > -1 && iItem < (int)m_items.size())
return m_items[iItem];
return CFileItemPtr();
}
const CFileItemPtr CFileItemList::Get(int iItem) const
{
CSingleLock lock(m_lock);
if (iItem > -1 && iItem < (int)m_items.size())
return m_items[iItem];
return CFileItemPtr();
}
CFileItemPtr CFileItemList::Get(const CStdString& strPath)
{
CSingleLock lock(m_lock);
CStdString pathToCheck(strPath);
pathToCheck.ToLower();
if (m_fastLookup)
{
IMAPFILEITEMS it=m_map.find(pathToCheck);
if (it != m_map.end())
return it->second;
return CFileItemPtr();
}
// slow method...
for (unsigned int i = 0; i < m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (pItem->GetPath().Equals(pathToCheck))
return pItem;
}
return CFileItemPtr();
}
const CFileItemPtr CFileItemList::Get(const CStdString& strPath) const
{
CSingleLock lock(m_lock);
CStdString pathToCheck(strPath);
pathToCheck.ToLower();
if (m_fastLookup)
{
map::const_iterator it=m_map.find(pathToCheck);
if (it != m_map.end())
return it->second;
return CFileItemPtr();
}
// slow method...
for (unsigned int i = 0; i < m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (pItem->GetPath().Equals(pathToCheck))
return pItem;
}
return CFileItemPtr();
}
int CFileItemList::Size() const
{
CSingleLock lock(m_lock);
return (int)m_items.size();
}
bool CFileItemList::IsEmpty() const
{
CSingleLock lock(m_lock);
return (m_items.size() <= 0);
}
void CFileItemList::Reserve(int iCount)
{
CSingleLock lock(m_lock);
m_items.reserve(iCount);
}
void CFileItemList::Sort(FILEITEMLISTCOMPARISONFUNC func)
{
CSingleLock lock(m_lock);
DWORD dwStart = GetTickCount();
std::stable_sort(m_items.begin(), m_items.end(), func);
DWORD dwElapsed = GetTickCount() - dwStart;
CLog::Log(LOGDEBUG,"%s, sorting took %u millis", __FUNCTION__, dwElapsed);
}
void CFileItemList::FillSortFields(FILEITEMFILLFUNC func)
{
CSingleLock lock(m_lock);
std::for_each(m_items.begin(), m_items.end(), func);
}
void CFileItemList::Sort(SORT_METHOD sortMethod, SORT_ORDER sortOrder)
{
// Already sorted?
if (sortMethod==m_sortMethod && m_sortOrder==sortOrder)
return;
switch (sortMethod)
{
case SORT_METHOD_LABEL:
case SORT_METHOD_LABEL_IGNORE_FOLDERS:
FillSortFields(SSortFileItem::ByLabel);
break;
case SORT_METHOD_LABEL_IGNORE_THE:
FillSortFields(SSortFileItem::ByLabelNoThe);
break;
case SORT_METHOD_DATE:
FillSortFields(SSortFileItem::ByDate);
break;
case SORT_METHOD_SIZE:
FillSortFields(SSortFileItem::BySize);
break;
case SORT_METHOD_BITRATE:
FillSortFields(SSortFileItem::ByBitrate);
break;
case SORT_METHOD_DRIVE_TYPE:
FillSortFields(SSortFileItem::ByDriveType);
break;
case SORT_METHOD_TRACKNUM:
FillSortFields(SSortFileItem::BySongTrackNum);
break;
case SORT_METHOD_EPISODE:
FillSortFields(SSortFileItem::ByEpisodeNum);
break;
case SORT_METHOD_DURATION:
FillSortFields(SSortFileItem::BySongDuration);
break;
case SORT_METHOD_TITLE_IGNORE_THE:
FillSortFields(SSortFileItem::BySongTitleNoThe);
break;
case SORT_METHOD_TITLE:
FillSortFields(SSortFileItem::BySongTitle);
break;
case SORT_METHOD_ARTIST:
FillSortFields(SSortFileItem::BySongArtist);
break;
case SORT_METHOD_ARTIST_IGNORE_THE:
FillSortFields(SSortFileItem::BySongArtistNoThe);
break;
case SORT_METHOD_ALBUM:
FillSortFields(SSortFileItem::BySongAlbum);
break;
case SORT_METHOD_ALBUM_IGNORE_THE:
FillSortFields(SSortFileItem::BySongAlbumNoThe);
break;
case SORT_METHOD_GENRE:
FillSortFields(SSortFileItem::ByGenre);
break;
case SORT_METHOD_DATEADDED:
FillSortFields(SSortFileItem::ByDateAdded);
break;
case SORT_METHOD_FILE:
FillSortFields(SSortFileItem::ByFile);
break;
case SORT_METHOD_VIDEO_RATING:
FillSortFields(SSortFileItem::ByMovieRating);
break;
case SORT_METHOD_VIDEO_TITLE:
FillSortFields(SSortFileItem::ByMovieTitle);
break;
case SORT_METHOD_VIDEO_SORT_TITLE:
FillSortFields(SSortFileItem::ByMovieSortTitle);
break;
case SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE:
FillSortFields(SSortFileItem::ByMovieSortTitleNoThe);
break;
case SORT_METHOD_YEAR:
FillSortFields(SSortFileItem::ByYear);
break;
case SORT_METHOD_PRODUCTIONCODE:
FillSortFields(SSortFileItem::ByProductionCode);
break;
case SORT_METHOD_PROGRAM_COUNT:
case SORT_METHOD_PLAYLIST_ORDER:
// TODO: Playlist order is hacked into program count variable (not nice, but ok until 2.0)
FillSortFields(SSortFileItem::ByProgramCount);
break;
case SORT_METHOD_SONG_RATING:
FillSortFields(SSortFileItem::BySongRating);
break;
case SORT_METHOD_MPAA_RATING:
FillSortFields(SSortFileItem::ByMPAARating);
break;
case SORT_METHOD_VIDEO_RUNTIME:
FillSortFields(SSortFileItem::ByMovieRuntime);
break;
case SORT_METHOD_STUDIO:
FillSortFields(SSortFileItem::ByStudio);
break;
case SORT_METHOD_STUDIO_IGNORE_THE:
FillSortFields(SSortFileItem::ByStudioNoThe);
break;
case SORT_METHOD_FULLPATH:
FillSortFields(SSortFileItem::ByFullPath);
break;
case SORT_METHOD_LASTPLAYED:
FillSortFields(SSortFileItem::ByLastPlayed);
break;
case SORT_METHOD_LISTENERS:
FillSortFields(SSortFileItem::ByListeners);
break;
default:
break;
}
if (sortMethod == SORT_METHOD_FILE ||
sortMethod == SORT_METHOD_VIDEO_SORT_TITLE ||
sortMethod == SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE ||
sortMethod == SORT_METHOD_LABEL_IGNORE_FOLDERS)
Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::IgnoreFoldersAscending : SSortFileItem::IgnoreFoldersDescending);
else if (sortMethod != SORT_METHOD_NONE && sortMethod != SORT_METHOD_UNSORTED)
Sort(sortOrder==SORT_ORDER_ASC ? SSortFileItem::Ascending : SSortFileItem::Descending);
m_sortMethod=sortMethod;
m_sortOrder=sortOrder;
}
void CFileItemList::Randomize()
{
CSingleLock lock(m_lock);
random_shuffle(m_items.begin(), m_items.end());
}
void CFileItemList::Archive(CArchive& ar)
{
CSingleLock lock(m_lock);
if (ar.IsStoring())
{
CFileItem::Archive(ar);
int i = 0;
if (m_items.size() > 0 && m_items[0]->IsParentFolder())
i = 1;
ar << (int)(m_items.size() - i);
ar << m_fastLookup;
ar << (int)m_sortMethod;
ar << (int)m_sortOrder;
ar << (int)m_cacheToDisc;
ar << (int)m_sortDetails.size();
for (unsigned int j = 0; j < m_sortDetails.size(); ++j)
{
const SORT_METHOD_DETAILS &details = m_sortDetails[j];
ar << (int)details.m_sortMethod;
ar << details.m_buttonLabel;
ar << details.m_labelMasks.m_strLabelFile;
ar << details.m_labelMasks.m_strLabelFolder;
ar << details.m_labelMasks.m_strLabel2File;
ar << details.m_labelMasks.m_strLabel2Folder;
}
ar << m_content;
for (; i < (int)m_items.size(); ++i)
{
CFileItemPtr pItem = m_items[i];
ar << *pItem;
}
}
else
{
CFileItemPtr pParent;
if (!IsEmpty())
{
CFileItemPtr pItem=m_items[0];
if (pItem->IsParentFolder())
pParent.reset(new CFileItem(*pItem));
}
SetFastLookup(false);
Clear();
CFileItem::Archive(ar);
int iSize = 0;
ar >> iSize;
if (iSize <= 0)
return ;
if (pParent)
{
m_items.reserve(iSize + 1);
m_items.push_back(pParent);
}
else
m_items.reserve(iSize);
bool fastLookup=false;
ar >> fastLookup;
int tempint;
ar >> (int&)tempint;
m_sortMethod = SORT_METHOD(tempint);
ar >> (int&)tempint;
m_sortOrder = SORT_ORDER(tempint);
ar >> (int&)tempint;
m_cacheToDisc = CACHE_TYPE(tempint);
unsigned int detailSize = 0;
ar >> detailSize;
for (unsigned int j = 0; j < detailSize; ++j)
{
SORT_METHOD_DETAILS details;
ar >> (int&)tempint;
details.m_sortMethod = SORT_METHOD(tempint);
ar >> details.m_buttonLabel;
ar >> details.m_labelMasks.m_strLabelFile;
ar >> details.m_labelMasks.m_strLabelFolder;
ar >> details.m_labelMasks.m_strLabel2File;
ar >> details.m_labelMasks.m_strLabel2Folder;
m_sortDetails.push_back(details);
}
ar >> m_content;
for (int i = 0; i < iSize; ++i)
{
CFileItemPtr pItem(new CFileItem);
ar >> *pItem;
Add(pItem);
}
SetFastLookup(fastLookup);
}
}
void CFileItemList::FillInDefaultIcons()
{
CSingleLock lock(m_lock);
for (int i = 0; i < (int)m_items.size(); ++i)
{
CFileItemPtr pItem = m_items[i];
pItem->FillInDefaultIcon();
}
}
void CFileItemList::SetMusicThumbs()
{
CSingleLock lock(m_lock);
//cache thumbnails directory
g_directoryCache.InitMusicThumbCache();
for (int i = 0; i < (int)m_items.size(); ++i)
{
CFileItemPtr pItem = m_items[i];
pItem->SetMusicThumb();
}
g_directoryCache.ClearMusicThumbCache();
}
int CFileItemList::GetFolderCount() const
{
CSingleLock lock(m_lock);
int nFolderCount = 0;
for (int i = 0; i < (int)m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (pItem->m_bIsFolder)
nFolderCount++;
}
return nFolderCount;
}
int CFileItemList::GetObjectCount() const
{
CSingleLock lock(m_lock);
int numObjects = (int)m_items.size();
if (numObjects && m_items[0]->IsParentFolder())
numObjects--;
return numObjects;
}
int CFileItemList::GetFileCount() const
{
CSingleLock lock(m_lock);
int nFileCount = 0;
for (int i = 0; i < (int)m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (!pItem->m_bIsFolder)
nFileCount++;
}
return nFileCount;
}
int CFileItemList::GetSelectedCount() const
{
CSingleLock lock(m_lock);
int count = 0;
for (int i = 0; i < (int)m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (pItem->IsSelected())
count++;
}
return count;
}
void CFileItemList::FilterCueItems()
{
CSingleLock lock(m_lock);
// Handle .CUE sheet files...
VECSONGS itemstoadd;
CStdStringArray itemstodelete;
for (int i = 0; i < (int)m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (!pItem->m_bIsFolder)
{ // see if it's a .CUE sheet
if (pItem->IsCUESheet())
{
CCueDocument cuesheet;
if (cuesheet.Parse(pItem->GetPath()))
{
VECSONGS newitems;
cuesheet.GetSongs(newitems);
std::vector MediaFileVec;
cuesheet.GetMediaFiles(MediaFileVec);
// queue the cue sheet and the underlying media file for deletion
for(std::vector::iterator itMedia = MediaFileVec.begin(); itMedia != MediaFileVec.end(); itMedia++)
{
CStdString strMediaFile = *itMedia;
CStdString fileFromCue = strMediaFile; // save the file from the cue we're matching against,
// as we're going to search for others here...
bool bFoundMediaFile = CFile::Exists(strMediaFile);
// queue the cue sheet and the underlying media file for deletion
if (!bFoundMediaFile)
{
// try file in same dir, not matching case...
if (Contains(strMediaFile))
{
bFoundMediaFile = true;
}
else
{
// try removing the .cue extension...
strMediaFile = pItem->GetPath();
URIUtils::RemoveExtension(strMediaFile);
CFileItem item(strMediaFile, false);
if (item.IsAudio() && Contains(strMediaFile))
{
bFoundMediaFile = true;
}
else
{ // try replacing the extension with one of our allowed ones.
CStdStringArray extensions;
StringUtils::SplitString(g_settings.m_musicExtensions, "|", extensions);
for (unsigned int i = 0; i < extensions.size(); i++)
{
strMediaFile = URIUtils::ReplaceExtension(pItem->GetPath(), extensions[i]);
CFileItem item(strMediaFile, false);
if (!item.IsCUESheet() && !item.IsPlayList() && Contains(strMediaFile))
{
bFoundMediaFile = true;
break;
}
}
}
}
}
if (bFoundMediaFile)
{
itemstodelete.push_back(pItem->GetPath());
itemstodelete.push_back(strMediaFile);
// get the additional stuff (year, genre etc.) from the underlying media files tag.
CMusicInfoTag tag;
auto_ptr pLoader (CMusicInfoTagLoaderFactory::CreateLoader(strMediaFile));
if (NULL != pLoader.get())
{
// get id3tag
pLoader->Load(strMediaFile, tag);
}
// fill in any missing entries from underlying media file
for (int j = 0; j < (int)newitems.size(); j++)
{
CSong song = newitems[j];
// only for songs that actually match the current media file
if (song.strFileName == fileFromCue)
{
// we might have a new media file from the above matching code
song.strFileName = strMediaFile;
if (tag.Loaded())
{
if (song.strAlbum.empty() && !tag.GetAlbum().empty()) song.strAlbum = tag.GetAlbum();
if (song.strAlbumArtist.empty() && !tag.GetAlbumArtist().empty()) song.strAlbumArtist = tag.GetAlbumArtist();
if (song.strGenre.empty() && !tag.GetGenre().empty()) song.strGenre = tag.GetGenre();
if (song.strArtist.empty() && !tag.GetArtist().empty()) song.strArtist = tag.GetArtist();
if (tag.GetDiscNumber()) song.iTrack |= (tag.GetDiscNumber() << 16); // see CMusicInfoTag::GetDiscNumber()
SYSTEMTIME dateTime;
tag.GetReleaseDate(dateTime);
if (dateTime.wYear) song.iYear = dateTime.wYear;
}
if (!song.iDuration && tag.GetDuration() > 0)
{ // must be the last song
song.iDuration = (tag.GetDuration() * 75 - song.iStartOffset + 37) / 75;
}
// add this item to the list
itemstoadd.push_back(song);
}
}
}
else
{ // remove the .cue sheet from the directory
itemstodelete.push_back(pItem->GetPath());
}
}
}
else
{ // remove the .cue sheet from the directory (can't parse it - no point listing it)
itemstodelete.push_back(pItem->GetPath());
}
}
}
}
// now delete the .CUE files and underlying media files.
for (int i = 0; i < (int)itemstodelete.size(); i++)
{
for (int j = 0; j < (int)m_items.size(); j++)
{
CFileItemPtr pItem = m_items[j];
if (stricmp(pItem->GetPath().c_str(), itemstodelete[i].c_str()) == 0)
{ // delete this item
m_items.erase(m_items.begin() + j);
break;
}
}
}
// and add the files from the .CUE sheet
for (int i = 0; i < (int)itemstoadd.size(); i++)
{
// now create the file item, and add to the item list.
CFileItemPtr pItem(new CFileItem(itemstoadd[i]));
m_items.push_back(pItem);
}
}
// Remove the extensions from the filenames
void CFileItemList::RemoveExtensions()
{
CSingleLock lock(m_lock);
for (int i = 0; i < Size(); ++i)
m_items[i]->RemoveExtension();
}
void CFileItemList::Stack()
{
CSingleLock lock(m_lock);
// not allowed here
if (IsVirtualDirectoryRoot() || IsLiveTV())
return;
SetProperty("isstacked", "1");
// items needs to be sorted for stuff below to work properly
Sort(SORT_METHOD_LABEL, SORT_ORDER_ASC);
// stack folders
int i = 0;
for (i = 0; i < Size(); ++i)
{
CFileItemPtr item = Get(i);
// combined the folder checks
if (item->m_bIsFolder && !item->IsPlugin())
{
// only check known fast sources?
// xbms included because it supports file existance
// NOTES:
// 1. xbms would not have worked previously: item->GetPath().Left(5).Equals("xbms", false)
// 2. rars and zips may be on slow sources? is this supposed to be allowed?
if( !item->IsRemote()
|| item->IsSmb()
|| item->IsXBMS()
|| URIUtils::IsInRAR(item->GetPath())
|| URIUtils::IsInZIP(item->GetPath())
)
{
// stack cd# folders if contains only a single video file
// NOTE: if we're doing this anyway, why not collapse *all* folders with just a single video file?
CStdString folderName = item->GetLabel();
if (folderName.Left(2).Equals("CD") && StringUtils::IsNaturalNumber(folderName.Mid(2)))
{
CFileItemList items;
CDirectory::GetDirectory(item->GetPath(),items,g_settings.m_videoExtensions,true);
// optimized to only traverse listing once by checking for filecount
// and recording last file item for later use
int nFiles = 0;
int index = -1;
for (int j = 0; j < items.Size(); j++)
{
if (!items[j]->m_bIsFolder)
{
nFiles++;
index = j;
}
if (nFiles > 1)
break;
}
if (nFiles == 1)
{
*item = *items[index];
}
}
// check for dvd folders
else
{
CStdString path;
CStdString dvdPath;
URIUtils::AddFileToFolder(item->GetPath(), "VIDEO_TS.IFO", path);
if (CFile::Exists(path))
dvdPath = path;
else
{
URIUtils::AddFileToFolder(item->GetPath(), "VIDEO_TS", dvdPath);
URIUtils::AddFileToFolder(dvdPath, "VIDEO_TS.IFO", path);
dvdPath.Empty();
if (CFile::Exists(path))
dvdPath = path;
}
if (!dvdPath.IsEmpty())
{
// NOTE: should this be done for the CD# folders too?
/* set the thumbnail based on folder */
item->SetCachedVideoThumb();
if (!item->HasThumbnail())
item->SetUserVideoThumb();
item->m_bIsFolder = false;
item->SetPath(dvdPath);
item->SetLabel2("");
item->SetLabelPreformated(true);
m_sortMethod = SORT_METHOD_NONE; /* sorting is now broken */
/* override the previously set thumb if video_ts.ifo has any */
/* otherwise user can't set icon on the stacked file as that */
/* will allways be set on the video_ts.ifo file */
CStdString thumb(item->GetCachedVideoThumb());
if(CFile::Exists(thumb))
item->SetThumbnailImage(thumb);
else
item->SetUserVideoThumb();
}
}
}
}
}
// Precompile our REs
VECCREGEXP stackRegExps;
CRegExp tmpRegExp(true);
const CStdStringArray& strStackRegExps = g_advancedSettings.m_videoStackRegExps;
CStdStringArray::const_iterator strRegExp = strStackRegExps.begin();
while (strRegExp != strStackRegExps.end())
{
if (tmpRegExp.RegComp(*strRegExp))
{
if (tmpRegExp.GetCaptureTotal() == 4)
stackRegExps.push_back(tmpRegExp);
else
CLog::Log(LOGERROR, "Invalid video stack RE (%s). Must have 4 captures.", strRegExp->c_str());
}
strRegExp++;
}
// now stack the files, some of which may be from the previous stack iteration
i = 0;
while (i < Size())
{
CFileItemPtr item1 = Get(i);
// set property
item1->SetProperty("isstacked", "1");
// skip folders, nfo files, playlists
if (item1->m_bIsFolder
|| item1->IsParentFolder()
|| item1->IsNFO()
|| item1->IsPlayList()
)
{
// increment index
i++;
continue;
}
int64_t size = 0;
size_t offset = 0;
CStdString stackName;
CStdString file1;
CStdString filePath;
vector stack;
VECCREGEXP::iterator expr = stackRegExps.begin();
URIUtils::Split(item1->GetPath(), filePath, file1);
int j;
while (expr != stackRegExps.end())
{
if (expr->RegFind(file1, offset) != -1)
{
CStdString Title1 = expr->GetMatch(1),
Volume1 = expr->GetMatch(2),
Ignore1 = expr->GetMatch(3),
Extension1 = expr->GetMatch(4);
if (offset)
Title1 = file1.substr(0, expr->GetSubStart(2));
j = i + 1;
while (j < Size())
{
CFileItemPtr item2 = Get(j);
// skip folders, nfo files, playlists
if (item2->m_bIsFolder
|| item2->IsParentFolder()
|| item2->IsNFO()
|| item2->IsPlayList()
)
{
// increment index
j++;
continue;
}
CStdString file2, filePath2;
URIUtils::Split(item2->GetPath(), filePath2, file2);
if (expr->RegFind(file2, offset) != -1)
{
CStdString Title2 = expr->GetMatch(1),
Volume2 = expr->GetMatch(2),
Ignore2 = expr->GetMatch(3),
Extension2 = expr->GetMatch(4);
if (offset)
Title2 = file2.substr(0, expr->GetSubStart(2));
if (Title1.Equals(Title2))
{
if (!Volume1.Equals(Volume2))
{
if (Ignore1.Equals(Ignore2) && Extension1.Equals(Extension2))
{
if (stack.size() == 0)
{
stackName = Title1 + Ignore1 + Extension1;
stack.push_back(i);
size += item1->m_dwSize;
}
stack.push_back(j);
size += item2->m_dwSize;
}
else // Sequel
{
offset = 0;
expr++;
break;
}
}
else if (!Ignore1.Equals(Ignore2)) // False positive, try again with offset
{
offset = expr->GetSubStart(3);
break;
}
else // Extension mismatch
{
offset = 0;
expr++;
break;
}
}
else // Title mismatch
{
offset = 0;
expr++;
break;
}
}
else // No match 2, next expression
{
offset = 0;
expr++;
break;
}
j++;
}
if (j == Size())
expr = stackRegExps.end();
}
else // No match 1
{
offset = 0;
expr++;
}
if (stack.size() > 1)
{
// have a stack, remove the items and add the stacked item
// dont actually stack a multipart rar set, just remove all items but the first
CStdString stackPath;
if (Get(stack[0])->IsRAR())
stackPath = Get(stack[0])->GetPath();
else
{
CStackDirectory dir;
stackPath = dir.ConstructStackPath(*this, stack);
}
item1->SetPath(stackPath);
// clean up list
for (unsigned k = 1; k < stack.size(); k++)
Remove(i+1);
// item->m_bIsFolder = true; // don't treat stacked files as folders
// the label may be in a different char set from the filename (eg over smb
// the label is converted from utf8, but the filename is not)
if (!g_guiSettings.GetBool("filelists.showextensions"))
URIUtils::RemoveExtension(stackName);
CURL::Decode(stackName);
item1->SetLabel(stackName);
item1->m_dwSize = size;
break;
}
}
i++;
}
}
bool CFileItemList::Load(int windowID)
{
CFile file;
if (file.Open(GetDiscCacheFile(windowID)))
{
CLog::Log(LOGDEBUG,"Loading fileitems [%s]",GetPath().c_str());
CArchive ar(&file, CArchive::load);
ar >> *this;
CLog::Log(LOGDEBUG," -- items: %i, directory: %s sort method: %i, ascending: %s",Size(),GetPath().c_str(), m_sortMethod, m_sortOrder ? "true" : "false");
ar.Close();
file.Close();
return true;
}
return false;
}
bool CFileItemList::Save(int windowID)
{
int iSize = Size();
if (iSize <= 0)
return false;
CLog::Log(LOGDEBUG,"Saving fileitems [%s]",GetPath().c_str());
CFile file;
if (file.OpenForWrite(GetDiscCacheFile(windowID), true)) // overwrite always
{
CArchive ar(&file, CArchive::store);
ar << *this;
CLog::Log(LOGDEBUG," -- items: %i, sort method: %i, ascending: %s",iSize,m_sortMethod, m_sortOrder ? "true" : "false");
ar.Close();
file.Close();
return true;
}
return false;
}
void CFileItemList::RemoveDiscCache(int windowID) const
{
CStdString cacheFile(GetDiscCacheFile(windowID));
if (CFile::Exists(cacheFile))
{
CLog::Log(LOGDEBUG,"Clearing cached fileitems [%s]",GetPath().c_str());
CFile::Delete(cacheFile);
}
}
CStdString CFileItemList::GetDiscCacheFile(int windowID) const
{
CStdString strPath(GetPath());
URIUtils::RemoveSlashAtEnd(strPath);
Crc32 crc;
crc.ComputeFromLowerCase(strPath);
CStdString cacheFile;
if (IsCDDA() || IsOnDVD())
cacheFile.Format("special://temp/r-%08x.fi", (unsigned __int32)crc);
else if (IsMusicDb())
cacheFile.Format("special://temp/mdb-%08x.fi", (unsigned __int32)crc);
else if (IsVideoDb())
cacheFile.Format("special://temp/vdb-%08x.fi", (unsigned __int32)crc);
else if (windowID)
cacheFile.Format("special://temp/%i-%08x.fi", windowID, (unsigned __int32)crc);
else
cacheFile.Format("special://temp/%08x.fi", (unsigned __int32)crc);
return cacheFile;
}
bool CFileItemList::AlwaysCache() const
{
// some database folders are always cached
if (IsMusicDb())
return CMusicDatabaseDirectory::CanCache(GetPath());
if (IsVideoDb())
return CVideoDatabaseDirectory::CanCache(GetPath());
return false;
}
void CFileItemList::SetCachedVideoThumbs()
{
CSingleLock lock(m_lock);
// TODO: Investigate caching time to see if it speeds things up
for (unsigned int i = 0; i < m_items.size(); ++i)
{
CFileItemPtr pItem = m_items[i];
pItem->SetCachedVideoThumb();
}
}
void CFileItemList::SetCachedProgramThumbs()
{
CSingleLock lock(m_lock);
// TODO: Investigate caching time to see if it speeds things up
for (unsigned int i = 0; i < m_items.size(); ++i)
{
CFileItemPtr pItem = m_items[i];
pItem->SetCachedProgramThumb();
}
}
void CFileItemList::SetCachedMusicThumbs()
{
CSingleLock lock(m_lock);
// TODO: Investigate caching time to see if it speeds things up
for (unsigned int i = 0; i < m_items.size(); ++i)
{
CFileItemPtr pItem = m_items[i];
pItem->SetCachedMusicThumb();
}
}
CStdString CFileItem::GetCachedPictureThumb() const
{
return GetCachedThumb(m_strPath,g_settings.GetPicturesThumbFolder(),true);
}
void CFileItem::SetCachedMusicThumb()
{
// if it already has a thumbnail, then return
if (HasThumbnail() || m_bIsShareOrDrive) return ;
// streams do not have thumbnails
if (IsInternetStream()) return ;
// music db items already have thumbs or there is no thumb available
if (IsMusicDb()) return;
// ignore the parent dir items
if (IsParentFolder()) return;
CStdString cachedThumb(GetPreviouslyCachedMusicThumb());
if (!cachedThumb.IsEmpty())
SetThumbnailImage(cachedThumb);
// SetIconImage(cachedThumb);
}
CStdString CFileItem::GetPreviouslyCachedMusicThumb() const
{
// look if an album thumb is available,
// could be any file with tags loaded or
// a directory in album window
CStdString strAlbum, strArtist;
if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
{
strAlbum = m_musicInfoTag->GetAlbum();
if (!m_musicInfoTag->GetAlbumArtist().IsEmpty())
strArtist = m_musicInfoTag->GetAlbumArtist();
else
strArtist = m_musicInfoTag->GetArtist();
}
if (!strAlbum.IsEmpty() && !strArtist.IsEmpty())
{
// try permanent album thumb using "album name + artist name"
CStdString thumb(CUtil::GetCachedAlbumThumb(strAlbum, strArtist));
if (CFile::Exists(thumb))
return thumb;
}
// if a file, try to find a cached filename.tbn
if (!m_bIsFolder)
{
// look for locally cached tbn
CStdString thumb(CUtil::GetCachedMusicThumb(m_strPath));
if (CFile::Exists(thumb))
return thumb;
}
// try and find a cached folder thumb (folder.jpg or folder.tbn)
CStdString strPath;
if (!m_bIsFolder)
URIUtils::GetDirectory(m_strPath, strPath);
else
strPath = m_strPath;
// music thumbs are cached without slash at end
URIUtils::RemoveSlashAtEnd(strPath);
CStdString thumb(CUtil::GetCachedMusicThumb(strPath));
if (CFile::Exists(thumb))
return thumb;
return "";
}
CStdString CFileItem::GetUserMusicThumb(bool alwaysCheckRemote /* = false */) const
{
if (m_strPath.IsEmpty()
|| m_bIsShareOrDrive
|| IsInternetStream()
|| URIUtils::IsUPnP(m_strPath)
|| IsPlugin()
|| (URIUtils::IsFTP(m_strPath) && !g_advancedSettings.m_bFTPThumbs)
|| IsParentFolder()
|| IsMusicDb())
return "";
// we first check for .tbn or .tbn
CStdString fileThumb(GetTBNFile());
if (CFile::Exists(fileThumb))
return fileThumb;
// if a folder, check for folder.jpg
if (m_bIsFolder && !IsFileFolder() && (!IsRemote() || alwaysCheckRemote || g_guiSettings.GetBool("musicfiles.findremotethumbs")))
{
CStdStringArray thumbs;
StringUtils::SplitString(g_advancedSettings.m_musicThumbs, "|", thumbs);
for (unsigned int i = 0; i < thumbs.size(); ++i)
{
CStdString folderThumb(GetFolderThumb(thumbs[i]));
if (CFile::Exists(folderThumb))
{
return folderThumb;
}
}
}
// this adds support for files which inherit a folder.jpg icon which has not been cached yet.
// this occurs when queueing a top-level folder which has not been traversed yet.
else if (!IsRemote() || alwaysCheckRemote || g_guiSettings.GetBool("musicfiles.findremotethumbs"))
{
CStdString strFolder, strFile;
URIUtils::Split(m_strPath, strFolder, strFile);
if (!m_strPath.Equals(strFolder)) // any more parents to inherit from?
{
CFileItem folderItem(strFolder, true);
folderItem.SetMusicThumb(alwaysCheckRemote);
if (folderItem.HasThumbnail())
return folderItem.GetThumbnailImage();
}
}
// No thumb found
return "";
}
void CFileItem::SetUserMusicThumb(bool alwaysCheckRemote /* = false */)
{
// caches as the local thumb
CStdString thumb(GetUserMusicThumb(alwaysCheckRemote));
if (!thumb.IsEmpty())
{
CStdString cachedThumb(CUtil::GetCachedMusicThumb(m_strPath));
CPicture pic;
pic.CreateThumbnail(thumb, cachedThumb);
}
SetCachedMusicThumb();
}
void CFileItem::SetCachedPictureThumb()
{
if (IsParentFolder()) return;
CStdString cachedThumb(GetCachedPictureThumb());
if (CFile::Exists(cachedThumb))
SetThumbnailImage(cachedThumb);
}
CStdString CFileItem::GetCachedVideoThumb() const
{
if (IsStack())
return GetCachedThumb(CStackDirectory::GetFirstStackedFile(m_strPath),g_settings.GetVideoThumbFolder(),true);
else if (IsVideoDb() && HasVideoInfoTag())
{
if (m_bIsFolder && !GetVideoInfoTag()->m_strPath.IsEmpty())
return GetCachedThumb(GetVideoInfoTag()->m_strPath, g_settings.GetVideoThumbFolder(), true);
else if (!GetVideoInfoTag()->m_strFileNameAndPath.IsEmpty())
return GetCachedThumb(GetVideoInfoTag()->m_strFileNameAndPath, g_settings.GetVideoThumbFolder(), true);
}
return GetCachedThumb(m_strPath,g_settings.GetVideoThumbFolder(),true);
}
CStdString CFileItem::GetCachedEpisodeThumb() const
{
// get the locally cached thumb
CStdString strCRC;
strCRC.Format("%sepisode%i",GetVideoInfoTag()->m_strFileNameAndPath.c_str(),GetVideoInfoTag()->m_iEpisode);
return GetCachedThumb(strCRC,g_settings.GetVideoThumbFolder(),true);
}
void CFileItem::SetCachedVideoThumb()
{
if (IsParentFolder()) return;
if (HasThumbnail()) return;
CStdString cachedThumb(GetCachedVideoThumb());
if (HasVideoInfoTag() && !m_bIsFolder &&
GetVideoInfoTag()->m_iEpisode > -1 &&
CFile::Exists(GetCachedEpisodeThumb()))
{
SetThumbnailImage(GetCachedEpisodeThumb());
}
else if (CFile::Exists(cachedThumb))
SetThumbnailImage(cachedThumb);
}
// Gets the .tbn filename from a file or folder name.
// .ext -> .tbn
// / -> .tbn
CStdString CFileItem::GetTBNFile() const
{
CStdString thumbFile;
CStdString strFile = m_strPath;
if (IsStack())
{
CStdString strPath, strReturn;
URIUtils::GetParentPath(m_strPath,strPath);
CFileItem item(CStackDirectory::GetFirstStackedFile(strFile),false);
CStdString strTBNFile = item.GetTBNFile();
URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile),strReturn);
if (CFile::Exists(strReturn))
return strReturn;
URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(CStackDirectory::GetStackedTitlePath(strFile)),strFile);
}
if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
{
CStdString strPath, strParent;
URIUtils::GetDirectory(strFile,strPath);
URIUtils::GetParentPath(strPath,strParent);
URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath),strFile);
}
CURL url(strFile);
strFile = url.GetFileName();
if (m_bIsFolder && !IsFileFolder())
URIUtils::RemoveSlashAtEnd(strFile);
if (!strFile.IsEmpty())
{
if (m_bIsFolder && !IsFileFolder())
thumbFile = strFile + ".tbn"; // folder, so just add ".tbn"
else
thumbFile = URIUtils::ReplaceExtension(strFile, ".tbn");
url.SetFileName(thumbFile);
thumbFile = url.Get();
}
return thumbFile;
}
CStdString CFileItem::GetUserVideoThumb() const
{
if (IsTuxBox())
{
if (!m_bIsFolder)
return g_tuxbox.GetPicon(GetLabel());
else return "";
}
if (m_strPath.IsEmpty()
|| m_bIsShareOrDrive
|| IsInternetStream()
|| URIUtils::IsUPnP(m_strPath)
|| m_strPath.Left(4).Equals("dvd:")
|| (URIUtils::IsFTP(m_strPath) && !g_advancedSettings.m_bFTPThumbs)
|| IsPlugin()
|| IsParentFolder()
|| IsLiveTV())
return "";
// 1. check .tbn or .tbn
CStdString fileThumb(GetTBNFile());
if (CFile::Exists(fileThumb))
return fileThumb;
// 2. - check movie.tbn, as long as it's not a folder
if (!m_bIsFolder)
{
CStdString strPath, movietbnFile;
URIUtils::GetParentPath(m_strPath, strPath);
URIUtils::AddFileToFolder(strPath, "movie.tbn", movietbnFile);
if (CFile::Exists(movietbnFile))
return movietbnFile;
}
// 3. check folder image in_m_dvdThumbs (folder.jpg)
if (m_bIsFolder && !IsFileFolder())
{
CStdStringArray thumbs;
StringUtils::SplitString(g_advancedSettings.m_dvdThumbs, "|", thumbs);
for (unsigned int i = 0; i < thumbs.size(); ++i)
{
CStdString folderThumb(GetFolderThumb(thumbs[i]));
if (CFile::Exists(folderThumb))
{
return folderThumb;
}
}
}
// No thumb found
return "";
}
CStdString CFileItem::GetFolderThumb(const CStdString &folderJPG /* = "folder.jpg" */) const
{
CStdString folderThumb;
CStdString strFolder = m_strPath;
if (IsStack() ||
URIUtils::IsInRAR(strFolder) ||
URIUtils::IsInZIP(strFolder))
{
URIUtils::GetParentPath(m_strPath,strFolder);
}
if (IsMultiPath())
strFolder = CMultiPathDirectory::GetFirstPath(m_strPath);
URIUtils::AddFileToFolder(strFolder, folderJPG, folderThumb);
return folderThumb;
}
CStdString CFileItem::GetMovieName(bool bUseFolderNames /* = false */) const
{
if (IsLabelPreformated())
return GetLabel();
CStdString strMovieName = m_strPath;
if (IsMultiPath())
strMovieName = CMultiPathDirectory::GetFirstPath(m_strPath);
if (URIUtils::IsStack(strMovieName))
strMovieName = CStackDirectory::GetStackedTitlePath(strMovieName);
if ((!m_bIsFolder || IsDVDFile(false, true) || URIUtils::IsInArchive(m_strPath)) && bUseFolderNames)
{
URIUtils::GetParentPath(m_strPath, strMovieName);
if (URIUtils::IsInArchive(m_strPath) || strMovieName.Find( "VIDEO_TS" ) != -1)
{
CStdString strArchivePath;
URIUtils::GetParentPath(strMovieName, strArchivePath);
strMovieName = strArchivePath;
}
}
URIUtils::RemoveSlashAtEnd(strMovieName);
strMovieName = URIUtils::GetFileName(strMovieName);
CURL::Decode(strMovieName);
return strMovieName;
}
void CFileItem::SetVideoThumb()
{
if (HasThumbnail()) return;
SetCachedVideoThumb();
if (!HasThumbnail())
SetUserVideoThumb();
}
void CFileItem::SetUserVideoThumb()
{
if (m_bIsShareOrDrive) return;
if (IsParentFolder()) return;
// caches as the local thumb
CStdString thumb(GetUserVideoThumb());
if (!thumb.IsEmpty())
{
CStdString cachedThumb(GetCachedVideoThumb());
CPicture pic;
pic.CreateThumbnail(thumb, cachedThumb);
}
SetCachedVideoThumb();
}
bool CFileItem::CacheLocalFanart() const
{
// first check for an already cached fanart image
CStdString cachedFanart(GetCachedFanart());
if (CFile::Exists(cachedFanart))
return true;
// we don't have a cached image, so let's see if the user has a local image, and cache it if so
CStdString localFanart(GetLocalFanart());
if (!localFanart.IsEmpty())
{
CPicture pic;
return pic.CacheFanart(localFanart, cachedFanart);
}
return false;
}
CStdString CFileItem::GetLocalFanart() const
{
if (IsVideoDb())
{
if (!HasVideoInfoTag())
return ""; // nothing can be done
CFileItem dbItem(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath, m_bIsFolder);
return dbItem.GetLocalFanart();
}
CStdString strFile2;
CStdString strFile = m_strPath;
if (IsStack())
{
CStdString strPath;
URIUtils::GetParentPath(m_strPath,strPath);
CStackDirectory dir;
CStdString strPath2;
strPath2 = dir.GetStackedTitlePath(strFile);
URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2),strFile);
CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
CStdString strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-fanart"));
URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile),strFile2);
}
if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
{
CStdString strPath, strParent;
URIUtils::GetDirectory(strFile,strPath);
URIUtils::GetParentPath(strPath,strParent);
URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath),strFile);
}
// no local fanart available for these
if (IsInternetStream()
|| URIUtils::IsUPnP(strFile)
|| IsLiveTV()
|| IsPlugin()
|| m_strPath.Left(4).Equals("dvd:")
|| (URIUtils::IsFTP(strFile) && !g_advancedSettings.m_bFTPThumbs)
|| m_strPath.IsEmpty())
return "";
CStdString strDir;
URIUtils::GetDirectory(strFile, strDir);
if (strDir.IsEmpty())
return "";
CFileItemList items;
CDirectory::GetDirectory(strDir, items, g_settings.m_pictureExtensions, false, false, DIR_CACHE_ALWAYS, false);
CStdStringArray fanarts;
StringUtils::SplitString(g_advancedSettings.m_fanartImages, "|", fanarts);
strFile = URIUtils::ReplaceExtension(strFile, "-fanart");
fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile));
if (!strFile2.IsEmpty())
fanarts.insert(m_bIsFolder ? fanarts.end() : fanarts.begin(), URIUtils::GetFileName(strFile2));
for (unsigned int i = 0; i < fanarts.size(); ++i)
{
for (int j = 0; j < items.Size(); j++)
{
CStdString strCandidate = URIUtils::GetFileName(items[j]->m_strPath);
URIUtils::RemoveExtension(strCandidate);
CStdString strFanart = fanarts[i];
URIUtils::RemoveExtension(strFanart);
if (strCandidate.CompareNoCase(strFanart) == 0)
return items[j]->m_strPath;
}
}
return "";
}
CStdString CFileItem::GetCachedFanart() const
{
// get the locally cached thumb
if (IsVideoDb())
{
if (!HasVideoInfoTag())
return "";
if (!GetVideoInfoTag()->m_strArtist.IsEmpty())
return GetCachedThumb(GetVideoInfoTag()->m_strArtist,g_settings.GetMusicFanartFolder());
if (!m_bIsFolder && !GetVideoInfoTag()->m_strShowTitle.IsEmpty())
{
CVideoDatabase database;
database.Open();
int iShowId = database.GetTvShowId(GetVideoInfoTag()->m_strPath);
CStdString showPath;
database.GetFilePathById(iShowId,showPath,VIDEODB_CONTENT_TVSHOWS);
return GetCachedThumb(showPath,g_settings.GetVideoFanartFolder());
}
return GetCachedThumb(m_bIsFolder ? GetVideoInfoTag()->m_strPath : GetVideoInfoTag()->m_strFileNameAndPath,g_settings.GetVideoFanartFolder());
}
if (HasMusicInfoTag())
return GetCachedThumb(GetMusicInfoTag()->GetArtist(),g_settings.GetMusicFanartFolder());
return GetCachedThumb(m_strPath,g_settings.GetVideoFanartFolder());
}
CStdString CFileItem::GetCachedThumb(const CStdString &path, const CStdString &path2, bool split)
{
// get the locally cached thumb
Crc32 crc;
crc.ComputeFromLowerCase(path);
CStdString thumb;
if (split)
{
CStdString hex;
hex.Format("%08x", (__int32)crc);
thumb.Format("%c\\%08x.tbn", hex[0], (unsigned __int32)crc);
}
else
thumb.Format("%08x.tbn", (unsigned __int32)crc);
return URIUtils::AddFileToFolder(path2, thumb);
}
CStdString CFileItem::GetCachedProgramThumb() const
{
// get the locally cached thumb
Crc32 crc;
if (IsOnDVD())
{
CStdString strDesc;
CUtil::GetXBEDescription(m_strPath,strDesc);
CStdString strCRC;
strCRC.Format("%s%u",strDesc.c_str(),CUtil::GetXbeID(m_strPath));
crc.ComputeFromLowerCase(strCRC);
}
else
crc.ComputeFromLowerCase(m_strPath);
CStdString hex;
hex.Format("%08x", (__int32)crc);
CStdString thumb;
thumb.Format("%s\\%c\\%08x.tbn", g_settings.GetProgramsThumbFolder().c_str(), hex[0], (unsigned __int32)crc);
return thumb;
}
CStdString CFileItem::GetCachedGameSaveThumb() const
{
CStdString extension;
URIUtils::GetExtension(m_strPath,extension);
if (extension.Equals(".xbx")) // savemeta.xbx - cache thumb
{
CStdString thumb = GetCachedThumb(m_strPath,g_settings.GetGameSaveThumbFolder());
CLog::Log(LOGDEBUG, "Thumb (%s)",thumb.c_str());
if (!CFile::Exists(thumb))
{
CStdString strTitleImage, strParent, strParentSave, strParentTitle;
URIUtils::GetDirectory(m_strPath,strTitleImage);
URIUtils::GetParentPath(strTitleImage,strParent);
URIUtils::AddFileToFolder(strTitleImage,"saveimage.xbx",strTitleImage);
URIUtils::AddFileToFolder(strParent,"saveimage.xbx",strParentSave);
URIUtils::AddFileToFolder(strParent,"titleimage.xbx",strParentTitle);
//URIUtils::AddFileToFolder(strTitleImageCur,"titleimage.xbx",m_strPath);
if (CFile::Exists(strTitleImage))
CUtil::CacheXBEIcon(strTitleImage, thumb);
else if (CFile::Exists(strParentSave))
CUtil::CacheXBEIcon(strParentSave,thumb);
else if (CFile::Exists(strParentTitle))
CUtil::CacheXBEIcon(strParentTitle,thumb);
else
thumb = "";
}
return thumb;
}
else if (CDirectory::Exists(m_strPath))
{
// get the save game id
CStdString fullPath(m_strPath);
URIUtils::RemoveSlashAtEnd(fullPath);
CStdString fileName(URIUtils::GetFileName(fullPath));
CStdString thumb;
thumb.Format("%s\\%s.tbn", g_settings.GetGameSaveThumbFolder().c_str(), fileName.c_str());
CLog::Log(LOGDEBUG, "Thumb (%s)",thumb.c_str());
if (!CFile::Exists(thumb))
{
CStdString titleimageXBX;
CStdString saveimageXBX;
URIUtils::AddFileToFolder(m_strPath, "titleimage.xbx", titleimageXBX);
URIUtils::AddFileToFolder(m_strPath,"saveimage.xbx",saveimageXBX);
/*if (CFile::Exists(saveimageXBX))
{
CUtil::CacheXBEIcon(saveimageXBX, thumb);
CLog::Log(LOGDEBUG, "saveimageXBX (%s)",saveimageXBX.c_str());
}*/
if (CFile::Exists(titleimageXBX))
{
CLog::Log(LOGDEBUG, "titleimageXBX (%s)",titleimageXBX.c_str());
CUtil::CacheXBEIcon(titleimageXBX, thumb);
}
}
return thumb;
}
return "";
}
void CFileItem::SetCachedProgramThumb()
{
// don't set any thumb for programs on DVD, as they're bound to be named the
// same (D:\default.xbe).
if (IsParentFolder()) return;
CStdString thumb(GetCachedProgramThumb());
if (CFile::Exists(thumb))
SetThumbnailImage(thumb);
}
void CFileItem::SetUserProgramThumb()
{
if (m_bIsShareOrDrive) return;
if (IsParentFolder()) return;
if (IsShortCut())
{
CShortcut shortcut;
if ( shortcut.Create(m_strPath) && !shortcut.m_strThumb.IsEmpty() )
{
m_strThumbnailImage = shortcut.m_strThumb;
return;
}
}
// 1. Try .tbn
CStdString fileThumb(GetTBNFile());
CStdString thumb(GetCachedProgramThumb());
if (CFile::Exists(fileThumb))
{ // cache
CPicture pic;
if (pic.CreateThumbnail(fileThumb, thumb))
SetThumbnailImage(thumb);
}
else if (IsXBE())
{
// 2. check for avalaunch_icon.jpg
CStdString directory;
URIUtils::GetDirectory(m_strPath, directory);
CStdString avalaunchIcon;
URIUtils::AddFileToFolder(directory, "avalaunch_icon.jpg", avalaunchIcon);
if (CFile::Exists(avalaunchIcon))
{
CPicture pic;
if (pic.CreateThumbnail(avalaunchIcon, thumb))
SetThumbnailImage(thumb);
}
else if (CUtil::CacheXBEIcon(m_strPath, thumb))
SetThumbnailImage(thumb);
}
else if (m_bIsFolder)
{
// 3. cache the folder image
CStdString folderThumb(GetFolderThumb());
if (CFile::Exists(folderThumb))
{
CPicture pic;
if (pic.CreateThumbnail(folderThumb, thumb))
SetThumbnailImage(thumb);
}
}
}
/*void CFileItem::SetThumb()
{
// we need to know the type of file at this point
// as differing views have differing inheritance rules for thumbs:
// Videos:
// Folders only use /folder.jpg or .tbn
// Files use .tbn
// * Thumbs are cached from here using file or folder path
// Music:
// Folders only use /folder.jpg or .tbn
// Files use .tbn or the album/path cached thumb or inherit from the folder
// * Thumbs are cached from here using file or folder path
// Programs:
// Folders only use /folder.jpg or .tbn
// Files use .tbn or the embedded xbe. Shortcuts have the additional step of the tag to check
// * Thumbs are cached from here using file or folder path
// Pictures:
// Folders use /folder.jpg or .tbn, or auto-generated from 4 images in the folder
// Files use .tbn or a resized version of the picture
// * Thumbs are cached from here using file or folder path
}*/
void CFileItemList::SetProgramThumbs()
{
// TODO: Is there a speed up if we cache the program thumbs first?
for (unsigned int i = 0; i < m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (pItem->IsParentFolder())
continue;
pItem->SetCachedProgramThumb();
if (!pItem->HasThumbnail())
pItem->SetUserProgramThumb();
}
}
bool CFileItem::LoadMusicTag()
{
// not audio
if (!IsAudio())
return false;
// already loaded?
if (HasMusicInfoTag() && m_musicInfoTag->Loaded())
return true;
// check db
CMusicDatabase musicDatabase;
if (musicDatabase.Open())
{
CSong song;
if (musicDatabase.GetSongByFileName(m_strPath, song))
{
GetMusicInfoTag()->SetSong(song);
SetThumbnailImage(song.strThumb);
return true;
}
musicDatabase.Close();
}
// load tag from file
CLog::Log(LOGDEBUG, "%s: loading tag information for file: %s", __FUNCTION__, m_strPath.c_str());
CMusicInfoTagLoaderFactory factory;
auto_ptr pLoader (factory.CreateLoader(m_strPath));
if (NULL != pLoader.get())
{
if (pLoader->Load(m_strPath, *GetMusicInfoTag()))
return true;
}
// no tag - try some other things
if (IsCDDA())
{
// we have the tracknumber...
int iTrack = GetMusicInfoTag()->GetTrackNumber();
if (iTrack >= 1)
{
CStdString strText = g_localizeStrings.Get(554); // "Track"
if (strText.GetAt(strText.size() - 1) != ' ')
strText += " ";
CStdString strTrack;
strTrack.Format(strText + "%i", iTrack);
GetMusicInfoTag()->SetTitle(strTrack);
GetMusicInfoTag()->SetLoaded(true);
return true;
}
}
else
{
CStdString fileName = URIUtils::GetFileName(m_strPath);
URIUtils::RemoveExtension(fileName);
for (unsigned int i = 0; i < g_advancedSettings.m_musicTagsFromFileFilters.size(); i++)
{
CLabelFormatter formatter(g_advancedSettings.m_musicTagsFromFileFilters[i], "");
if (formatter.FillMusicTag(fileName, GetMusicInfoTag()))
{
GetMusicInfoTag()->SetLoaded(true);
return true;
}
}
}
return false;
}
void CFileItem::SetCachedGameSavesThumb()
{
if (IsParentFolder()) return;
CStdString thumb(GetCachedGameSaveThumb());
if (CFile::Exists(thumb))
SetThumbnailImage(thumb);
}
void CFileItemList::SetCachedGameSavesThumbs()
{
// TODO: Investigate caching time to see if it speeds things up
for (unsigned int i = 0; i < m_items.size(); ++i)
{
CFileItemPtr pItem = m_items[i];
pItem->SetCachedGameSavesThumb();
}
}
void CFileItemList::SetGameSavesThumbs()
{
// No User thumbs
// TODO: Is there a speed up if we cache the program thumbs first?
for (unsigned int i = 0; i < m_items.size(); i++)
{
CFileItemPtr pItem = m_items[i];
if (pItem->IsParentFolder())
continue;
pItem->SetCachedGameSavesThumb(); // was pItem->SetCachedProgramThumb(); oringally
}
}
void CFileItemList::Swap(unsigned int item1, unsigned int item2)
{
if (item1 != item2 && item1 < m_items.size() && item2 < m_items.size())
std::swap(m_items[item1], m_items[item2]);
}
bool CFileItemList::UpdateItem(const CFileItem *item)
{
if (!item) return false;
CFileItemPtr oldItem = Get(item->GetPath());
if (oldItem)
*oldItem = *item;
return oldItem;
}
void CFileItemList::AddSortMethod(SORT_METHOD sortMethod, int buttonLabel, const LABEL_MASKS &labelMasks)
{
SORT_METHOD_DETAILS sort;
sort.m_sortMethod=sortMethod;
sort.m_buttonLabel=buttonLabel;
sort.m_labelMasks=labelMasks;
m_sortDetails.push_back(sort);
}
void CFileItemList::SetReplaceListing(bool replace)
{
m_replaceListing = replace;
}
void CFileItemList::ClearSortState()
{
m_sortMethod=SORT_METHOD_NONE;
m_sortOrder=SORT_ORDER_NONE;
}
CVideoInfoTag* CFileItem::GetVideoInfoTag()
{
if (!m_videoInfoTag)
m_videoInfoTag = new CVideoInfoTag;
return m_videoInfoTag;
}
CPictureInfoTag* CFileItem::GetPictureInfoTag()
{
if (!m_pictureInfoTag)
m_pictureInfoTag = new CPictureInfoTag;
return m_pictureInfoTag;
}
MUSIC_INFO::CMusicInfoTag* CFileItem::GetMusicInfoTag()
{
if (!m_musicInfoTag)
m_musicInfoTag = new MUSIC_INFO::CMusicInfoTag;
return m_musicInfoTag;
}
CStdString CFileItem::FindTrailer() const
{
CStdString strFile2;
CStdString strFile = m_strPath;
if (IsStack())
{
CStdString strPath;
URIUtils::GetParentPath(m_strPath,strPath);
CStackDirectory dir;
CStdString strPath2;
strPath2 = dir.GetStackedTitlePath(strFile);
URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2),strFile);
CFileItem item(dir.GetFirstStackedFile(m_strPath),false);
CStdString strTBNFile(URIUtils::ReplaceExtension(item.GetTBNFile(), "-trailer"));
URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile),strFile2);
}
if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile))
{
CStdString strPath, strParent;
URIUtils::GetDirectory(strFile,strPath);
URIUtils::GetParentPath(strPath,strParent);
URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath),strFile);
}
// no local trailer available for these
if (IsInternetStream()
|| URIUtils::IsUPnP(strFile)
|| IsLiveTV()
|| IsPlugin()
|| m_strPath.Left(4).Equals("dvd:"))
return "";
CStdString strDir;
URIUtils::GetDirectory(strFile, strDir);
CFileItemList items;
CDirectory::GetDirectory(strDir, items, g_settings.m_videoExtensions, true, false, DIR_CACHE_ALWAYS, false);
URIUtils::RemoveExtension(strFile);
strFile += "-trailer";
CStdString strFile3 = URIUtils::AddFileToFolder(strDir, "movie-trailer");
// Precompile our REs
VECCREGEXP matchRegExps;
CRegExp tmpRegExp(true);
const CStdStringArray& strMatchRegExps = g_advancedSettings.m_trailerMatchRegExps;
CStdStringArray::const_iterator strRegExp = strMatchRegExps.begin();
while (strRegExp != strMatchRegExps.end())
{
if (tmpRegExp.RegComp(*strRegExp))
{
matchRegExps.push_back(tmpRegExp);
}
strRegExp++;
}
CStdString strTrailer;
for (int i = 0; i < items.Size(); i++)
{
CStdString strCandidate = items[i]->m_strPath;
URIUtils::RemoveExtension(strCandidate);
if (strCandidate.CompareNoCase(strFile) == 0 ||
strCandidate.CompareNoCase(strFile2) == 0 ||
strCandidate.CompareNoCase(strFile3) == 0)
{
strTrailer = items[i]->m_strPath;
break;
}
else
{
VECCREGEXP::iterator expr = matchRegExps.begin();
while (expr != matchRegExps.end())
{
if (expr->RegFind(strCandidate) != -1)
{
strTrailer = items[i]->m_strPath;
i = items.Size();
break;
}
expr++;
}
}
}
return strTrailer;
}
VIDEODB_CONTENT_TYPE CFileItem::GetVideoContentType() const
{
VIDEODB_CONTENT_TYPE type = VIDEODB_CONTENT_MOVIES;
if (HasVideoInfoTag() && !GetVideoInfoTag()->m_strShowTitle.IsEmpty()) // tvshow
type = VIDEODB_CONTENT_TVSHOWS;
if (HasVideoInfoTag() && GetVideoInfoTag()->m_iSeason > -1 && !m_bIsFolder) // episode
type = VIDEODB_CONTENT_EPISODES;
if (HasVideoInfoTag() && !GetVideoInfoTag()->m_strArtist.IsEmpty())
type = VIDEODB_CONTENT_MUSICVIDEOS;
return type;
}