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.
9 #include "PlayListASX.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"
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
;
39 // consume blank rows, and blanks
40 while ((stream
.peek() == '\r' || stream
.peek() == '\n' || stream
.peek() == ' ') &&
44 if (stream
.peek() == '[')
46 // this is an [section] part, just ignore it
47 while (stream
.good() && stream
.peek() != '\r' && stream
.peek() != '\n')
54 while (stream
.peek() != '\r' && stream
.peek() != '\n' && stream
.peek() != '=' && stream
.good())
58 if (stream
.get() != '=')
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
77 bool CPlayListASX::LoadData(std::istream
& stream
)
79 CLog::Log(LOGINFO
, "Parsing ASX");
81 if (stream
.peek() == '[')
83 return LoadAsxIniInfo(stream
);
87 std::string
asxStream(std::istreambuf_iterator
<char>(stream
), {});
90 xmlDoc
.Parse(asxStream
);
94 CLog::Log(LOGERROR
, "Unable to parse ASX info Error: {}", xmlDoc
.ErrorStr());
98 auto* srcRootElement
= xmlDoc
.RootElement();
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();
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();
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();
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();
152 { // multiple references may appear for one entry
153 // duration may exist on this level too
154 value
= XMLUtils::GetAttribute(refElement
, "href");
160 CLog::Log(LOGINFO
, "Adding element {}, {}", title
, value
);
161 CFileItemPtr
newItem(new CFileItem(title
));
162 newItem
->SetPath(value
);
165 refElement
= refElement
->NextSiblingElement("ref");
168 else if (value
== "entryref")
170 value
= XMLUtils::GetAttribute(element
, "href");
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
))
179 element
= element
->NextSiblingElement();
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();
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
);
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
);
236 } // namespace KODI::PLAYLIST