/*************************************************************************** * Copyright (C) 2019-2021 by Ilya Kotov * * forkotov02@ya.ru * * * * Based on Amarok 2 Ultimate Lyrics script * * Copyright (C) 2009-2010 Vladimir Brkic * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include #include #include "lyricsprovider.h" LyricsProvider::LyricsProvider() {} void LyricsProvider::setName(const QString &name) { m_name = name; } void LyricsProvider::setTitle(const QString &title) { m_title = title; } void LyricsProvider::setCharset(const QString &charset) { m_charset = charset; } void LyricsProvider::setUrl(const QString &url) { m_url = url; } void LyricsProvider::addUrlFormat(const QString &replace, const QString &with) { m_urlFormats << UrlFormat{ .replace = replace, .with = with }; } void LyricsProvider::addRule(const QList > &args, bool exclude) { Rule rule; for(const QPair &i : qAsConst(args)) { Item item; if(!i.first.isEmpty() && !i.second.isEmpty()) { item.begin = i.first; item.end = i.second; } else if(i.first.contains("://")) //url { item.url = i.first; } else { item.tag = i.first; } rule << item; } if(exclude) m_excludeRules << rule; else m_extractRules << rule; } void LyricsProvider::addInvalidIndicator(const QString &indicator) { m_invalidIndicators << indicator; } QString LyricsProvider::getUrl(const TrackInfo &track) const { QString url = m_url; const QMap replaceMap = generateReplaceMap(track); QMap::const_iterator it = replaceMap.constBegin(); while(it != replaceMap.constEnd()) { QString value = it.value(); for(const UrlFormat &format: m_urlFormats) value.replace(QRegularExpression(QString("[%1]").arg(QRegularExpression::escape(format.replace))), format.with); url.replace(it.key(), it.value()); ++it; } return url; } QString LyricsProvider::format(const QByteArray &data, const TrackInfo &track) const { QTextCodec *codec = QTextCodec::codecForName(m_charset.toLatin1().constData()); if(!codec) codec = QTextCodec::codecForName("UTF-8"); const QString content = codec->toUnicode(data); QString out; for(const QString &indicator : qAsConst(m_invalidIndicators)) { if(content.contains(indicator)) return QString(); } if(m_skipRules) return content; const QMap replaceMap = generateReplaceMap(track); for(const Rule &rule : qAsConst(m_extractRules)) { Rule tmpRule = rule; QString tmpContent = content; for(Item &item : tmpRule) { QMap::const_iterator it = replaceMap.constBegin(); while(it != replaceMap.constEnd()) { item.begin.replace(it.key(), it.value()); item.url.replace(it.key(), it.value()); ++it; } } tmpContent = extract(tmpContent, tmpRule); if(!tmpContent.isEmpty()) { for(const Rule &excludeRule : qAsConst(m_excludeRules)) tmpContent = exclude(tmpContent, excludeRule); } if(tmpContent.isEmpty()) { tmpContent = content; } else { out = tmpContent; break; } } while (out.endsWith("
")) { out.chop(6); out = out.trimmed(); } while (out.endsWith("
")) { out.chop(4); out = out.trimmed(); } return out; } const QString &LyricsProvider::name() const { return m_name; } void LyricsProvider::skipRules(bool skip) { m_skipRules = skip; } QString LyricsProvider::fixCase(const QString &title) const { QString out; QString::const_iterator it = title.constBegin(); while (it != title.constEnd()) { if(it == title.constBegin() || (it - 1)->isSpace()) out.append(it->toUpper()); else out.append(*it); ++it; } return out; } QMap LyricsProvider::generateReplaceMap(const TrackInfo &track) const { QMap replaceMap = { { "{artist}", track.value(Qmmp::ARTIST).toLower() }, { "{artist2}", track.value(Qmmp::ARTIST).toLower().remove(' ') }, { "{Artist}", track.value(Qmmp::ARTIST) }, { "{ARTIST}", track.value(Qmmp::ARTIST).toUpper() }, { "{a}", track.value(Qmmp::ARTIST).left(1).toLower() }, { "{album}", track.value(Qmmp::ALBUM).toLower() }, { "{album2}", track.value(Qmmp::ALBUM).toLower().remove(' ') }, { "{Album}", track.value(Qmmp::ALBUM) }, { "{title}", track.value(Qmmp::TITLE).toLower() }, { "{Title}", track.value(Qmmp::TITLE) }, { "{Title2}", fixCase(track.value(Qmmp::TITLE)) }, { "{track}", track.value(Qmmp::TRACK) }, { "{year}", track.value(Qmmp::YEAR) } }; return replaceMap; } QString LyricsProvider::extract(const QString &content, const Rule &rule) const { QString out = content; for(const Item &item : qAsConst(rule)) { if(!item.url.isEmpty()) { QString url = item.url; QString id = rule.count() >= 2 ? out.section(rule[1].begin, 1).section(rule[1].end, 0, 0) : QString(); url.replace("{id}", id); return url; } else if(!item.tag.isEmpty()) { const QRegularExpression re("<(\\w+).*>"); QRegularExpressionMatch m = re.match(item.tag); out = out.section(item.tag, 1).section(QString("").arg(m.captured(1)), 0, 0); } else { out = out.section(item.begin, 1).section(item.end, 0, 0); } } return out.trimmed(); } QString LyricsProvider::exclude(const QString &content, const LyricsProvider::Rule &rule) const { QString out = content; for(const Item &item : qAsConst(rule)) { if(!item.tag.isEmpty()) { const QRegularExpression re("<(\\w+).*>"); QRegularExpressionMatch m = re.match(item.tag); out = out.section(item.tag, 0, 0) + out.section(item.tag, 1).section(QString("").arg(m.captured(1)), 1); } else { out = out.section(item.begin, 0, 0) + out.section(item.begin, 1).section(item.end, 1); } } return out.trimmed(); }