summaryrefslogtreecommitdiffstats
path: root/xbmc/playlists/PlayListXSPF.cpp
blob: 6f665a790cba78c0d43bc82e7e490d6411748129 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 *  Copyright (C) 2018 Tyler Szabo
 *  Copyright (C) 2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#include "PlayListXSPF.h"

#include "FileItem.h"
#include "URL.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/XBMCTinyXML.h"
#include "utils/log.h"

using namespace PLAYLIST;

namespace
{

constexpr char const* LOCATION_TAGNAME = "location";
constexpr char const* PLAYLIST_TAGNAME = "playlist";
constexpr char const* TITLE_TAGNAME = "title";
constexpr char const* TRACK_TAGNAME = "track";
constexpr char const* TRACKLIST_TAGNAME = "trackList";

std::string GetXMLText(const TiXmlElement* pXmlElement)
{
  std::string result;
  if (pXmlElement)
  {
    const char* const innerText = pXmlElement->GetText();
    if (innerText)
      result = innerText;
  }
  return result;
}

}

CPlayListXSPF::CPlayListXSPF(void) = default;

CPlayListXSPF::~CPlayListXSPF(void) = default;

bool CPlayListXSPF::Load(const std::string& strFileName)
{
  CXBMCTinyXML xmlDoc;

  if (!xmlDoc.LoadFile(strFileName))
  {
    CLog::Log(LOGERROR, "Error parsing XML file {} ({}, {}): {}", strFileName, xmlDoc.ErrorRow(),
              xmlDoc.ErrorCol(), xmlDoc.ErrorDesc());
    return false;
  }

  TiXmlElement* pPlaylist = xmlDoc.FirstChildElement(PLAYLIST_TAGNAME);
  if (!pPlaylist)
  {
    CLog::Log(LOGERROR, "Error parsing XML file {}: missing root element {}", strFileName,
              PLAYLIST_TAGNAME);
    return false;
  }

  TiXmlElement* pTracklist = pPlaylist->FirstChildElement(TRACKLIST_TAGNAME);
  if (!pTracklist)
  {
    CLog::Log(LOGERROR, "Error parsing XML file {}: missing element {}", strFileName,
              TRACKLIST_TAGNAME);
    return false;
  }

  Clear();
  URIUtils::GetParentPath(strFileName, m_strBasePath);

  m_strPlayListName = GetXMLText(pPlaylist->FirstChildElement(TITLE_TAGNAME));

  TiXmlElement* pCurTrack = pTracklist->FirstChildElement(TRACK_TAGNAME);
  while (pCurTrack)
  {
    std::string location = GetXMLText(pCurTrack->FirstChildElement(LOCATION_TAGNAME));
    if (!location.empty())
    {
      std::string label = GetXMLText(pCurTrack->FirstChildElement(TITLE_TAGNAME));

      CFileItemPtr newItem(new CFileItem(label));

      CURL uri(location);

      // at the time of writing CURL doesn't handle file:// URI scheme the way
      // it's presented in this format, parse to local path instead
      std::string localpath;
      if (StringUtils::StartsWith(location, "file:///"))
      {
#ifndef TARGET_WINDOWS
        // Linux absolute path must start with root
        localpath = "/";
#endif
        // Path starts after "file:///"
        localpath += CURL::Decode(location.substr(8));
      }
      else if (uri.GetProtocol().empty())
      {
        localpath = URIUtils::AppendSlash(m_strBasePath) + CURL::Decode(location);
      }

      if (!localpath.empty())
      {
        // We don't use URIUtils::CanonicalizePath because m_strBasePath may be a
        // protocol e.g. smb
#ifdef TARGET_WINDOWS
        StringUtils::Replace(localpath, "/", "\\");
#endif
        localpath = URIUtils::GetRealPath(localpath);

        newItem->SetPath(localpath);
      }
      else
      {
        newItem->SetURL(uri);
      }

      Add(newItem);
    }

    pCurTrack = pCurTrack->NextSiblingElement(TRACK_TAGNAME);
  }

  return true;
}