[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / playlists / PlayListM3U.cpp
blob572bdced779c8a850cdb3495ef84a98d51a0d907
1 /*
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.
7 */
9 #include "PlayListM3U.h"
11 #include "FileItem.h"
12 #include "URL.h"
13 #include "Util.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/URIUtils.h"
19 #include "utils/log.h"
20 #include "video/VideoFileItemClassify.h"
21 #include "video/VideoInfoTag.h"
23 #include <inttypes.h>
25 using namespace XFILE;
27 namespace KODI::PLAYLIST
30 const char* CPlayListM3U::StartMarker = "#EXTCPlayListM3U::M3U";
31 const char* CPlayListM3U::InfoMarker = "#EXTINF";
32 const char* CPlayListM3U::ArtistMarker = "#EXTART";
33 const char* CPlayListM3U::AlbumMarker = "#EXTALB";
34 const char* CPlayListM3U::PropertyMarker = "#KODIPROP";
35 const char* CPlayListM3U::VLCOptMarker = "#EXTVLCOPT";
36 const char* CPlayListM3U::StreamMarker = "#EXT-X-STREAM-INF";
37 const char* CPlayListM3U::BandwidthMarker = "BANDWIDTH";
38 const char* CPlayListM3U::OffsetMarker = "#EXT-KX-OFFSET";
40 // example m3u file:
41 // #EXTM3U
42 // #EXTART:Demo Artist
43 // #EXTALB:Demo Album
44 // #KODIPROP:name=value
45 // #EXTINF:5,demo
46 // E:\Program Files\Winamp3\demo.mp3
50 // example m3u8 containing streams of different bitrates
51 // #EXTM3U
52 // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1600000
53 // playlist_1600.m3u8
54 // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=3000000
55 // playlist_3000.m3u8
56 // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=800000
57 // playlist_800.m3u8
60 CPlayListM3U::CPlayListM3U(void) = default;
62 CPlayListM3U::~CPlayListM3U(void) = default;
65 bool CPlayListM3U::Load(const std::string& strFileName)
67 char szLine[4096];
68 std::string strLine;
69 std::string strInfo;
70 std::vector<std::pair<std::string, std::string> > properties;
72 int lDuration = 0;
73 int iStartOffset = 0;
74 int iEndOffset = 0;
76 bool utf8 = false;
77 if (URIUtils::GetExtension(strFileName) == ".m3u8")
78 utf8 = true;
80 Clear();
82 m_strPlayListName = URIUtils::GetFileName(strFileName);
83 URIUtils::GetParentPath(strFileName, m_strBasePath);
85 CFile file;
86 if (!file.Open(strFileName) )
88 file.Close();
89 return false;
92 while (file.ReadString(szLine, 4095))
94 strLine = szLine;
95 StringUtils::Trim(strLine);
97 if (StringUtils::StartsWith(strLine, InfoMarker))
99 // start of info
100 size_t iColon = strLine.find(':');
101 size_t iComma = strLine.find(',');
102 if (iColon != std::string::npos &&
103 iComma != std::string::npos &&
104 iComma > iColon)
106 // Read the info and duration
107 iColon++;
108 std::string strLength = strLine.substr(iColon, iComma - iColon);
109 lDuration = atoi(strLength.c_str());
110 iComma++;
111 strInfo = strLine.substr(iComma);
112 if (!utf8)
113 g_charsetConverter.unknownToUTF8(strInfo);
116 else if (StringUtils::StartsWith(strLine, OffsetMarker))
118 size_t iColon = strLine.find(':');
119 size_t iComma = strLine.find(',');
120 if (iColon != std::string::npos &&
121 iComma != std::string::npos &&
122 iComma > iColon)
124 // Read the start and end offset
125 iColon++;
126 iStartOffset = atoi(strLine.substr(iColon, iComma - iColon).c_str());
127 iComma++;
128 iEndOffset = atoi(strLine.substr(iComma).c_str());
131 else if (StringUtils::StartsWith(strLine, PropertyMarker)
132 || StringUtils::StartsWith(strLine, VLCOptMarker))
134 size_t iColon = strLine.find(':');
135 size_t iEqualSign = strLine.find('=');
136 if (iColon != std::string::npos &&
137 iEqualSign != std::string::npos &&
138 iEqualSign > iColon)
140 std::string strFirst, strSecond;
141 properties.emplace_back(
142 StringUtils::Trim((strFirst = strLine.substr(iColon + 1, iEqualSign - iColon - 1))),
143 StringUtils::Trim((strSecond = strLine.substr(iEqualSign + 1))));
146 else if (strLine != StartMarker &&
147 !StringUtils::StartsWith(strLine, ArtistMarker) &&
148 !StringUtils::StartsWith(strLine, AlbumMarker))
150 std::string strFileName = strLine;
152 if (!strFileName.empty() && strFileName[0] == '#')
153 continue; // assume a comment or something else we don't support
155 // Skip self - do not load playlist recursively
156 // We compare case-less in case user has input incorrect case of the current playlist
157 if (StringUtils::EqualsNoCase(URIUtils::GetFileName(strFileName), m_strPlayListName))
158 continue;
160 if (strFileName.length() > 0)
162 if (!utf8)
163 g_charsetConverter.unknownToUTF8(strFileName);
165 // If no info was read from from the extended tag information, use the file name
166 if (strInfo.length() == 0)
168 strInfo = URIUtils::GetFileName(strFileName);
171 // should substitution occur before or after charset conversion??
172 strFileName = URIUtils::SubstitutePath(strFileName);
174 // Get the full path file name and add it to the the play list
175 CUtil::GetQualifiedFilename(m_strBasePath, strFileName);
176 CFileItemPtr newItem(new CFileItem(strInfo));
177 newItem->SetPath(strFileName);
178 if (iStartOffset != 0 || iEndOffset != 0)
180 newItem->SetStartOffset(iStartOffset);
181 newItem->m_lStartPartNumber = 1;
182 newItem->SetProperty("item_start", iStartOffset);
183 newItem->SetEndOffset(iEndOffset);
184 // Prevent load message from file and override offset set here
185 newItem->GetMusicInfoTag()->SetLoaded();
186 newItem->GetMusicInfoTag()->SetTitle(strInfo);
187 if (iEndOffset)
188 lDuration = static_cast<int>(CUtil::ConvertMilliSecsToSecsIntRounded(iEndOffset - iStartOffset));
190 if (VIDEO::IsVideo(*newItem) &&
191 !newItem->HasVideoInfoTag()) // File is a video and needs a VideoInfoTag
192 newItem->GetVideoInfoTag()->Reset(); // Force VideoInfoTag creation
193 if (lDuration && MUSIC::IsAudio(*newItem))
194 newItem->GetMusicInfoTag()->SetDuration(lDuration);
195 for (auto &prop : properties)
197 newItem->SetProperty(prop.first, prop.second);
200 newItem->SetMimeType(newItem->GetProperty("mimetype").asString());
201 if (!newItem->GetMimeType().empty())
202 newItem->SetContentLookup(false);
204 Add(newItem);
206 // Reset the values just in case there part of the file have the extended marker
207 // and part don't
208 strInfo = "";
209 lDuration = 0;
210 iStartOffset = 0;
211 iEndOffset = 0;
212 properties.clear();
217 file.Close();
218 return true;
221 void CPlayListM3U::Save(const std::string& strFileName) const
223 if (!m_vecItems.size())
224 return;
225 bool utf8 = false;
226 if (URIUtils::GetExtension(strFileName) == ".m3u8")
227 utf8 = true;
228 std::string strPlaylist = CUtil::MakeLegalPath(strFileName);
229 CFile file;
230 if (!file.OpenForWrite(strPlaylist,true))
232 CLog::Log(LOGERROR, "Could not save M3U playlist: [{}]", strPlaylist);
233 return;
235 std::string strLine = StringUtils::Format("{}\n", StartMarker);
236 if (file.Write(strLine.c_str(), strLine.size()) != static_cast<ssize_t>(strLine.size()))
237 return; // error
239 for (int i = 0; i < (int)m_vecItems.size(); ++i)
241 CFileItemPtr item = m_vecItems[i];
242 std::string strDescription=item->GetLabel();
243 if (!utf8)
244 g_charsetConverter.utf8ToStringCharset(strDescription);
245 strLine = StringUtils::Format("{}:{},{}\n", InfoMarker,
246 item->GetMusicInfoTag()->GetDuration(), strDescription);
247 if (file.Write(strLine.c_str(), strLine.size()) != static_cast<ssize_t>(strLine.size()))
248 return; // error
249 if (item->GetStartOffset() != 0 || item->GetEndOffset() != 0)
251 strLine = StringUtils::Format("{}:{},{}\n", OffsetMarker, item->GetStartOffset(),
252 item->GetEndOffset());
253 file.Write(strLine.c_str(),strLine.size());
255 std::string strFileName = ResolveURL(item);
256 if (!utf8)
257 g_charsetConverter.utf8ToStringCharset(strFileName);
258 strLine = StringUtils::Format("{}\n", strFileName);
259 if (file.Write(strLine.c_str(), strLine.size()) != static_cast<ssize_t>(strLine.size()))
260 return; // error
262 file.Close();
265 std::map< std::string, std::string > CPlayListM3U::ParseStreamLine(const std::string &streamLine)
267 std::map< std::string, std::string > params;
269 // ensure the line has something beyond the stream marker and ':'
270 if (streamLine.size() < strlen(StreamMarker) + 2)
271 return params;
273 // get the actual params following the :
274 std::string strParams(streamLine.substr(strlen(StreamMarker) + 1));
276 // separate the parameters
277 std::vector<std::string> vecParams = StringUtils::Split(strParams, ",");
278 for (std::vector<std::string>::iterator i = vecParams.begin(); i != vecParams.end(); ++i)
280 // split the param, ensure there was an =
281 StringUtils::Trim(*i);
282 std::vector<std::string> vecTuple = StringUtils::Split(*i, "=");
283 if (vecTuple.size() < 2)
284 continue;
286 // remove white space from name and value and store it in the dictionary
287 StringUtils::Trim(vecTuple[0]);
288 StringUtils::Trim(vecTuple[1]);
289 params[vecTuple[0]] = vecTuple[1];
292 return params;
295 } // namespace KODI::PLAYLIST