2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "PlayListPLS.h"
12 #include "PlayListFactory.h"
14 #include "filesystem/File.h"
15 #include "music/MusicFileItemClassify.h"
16 #include "music/tags/MusicInfoTag.h"
17 #include "utils/CharsetConverter.h"
18 #include "utils/StringUtils.h"
19 #include "utils/URIUtils.h"
20 #include "utils/log.h"
26 using namespace XFILE
;
28 #define START_PLAYLIST_MARKER "[playlist]" // may be case-insensitive (equivalent to .ini file on win32)
29 #define PLAYLIST_NAME "PlaylistName"
31 namespace KODI::PLAYLIST
34 /*----------------------------------------------------------------------
36 PlaylistName=Playlist 001
37 File1=E:\Program Files\Winamp3\demo.mp3
40 File2=E:\Program Files\Winamp3\demo.mp3
45 ----------------------------------------------------------------------*/
46 CPlayListPLS::CPlayListPLS(void) = default;
48 CPlayListPLS::~CPlayListPLS(void) = default;
50 bool CPlayListPLS::Load(const std::string
&strFile
)
52 //read it from the file
53 std::string
strFileName(strFile
);
54 m_strPlayListName
= URIUtils::GetFileName(strFileName
);
58 bool bShoutCast
= false;
59 if( StringUtils::StartsWithNoCase(strFileName
, "shout://") )
61 strFileName
.replace(0, 8, "http://");
66 URIUtils::GetParentPath(strFileName
, m_strBasePath
);
69 if (!file
.Open(strFileName
) )
75 if (file
.GetLength() > 1024*1024)
77 CLog::Log(LOGWARNING
, "{} - File is larger than 1 MB, most likely not a playlist",
85 // run through looking for the [playlist] marker.
86 // if we find another http stream, then load it.
89 if ( !file
.ReadString(szLine
, sizeof(szLine
) ) )
95 StringUtils::Trim(strLine
);
96 if(StringUtils::EqualsNoCase(strLine
, START_PLAYLIST_MARKER
))
99 // if there is something else before playlist marker, this isn't a pls file
104 bool bFailed
= false;
105 while (file
.ReadString(szLine
, sizeof(szLine
) ) )
108 StringUtils::RemoveCRLF(strLine
);
109 size_t iPosEqual
= strLine
.find('=');
110 if (iPosEqual
!= std::string::npos
)
112 std::string strLeft
= strLine
.substr(0, iPosEqual
);
114 std::string strValue
= strLine
.substr(iPosEqual
);
115 StringUtils::ToLower(strLeft
);
116 StringUtils::TrimLeft(strLeft
);
118 if (strLeft
== "numberofentries")
120 m_vecItems
.reserve(atoi(strValue
.c_str()));
122 else if (StringUtils::StartsWith(strLeft
, "file"))
124 std::vector
<int>::size_type idx
= atoi(strLeft
.c_str() + 4);
131 // Skip self - do not load playlist recursively
132 if (StringUtils::EqualsNoCase(URIUtils::GetFileName(strValue
),
133 URIUtils::GetFileName(strFileName
)))
136 if (m_vecItems
[idx
- 1]->GetLabel().empty())
137 m_vecItems
[idx
- 1]->SetLabel(URIUtils::GetFileName(strValue
));
138 CFileItem
item(strValue
, false);
139 if (bShoutCast
&& !MUSIC::IsAudio(item
))
140 strValue
.replace(0, 7, "shout://");
142 strValue
= URIUtils::SubstitutePath(strValue
);
143 CUtil::GetQualifiedFilename(m_strBasePath
, strValue
);
144 g_charsetConverter
.unknownToUTF8(strValue
);
145 m_vecItems
[idx
- 1]->SetPath(strValue
);
147 else if (StringUtils::StartsWith(strLeft
, "title"))
149 std::vector
<int>::size_type idx
= atoi(strLeft
.c_str() + 5);
155 g_charsetConverter
.unknownToUTF8(strValue
);
156 m_vecItems
[idx
- 1]->SetLabel(strValue
);
158 else if (StringUtils::StartsWith(strLeft
, "length"))
160 std::vector
<int>::size_type idx
= atoi(strLeft
.c_str() + 6);
166 m_vecItems
[idx
- 1]->GetMusicInfoTag()->SetDuration(atol(strValue
.c_str()));
168 else if (strLeft
== "playlistname")
170 m_strPlayListName
= strValue
;
171 g_charsetConverter
.unknownToUTF8(m_strPlayListName
);
180 "File {} is not a valid PLS playlist. Location of first file,title or length is not "
181 "permitted (eg. File0 should be File1)",
182 URIUtils::GetFileName(strFileName
));
186 // check for missing entries
187 ivecItems p
= m_vecItems
.begin();
188 while ( p
!= m_vecItems
.end())
190 if ((*p
)->GetPath().empty())
192 p
= m_vecItems
.erase(p
);
203 void CPlayListPLS::Save(const std::string
& strFileName
) const
205 if (!m_vecItems
.size()) return ;
206 std::string strPlaylist
= CUtil::MakeLegalPath(strFileName
);
208 if (!file
.OpenForWrite(strPlaylist
, true))
210 CLog::Log(LOGERROR
, "Could not save PLS playlist: [{}]", strPlaylist
);
214 write
+= StringUtils::Format("{}\n", START_PLAYLIST_MARKER
);
215 std::string strPlayListName
=m_strPlayListName
;
216 g_charsetConverter
.utf8ToStringCharset(strPlayListName
);
217 write
+= StringUtils::Format("PlaylistName={}\n", strPlayListName
);
219 for (int i
= 0; i
< (int)m_vecItems
.size(); ++i
)
221 CFileItemPtr item
= m_vecItems
[i
];
222 std::string strFileName
=item
->GetPath();
223 g_charsetConverter
.utf8ToStringCharset(strFileName
);
224 std::string strDescription
=item
->GetLabel();
225 g_charsetConverter
.utf8ToStringCharset(strDescription
);
226 write
+= StringUtils::Format("File{}={}\n", i
+ 1, strFileName
);
227 write
+= StringUtils::Format("Title{}={}\n", i
+ 1, strDescription
.c_str());
229 StringUtils::Format("Length{}={}\n", i
+ 1, item
->GetMusicInfoTag()->GetDuration() / 1000);
232 write
+= StringUtils::Format("NumberOfEntries={0}\n", m_vecItems
.size());
233 write
+= StringUtils::Format("Version=2\n");
234 file
.Write(write
.c_str(), write
.size());
238 bool CPlayListPLS::Resize(std::vector
<int>::size_type newSize
)
243 while (m_vecItems
.size() < newSize
)
245 CFileItemPtr
fileItem(new CFileItem());
246 m_vecItems
.push_back(fileItem
);
251 } // namespace KODI::PLAYLIST