File indexing completed on 2023-05-30 11:40:29
0001 /* 0002 Telepathy status message parser class 0003 Copyright (C) 2017 James D. Smith <smithjd15@gmail.com> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Lesser General Public 0007 License as published by the Free Software Foundation; either 0008 version 2.1 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Lesser General Public License for more details. 0014 0015 You should have received a copy of the GNU Lesser General Public 0016 License along with this library; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0018 */ 0019 0020 #include <QTimer> 0021 #include <QElapsedTimer> 0022 #include <QDateTime> 0023 #include <QHash> 0024 #include <QString> 0025 #include <QRegularExpression> 0026 0027 #include "status-message-parser.h" 0028 #include "telepathy-mpris.h" 0029 #include "ktp_kded_debug.h" 0030 0031 static const QList<QString> parserTokens = QList<QString>() << QLatin1String("%tr") 0032 << QLatin1String("%te") << QLatin1String("%time") << QLatin1String("%utc") 0033 << QLatin1String("%title") << QLatin1String("%artist") << QLatin1String("%album") 0034 << QLatin1String("%track") << QLatin1String("%tu") << QLatin1String("%tx") 0035 << QLatin1String("%xm") << QLatin1String("%tf") << QLatin1String("%uf") 0036 << QLatin1String("%sp") << QLatin1String("%um"); 0037 0038 StatusMessageParser::StatusMessageParser(QObject *parent) 0039 : QObject(parent), 0040 m_elapsedTimer(new QElapsedTimer()), 0041 m_updateTimer(new QTimer(this)), 0042 m_expireTimer(new QTimer(this)), 0043 m_mpris(new TelepathyMPRIS(this)) 0044 { 0045 clearStatusMessage(); 0046 0047 m_expireTimer->setSingleShot(true); 0048 connect(m_expireTimer, &QTimer::timeout, [this] { 0049 Q_EMIT statusMessageChanged(parseStatusMessage(m_followUp)); 0050 }); 0051 0052 connect(m_updateTimer, &QTimer::timeout, [this] { 0053 updateMessage(); 0054 Q_EMIT statusMessageChanged(m_statusMessage); 0055 }); 0056 0057 connect(m_mpris, &TelepathyMPRIS::playerChange, [this] { 0058 if ((m_mpris->player()->playState <= TelepathyMPRIS::Stopped) && m_nowPlayingExpire) { 0059 parseStatusMessage(m_followUp); 0060 } 0061 0062 updateMessage(); 0063 Q_EMIT statusMessageChanged(m_statusMessage); 0064 }); 0065 } 0066 0067 StatusMessageParser::~StatusMessageParser() 0068 { 0069 } 0070 0071 QString StatusMessageParser::statusMessage() const 0072 { 0073 return m_statusMessage; 0074 } 0075 0076 QHash<QString, QString> StatusMessageParser::tokens() const 0077 { 0078 return m_tokens; 0079 } 0080 0081 QString StatusMessageParser::parseStatusMessage(QString message) 0082 { 0083 if (message.isEmpty()) { 0084 clearStatusMessage(); 0085 return QString(); 0086 } else { 0087 m_tokens.clear(); 0088 } 0089 0090 QRegularExpression tokenRegexp(QLatin1String("([\\%|\\\\\\%]+[a-z]+\\+\"([^\"]*|[\"]*)\"*)*(?1)|[\\%|\\\\\\%]+[a-z]+\\+?[\\w\\.]+")); 0091 QRegularExpressionMatchIterator tokens = tokenRegexp.globalMatch(message); 0092 while (tokens.hasNext()) { 0093 QRegularExpressionMatch fullmatch = tokens.next(); 0094 QString token = fullmatch.captured(0).section(QLatin1String("+"), 0, 0); 0095 QString tokenParam = fullmatch.captured(0).section(QLatin1String("+"), 1); 0096 0097 if (!parserTokens.contains(token)) { 0098 qCDebug(KTP_KDED_MODULE) << "token match ignore" << token; 0099 continue; 0100 } else if (!tokenParam.isEmpty()) { 0101 if (tokenParam.startsWith(QLatin1String("\"")) && tokenParam.endsWith(QLatin1String("\""))) { 0102 tokenParam.remove(0, 1); 0103 tokenParam.remove(-1, 1); 0104 } 0105 qCDebug(KTP_KDED_MODULE) << "token command match" << token << tokenParam; 0106 } else { 0107 qCDebug(KTP_KDED_MODULE) << "token match" << token; 0108 } 0109 0110 m_tokens.insert(token, tokenParam); 0111 0112 if ((token == QLatin1String("%tr")) && !tokenParam.isEmpty()) { 0113 m_expireTimer->stop(); 0114 m_expireTimer->setInterval(tokenParam.toDouble() * 1000 * 60); 0115 message.replace(fullmatch.captured(0), token); 0116 } 0117 0118 if ((token == QLatin1String("%te")) && !tokenParam.isEmpty()) { 0119 m_elapsedTimer->invalidate(); 0120 m_intervalElapsed = (tokenParam.toDouble() * 1000 * 60); 0121 message.replace(fullmatch.captured(0), token); 0122 } 0123 0124 if ((token == QLatin1String("%time")) && !tokenParam.isEmpty()) { 0125 m_expireTimer->stop(); 0126 m_expireTimer->setInterval(tokenParam.toDouble() * 1000 * 60); 0127 message.replace(fullmatch.captured(0), QDateTime::currentDateTime().addMSecs(tokenParam.toDouble() * 1000 * 60).toString(m_timeFormat)); 0128 } 0129 0130 if ((token == QLatin1String("%utc")) && !tokenParam.isEmpty()) { 0131 m_expireTimer->stop(); 0132 m_expireTimer->setInterval(tokenParam.toDouble() * 1000 * 60); 0133 message.replace(fullmatch.captured(0), QDateTime::currentDateTimeUtc().addMSecs(tokenParam.toDouble() * 1000 * 60).toString(m_utcFormat)); 0134 } 0135 0136 if ((token == QLatin1String("%tu"))) { 0137 m_updateTimer->stop(); 0138 m_updateTimer->setInterval(tokenParam.toDouble() * 1000 * 60); 0139 message.remove(fullmatch.captured(0)); 0140 } 0141 0142 if ((token == QLatin1String("%tx"))) { 0143 m_nowPlayingExpire = (tokenParam == QLatin1String("np")); 0144 m_expireTimer->stop(); 0145 m_expireTimer->setInterval(tokenParam.toDouble() * 1000 * 60); 0146 message.remove(fullmatch.captured(0)); 0147 } 0148 0149 if ((token == QLatin1String("%xm"))) { 0150 m_followUp = tokenParam; 0151 message.remove(fullmatch.captured(0)); 0152 } 0153 0154 if ((token == QLatin1String("%tf"))) { 0155 m_timeFormat = tokenParam; 0156 message.remove(fullmatch.captured(0)); 0157 } 0158 0159 if ((token == QLatin1String("%uf"))) { 0160 m_utcFormat = tokenParam; 0161 message.remove(fullmatch.captured(0)); 0162 } 0163 0164 if ((token == QLatin1String("%sp"))) { 0165 m_separator = tokenParam; 0166 message.remove(fullmatch.captured(0)); 0167 } 0168 0169 if ((token == QLatin1String("%um"))) { 0170 message.remove(fullmatch.captured(0)); 0171 } 0172 } 0173 0174 if (m_tokens.contains(QLatin1String("%time"))) { 0175 message.replace(QRegularExpression(QLatin1String("\\B%time\\b")), QDateTime::currentDateTime().toString(m_timeFormat)); 0176 } 0177 0178 if (m_tokens.contains(QLatin1String("%utc"))) { 0179 message.replace(QRegularExpression(QLatin1String("\\B%utc\\b")), QDateTime::currentDateTimeUtc().toString(m_utcFormat)); 0180 } 0181 0182 if ((m_expireTimer->interval() != 0) && !m_expireTimer->isActive()) { 0183 m_expireTimer->start(); 0184 } 0185 0186 if (!m_elapsedTimer->isValid() && m_tokens.contains(QLatin1String("%te"))) { 0187 m_elapsedTimer->start(); 0188 } 0189 0190 message.replace(QRegularExpression(QLatin1String("\\B\\\\%(?=[a-z]+)")), QLatin1String("%")); 0191 0192 auto hasMpris = [] (const QString &message) { 0193 bool hasMpris = message.contains(QRegularExpression(QLatin1String("\\B%title\\b"))) 0194 || message.contains(QRegularExpression(QLatin1String("\\B%artist\\b"))) 0195 || message.contains(QRegularExpression(QLatin1String("\\B%album\\b"))) 0196 || message.contains(QRegularExpression(QLatin1String("\\B%track\\b"))); 0197 0198 return hasMpris; 0199 }; 0200 0201 if (hasMpris(message) != hasMpris(m_parsedMessage)) { 0202 m_parsedMessage = message; 0203 m_mpris->enable(hasMpris(message)); 0204 } else { 0205 m_parsedMessage = message; 0206 } 0207 0208 updateMessage(); 0209 0210 m_updateTimer->start(); 0211 0212 return m_statusMessage; 0213 } 0214 0215 void StatusMessageParser::updateMessage() 0216 { 0217 QString message = m_parsedMessage; 0218 QString remaining; 0219 QString elapsed; 0220 QString title, artist, album, trackNr; 0221 title = artist = album = trackNr = m_separator; 0222 0223 uint remainingTime = std::round(m_expireTimer->remainingTime() / 1000 / 60); 0224 if (remainingTime < 1) { 0225 remaining = QLatin1String("<1"); 0226 } else { 0227 remaining = QString::number(remainingTime); 0228 } 0229 0230 uint elapsedTime = std::round((m_elapsedTimer->elapsed() + m_intervalElapsed) / 1000 / 60); 0231 if (elapsedTime < 1) { 0232 elapsed = QLatin1String("<1"); 0233 } else { 0234 elapsed = QString::number(elapsedTime); 0235 } 0236 0237 if (m_tokens.contains(QLatin1String("%tr"))) { 0238 message.replace(QRegularExpression(QLatin1String("%tr")), remaining); 0239 } 0240 0241 if (m_tokens.contains(QLatin1String("%te"))) { 0242 message.replace(QRegularExpression(QLatin1String("%te")), elapsed); 0243 } 0244 0245 if (m_mpris->player()->playState == TelepathyMPRIS::Playing) { 0246 if (!m_mpris->player()->metadata.value(QLatin1String("xesam:title")).toString().isEmpty()) { 0247 title = m_mpris->player()->metadata.value(QLatin1String("xesam:title")).toString(); 0248 } 0249 0250 if (!m_mpris->player()->metadata.value(QLatin1String("xesam:artist")).toString().isEmpty()) { 0251 artist = m_mpris->player()->metadata.value(QLatin1String("xesam:artist")).toString(); 0252 } else if (!m_mpris->player()->metadata.value(QLatin1String("xesam:albumArtist")).toString().isEmpty()) { 0253 artist = m_mpris->player()->metadata.value(QLatin1String("xesam:albumArtist")).toString(); 0254 } 0255 0256 if (!m_mpris->player()->metadata.value(QLatin1String("xesam:album")).toString().isEmpty()) { 0257 album = m_mpris->player()->metadata.value(QLatin1String("xesam:album")).toString(); 0258 } 0259 0260 if (!m_mpris->player()->metadata.value(QLatin1String("xesam:trackNumber")).toString().isEmpty()) { 0261 trackNr = m_mpris->player()->metadata.value(QLatin1String("xesam:trackNumber")).toString(); 0262 } 0263 } 0264 0265 if (m_tokens.contains(QLatin1String("%title"))) { 0266 message.replace(QRegularExpression(QLatin1String("\\B(?<![\\\\|%])%title\\b")), title); 0267 } 0268 0269 if (m_tokens.contains(QLatin1String("%album"))) { 0270 message.replace(QRegularExpression(QLatin1String("\\B(?<![\\\\|%])%album\\b")), album); 0271 } 0272 0273 if (m_tokens.contains(QLatin1String("%artist"))) { 0274 message.replace(QRegularExpression(QLatin1String("\\B(?<![\\\\|%])%artist\\b")), artist); 0275 } 0276 0277 if (m_tokens.contains(QLatin1String("%track"))) { 0278 message.replace(QRegularExpression(QLatin1String("\\B(?<![\\\\|%])%track\\b")), trackNr); 0279 } 0280 0281 m_statusMessage = message.simplified(); 0282 } 0283 0284 void StatusMessageParser::clearStatusMessage() 0285 { 0286 m_expireTimer->stop(); 0287 m_updateTimer->stop(); 0288 m_updateTimer->setInterval(300000); 0289 m_elapsedTimer->invalidate(); 0290 m_intervalElapsed = 0; 0291 0292 m_nowPlayingExpire = false; 0293 m_mpris->enable(false); 0294 0295 m_statusMessage.clear(); 0296 m_parsedMessage.clear(); 0297 m_followUp.clear(); 0298 m_tokens.clear(); 0299 0300 m_timeFormat = QLatin1String("h:mm AP t"); 0301 m_utcFormat = QLatin1String("hh:mm t"); 0302 m_separator = QLatin1String("..."); 0303 }