diff --git a/CMakeLists.txt b/CMakeLists.txt index 53e0bb6dc..9704854c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,6 +354,7 @@ endif (MSVC) add_subdirectory(po) add_subdirectory(support) add_subdirectory(streams/icons) +add_subdirectory(streams/providers) if (ENABLE_PROXY_CONFIG) set(CANTATA_SRCS ${CANTATA_SRCS} network/proxysettings.cpp) diff --git a/ChangeLog b/ChangeLog index d1c046c89..6768045ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ ----- 1. Add support for opus audio format - AudioCD encoding, transcoding, HTTP server, etc. +2. Add support for user-installable stream providers. Need to copy + name.xml.gz, and name.svg/name.png to ~/.config/cantata/streams - or + PREFIX/share/cantata/streams 1.1.1 ----- diff --git a/models/streamsmodel.cpp b/models/streamsmodel.cpp index 8fc799176..90f54e110 100644 --- a/models/streamsmodel.cpp +++ b/models/streamsmodel.cpp @@ -38,12 +38,14 @@ #include "digitallyimported.h" #include "qjson/parser.h" #include "qtiocompressor/qtiocompressor.h" +#include "utils.h" #include #include #include #include #include #include +#include #include #include #include @@ -54,6 +56,9 @@ #include K_GLOBAL_STATIC(StreamsModel, instance) #endif +#if defined Q_OS_WIN +#include +#endif StreamsModel * StreamsModel::self() { @@ -116,6 +121,25 @@ static QIcon getIcon(const QString &name) return icon.isNull() ? Icons::self()->streamCategoryIcon : icon; } +static QIcon getExternalIcon(const QString &xmlFile) +{ + QIcon icon; + QString iconFile=xmlFile; + iconFile.replace(".xml.gz", ".svg"); + + if (QFile::exists(iconFile)) { + icon.addFile(iconFile); + } else { + iconFile=xmlFile; + iconFile.replace(".xml.gz", ".png"); + if (QFile::exists(iconFile)) { + icon.addFile(iconFile); + } + } + + return icon; +} + static QString categoryCacheName(const QString &name, bool createDir=false) { return Utils::cacheDir(StreamsModel::constCacheDir, createDir)+name+StreamsModel::constCacheExt; @@ -264,7 +288,16 @@ QList StreamsModel::CategoryItem::loadCache() } } - return newItems; + return QList(); +} + +QList StreamsModel::XmlCategoryItem::loadCache() +{ + if (QFile::exists(cacheName)) { + return loadXml(cacheName); + } + + return QList(); } bool StreamsModel::CategoryItem::saveXml(const QString &fileName, bool format) const @@ -460,6 +493,7 @@ StreamsModel::StreamsModel(QObject *parent) favourites=new FavouritesCategoryItem(constFavouritesUrl, i18n("Favorites"), root, getIcon("favourites")); root->children.append(favourites); buildListenLive(); + buildXml(); addBookmarkAction = ActionCollection::get()->createAction("bookmarkcategory", i18n("Bookmark Category"), Icon("bookmark-new")); addToFavouritesAction = ActionCollection::get()->createAction("addtofavourites", i18n("Add Stream To Favorites"), favouritesIcon()); configureAction = ActionCollection::get()->createAction("configurestreams", i18n("Configure Streams"), Icons::self()->configureIcon); @@ -1668,3 +1702,29 @@ void StreamsModel::buildListenLive() } } } + +void StreamsModel::buildXml() +{ + #ifdef Q_OS_WIN + QStringList dirs=QStringList() << QCoreApplication::applicationDirPath()+"/streams/"; + #else + QStringList dirs=QStringList() << INSTALL_PREFIX "/share/cantata/streams/" + << Utils::configDir("streams"); + #endif + QSet added; + + foreach (const QString &dir, dirs) { + if (dir.isEmpty()) { + continue; + } + QDir d(dir); + QStringList files=d.entryList(QStringList() << "*.xml.gz", QDir::Files|QDir::Readable); + foreach (const QString &file, files) { + if (!added.contains(file)) { + CategoryItem *cat=new XmlCategoryItem(Utils::getFile(file).remove(".xml.gz"), root, getExternalIcon(dir+file), dir+file); + added.insert(file); + root->children.append(cat); + } + } + } +} diff --git a/models/streamsmodel.h b/models/streamsmodel.h index 8f329f515..f641566dc 100644 --- a/models/streamsmodel.h +++ b/models/streamsmodel.h @@ -79,7 +79,7 @@ public: CategoryItem * getBookmarksCategory(); CategoryItem * createBookmarksCategory(); void saveCache() const; - QList loadCache(); + virtual QList loadCache(); bool saveXml(const QString &fileName, bool format=false) const; bool saveXml(QIODevice *dev, bool format=false) const; QList loadXml(const QString &fileName, bool importing=false); @@ -119,6 +119,13 @@ public: bool canConfigure() const { return true; } }; + struct XmlCategoryItem : public CategoryItem + { + XmlCategoryItem(const QString &n, CategoryItem *p, const QIcon &i, const QString &cn) + : CategoryItem("-", n, p, i, cn) { } + QList loadCache(); + }; + static const QString constPrefix; static const QString constCacheDir; static const QString constCacheExt; @@ -198,6 +205,7 @@ private: Item * toItem(const QModelIndex &index) const { return index.isValid() ? static_cast(index.internalPointer()) : root; } bool loadFavourites(const QString &fileName, const QModelIndex &index, bool importing=false); void buildListenLive(); + void buildXml(); private: QMap jobs; diff --git a/streams/providers/1.fm.png b/streams/providers/1.fm.png new file mode 100644 index 000000000..4b37ecf42 Binary files /dev/null and b/streams/providers/1.fm.png differ diff --git a/streams/providers/1.fm.xml.gz b/streams/providers/1.fm.xml.gz new file mode 100644 index 000000000..f3eefd22d Binary files /dev/null and b/streams/providers/1.fm.xml.gz differ diff --git a/streams/providers/CMakeLists.txt b/streams/providers/CMakeLists.txt new file mode 100644 index 000000000..f169279b5 --- /dev/null +++ b/streams/providers/CMakeLists.txt @@ -0,0 +1,6 @@ +file(GLOB provider_files *.xml.gz *.png *.svg) +if (WIN32) + install(FILES ${provider_files} DESTINATION ${CMAKE_INSTALL_PREFIX}/streams/) +else (WIN32) + install(FILES ${provider_files} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/${CMAKE_PROJECT_NAME}/streams/) +endif (WIN32)