Files
cantata/playlists/rulesplaylists.cpp
Craig Drummond b6bd94c236 Update (c) year
2022-01-08 21:24:07 +00:00

381 lines
13 KiB
C++

/*
* Cantata
*
* Copyright (c) 2011-2022 Craig Drummond <craig.p.drummond@gmail.com>
*
* ----
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "dynamicplaylists.h"
#include "config.h"
#include "support/utils.h"
#include "support/monoicon.h"
#include "widgets/icons.h"
#include "models/roles.h"
#include "gui/settings.h"
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QProcess>
#include <QTimer>
#include <QIcon>
#include <QUrl>
#include <QNetworkProxy>
#include <signal.h>
#include <stdio.h>
const QString RulesPlaylists::constExtension=QLatin1String(".rules");
const QString RulesPlaylists::constRuleKey=QLatin1String("Rule");
const QString RulesPlaylists::constArtistKey=QLatin1String("Artist");
const QString RulesPlaylists::constSimilarArtistsKey=QLatin1String("SimilarArtists");
const QString RulesPlaylists::constAlbumArtistKey=QLatin1String("AlbumArtist");
const QString RulesPlaylists::constComposerKey=QLatin1String("Composer");
const QString RulesPlaylists::constCommentKey=QLatin1String("Comment");
const QString RulesPlaylists::constAlbumKey=QLatin1String("Album");
const QString RulesPlaylists::constTitleKey=QLatin1String("Title");
const QString RulesPlaylists::constGenreKey=QLatin1String("Genre");
const QString RulesPlaylists::constDateKey=QLatin1String("Date");
const QString RulesPlaylists::constRatingKey=QLatin1String("Rating");
const QString RulesPlaylists::constIncludeUnratedKey=QLatin1String("IncludeUnrated");
const QString RulesPlaylists::constDurationKey=QLatin1String("Duration");
const QString RulesPlaylists::constNumTracksKey=QLatin1String("NumTracks");
const QString RulesPlaylists::constMaxAgeKey=QLatin1String("MaxAge");
const QString RulesPlaylists::constFileKey=QLatin1String("File");
const QString RulesPlaylists::constExactKey=QLatin1String("Exact");
const QString RulesPlaylists::constExcludeKey=QLatin1String("Exclude");
const QString RulesPlaylists::constOrderKey=QLatin1String("Order");
const QString RulesPlaylists::constOrderAscendingKey=QLatin1String("OrderAscending");
const QChar RulesPlaylists::constRangeSep=QLatin1Char('-');
const QChar RulesPlaylists::constKeyValSep=QLatin1Char(':');
RulesPlaylists::Order RulesPlaylists::toOrder(const QString &str)
{
for (int i=0; i<Order_Count; ++i) {
if (orderStr((Order)i)==str) {
return (Order)i;
}
}
return Order_Random;
}
QString RulesPlaylists::orderStr(Order order)
{
switch(order) {
case Order_AlbumArtist: return constAlbumArtistKey;
case Order_Artist: return constArtistKey;
case Order_Album: return constAlbumKey;
case Order_Composer: return constComposerKey;
case Order_Date: return constDateKey;
case Order_Genre: return constGenreKey;
case Order_Rating: return constRatingKey;
case Order_Title: return constOrderKey;
case Order_Age: return "Age";
default:
case Order_Random: return "Random";
}
}
QString RulesPlaylists::orderName(Order order)
{
switch(order) {
case Order_AlbumArtist: return tr("Album Artist");
case Order_Artist: return tr("Artist");
case Order_Album: return tr("Album");
case Order_Composer: return tr("Composer");
case Order_Date: return tr("Date");
case Order_Genre: return tr("Genre");
case Order_Rating: return tr("Rating");
case Order_Title: return tr("Title");
case Order_Age: return tr("File Age");
default:
case Order_Random: return tr("Random");
}
}
RulesPlaylists::RulesPlaylists(int icon, const QString &dir)
: rulesDir(dir)
{
icn=MonoIcon::icon((FontAwesome::icon)icon, Utils::monoIconColor());
loadLocal();
}
QVariant RulesPlaylists::headerData(int, Qt::Orientation, int) const
{
return QVariant();
}
int RulesPlaylists::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : entryList.count();
}
bool RulesPlaylists::hasChildren(const QModelIndex &parent) const
{
return !parent.isValid();
}
QModelIndex RulesPlaylists::parent(const QModelIndex &) const
{
return QModelIndex();
}
QModelIndex RulesPlaylists::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() || !hasIndex(row, column, parent) || row>=entryList.count()) {
return QModelIndex();
}
return createIndex(row, column);
}
QVariant RulesPlaylists::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
switch (role) {
case Cantata::Role_TitleText:
return title();
case Cantata::Role_SubText:
return descr();
case Qt::DecorationRole:
return icon();
}
return QVariant();
}
if (index.parent().isValid() || index.row()>=entryList.count()) {
return QVariant();
}
switch (role) {
case Qt::ToolTipRole:
if (!Settings::self()->infoTooltips()) {
return QVariant();
}
case Qt::DisplayRole:
return entryList.at(index.row()).name;
case Cantata::Role_SubText: {
const Entry &e=entryList.at(index.row());
return tr("%n Rule(s)", "", e.rules.count())+(e.haveRating() ? tr(", Rating: %1..%2")
.arg((double)e.ratingFrom/Song::Rating_Step).arg((double)e.ratingTo/Song::Rating_Step) : QString()) +
(isDynamic() ? QString() : (QLatin1String(", ") + orderName(e.order))) +
(isDynamic() || Order_Random==e.order ? QString() : (" ("+(e.orderAscending ? tr("Ascending") : tr("Descending"))+")"));
}
default:
return QVariant();
}
}
Qt::ItemFlags RulesPlaylists::flags(const QModelIndex &index) const
{
if (index.isValid()) {
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
return Qt::NoItemFlags;
}
RulesPlaylists::Entry RulesPlaylists::entry(const QString &e)
{
if (!e.isEmpty()) {
QList<Entry>::Iterator it=find(e);
if (it!=entryList.end()) {
return *it;
}
}
Entry def;
def.numTracks=defaultNumTracks();
return def;
}
bool RulesPlaylists::save(const Entry &e)
{
if (e.name.isEmpty()) {
return false;
}
QString string;
QTextStream str(&string);
if (e.numTracks >= minTracks() && e.numTracks <= maxTracks()) {
str << constNumTracksKey << constKeyValSep << e.numTracks << '\n';
}
if (0!=e.ratingFrom || 0!=e.ratingTo) {
str << constRatingKey << constKeyValSep << e.ratingFrom << constRangeSep << e.ratingTo << '\n';
str << constIncludeUnratedKey << constKeyValSep << (e.includeUnrated ? "true" : "false") << '\n';
}
if (0!=e.minDuration || 0!=e.maxDuration) {
str << constDurationKey << constKeyValSep << e.minDuration << constRangeSep << e.maxDuration << '\n';
}
if (0!=e.maxAge) {
str << constMaxAgeKey << constKeyValSep << e.maxAge << '\n';
}
if (Order_Random!=e.order) {
str << constOrderKey << constKeyValSep << orderStr(e.order) << '\n'
<< constOrderAscendingKey << constKeyValSep << (e.orderAscending ? "true" : "false") << '\n';
}
for (const Rule &rule: e.rules) {
if (!rule.isEmpty()) {
str << constRuleKey << '\n';
Rule::ConstIterator it(rule.constBegin());
Rule::ConstIterator end(rule.constEnd());
for (; it!=end; ++it) {
str << it.key() << constKeyValSep << it.value() << '\n';
}
}
}
if (isRemote()) {
return saveRemote(string, e);
}
QFile f(Utils::dataDir(rulesDir, true)+e.name+constExtension);
if (f.open(QIODevice::WriteOnly|QIODevice::Text)) {
QTextStream out(&f);
out.setCodec("UTF-8");
out << string;
updateEntry(e);
return true;
}
return false;
}
void RulesPlaylists::updateEntry(const Entry &e)
{
QList<RulesPlaylists::Entry>::Iterator it=find(e.name);
if (it!=entryList.end()) {
entryList.replace(it-entryList.begin(), e);
QModelIndex idx=index(it-entryList.begin(), 0, QModelIndex());
emit dataChanged(idx, idx);
} else {
beginInsertRows(QModelIndex(), entryList.count(), entryList.count());
entryList.append(e);
endInsertRows();
}
}
void RulesPlaylists::del(const QString &name)
{
QList<RulesPlaylists::Entry>::Iterator it=find(name);
if (it==entryList.end()) {
return;
}
QString fName(Utils::dataDir(rulesDir, false)+name+constExtension);
bool isCurrent=currentEntry==name;
if (!QFile::exists(fName) || QFile::remove(fName)) {
if (isCurrent) {
stop();
}
beginRemoveRows(QModelIndex(), it-entryList.begin(), it-entryList.begin());
entryList.erase(it);
endRemoveRows();
return;
}
}
QList<RulesPlaylists::Entry>::Iterator RulesPlaylists::find(const QString &e)
{
QList<RulesPlaylists::Entry>::Iterator it(entryList.begin());
QList<RulesPlaylists::Entry>::Iterator end(entryList.end());
for (; it!=end; ++it) {
if ((*it).name==e) {
break;
}
}
return it;
}
void RulesPlaylists::loadLocal()
{
beginResetModel();
entryList.clear();
currentEntry=QString();
// Load all current enttries...
QString dirName=Utils::dataDir(rulesDir);
QDir d(dirName);
if (d.exists()) {
QStringList rulesFiles=d.entryList(QStringList() << QChar('*')+constExtension);
for (const QString &rf: rulesFiles) {
QFile f(dirName+rf);
if (f.open(QIODevice::ReadOnly|QIODevice::Text)) {
QStringList keys=QStringList() << constArtistKey << constSimilarArtistsKey << constAlbumArtistKey << constDateKey
<< constExactKey << constAlbumKey << constTitleKey << constGenreKey << constFileKey << constExcludeKey;
Entry e;
e.name=rf.left(rf.length()-constExtension.length());
e.numTracks=defaultNumTracks();
Rule r;
QTextStream in(&f);
in.setCodec("UTF-8");
QStringList lines = in.readAll().split('\n', CANTATA_SKIP_EMPTY);
for (const QString &line: lines) {
QString str=line.trimmed();
if (str.isEmpty() || str.startsWith('#')) {
continue;
}
if (str==constRuleKey) {
if (!r.isEmpty()) {
e.rules.append(r);
r.clear();
}
} else if (str.startsWith(constRatingKey+constKeyValSep)) {
QStringList vals=str.mid(constRatingKey.length()+1).split(constRangeSep);
if (2==vals.count()) {
e.ratingFrom=vals.at(0).toUInt();
e.ratingTo=vals.at(1).toUInt();
}
} else if (str.startsWith(constIncludeUnratedKey+constKeyValSep)) {
e.includeUnrated="true"==str.mid(constIncludeUnratedKey.length()+1);
} else if (str.startsWith(constDurationKey+constKeyValSep)) {
QStringList vals=str.mid(constDurationKey.length()+1).split(constRangeSep);
if (2==vals.count()) {
e.minDuration=vals.at(0).toUInt();
e.maxDuration=vals.at(1).toUInt();
}
} else if (str.startsWith(constOrderKey+constKeyValSep)) {
e.order=toOrder(str.mid(constOrderKey.length()+1));
} else if (str.startsWith(constOrderAscendingKey+constKeyValSep)) {
e.orderAscending="true"==str.mid(constOrderAscendingKey.length()+1);
} else if (str.startsWith(constNumTracksKey+constKeyValSep)) {
e.numTracks=str.mid(constNumTracksKey.length()+1).toUInt();
} else if (str.startsWith(constMaxAgeKey+constKeyValSep)) {
e.maxAge=str.mid(constMaxAgeKey.length()+1).toUInt();
} else {
for (const QString &k: keys) {
if (str.startsWith(k+constKeyValSep)) {
r.insert(k, str.mid(k.length()+1));
}
}
}
}
if (!r.isEmpty()) {
e.rules.append(r);
r.clear();
}
entryList.append(e);
}
}
}
endResetModel();
}
#include "moc_rulesplaylists.cpp"