Warning, file /utilities/telly-skout/src/xmltvfetcher.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 // SPDX-FileCopyrightText: 2022 Plata Hill <plata.hill@kdemail.net> 0002 // SPDX-License-Identifier: LGPL-2.1-or-later 0003 0004 #include "xmltvfetcher.h" 0005 0006 #include "TellySkoutSettings.h" 0007 #include "programdata.h" 0008 0009 #include <KLocalizedString> 0010 0011 #include <QDateTime> 0012 #include <QDebug> 0013 #include <QString> 0014 #include <QStringView> 0015 0016 XmltvFetcher::XmltvFetcher() 0017 : m_doc("xmltv") 0018 { 0019 m_provider.get(QUrl::fromLocalFile(TellySkoutSettings::xmltvFile()), std::bind(&XmltvFetcher::open, this, std::placeholders::_1), [](const Error &error) { 0020 Q_UNUSED(error) 0021 qCritical() << "Failed to open" << TellySkoutSettings::xmltvFile(); 0022 }); 0023 0024 connect(TellySkoutSettings::self(), &TellySkoutSettings::xmltvFileChanged, this, [this]() { 0025 m_provider.get(QUrl::fromLocalFile(TellySkoutSettings::xmltvFile()), 0026 std::bind(&XmltvFetcher::open, this, std::placeholders::_1), 0027 [](const Error &error) { 0028 Q_UNUSED(error) 0029 qCritical() << "Failed to open" << TellySkoutSettings::xmltvFile(); 0030 }); 0031 }); 0032 } 0033 0034 void XmltvFetcher::fetchGroups(std::function<void(const QVector<GroupData> &)> callback, std::function<void(const Error &)> errorCallback) 0035 { 0036 Q_UNUSED(errorCallback); 0037 0038 QVector<GroupData> groups; 0039 0040 GroupData data; 0041 data.m_id = GroupId("xmltv"); 0042 data.m_name = i18n("XMLTV"); 0043 data.m_url = TellySkoutSettings::xmltvFile(); 0044 0045 groups.push_back(data); 0046 0047 if (callback) { 0048 callback(groups); 0049 } 0050 } 0051 0052 void XmltvFetcher::fetchGroup(const QString &url, 0053 const GroupId &groupId, 0054 std::function<void(const QList<ChannelData> &)> callback, 0055 std::function<void(const Error &)> errorCallback) 0056 { 0057 Q_UNUSED(errorCallback) 0058 0059 qDebug() << "Starting to fetch group (" << groupId.value() << ", " << url << ")"; 0060 0061 QMap<ChannelId, ChannelData> channels; 0062 0063 QDomNodeList nodes = m_doc.elementsByTagName("channel"); 0064 0065 for (int i = 0; i < nodes.count(); i++) { 0066 QDomNode elm = nodes.at(i); 0067 if (elm.isElement()) { 0068 const QDomNamedNodeMap &attributes = elm.attributes(); 0069 const ChannelId id = ChannelId(attributes.namedItem("id").toAttr().value()); 0070 0071 const QString &name = elm.firstChildElement("display-name").text(); 0072 const QString &icon = elm.firstChildElement("icon").attributes().namedItem("src").toAttr().value(); 0073 0074 fetchChannel(id, name, icon, channels); 0075 } 0076 } 0077 0078 if (callback) { 0079 callback(channels.values()); 0080 } 0081 } 0082 0083 void XmltvFetcher::fetchProgram(const ChannelId &channelId, 0084 std::function<void(const QVector<ProgramData> &)> callback, 0085 std::function<void(const Error &)> errorCallback) 0086 { 0087 Q_UNUSED(errorCallback) 0088 0089 QVector<ProgramData> programs; 0090 0091 QDomNodeList programNodes = m_doc.elementsByTagName("programme"); 0092 for (int i = 0; i < programNodes.count(); i++) { 0093 const QDomNode &program = programNodes.at(i); 0094 const QDomNamedNodeMap &attributes = program.attributes(); 0095 0096 if (channelId.value() != attributes.namedItem("channel").toAttr().value()) { 0097 continue; // TODO: do not loop all programs for each channel 0098 } 0099 programs.push_back(processProgram(program)); 0100 } 0101 0102 if (callback) { 0103 callback(programs); 0104 } 0105 } 0106 0107 void XmltvFetcher::fetchProgramDescription(const ChannelId &channelId, 0108 const ProgramId &programId, 0109 const QString &url, 0110 std::function<void(const QString &)> callback, 0111 std::function<void(const Error &)> errorCallback) 0112 { 0113 Q_UNUSED(channelId) 0114 Q_UNUSED(programId) 0115 Q_UNUSED(url) 0116 Q_UNUSED(callback) 0117 Q_UNUSED(errorCallback) 0118 0119 // nothing to be done (already fetched as part of the program) 0120 } 0121 0122 QString XmltvFetcher::image(const QString &url, std::function<void()> callback, std::function<void(const Error &)> errorCallback) 0123 { 0124 Q_UNUSED(url); 0125 Q_UNUSED(callback) 0126 Q_UNUSED(errorCallback) 0127 0128 return ""; 0129 } 0130 0131 QString XmltvFetcher::imagePath(const QString &url) 0132 { 0133 Q_UNUSED(url); 0134 return ""; 0135 } 0136 0137 void XmltvFetcher::open(QByteArray data) 0138 { 0139 QString errorMsg; 0140 int errorLine; 0141 int errorColumn; 0142 if (!m_doc.setContent(data, &errorMsg, &errorLine, &errorColumn)) { 0143 qCritical() << "Could not read XML:" << errorMsg << "(l" << errorLine << ":" << errorColumn << ")"; 0144 } 0145 } 0146 0147 void XmltvFetcher::fetchChannel(const ChannelId &channelId, const QString &name, const QString &icon, QMap<ChannelId, ChannelData> &channels) 0148 { 0149 if (!channels.contains(channelId)) { 0150 ChannelData data; 0151 data.m_id = channelId; 0152 data.m_name = name; 0153 data.m_url = ""; 0154 data.m_image = icon; 0155 0156 channels.insert(channelId, data); 0157 } 0158 } 0159 0160 ProgramData XmltvFetcher::processProgram(const QDomNode &program) 0161 { 0162 ProgramData data; 0163 0164 const QDomNamedNodeMap &attributes = program.attributes(); 0165 data.m_channelId = ChannelId(attributes.namedItem("channel").toAttr().value()); 0166 const QString &startTimeString = attributes.namedItem("start").toAttr().value(); 0167 QDateTime startTime = QDateTime::fromString(startTimeString.left(14), "yyyyMMddHHmmss"); 0168 const int startTimeOffset = QStringView{startTimeString}.right(5).left(3).toInt(); 0169 startTime.setOffsetFromUtc(startTimeOffset * 3600); 0170 startTime = startTime.toUTC(); 0171 data.m_startTime = startTime; 0172 // channel + start time can be used as ID 0173 data.m_id = ProgramId(data.m_channelId.value() + "_" + QString::number(startTime.toSecsSinceEpoch())); 0174 const QString &stopTimeString = attributes.namedItem("stop").toAttr().value(); 0175 QDateTime stopTime = QDateTime::fromString(stopTimeString.left(14), "yyyyMMddHHmmss"); 0176 const int stopTimeOffset = QStringView{stopTimeString}.right(5).left(3).toInt(); 0177 stopTime.setOffsetFromUtc(stopTimeOffset * 3600); 0178 stopTime = stopTime.toUTC(); 0179 data.m_stopTime = stopTime; 0180 data.m_title = program.namedItem("title").toElement().text(); 0181 data.m_subtitle = program.namedItem("sub-title").toElement().text(); 0182 data.m_description = program.namedItem("desc").toElement().text(); 0183 0184 if (program.isElement()) { 0185 QDomNodeList categoryNodes = program.toElement().elementsByTagName("category"); 0186 for (int i = 0; i < categoryNodes.count(); i++) { 0187 data.m_categories.push_back(categoryNodes.at(i).toElement().text()); 0188 } 0189 } 0190 0191 data.m_descriptionFetched = true; 0192 0193 return data; 0194 }