[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / playlists / PlayListASX.cpp
blob1ee018394f305797fa4c0072aa2136fd916cc4b0
1 /*
2 * Copyright (C) 2024 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 "PlayListASX.h"
11 #include "FileItem.h"
12 #include "PlayListFactory.h"
13 #include "filesystem/File.h"
14 #include "utils/StringUtils.h"
15 #include "utils/XBMCTinyXML2.h"
16 #include "utils/XMLUtils.h"
17 #include "utils/log.h"
18 #include "video/VideoFileItemClassify.h"
19 #include "video/VideoInfoTag.h"
21 #include <iostream>
22 #include <string>
24 #include <tinyxml2.h>
26 using namespace XFILE;
28 namespace KODI::PLAYLIST
31 bool CPlayListASX::LoadAsxIniInfo(std::istream& stream)
33 CLog::Log(LOGINFO, "Parsing INI style ASX");
35 std::string name, value;
37 while (stream.good())
39 // consume blank rows, and blanks
40 while ((stream.peek() == '\r' || stream.peek() == '\n' || stream.peek() == ' ') &&
41 stream.good())
42 stream.get();
44 if (stream.peek() == '[')
46 // this is an [section] part, just ignore it
47 while (stream.good() && stream.peek() != '\r' && stream.peek() != '\n')
48 stream.get();
49 continue;
51 name = "";
52 value = "";
53 // consume name
54 while (stream.peek() != '\r' && stream.peek() != '\n' && stream.peek() != '=' && stream.good())
55 name += stream.get();
57 // consume =
58 if (stream.get() != '=')
59 continue;
61 // consume value
62 while (stream.peek() != '\r' && stream.peek() != '\n' && stream.good())
63 value += stream.get();
65 CLog::Log(LOGINFO, "Adding element {}={}", name, value);
66 CFileItemPtr newItem(new CFileItem(value));
67 newItem->SetPath(value);
68 if (VIDEO::IsVideo(*newItem) &&
69 !newItem->HasVideoInfoTag()) // File is a video and needs a VideoInfoTag
70 newItem->GetVideoInfoTag()->Reset(); // Force VideoInfoTag creation
71 Add(newItem);
74 return true;
77 bool CPlayListASX::LoadData(std::istream& stream)
79 CLog::Log(LOGINFO, "Parsing ASX");
81 if (stream.peek() == '[')
83 return LoadAsxIniInfo(stream);
85 else
87 std::string asxStream(std::istreambuf_iterator<char>(stream), {});
89 CXBMCTinyXML2 xmlDoc;
90 xmlDoc.Parse(asxStream);
92 if (xmlDoc.Error())
94 CLog::Log(LOGERROR, "Unable to parse ASX info Error: {}", xmlDoc.ErrorStr());
95 return false;
98 auto* srcRootElement = xmlDoc.RootElement();
100 if (!srcRootElement)
101 return false;
103 // lowercase every element - copy to second temp doc
104 tinyxml2::XMLDocument targetDoc;
105 std::string value = srcRootElement->Value();
107 StringUtils::ToLower(value);
108 auto targetRootElement = targetDoc.NewElement(value.c_str());
110 auto* rootAttrib = srcRootElement->ToElement()->FirstAttribute();
111 while (rootAttrib)
113 std::string attribName = rootAttrib->Name();
114 auto attribValue = rootAttrib->Value();
115 StringUtils::ToLower(attribName);
116 targetRootElement->SetAttribute(attribName.c_str(), attribValue);
117 rootAttrib = rootAttrib->Next();
120 auto* sourceNode = srcRootElement->FirstChild();
121 while (sourceNode)
123 // Function to check all child elements and lowercase the elem/attrib names
124 recurseLowercaseNames(*targetRootElement, sourceNode);
126 sourceNode = sourceNode->NextSiblingElement();
129 targetDoc.InsertFirstChild(targetRootElement);
131 // now data is lowercased, we can parse contents
132 std::string roottitle;
133 auto* element = targetDoc.RootElement()->FirstChildElement();
134 while (element)
136 value = element->Value();
137 if (value == "title" && !element->NoChildren())
139 roottitle = element->FirstChild()->Value();
141 else if (value == "entry")
143 std::string title(roottitle);
145 auto* refElement = element->FirstChildElement("ref");
146 auto* titleElement = element->FirstChildElement("title");
148 if (titleElement && !titleElement->NoChildren())
149 title = titleElement->FirstChild()->Value();
151 while (refElement)
152 { // multiple references may appear for one entry
153 // duration may exist on this level too
154 value = XMLUtils::GetAttribute(refElement, "href");
155 if (!value.empty())
157 if (title.empty())
158 title = value;
160 CLog::Log(LOGINFO, "Adding element {}, {}", title, value);
161 CFileItemPtr newItem(new CFileItem(title));
162 newItem->SetPath(value);
163 Add(newItem);
165 refElement = refElement->NextSiblingElement("ref");
168 else if (value == "entryref")
170 value = XMLUtils::GetAttribute(element, "href");
171 if (!value.empty())
172 { // found an entryref, let's try loading that url
173 std::unique_ptr<CPlayList> playlist(CPlayListFactory::Create(value));
174 if (nullptr != playlist)
175 if (playlist->Load(value))
176 Add(*playlist);
179 element = element->NextSiblingElement();
183 return true;
186 void CPlayListASX::recurseLowercaseNames(tinyxml2::XMLNode& targetNode,
187 tinyxml2::XMLNode* sourceNode)
189 if (sourceNode->ToElement())
191 std::string strNodeValue = sourceNode->Value();
192 StringUtils::ToLower(strNodeValue);
193 auto* targetElement = targetNode.GetDocument()->NewElement(strNodeValue.c_str());
195 auto* attrib = sourceNode->ToElement()->FirstAttribute();
196 while (attrib)
198 std::string attribName = attrib->Name();
199 auto attribValue = attrib->Value();
200 StringUtils::ToLower(attribName);
201 targetElement->SetAttribute(attribName.c_str(), attribValue);
202 attrib = attrib->Next();
205 if (!sourceNode->NoChildren())
207 for (auto* child = sourceNode->FirstChild(); child != nullptr; child = child->NextSibling())
209 recurseLowercaseNames(*targetElement, child);
213 targetNode.InsertEndChild(targetElement);
214 return;
216 else if (sourceNode->ToText())
218 auto* sourceTextElement = sourceNode->ToText();
219 auto* sourceText = sourceTextElement->Value();
220 auto* targetText = targetNode.GetDocument()->NewText(sourceText);
222 if (!sourceNode->NoChildren())
224 for (auto* child = sourceNode->FirstChildElement(); child != nullptr;
225 child = child->NextSiblingElement())
227 recurseLowercaseNames(*targetText, child);
231 targetNode.InsertEndChild(targetText);
232 return;
236 } // namespace KODI::PLAYLIST