Files
cantata/models/streamsmodel.h
Craig Drummond b6bd94c236 Update (c) year
2022-01-08 21:24:07 +00:00

287 lines
12 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.
*/
#ifndef STREAMSMODEL_H
#define STREAMSMODEL_H
#include "config.h"
#include <QString>
#include "gui/apikeys.h"
#include "actionmodel.h"
#include "mpd-interface/stream.h"
#include "mpd-interface/playlist.h"
#include "support/icon.h"
#include <QList>
#include <QMap>
#include <QSet>
#include <QDateTime>
class NetworkJob;
class QNetworkRequest;
class QXmlStreamReader;
class QIODevice;
class StreamsModel : public ActionModel
{
Q_OBJECT
public:
struct CategoryItem;
struct Item : public Stream
{
Item(const QString &u, const QString &n=QString(), CategoryItem *p=nullptr, const QString &sub=QString()) : Stream(u, n), subText(sub), parent(p) { }
~Item() override { }
QString modifiedName() const;
QString subText;
CategoryItem *parent;
virtual bool isCategory() const { return false; }
CategoryItem *getTopLevelCategory() const;
virtual QString fullUrl() const { return url; }
};
struct CategoryItem : public Item
{
enum State
{
Initial,
Fetching,
Fetched
};
CategoryItem(const QString &u, const QString &n=QString(), CategoryItem *p=nullptr, const QIcon &i=QIcon(),
const QString &cn=QString(), const QString &bn=QString(), bool modName=false)
: Item(u, n, p), state(Initial), isAll(false), isBookmarks(false), supportsBookmarks(false),
canBookmark(false), addCatToModifiedName(modName), icon(i), cacheName(cn),
bookmarksName(bn), configName(cn.isEmpty() ? bn : cn) { }
~CategoryItem() override { qDeleteAll(children); }
bool isCategory() const override { return true; }
virtual bool isFavourites() const { return false; }
virtual bool isBuiltIn() const { return true; }
virtual void removeCache();
bool isTopLevel() const { return parent && nullptr==parent->parent; }
virtual bool canReload() const { return !cacheName.isEmpty() || isTopLevel() || !url.isEmpty(); }
void removeBookmarks();
void saveBookmarks();
QList<Item *> loadBookmarks();
CategoryItem * getBookmarksCategory();
CategoryItem * createBookmarksCategory();
void saveCache() const;
virtual QList<Item *> loadCache();
bool saveXml(const QString &fileName, bool format=false) const;
bool saveXml(QIODevice *dev, bool format=false) const;
QList<Item *> loadXml(const QString &fileName);
virtual QList<Item *> loadXml(QIODevice *dev);
virtual void addHeaders(QNetworkRequest &) { }
virtual NetworkJob * fetchSecondardyUrl() { return nullptr; }
State state;
bool isAll : 1;
bool isBookmarks : 1; // 'Virtual' bookmarks category...
bool supportsBookmarks : 1; // Intended for top-level items, indicates if bookmarks can be added
bool canBookmark : 1; // Can this category be bookmark'ed in top-level parent? can have the cache
bool addCatToModifiedName : 1; // When adding to playqueue/favourites, should name contian category?
QList<Item *> children;
QIcon icon;
QString cacheName;
QString bookmarksName;
QString configName;
};
struct FavouritesCategoryItem : public CategoryItem
{
FavouritesCategoryItem(const QString &u, const QString &n, CategoryItem *p, const QIcon &i)
: CategoryItem(u, n, p, i), importedOld(false) { }
QList<Item *> loadXml(QIODevice *dev) override;
bool isFavourites() const override { return true; }
bool canReload() const override { return false; }
bool importedOld;
};
struct IceCastCategoryItem : public CategoryItem
{
IceCastCategoryItem(const QString &u, const QString &n=QString(), CategoryItem *p=nullptr, const QIcon &i=QIcon(),
const QString &cn=QString(), const QString &bn=QString())
: CategoryItem(u, n, p, i, cn, bn) { }
void addHeaders(QNetworkRequest &req) override;
};
struct ShoutCastCategoryItem : public CategoryItem
{
ShoutCastCategoryItem(const QString &u, const QString &n=QString(), CategoryItem *p=nullptr, const QIcon &i=QIcon(),
const QString &cn=QString(), const QString &bn=QString())
: CategoryItem(u, n, p, i, cn, bn) { }
void addHeaders(QNetworkRequest &req) override;
NetworkJob * fetchSecondardyUrl() override;
QString fullUrl() const override { return ApiKeys::self()->addKey(url, ApiKeys::ShoutCast); }
};
struct XmlCategoryItem : public CategoryItem
{
XmlCategoryItem(const QString &n, CategoryItem *p, const QIcon &i, const QString &cn)
: CategoryItem(QLatin1String("-"), n, p, i, cn, QString(), true) { }
QList<Item *> loadCache() override;
bool canReload() const override { return false; }
void removeCache() override { }
bool isBuiltIn() const override { return false; }
};
struct Category
{
Category(const QString &n, const QIcon &i, const QString &k, bool b, bool h)
: name(n), icon(i), key(k), builtin(b), hidden(h) { }
QString name;
QIcon icon;
QString key;
bool builtin;
bool hidden;
};
static const QString constPrefix;
static const QString constSubDir;
static const QString constCacheExt;
static const QString constShoutCastHost;
static const QString constCommunityHost;
static const QString constCompressedXmlFile;
static const QString constXmlFile;
static const QString constSettingsFile;
static const QString constPngIcon;
static const QString constSvgIcon;
static const QStringList constInstallFiles;
static StreamsModel * self();
static QString favouritesDir();
static QString modifyUrl(const QString &u, bool addPrefix=true, const QString &name=QString());
// static bool validProtocol(const QString &file);
StreamsModel(QObject *parent = nullptr);
~StreamsModel() override;
QString name() const;
QString title() const;
QString descr() const;
const QIcon & icon() const { return icn; }
QModelIndex index(int, int, const QModelIndex & = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &) const override;
QVariant data(const QModelIndex &, int) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool hasChildren(const QModelIndex &index) const override;
bool canFetchMore(const QModelIndex &index) const override;
void fetchMore(const QModelIndex &index) override;
void reload(const QModelIndex &index);
bool exportFavourites(const QString &fileName);
void importIntoFavourites(const QString &fileName);
bool haveFavourites() const { return !favourites->children.isEmpty(); }
void removeFromFavourites(const QModelIndexList &indexes);
bool addToFavourites(const QString &url, const QString &name);
QString favouritesNameForUrl(const QString &u);
bool nameExistsInFavourites(const QString &n);
void updateFavouriteStream(const QString &url, const QString &name, const QModelIndex &idx);
bool addBookmark(const QString &url, const QString &name, CategoryItem *bookmarkParentCat);
void removeBookmark(const QModelIndex &index);
void removeAllBookmarks(const QModelIndex &index);
QModelIndex favouritesIndex() const;
const QIcon & favouritesIcon() const { return favourites->icon; }
bool isTuneIn(const CategoryItem *cat) const { return tuneIn==cat; }
bool isShoutCast(const CategoryItem *cat) const { return shoutCast==cat; }
CategoryItem * tuneInCat() const { return tuneIn; }
QStringList filenames(const QModelIndexList &indexes, bool addPrefix) const;
QMimeData * mimeData(const QModelIndexList &indexes) const override;
QStringList mimeTypes() const override;
Action *addBookmarkAct() { return addBookmarkAction; }
Action *addToFavouritesAct() { return addToFavouritesAction; }
Action *configureDiAct() { return configureDiAction; }
Action *reloadAct() { return reloadAction; }
void save();
QList<Category> getCategories() const;
void setHiddenCategories(const QSet<QString> &cats);
CategoryItem * addInstalledProvider(const QString &name, const QIcon &icon, const QString &streamsFileName, bool replace);
void removeInstalledProvider(const QString &key);
QModelIndex categoryIndex(const CategoryItem *cat) const;
Q_SIGNALS:
void loading();
void loaded();
void error(const QString &msg);
void categoriesChanged();
// Streams stored on MPD...
void listFavouriteStreams();
void saveFavouriteStream(const QString &url, const QString &name);
void removeFavouriteStreams(const QList<quint32> &positions);
void editFavouriteStream(const QString &url, const QString &name, quint32 position);
void favouritesLoaded();
void addedToFavourites(const QString &name);
public:
static QList<Item *> parseRadioTimeResponse(QIODevice *dev, CategoryItem *cat, bool parseSubText=false);
static QList<Item *> parseIceCastResponse(QIODevice *dev, CategoryItem *cat);
static QList<Item *> parseShoutCastSearchResponse(QIODevice *dev, CategoryItem *cat);
QList<Item *> parseShoutCastResponse(QIODevice *dev, CategoryItem *cat);
static QList<Item *> parseShoutCastLinks(QXmlStreamReader &doc, CategoryItem *cat);
static QList<Item *> parseShoutCastStations(QXmlStreamReader &doc, CategoryItem *cat);
static QList<Item *> parseCommunityStations(QIODevice *dev, CategoryItem *cat);
static Item * parseRadioTimeEntry(QXmlStreamReader &doc, CategoryItem *parent, bool parseSubText=false);
private Q_SLOTS:
void jobFinished();
// Responses from MPD...
void savedFavouriteStream(const QString &url, const QString &name);
void removedFavouriteStreams(const QList<quint32> &removed);
// void editedFavouriteStream(const QString &url, const QString &name, quint32 position);
void favouriteStreams(const QList<Stream> &streams);
void mpdConnectionState(bool c);
private:
bool loadCache(CategoryItem *cat);
Item * toItem(const QModelIndex &index) const { return index.isValid() ? static_cast<Item*>(index.internalPointer()) : root; }
void loadInstalledProviders();
private:
QMap<NetworkJob *, CategoryItem *> jobs;
CategoryItem *root;
FavouritesCategoryItem *favourites;
CategoryItem *tuneIn;
CategoryItem *shoutCast;
Action *addBookmarkAction;
Action *addToFavouritesAction;
Action *configureDiAction;
Action *reloadAction;
QList<Item *> hiddenCategories;
QIcon icn;
};
#endif