Allow streams to have multiple genres.
This commit is contained in:
@@ -85,6 +85,7 @@
|
||||
delete the item. This helps remobing the entry from the Unity menubar.
|
||||
49. If all streams (and categories) have been removed, then remove streams xml
|
||||
file as well.
|
||||
50. Allow streams to have multiple genres.
|
||||
|
||||
0.9.2
|
||||
-----
|
||||
|
||||
@@ -70,12 +70,13 @@ const QLatin1String StreamsModel::constDefaultCategoryIcon("inode-directory");
|
||||
static const QString constStreamCategoryMimeType("cantata/streams-category");
|
||||
static const QString constStreamMimeType("cantata/stream");
|
||||
static const QLatin1String constSeparator("##Cantata##");
|
||||
const QLatin1String StreamsModel::constGenreSeparator("|");
|
||||
|
||||
static QString encodeStreamItem(StreamsModel::StreamItem *i)
|
||||
{
|
||||
return i->name.replace(constSeparator, " ")+constSeparator+
|
||||
i->url.toString()+constSeparator+
|
||||
i->genre+constSeparator+
|
||||
i->genreString()+constSeparator+
|
||||
i->icon+constSeparator+
|
||||
i->parent->name;
|
||||
}
|
||||
@@ -469,8 +470,10 @@ bool StreamsModel::save(const QString &filename, const QSet<StreamsModel::Item *
|
||||
if (!s->icon.isEmpty() && s->icon!=Icons::streamIcon.name()) {
|
||||
doc.writeAttribute("icon", s->icon);
|
||||
}
|
||||
if (!s->genre.isEmpty() && s->genre!=unknown) {
|
||||
doc.writeAttribute("genre", s->genre);
|
||||
QSet<QString> genres=s->genres;
|
||||
genres.remove(unknown);
|
||||
if (!genres.isEmpty()) {
|
||||
doc.writeAttribute("genre", QStringList(genres.toList()).join(constGenreSeparator));
|
||||
}
|
||||
doc.writeEndElement();
|
||||
}
|
||||
@@ -492,7 +495,7 @@ bool StreamsModel::add(const QString &cat, const QString &name, const QString &g
|
||||
}
|
||||
|
||||
beginInsertRows(createIndex(items.indexOf(c), 0, c), c->streams.count(), c->streams.count());
|
||||
StreamItem *stream=new StreamItem(name, genre, icon.isEmpty() || icon==Icons::streamIcon.name() ? QString() : icon, QUrl(url), c);
|
||||
StreamItem *stream=new StreamItem(name, genreSet(genre), icon.isEmpty() || icon==Icons::streamIcon.name() ? QString() : icon, QUrl(url), c);
|
||||
c->itemMap.insert(url, stream);
|
||||
c->streams.append(stream);
|
||||
endInsertRows();
|
||||
@@ -561,7 +564,6 @@ void StreamsModel::editStream(const QModelIndex &index, const QString &oldCat, c
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl u(url);
|
||||
int row=index.row();
|
||||
|
||||
if (row<cat->streams.count()) {
|
||||
@@ -574,8 +576,9 @@ void StreamsModel::editStream(const QModelIndex &index, const QString &oldCat, c
|
||||
cat->itemMap.remove(oldUrl);
|
||||
cat->itemMap.insert(url, stream);
|
||||
}
|
||||
if (stream->genre!=genre) {
|
||||
stream->genre=genre;
|
||||
QSet<QString> genres=genreSet(genre);
|
||||
if (stream->genres!=genres) {
|
||||
stream->genres=genres;
|
||||
updateGenres();
|
||||
}
|
||||
emit dataChanged(index, index);
|
||||
@@ -902,10 +905,8 @@ void StreamsModel::updateGenres()
|
||||
foreach (CategoryItem *c, items) {
|
||||
c->genres.clear();
|
||||
foreach (const StreamItem *s, c->streams) {
|
||||
if (!s->genre.isEmpty()) {
|
||||
c->genres.insert(s->genre);
|
||||
genres.insert(s->genre);
|
||||
}
|
||||
c->genres+=s->genres;
|
||||
genres+=s->genres;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <QUrl>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
|
||||
class QTimer;
|
||||
class QIODevice;
|
||||
@@ -40,6 +41,11 @@ class StreamsModel : public QAbstractItemModel
|
||||
public:
|
||||
|
||||
static const QLatin1String constDefaultCategoryIcon;
|
||||
static QString prefixUrl(const QString &n, bool addPrefix=true);
|
||||
static QString dir();
|
||||
static const QLatin1String constGenreSeparator;
|
||||
|
||||
static QSet<QString> genreSet(const QString &str) { return str.split(constGenreSeparator, QString::SkipEmptyParts).toSet(); }
|
||||
|
||||
struct Item
|
||||
{
|
||||
@@ -53,9 +59,11 @@ public:
|
||||
struct CategoryItem;
|
||||
struct StreamItem : public Item
|
||||
{
|
||||
StreamItem(const QString &n, const QString &g, const QString &i, const QUrl &u, CategoryItem *p=0) : Item(n, i), genre(g), url(u), parent(p) { }
|
||||
StreamItem(const QString &n, const QString &g, const QString &i, const QUrl &u, CategoryItem *p=0) : Item(n, i), genres(genreSet(g)), url(u), parent(p) { }
|
||||
StreamItem(const QString &n, const QSet<QString> &g, const QString &i, const QUrl &u, CategoryItem *p=0) : Item(n, i), genres(g), url(u), parent(p) { }
|
||||
bool isCategory() { return false; }
|
||||
QString genre;
|
||||
QString genreString() const { return QStringList(genres.toList()).join(constGenreSeparator); }
|
||||
QSet<QString> genres;
|
||||
QUrl url;
|
||||
CategoryItem *parent;
|
||||
};
|
||||
@@ -71,9 +79,6 @@ public:
|
||||
QSet<QString> genres;
|
||||
};
|
||||
|
||||
static QString prefixUrl(const QString &n, bool addPrefix=true);
|
||||
static QString dir();
|
||||
|
||||
static StreamsModel * self();
|
||||
|
||||
StreamsModel();
|
||||
|
||||
@@ -63,7 +63,7 @@ bool StreamsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourc
|
||||
} else {
|
||||
StreamsModel::StreamItem *s = static_cast<StreamsModel::StreamItem *>(item);
|
||||
|
||||
if (!filterGenre.isEmpty() && s->genre!=filterGenre) {
|
||||
if (!filterGenre.isEmpty() && !s->genres.contains(filterGenre)) {
|
||||
return false;
|
||||
}
|
||||
return matchesFilter(QStringList() << s->name);
|
||||
|
||||
@@ -103,6 +103,7 @@ StreamDialog::StreamDialog(const QStringList &categories, const QStringList &gen
|
||||
layout->setWidget(row++, QFormLayout::FieldRole, catCombo);
|
||||
layout->setWidget(row, QFormLayout::LabelRole, new BuddyLabel(i18n("Genre:"), wid, genreCombo));
|
||||
layout->setWidget(row++, QFormLayout::FieldRole, genreCombo);
|
||||
layout->setWidget(row++, QFormLayout::SpanningRole, new QLabel(i18n("<i><b>NOTE:</b> Use '|' to split mutliple genres - e.g. 'Current|Classic'</i>"), this));
|
||||
layout->setWidget(row++, QFormLayout::SpanningRole, statusText);
|
||||
setCaption(i18n("Add Stream"));
|
||||
setMainWidget(wid);
|
||||
@@ -174,7 +175,7 @@ void StreamDialog::changed()
|
||||
// #endif
|
||||
);
|
||||
|
||||
statusText->setText(validProtocol ? QString() : i18n("<i>Invalid protocol</i>"));
|
||||
statusText->setText(validProtocol ? QString() : i18n("<i><b>ERROR:</b> Invalid protocol</i>"));
|
||||
}
|
||||
enableOk=enableOk && validProtocol;
|
||||
enableButton(Ok, enableOk);
|
||||
|
||||
@@ -68,18 +68,13 @@ static QUrl webStreamUrl(StreamsPage::WebStream type)
|
||||
}
|
||||
}
|
||||
|
||||
static QString fixGenre(const QString &g)
|
||||
static QString fixSingleGenre(const QString &g)
|
||||
{
|
||||
if (g.length()) {
|
||||
QString genre=g.toLower();
|
||||
QString genre=Song::capitalize(g);
|
||||
genre[0]=genre[0].toUpper();
|
||||
int pos=genre.indexOf('|');
|
||||
|
||||
if (pos>0) {
|
||||
genre=genre.left(pos);
|
||||
}
|
||||
|
||||
genre=genre.trimmed();
|
||||
genre=genre.replace(QLatin1String("Afro Caribbean"), QLatin1String("Afrocaribbean"));
|
||||
if (genre.length() < 3 ||
|
||||
QLatin1String("The")==genre || QLatin1String("All")==genre ||
|
||||
QLatin1String("Various")==genre || QLatin1String("Unknown")==genre ||
|
||||
@@ -89,7 +84,7 @@ static QString fixGenre(const QString &g)
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (genre==QLatin1String("R b") || genre==QLatin1String("R b")|| genre==QLatin1String("Rnb")) {
|
||||
if (genre==QLatin1String("R&B") || genre==QLatin1String("R B") || genre==QLatin1String("Rnb") || genre==QLatin1String("RnB")) {
|
||||
return QLatin1String("R&B");
|
||||
}
|
||||
if (genre==QLatin1String("Classic") || genre==QLatin1String("Classical")) {
|
||||
@@ -104,22 +99,33 @@ static QString fixGenre(const QString &g)
|
||||
if (genre==QLatin1String("Electronic") || genre==QLatin1String("Electronica") || genre==QLatin1String("Electric")) {
|
||||
return QLatin1String("Electronic");
|
||||
}
|
||||
if (genre==QLatin1String("Easy") || genre==QLatin1String("Easy listening")) {
|
||||
return QLatin1String("Easy listening");
|
||||
if (genre==QLatin1String("Easy") || genre==QLatin1String("Easy Listening")) {
|
||||
return QLatin1String("Easy Listening");
|
||||
}
|
||||
if (genre==QLatin1String("Hit") || genre==QLatin1String("Hits") || genre==QLatin1String("Easy listening")) {
|
||||
return QLatin1String("Hits");
|
||||
}
|
||||
if (genre==QLatin1String("Hip") || genre==QLatin1String("Hiphop") || genre==QLatin1String("Hip hop") || genre==QLatin1String("Hop hip")) {
|
||||
return QLatin1String("Hip hop");
|
||||
if (genre==QLatin1String("Hip") || genre==QLatin1String("Hiphop") || genre==QLatin1String("Hip Hop") || genre==QLatin1String("Hop Hip")) {
|
||||
return QLatin1String("Hip Hop");
|
||||
}
|
||||
if (genre==QLatin1String("News") || genre==QLatin1String("News talk")) {
|
||||
return QLatin1String("News");
|
||||
}
|
||||
if (genre==QLatin1String("Top40") || genre==QLatin1String("Top 40") || genre==QLatin1String("40top") || genre==QLatin1String("40 top")) {
|
||||
if (genre==QLatin1String("Top40") || genre==QLatin1String("Top 40") || genre==QLatin1String("40Top") || genre==QLatin1String("40 Top")) {
|
||||
return QLatin1String("Top 40");
|
||||
}
|
||||
|
||||
QStringList small=QStringList() << QLatin1String("Adult Contemporary") << QLatin1String("Alternative")
|
||||
<< QLatin1String("Community Radio") << QLatin1String("Local Service")
|
||||
<< QLatin1String("Multiultural") << QLatin1String("News")
|
||||
<< QLatin1String("Student") << QLatin1String("Urban");
|
||||
|
||||
foreach (const QString &s, small) {
|
||||
if (genre==s || genre.startsWith(s+" ") || genre.endsWith(" "+s)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert XX's to XXs
|
||||
if (genre.contains(QRegExp("^[0-9]0's$"))) {
|
||||
genre=genre.remove('\'');
|
||||
@@ -136,6 +142,28 @@ static QString fixGenre(const QString &g)
|
||||
return g;
|
||||
}
|
||||
|
||||
static QString fixGenres(const QString &genre)
|
||||
{
|
||||
QString g(genre);
|
||||
int pos=g.indexOf("<br");
|
||||
if (pos>3) {
|
||||
g=g.left(pos);
|
||||
}
|
||||
g=Song::capitalize(g);
|
||||
QStringList genres=g.split('|', QString::SkipEmptyParts);
|
||||
QStringList allGenres;
|
||||
|
||||
foreach (const QString &genre, genres) {
|
||||
allGenres+=genre.split('/', QString::SkipEmptyParts);
|
||||
}
|
||||
|
||||
QStringList fixed;
|
||||
foreach (const QString &genre, allGenres) {
|
||||
fixed.append(fixSingleGenre(genre));
|
||||
}
|
||||
return fixed.join(StreamsModel::constGenreSeparator);
|
||||
}
|
||||
|
||||
static void trimGenres(QMultiHash<QString, StreamsModel::StreamItem *> &genres)
|
||||
{
|
||||
QSet<QString> genreSet = genres.keys().toSet();
|
||||
@@ -143,7 +171,7 @@ static void trimGenres(QMultiHash<QString, StreamsModel::StreamItem *> &genres)
|
||||
if (genres.count(genre) < 3) {
|
||||
const QList<StreamsModel::StreamItem *> &smallGnre = genres.values(genre);
|
||||
foreach (StreamsModel::StreamItem* s, smallGnre) {
|
||||
s->genre = QString();
|
||||
s->genres.remove(genre);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,7 +202,7 @@ static QList<StreamsModel::StreamItem *> parseIceCast(const QByteArray &data)
|
||||
name=doc.readElementText();
|
||||
--level;
|
||||
} else if (QLatin1String("genre")==doc.name()) {
|
||||
genre=fixGenre(doc.readElementText());
|
||||
genre=fixGenres(doc.readElementText());
|
||||
--level;
|
||||
} else if (QLatin1String("listen_url")==doc.name()) {
|
||||
url=QUrl(doc.readElementText());
|
||||
@@ -185,7 +213,9 @@ static QList<StreamsModel::StreamItem *> parseIceCast(const QByteArray &data)
|
||||
if (2==level && QLatin1String("entry")==doc.name() && !name.isEmpty() && url.isValid() && !names.contains(name)) {
|
||||
StreamsModel::StreamItem *item=new StreamsModel::StreamItem(name, genre, QString(), url);
|
||||
streams.append(item);
|
||||
genres.insert(item->genre, item);
|
||||
foreach (const QString &genre, item->genres) {
|
||||
genres.insert(genre, item);
|
||||
}
|
||||
names.insert(item->name);
|
||||
}
|
||||
--level;
|
||||
@@ -222,7 +252,7 @@ static QList<StreamsModel::StreamItem *> parseSomaFm(const QByteArray &data)
|
||||
name=doc.readElementText();
|
||||
--level;
|
||||
} else if (QLatin1String("genre")==doc.name()) {
|
||||
genre=fixGenre(doc.readElementText());
|
||||
genre=fixGenres(doc.readElementText());
|
||||
--level;
|
||||
} else if (QLatin1String("fastpls")==doc.name()) {
|
||||
if (streamFormat.isEmpty() || QLatin1String("mp3")!=streamFormat) {
|
||||
@@ -236,7 +266,9 @@ static QList<StreamsModel::StreamItem *> parseSomaFm(const QByteArray &data)
|
||||
if (2==level && QLatin1String("channel")==doc.name() && !name.isEmpty() && url.isValid() && !names.contains(name)) {
|
||||
StreamsModel::StreamItem *item=new StreamsModel::StreamItem(name, genre, QString(), url);
|
||||
streams.append(item);
|
||||
genres.insert(item->genre, item);
|
||||
foreach (const QString &genre, item->genres) {
|
||||
genres.insert(genre, item);
|
||||
}
|
||||
names.insert(item->name);
|
||||
}
|
||||
--level;
|
||||
@@ -674,7 +706,7 @@ void StreamsPage::edit()
|
||||
StreamsModel::StreamItem *stream=static_cast<StreamsModel::StreamItem *>(item);
|
||||
QString url=stream->url.toString();
|
||||
QString cat=stream->parent->name;
|
||||
QString genre=stream->genre;
|
||||
QString genre=stream->genreString();
|
||||
|
||||
dlg.setEdit(cat, name, genre, icon, url);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user