diff --git a/CMakeLists.txt b/CMakeLists.txt index 05e9c62a4..85379c4b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,7 +324,7 @@ set(CANTATA_SRCS ${CANTATA_SRCS} widgets/actionlabel.cpp widgets/playqueueview.cpp widgets/groupedview.cpp widgets/actionitemdelegate.cpp widgets/textbrowser.cpp widgets/volumeslider.cpp widgets/menubutton.cpp widgets/icons.cpp widgets/toolbutton.cpp widgets/wizardpage.cpp widgets/searchwidget.cpp widgets/messageoverlay.cpp widgets/basicitemdelegate.cpp widgets/sizegrip.cpp - widgets/servicestatuslabel.cpp widgets/spacerwidget.cpp widgets/songdialog.cpp widgets/stretchheaderview.cpp + widgets/spacerwidget.cpp widgets/songdialog.cpp widgets/stretchheaderview.cpp widgets/tableview.cpp widgets/thinsplitterhandle.cpp widgets/coverwidget.cpp widgets/ratingwidget.cpp widgets/notelabel.cpp widgets/selectorlabel.cpp widgets/titlewidget.cpp widgets/multipagewidget.cpp widgets/singlepagewidget.cpp widgets/stackedpagewidget.cpp widgets/mirrormenu.cpp widgets/genrecombo.cpp widgets/volumecontrol.cpp @@ -332,9 +332,8 @@ set(CANTATA_SRCS ${CANTATA_SRCS} context/contextwidget.cpp context/view.cpp context/artistview.cpp context/albumview.cpp context/songview.cpp context/contextengine.cpp context/wikipediaengine.cpp context/wikipediasettings.cpp context/othersettings.cpp context/contextsettings.cpp context/togglelist.cpp context/lastfmengine.cpp context/metaengine.cpp context/onlineview.cpp - streams/streamspage.cpp streams/digitallyimportedsettings.cpp streams/streamssettings.cpp streams/streamdialog.cpp streams/tar.cpp - streams/streamproviderlistdialog.cpp streams/streamfetcher.cpp - models/streamsproxymodel.cpp models/streamsearchmodel.cpp models/digitallyimported.cpp models/musiclibraryitemroot.cpp + streams/streamspage.cpp streams/streamdialog.cpp streams/streamfetcher.cpp + models/streamsproxymodel.cpp models/streamsearchmodel.cpp models/musiclibraryitemroot.cpp models/musiclibraryitemartist.cpp models/musiclibraryitemalbum.cpp models/musiclibraryproxymodel.cpp models/playlistsmodel.cpp models/playlistsproxymodel.cpp models/playqueuemodel.cpp models/proxymodel.cpp models/actionmodel.cpp models/musiclibraryitem.cpp models/browsemodel.cpp models/searchmodel.cpp models/streamsmodel.cpp models/searchproxymodel.cpp models/sqllibrarymodel.cpp diff --git a/ChangeLog b/ChangeLog index 0e8c66e19..f47b259d5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,7 @@ 6. Disable CUE parsing in cantata by default, as MPD handles this better now. 7. Remember, and restore, main window position. 8. Disable categorized view, as its been reported to crash (#1530) +9. Remove stream providers, as many broken. 2.4.2 ----- diff --git a/models/digitallyimported.cpp b/models/digitallyimported.cpp deleted file mode 100644 index f5353ffa8..000000000 --- a/models/digitallyimported.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 "digitallyimported.h" -#include "support/configuration.h" -#include "network/networkaccessmanager.h" -#include "support/globalstatic.h" -#include -#include -#include -#include - -static const char * constDiGroup="DigitallyImported"; -static const QStringList constPremiumValues=QStringList() << QLatin1String("premium_high") << QLatin1String("premium_medium") << QLatin1String("premium"); -static const QUrl constAuthUrl(QLatin1String("http://api.audioaddict.com/v1/di/members/authenticate")); -const QString DigitallyImported::constApiUserName=QLatin1String("ephemeron"); -const QString DigitallyImported::constApiPassword=QLatin1String("dayeiph0ne@pp"); -const QString DigitallyImported::constPublicValue=QLatin1String("public3"); - -GLOBAL_STATIC(DigitallyImported, instance) - -DigitallyImported::DigitallyImported() - : job(nullptr) - , streamType(0) - , timer(nullptr) -{ - load(); -} - -DigitallyImported::~DigitallyImported() -{ -} - -void DigitallyImported::login() -{ - if (job) { - job->deleteLater(); - job=nullptr; - } - QNetworkRequest req(constAuthUrl); - addAuthHeader(req); - job=NetworkAccessManager::self()->postFormData(req, "username="+QUrl::toPercentEncoding(userName)+"&password="+QUrl::toPercentEncoding(password)); - connect(job, SIGNAL(finished()), SLOT(loginResponse())); -} - -void DigitallyImported::logout() -{ - if (job) { - job->deleteLater(); - job=nullptr; - } - listenHash=QString(); - expires=QDateTime(); - controlTimer(); -} - -void DigitallyImported::addAuthHeader(QNetworkRequest &req) const -{ - req.setRawHeader("Authorization", "Basic "+QString("%1:%2").arg(constApiUserName, constApiPassword).toLatin1().toBase64()); -} - -void DigitallyImported::load() -{ - Configuration cfg(constDiGroup); - - userName=cfg.get("userName", userName); - password=cfg.get("password", password); - listenHash=cfg.get("listenHash", listenHash); - streamType=cfg.get("streamType", streamType); - QString ex=cfg.get("expires", QString()); - - status=tr("Not logged in"); - if (ex.isEmpty()) { - listenHash=QString(); - } else { - expires=QDateTime::fromString(ex, Qt::ISODate); - // If we have expired, or are about to expire in 5 minutes, then clear the hash... - if (QDateTime::currentDateTime().secsTo(expires)<(5*60)) { - listenHash=QString(); - } else if (!listenHash.isEmpty()) { - status=tr("Logged in"); - } - } - controlTimer(); -} - -void DigitallyImported::save() -{ - Configuration cfg(constDiGroup); - - cfg.set("userName", userName); - cfg.set("password", password); - cfg.set("listenHash", listenHash); - cfg.set("streamType", streamType); - cfg.set("expires", expires.toString(Qt::ISODate)); - emit updated(); -} - -bool DigitallyImported::isDiUrl(const QString &u) const -{ - if (!u.startsWith(QLatin1String("http://"))) { - return false; - } - QUrl url(u); - if (!url.host().startsWith(QLatin1String("listen."))) { - return false; - } - QStringList pathParts=url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); - if (2!=pathParts.count()) { - return false; - } - return pathParts.at(0)==constPublicValue; -} - -QString DigitallyImported::modifyUrl(const QString &u) const -{ - if (listenHash.isEmpty()) { - return u; - } - QString premValue=constPremiumValues.at(streamType>0 && streamTypereadAll()).toVariant().toMap(); - - if (!data.contains("subscriptions")) { - status=tr("No subscriptions"); - emit loginStatus(false, status); - return; - } - - QVariantList subscriptions = data.value("subscriptions", QVariantList()).toList(); - if (subscriptions.isEmpty() || QLatin1String("active")!=subscriptions[0].toMap().value("status").toString()) { - status=tr("You do not have an active subscription"); - emit loginStatus(false, status); - return; - } - - if (!subscriptions[0].toMap().contains("expires_on") || !data.contains("listen_key")) { - status=tr("Unknown error"); - emit loginStatus(false, status); - return; - } - - QDateTime ex = QDateTime::fromString(subscriptions[0].toMap()["expires_on"].toString(), Qt::ISODate); - QString lh = data["listen_key"].toString(); - - if (ex!=expires || lh!=listenHash) { - expires=ex; - listenHash=lh; - save(); - } - status=tr("Logged in (expiry:%1)").arg(expires.toString(Qt::ISODate)); - controlTimer(); - emit loginStatus(true, status); -} - -void DigitallyImported::timeout() -{ - listenHash=QString(); - emit loginStatus(false, tr("Session expired")); -} - -void DigitallyImported::controlTimer() -{ - if (!expires.isValid() || QDateTime::currentDateTime().secsTo(expires)<15) { - if (timer && timer->isActive()) { - if (!listenHash.isEmpty()) { - timeout(); - } - timer->stop(); - } - } else { - if (!timer) { - timer=new QTimer(this); - connect(timer, SIGNAL(timeout()), SLOT(timeout())); - } - int secsTo=QDateTime::currentDateTime().secsTo(expires); - - if (secsTo>4) { - timer->start((secsTo-3)*1000); - } else { - timeout(); - } - } -} - -#include "moc_digitallyimported.cpp" diff --git a/models/digitallyimported.h b/models/digitallyimported.h deleted file mode 100644 index f29ce3ce3..000000000 --- a/models/digitallyimported.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 DIGITALLYIMPORTED_H -#define DIGITALLYIMPORTED_H - -#include -#include - -class QNetworkRequest; -class QNetworkReply; -class QTimer; - -class DigitallyImported : public QObject -{ - Q_OBJECT - -public: - static const QString constApiUserName; - static const QString constApiPassword; - static const QString constPublicValue; - static DigitallyImported * self(); - - DigitallyImported(); - ~DigitallyImported() override; - - void addAuthHeader(QNetworkRequest &req) const; - void load(); - void save(); - bool haveAccount() const { return !userName.isEmpty() && !password.isEmpty(); } - bool loggedIn() const { return !listenHash.isEmpty(); } - - const QString & user() const { return userName; } - const QString & pass() const { return password; } - int audioType() const { return streamType; } - const QDateTime & sessionExpiry() const { return expires; } - void setUser(const QString &u) { userName=u; } - void setPass(const QString &p) { password=p; } - void setAudioType(int a) { streamType=a; } - - const QString & statusString() const { return status; } - - bool isDiUrl(const QString &u) const; - QString modifyUrl(const QString &u) const; - -public Q_SLOTS: - void login(); - void logout(); - -Q_SIGNALS: - void loginStatus(bool ok, const QString &msg); - void updated(); - -private Q_SLOTS: - void timeout(); - void loginResponse(); - -private: - void controlTimer(); - -private: - QNetworkReply *job; - QString status; - QString userName; - QString password; - QString listenHash; - QDateTime expires; - int streamType; - QTimer *timer; -}; - -#endif diff --git a/models/streamsmodel.cpp b/models/streamsmodel.cpp index 91cbacac7..986247310 100644 --- a/models/streamsmodel.cpp +++ b/models/streamsmodel.cpp @@ -34,7 +34,6 @@ #include "support/action.h" #include "gui/stdactions.h" #include "support/actioncollection.h" -#include "digitallyimported.h" #include "qtiocompressor/qtiocompressor.h" #include "config.h" #include "support/globalstatic.h" @@ -478,11 +477,6 @@ NetworkJob * StreamsModel::ShoutCastCategoryItem::fetchSecondardyUrl() return nullptr; } -void StreamsModel::DiCategoryItem::addHeaders(QNetworkRequest &req) -{ - DigitallyImported::self()->addAuthHeader(req); -} - StreamsModel::StreamsModel(QObject *parent) : ActionModel(parent) , root(new CategoryItem(QString(), "root")) @@ -498,10 +492,8 @@ StreamsModel::StreamsModel(QObject *parent) root->children.append(shoutCast); favourites=new FavouritesCategoryItem(constFavouritesUrl, tr("Favorites"), root, MonoIcon::icon(FontAwesome::heart, MonoIcon::constRed)); root->children.append(favourites); - loadInstalledProviders(); addBookmarkAction = new Action(Icons::self()->addBookmarkIcon, tr("Bookmark Category"), this); addToFavouritesAction = new Action(favouritesIcon(), tr("Add Stream To Favorites"), this); - configureDiAction = new Action(Icons::self()->configureIcon, tr("Configure Digitally Imported"), this); reloadAction = new Action(Icons::self()->reloadIcon, tr("Reload"), this); QSet hidden=Settings::self()->hiddenStreamCategories().toSet(); @@ -639,9 +631,6 @@ QVariant StreamsModel::data(const QModelIndex &index, int role) const QList actions; if (item->isCategory()){ const CategoryItem *cat=static_cast(item); - if (cat->isDi()) { - actions << configureDiAction; - } if (cat->canReload()) { actions << reloadAction; } @@ -705,12 +694,7 @@ void StreamsModel::fetchMore(const QModelIndex &index) if (item->isCategory() && !item->url.isEmpty()) { CategoryItem *cat=static_cast(item); if (!cat->isFavourites() && !loadCache(cat)) { - QNetworkRequest req; - if (cat->isDi()) { - req=QNetworkRequest(constDiChannelListUrl.arg(cat->fullUrl().split(".").at(1))); - } else { - req=QNetworkRequest(cat->fullUrl()); - } + QNetworkRequest req=QNetworkRequest(cat->fullUrl()); cat->addHeaders(req); NetworkJob *job=NetworkAccessManager::self()->get(req); job->setProperty(constOrigUrlProperty, cat->fullUrl()); @@ -903,19 +887,13 @@ QModelIndex StreamsModel::favouritesIndex() const return createIndex(root->children.indexOf(favourites), 0, (void *)favourites); } -static QString addDiHash(const StreamsModel::Item *item) -{ - return ( (item->parent && item->parent->isDi()) || DigitallyImported::self()->isDiUrl(item->fullUrl()) ) - ? DigitallyImported::self()->modifyUrl(item->fullUrl()) : item->fullUrl(); -} - QStringList StreamsModel::filenames(const QModelIndexList &indexes, bool addPrefix) const { QStringList fnames; for (const QModelIndex &index: indexes) { Item *item=static_cast(index.internalPointer()); if (!item->isCategory()) { - fnames << modifyUrl(addDiHash(item), addPrefix, addPrefix ? item->modifiedName() : item->name); + fnames << modifyUrl(item->fullUrl(), addPrefix, addPrefix ? item->modifiedName() : item->name); } } @@ -951,12 +929,12 @@ QList StreamsModel::getCategories() const QList categories; for (Item *i: hiddenCategories) { categories.append(Category(i->name, static_cast(i)->icon, static_cast(i)->configName, - static_cast(i)->isBuiltIn(), true, static_cast(i)->isDi())); + static_cast(i)->isBuiltIn(), true)); } for (Item *i: root->children) { if (i!=favourites) { categories.append(Category(i->name, static_cast(i)->icon, static_cast(i)->configName, - static_cast(i)->isBuiltIn(), false, static_cast(i)->isDi())); + static_cast(i)->isBuiltIn(), false)); } } return categories; @@ -1047,14 +1025,8 @@ void StreamsModel::jobFinished() newItems=parseRadioTimeResponse(job->actualJob(), cat); } else if (constIceCastUrl==url) { newItems=parseIceCastResponse(job->actualJob(), cat); - } else if (cat->isSoma()) { - newItems=parseSomaFmResponse(job->actualJob(), cat); - } else if (constDiChannelListHost==job->origUrl().host()) { - newItems=parseDigitallyImportedResponse(job->actualJob(), cat); } else if (constShoutCastHost==job->origUrl().host()) { newItems=parseShoutCastResponse(job->actualJob(), cat); - } else if (cat->isListenLive()) { - newItems=parseListenLiveResponse(job->actualJob(), cat); } } @@ -1350,187 +1322,6 @@ QList StreamsModel::parseIceCastResponse(QIODevice *dev, C return newItems; } -QList StreamsModel::parseSomaFmResponse(QIODevice *dev, CategoryItem *cat) -{ - QList newItems; - QXmlStreamReader doc(dev); - while (!doc.atEnd()) { - doc.readNext(); - if (doc.isStartElement() && QLatin1String("channel")==doc.name()) { - Item *item = parseSomaFmEntry(doc, cat); - if (item) { - newItems.append(item); - } - } - } - return newItems; -} - -QList StreamsModel::parseDigitallyImportedResponse(QIODevice *dev, CategoryItem *cat) -{ - QList newItems; - QVariantMap data=QJsonDocument::fromJson(dev->readAll()).toVariant().toMap(); - QString listenHost=QLatin1String("listen.")+QUrl(cat->fullUrl()).host().remove("www."); - - if (data.contains("channel_filters")) { - QVariantList filters = data["channel_filters"].toList(); - - for (const QVariant &filter: filters) { - // Find the filter called "All" - QVariantMap filterMap = filter.toMap(); - if (filterMap.value("name", QString()).toString() != "All") { - continue; - } - - // Add all its stations to the result - QVariantList channels = filterMap.value("channels", QVariantList()).toList(); - for (const QVariant &channel: channels) { - QVariantMap channelMap = channel.toMap(); - QString url=constDiStdUrl.arg(listenHost).arg(channelMap.value("key").toString()); - newItems.append(new Item(url, channelMap.value("name").toString(), cat)); - } - - break; - } - } - - return newItems; -} - -struct ListenLiveStream { - enum Format { - Unknown, - WMA, - OGG, - MP3, - AAC - }; - - ListenLiveStream() : format(Unknown), bitrate(0) { } - bool operator<(const ListenLiveStream &o) const { return weight()>o.weight(); } - - int weight() const { return ((bitrate&0xff)<<4)+(format&0x0f); } - - void setFormat(const QString &f) { - if (QLatin1String("mp3")==f.toLower()) { - format=MP3; - } else if (QLatin1String("aacplus")==f.toLower()) { - format=AAC; - } else if (QLatin1String("ogg vorbis")==f.toLower()) { - format=OGG; - } else if (QLatin1String("windows media")==f.toLower()) { - format=WMA; - } else { - format=Unknown; - } - } - - QString url; - Format format; - unsigned int bitrate; -}; - -struct ListenLiveStationEntry { - ListenLiveStationEntry() { clear(); } - void clear() { name=location=QString(); streams.clear(); } - QString name; - QString location; - QList streams; -}; - -static QString getString(QString &str, const QString &start, const QString &end) -{ - QString rv; - int b=str.indexOf(start); - int e=-1==b ? -1 : str.indexOf(end, b+start.length()); - if (-1!=e) { - rv=str.mid(b+start.length(), e-(b+start.length())).trimmed(); - str=str.mid(e+end.length()); - } - return rv; -} - -QList StreamsModel::parseListenLiveResponse(QIODevice *dev, CategoryItem *cat) -{ - QList newItems; - - if (dev) { - ListenLiveStationEntry entry; - - while (!dev->atEnd()) { - QString line=QString::fromUtf8(dev->readLine()).trimmed().replace("> <", "><").replace("", "").replace("
", "
") - .replace(" ,", ","); - if (""==line) { - entry.clear(); - } else if (line.startsWith("", ""); - QString extra=getString(line, "", ""); - if (!extra.isEmpty()) { - entry.name+=" "+extra; - } - } else { - // Station URLs... - QString url; - QString bitrate; - int idx=0; - do { - url=getString(line, "href=\"", "\""); - bitrate=getString(line, ">", " Kbps"); - bool sameFormatAsLast=line.startsWith(","); - if (!url.isEmpty() && !bitrate.isEmpty() && !url.startsWith(QLatin1String("javascript")) && idx")) { - if (entry.location.isEmpty()) { - entry.location=getString(line, "", ""); - } - } else if (""==line) { - if (entry.streams.count()) { - std::sort(entry.streams.begin(), entry.streams.end()); - QString name; - QString url=entry.streams.at(0).url; - - if (QLatin1String("National")==entry.location || entry.name.endsWith(QLatin1Char('(')+entry.location+QLatin1Char(')'))) { - name=entry.name; - } else if (entry.name.endsWith(QLatin1Char(')'))) { - name=entry.name.left(entry.name.length()-1)+QLatin1String(", ")+entry.location+QLatin1Char(')'); - } else { - name=entry.name+QLatin1String(" (")+entry.location+QLatin1Char(')'); - } - - if (!name.isEmpty()) { - newItems.append(new Item(url, name, cat)); - } - } - } - } - } - - return newItems; -} - QList StreamsModel::parseShoutCastSearchResponse(QIODevice *dev, CategoryItem *cat) { QList newItems; @@ -1660,120 +1451,6 @@ StreamsModel::Item * StreamsModel::parseRadioTimeEntry(QXmlStreamReader &doc, Ca return item; } -StreamsModel::Item * StreamsModel::parseSomaFmEntry(QXmlStreamReader &doc, CategoryItem *parent) -{ - QString name; - QString url; - QString streamFormat; - - while (!doc.atEnd()) { - doc.readNext(); - - if (QXmlStreamReader::StartElement==doc.tokenType()) { - QStringRef elem = doc.name(); - - if (QLatin1String("title")==elem) { - name=doc.readElementText().trimmed(); - } else if (QLatin1String("fastpls")==elem) { - if (streamFormat.isEmpty() || QLatin1String("mp3")!=streamFormat) { - streamFormat=doc.attributes().value("format").toString(); - url=doc.readElementText(); - } - } - } else if (doc.isEndElement() && QLatin1String("channel")==doc.name()) { - break; - } - } - - return name.isEmpty() || url.isEmpty() ? nullptr : new Item(url, name, parent); -} - -void StreamsModel::loadInstalledProviders() -{ - QSet added; - QString dir=Utils::dataDir(StreamsModel::constSubDir); - QStringList subDirs=QDir(dir).entryList(QStringList() << "*", QDir::Dirs|QDir::Readable|QDir::NoDot|QDir::NoDotDot); - QStringList streamFiles=QStringList() << constCompressedXmlFile << constXmlFile << constSettingsFile; - for (const QString &sub: subDirs) { - if (!added.contains(sub)) { - for (const QString &streamFile: streamFiles) { - if (QFile::exists(dir+sub+Utils::constDirSep+streamFile)) { - addInstalledProvider(sub, getExternalIcon(dir+sub), dir+sub+Utils::constDirSep+streamFile, false); - added.insert(sub); - break; - } - } - } - } -} - -StreamsModel::CategoryItem * StreamsModel::addInstalledProvider(const QString &name, const QIcon &icon, const QString &streamsFileName, bool replace) -{ - CategoryItem *cat=nullptr; - if (streamsFileName.endsWith(constSettingsFile)) { - QFile file(streamsFileName); - if (file.open(QIODevice::ReadOnly)) { - QVariantMap map=QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); - QString type=map["type"].toString(); - QString url=map["url"].toString(); - - if (!url.isEmpty() && !type.isEmpty()) { - QStringList toRemove=QStringList() << " " << "." << "/" << "\\" << "(" << ")"; - QString cacheName=name; - for (const QString &rem: toRemove) { - cacheName=cacheName.replace(rem, ""); - } - cacheName=cacheName.toLower(); - if (type=="di") { - cat=new DiCategoryItem(url, name, root, icon, cacheName); - } else if (type=="soma") { - cat=new SomaCategoryItem(url, name, root, icon, cacheName, map["modName"].toBool()); - } else if (type=="listenlive") { - cat=new ListenLiveCategoryItem(url, name, root, icon, cacheName); - } - } - } - } else { - cat=new XmlCategoryItem(name, root, icon, streamsFileName); - } - - if (!cat) { - return nullptr; - } - - cat->configName="x-"+name; - - if (replace) { - // Remove any existing entry... - removeInstalledProvider(cat->configName); - } - - if (replace) { - beginInsertRows(QModelIndex(), root->children.count(), root->children.count()); - root->children.append(cat); - endInsertRows(); - } else { - root->children.append(cat); - } - return cat; -} - -void StreamsModel::removeInstalledProvider(const QString &key) -{ - for (Item *i: root->children) { - if (key==static_cast(i)->configName) { - int row=root->children.indexOf(i); - if (row>=0) { - static_cast(i)->removeCache(); - beginRemoveRows(QModelIndex(), row, row); - delete root->children.takeAt(row); - endRemoveRows(); - } - break; - } - } -} - QModelIndex StreamsModel::categoryIndex(const CategoryItem *cat) const { int row=root->children.indexOf(const_cast(cat)); diff --git a/models/streamsmodel.h b/models/streamsmodel.h index 27a1e0cf0..df84313f9 100644 --- a/models/streamsmodel.h +++ b/models/streamsmodel.h @@ -80,9 +80,6 @@ public: bool isCategory() const override { return true; } virtual bool isFavourites() const { return false; } virtual bool isBuiltIn() const { return true; } - virtual bool isDi() const { return false; } - virtual bool isSoma() const { return false; } - virtual bool isListenLive() const { return false; } virtual void removeCache(); bool isTopLevel() const { return parent && nullptr==parent->parent; } virtual bool canReload() const { return !cacheName.isEmpty() || isTopLevel() || !url.isEmpty(); } @@ -141,31 +138,6 @@ public: QString fullUrl() const override { return ApiKeys::self()->addKey(url, ApiKeys::ShoutCast); } }; - struct ListenLiveCategoryItem : public CategoryItem - { - ListenLiveCategoryItem(const QString &u, const QString &n, CategoryItem *p, const QIcon &i, const QString &cn) - : CategoryItem(u, n, p, i, cn) { } - bool isListenLive() const override { return true; } - bool isBuiltIn() const override { return false; } - }; - - struct DiCategoryItem : public CategoryItem - { - DiCategoryItem(const QString &u, const QString &n, CategoryItem *p, const QIcon &i, const QString &cn) - : CategoryItem(u, n, p, i, cn, QString(), true) { } - void addHeaders(QNetworkRequest &req) override; - bool isDi() const override { return true; } - bool isBuiltIn() const override { return false; } - }; - - struct SomaCategoryItem : public CategoryItem - { - SomaCategoryItem(const QString &u, const QString &n, CategoryItem *p, const QIcon &i, const QString &cn, bool mod) - : CategoryItem(u, n, p, i, cn, QString(), mod) { } - bool isSoma() const override { return true; } - bool isBuiltIn() const override { return false; } - }; - struct XmlCategoryItem : public CategoryItem { XmlCategoryItem(const QString &n, CategoryItem *p, const QIcon &i, const QString &cn) @@ -178,14 +150,13 @@ public: struct Category { - Category(const QString &n, const QIcon &i, const QString &k, bool b, bool h, bool c) - : name(n), icon(i), key(k), builtin(b), hidden(h), configurable(c) { } + 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; - bool configurable; }; static const QString constPrefix; @@ -276,16 +247,12 @@ Q_SIGNALS: public: static QList parseRadioTimeResponse(QIODevice *dev, CategoryItem *cat, bool parseSubText=false); static QList parseIceCastResponse(QIODevice *dev, CategoryItem *cat); - static QList parseSomaFmResponse(QIODevice *dev, CategoryItem *cat); - static QList parseDigitallyImportedResponse(QIODevice *dev, CategoryItem *cat); - static QList parseListenLiveResponse(QIODevice *dev, CategoryItem *cat); static QList parseShoutCastSearchResponse(QIODevice *dev, CategoryItem *cat); QList parseShoutCastResponse(QIODevice *dev, CategoryItem *cat); static QList parseShoutCastLinks(QXmlStreamReader &doc, CategoryItem *cat); static QList parseShoutCastStations(QXmlStreamReader &doc, CategoryItem *cat); static QList parseCommunityStations(QIODevice *dev, CategoryItem *cat); static Item * parseRadioTimeEntry(QXmlStreamReader &doc, CategoryItem *parent, bool parseSubText=false); - static Item * parseSomaFmEntry(QXmlStreamReader &doc, CategoryItem *parent); private Q_SLOTS: void jobFinished(); diff --git a/streams/digitallyimportedsettings.cpp b/streams/digitallyimportedsettings.cpp deleted file mode 100644 index 68c0f7b8f..000000000 --- a/streams/digitallyimportedsettings.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 "digitallyimportedsettings.h" -#include "models/digitallyimported.h" -#include "support/buddylabel.h" -#include "support/lineedit.h" -#include -#include - -DigitallyImportedSettings::DigitallyImportedSettings(QWidget *parent) - : Dialog(parent) -{ - setButtons(Ok|Cancel); - setCaption(tr("Digitally Imported Settings")); - QWidget *mainWidet = new QWidget(this); - setupUi(mainWidet); - setMainWidget(mainWidet); - - audio->addItem(tr("MP3 256k"), 0); - audio->addItem(tr("AAC 64k"), 1); - audio->addItem(tr("AAC 128k"), 2); - - connect(loginButton, SIGNAL(clicked()), this, SLOT(login())); - connect(DigitallyImported::self(), SIGNAL(loginStatus(bool,QString)), SLOT(loginStatus(bool,QString))); - - int h=fontMetrics().height(); - user->setMinimumWidth(h*20); - adjustSize(); - setMinimumSize(size()); -} - -void DigitallyImportedSettings::show() -{ - prevUser=DigitallyImported::self()->user(); - prevPass=DigitallyImported::self()->pass(); - wasLoggedIn=DigitallyImported::self()->loggedIn(); - prevAudioType=DigitallyImported::self()->audioType(); - - setState(); - user->setText(prevUser); - pass->setText(prevPass); - - for (int i=0; icount(); ++i) { - if (audio->itemData(i).toInt()==DigitallyImported::self()->audioType()) { - audio->setCurrentIndex(i); - break; - } - } - - loginStatusLabel->setText(DigitallyImported::self()->statusString()); - if (QDialog::Accepted==Dialog::exec()) { - QString u=user->text().trimmed(); - QString p=pass->text().trimmed(); - int at=audio->itemData(audio->currentIndex()).toInt(); - bool changed=false; - bool needToLogin=false; - if (u!=DigitallyImported::self()->user()) { - DigitallyImported::self()->setUser(u); - needToLogin=changed=true; - } - if (p!=DigitallyImported::self()->pass()) { - DigitallyImported::self()->setPass(p); - needToLogin=changed=true; - } - if (DigitallyImported::self()->audioType()!=at) { - DigitallyImported::self()->setAudioType(at); - changed=true; - } - if (needToLogin) { - DigitallyImported::self()->login(); - } - if (changed) { - DigitallyImported::self()->save(); - } - } else { - DigitallyImported::self()->setUser(prevUser); - DigitallyImported::self()->setPass(prevPass); - DigitallyImported::self()->setAudioType(prevAudioType); - if (wasLoggedIn) { - DigitallyImported::self()->login(); - } - } -} - -void DigitallyImportedSettings::login() -{ - if (DigitallyImported::self()->loggedIn()) { - loginStatusLabel->setText(tr("Not Authenticated")); - DigitallyImported::self()->logout(); - } else { - loginStatusLabel->setText(tr("Authenticating...")); - messageWidget->close(); - DigitallyImported::self()->setUser(user->text().trimmed()); - DigitallyImported::self()->setPass(pass->text().trimmed()); - DigitallyImported::self()->login(); - } -} - -void DigitallyImportedSettings::loginStatus(bool status, const QString &msg) -{ - loginStatusLabel->setText(status ? tr("Authenticated") : tr("Not Authenticated")); - if (status) { - messageWidget->close(); - } else { - messageWidget->setError(msg, true); - adjustSize(); - } - setState(); -} - -void DigitallyImportedSettings::setState() -{ - if (DigitallyImported::self()->sessionExpiry().isValid()) { - expiry->setText(DigitallyImported::self()->sessionExpiry().toString(Qt::ISODate)); - } else { - loginButton->setText(tr("Login")); - expiry->setText(QString()); - } - - expiry->setVisible(DigitallyImported::self()->sessionExpiry().isValid()); - expiryLabel->setVisible(expiry->isVisible()); - loginButton->setText(DigitallyImported::self()->loggedIn() ? tr("Logout") : tr("Login")); -} - -#include "moc_digitallyimportedsettings.cpp" diff --git a/streams/digitallyimportedsettings.h b/streams/digitallyimportedsettings.h deleted file mode 100644 index 5ab051802..000000000 --- a/streams/digitallyimportedsettings.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 DIGITALLYIMPORTED_SETTINGS_H -#define DIGITALLYIMPORTED_SETTINGS_H - -#include "support/dialog.h" -#include "ui_digitallyimportedsettings.h" - -class LineEdit; -class QComboBox; -class QPushButton; -class QLabel; - -class DigitallyImportedSettings : public Dialog, public Ui::DigitallyImportedSettings -{ - Q_OBJECT - -public: - DigitallyImportedSettings(QWidget *parent); - - void show(); - -private Q_SLOTS: - void login(); - void loginStatus(bool status, const QString &msg); - -private: - void setState(); - -private: - bool wasLoggedIn; - QString prevUser; - QString prevPass; - int prevAudioType; -}; - -#endif diff --git a/streams/digitallyimportedsettings.ui b/streams/digitallyimportedsettings.ui deleted file mode 100644 index 31b918934..000000000 --- a/streams/digitallyimportedsettings.ui +++ /dev/null @@ -1,208 +0,0 @@ - - - DigitallyImportedSettings - - - - 0 - 0 - 491 - 367 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - You can listen for free without an account, but Premium members can listen to higher quality streams without advertisements. Visit <a href="http://www.di.fm/premium/">http://www.di.fm/premium/</a> to upgrade to a premium account. - - - true - - - true - - - - - - - Premium Account - - - - QFormLayout::ExpandingFieldsGrow - - - - - Username: - - - user - - - - - - - - - - Password: - - - pass - - - - - - - QLineEdit::Password - - - - - - - Stream type: - - - audio - - - - - - - - - - Status: - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - Login - - - - - - - Session expiry: - - - - - - - - 0 - 0 - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - These settings apply to Digitally Imported, JazzRadio.com, RockRadio.com, and Sky.fm - - - - - - - If you enter account details, then a 'DI' status item will appear under the list of streams. This will indicate if you are logged in or not. - - - - - - - Qt::Vertical - - - - 20 - 6 - - - - - - - - - LineEdit - QLineEdit -
support/lineedit.h
-
- - BuddyLabel - QLabel -
support/buddylabel.h
-
- - NoteLabel - QLabel -
widgets/notelabel.h
-
- - MessageWidget - QFrame -
support/messagewidget.h
- 1 -
-
- - -
diff --git a/streams/streamproviderlistdialog.cpp b/streams/streamproviderlistdialog.cpp deleted file mode 100644 index b80861613..000000000 --- a/streams/streamproviderlistdialog.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 "streamproviderlistdialog.h" -#include "streamssettings.h" -#include "network/networkaccessmanager.h" -#include "widgets/basicitemdelegate.h" -#include "support/messagebox.h" -#include "support/squeezedtextlabel.h" -#include "support/spinner.h" -#include "support/monoicon.h" -#include "widgets/actionitemdelegate.h" -#include "widgets/messageoverlay.h" -#include "models/streamsmodel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define TEST_PROVIDERS - -static const QLatin1String constProviderBaseUrl("https://raw.githubusercontent.com/CDrummond/cantata-extra/master/streams/2.1/"); - -static QString fileMd5(const QString &fileName) -{ - QFile f(fileName); - if (f.open(QIODevice::ReadOnly)) { - return QString::fromLatin1(QCryptographicHash::hash(f.readAll(), QCryptographicHash::Md5).toHex()); - } - return QString(); -} - -static QString getMd5(const QString &p) -{ - QString dir=Utils::dataDir(StreamsModel::constSubDir, false); - if (dir.isEmpty()) { - return QString(); - } - dir+=Utils::constDirSep+p+Utils::constDirSep; - if (QFile::exists(dir+StreamsModel::constSettingsFile)) { - return fileMd5(dir+StreamsModel::constSettingsFile); - } - if (QFile::exists(dir+StreamsModel::constCompressedXmlFile)) { - return fileMd5(dir+StreamsModel::constCompressedXmlFile); - } - return QString(); -} - -class IconLabel : public QToolButton -{ -public: - IconLabel(const QIcon &icon, const QString &text, QWidget *p) - : QToolButton(p) - { - setIcon(icon); - setText(text); - setAutoRaise(true); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - QFont f(font()); - f.setItalic(true); - setFont(f); - setStyleSheet("QToolButton {border: 0}"); - setFocusPolicy(Qt::NoFocus); - } - - void paintEvent(QPaintEvent *) override - { - QStylePainter p(this); - QStyleOptionToolButton opt; - initStyleOption(&opt); - opt.state&=~(QStyle::State_MouseOver|QStyle::State_Sunken|QStyle::State_On); - opt.state|=QStyle::State_Raised; - p.drawComplexControl(QStyle::CC_ToolButton, opt); - } -}; - -StreamProviderListDialog::StreamProviderListDialog(StreamsSettings *parent) - : Dialog(parent, "StreamProviderListDialog") - , installed(MonoIcon::icon(FontAwesome::check, QColor(0, 220, 0))) - , updateable(MonoIcon::icon(FontAwesome::angledoubledown, QColor(0, 0, 220))) - , p(parent) - , job(nullptr) - , spinner(nullptr) - , msgOverlay(nullptr) -{ - QWidget *wid=new QWidget(this); - QBoxLayout *l=new QBoxLayout(QBoxLayout::TopToBottom, wid); - QWidget *legends=new QWidget(wid); - QBoxLayout *legendsLayout=new QBoxLayout(QBoxLayout::LeftToRight, legends); - legendsLayout->setMargin(0); - tree=new QTreeWidget(wid); - tree->setItemDelegate(new BasicItemDelegate(this)); - tree->header()->setVisible(false); - tree->header()->setStretchLastSection(true); - tree->setSelectionMode(QAbstractItemView::ExtendedSelection); - statusText=new SqueezedTextLabel(wid); - progress=new QProgressBar(wid); - legendsLayout->addWidget(new IconLabel(installed, tr("Installed"), legends)); - legendsLayout->addItem(new QSpacerItem(l->spacing(), 0, QSizePolicy::Fixed, QSizePolicy::Fixed)); - legendsLayout->addWidget(new IconLabel(updateable, tr("Update available"), legends)); - legendsLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); - l->addWidget(new QLabel(tr("Check the providers you wish to install/update."), wid)); - l->addWidget(tree); - l->addWidget(legends); - l->addItem(new QSpacerItem(0, l->spacing(), QSizePolicy::Fixed, QSizePolicy::Fixed)); - l->addWidget(statusText); - l->addWidget(progress); - l->setMargin(0); - connect(tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(itemChanged(QTreeWidgetItem*,int))); - setMainWidget(wid); - setButtons(User1|User2|Close); - enableButton(User1, false); - enableButton(User2, false); - setDefaultButton(Close); - setCaption(tr("Install/Update Stream Providers")); -} - -StreamProviderListDialog::~StreamProviderListDialog() -{ - if (job) { - disconnect(job, SIGNAL(finished()), this, SLOT(jobFinished())); - job->cancelAndDelete(); - job=nullptr; - } -} - -void StreamProviderListDialog::show(const QSet &installed) -{ - installedProviders.clear(); - for (const QString &inst: installed) { - installedProviders.insert(inst, getMd5(inst)); - } - - processItems.clear(); - checkedItems.clear(); - setState(false); - updateView(true); - - if (!tree->topLevelItemCount()) { - QTimer::singleShot(0, this, SLOT(getProviderList())); - } - exec(); -} - -void StreamProviderListDialog::getProviderList() -{ - if (!spinner) { - spinner=new Spinner(this); - spinner->setWidget(tree); - } - if (!msgOverlay) { - msgOverlay=new MessageOverlay(this); - msgOverlay->setWidget(tree); - } - #ifdef TEST_PROVIDERS - QFile f("list.xml"); - if (f.open(QIODevice::ReadOnly)) { - readProviders(&f); - } - #else - job=NetworkAccessManager::self()->get(QUrl(constProviderBaseUrl+"list.xml")); - connect(job, SIGNAL(finished()), this, SLOT(jobFinished())); - spinner->start(); - msgOverlay->setText(tr("Downloading list..."), -1, false); - #endif -} - -enum Categories { - Cat_General = 0, - Cat_DigitallyImported = 1, - Cat_ListenLive = 2, - - Cat_Total -}; - -enum Roles { - Role_Url = Qt::UserRole, - Role_Md5, - Role_Updateable, - Role_Category -}; - -static QString catName(int cat) { - switch (cat) { - default: - case Cat_General: return QObject::tr("General"); - case Cat_DigitallyImported: return QObject::tr("Digitally Imported"); - case Cat_ListenLive: return QObject::tr("Local and National Radio (ListenLive)"); - } -} - -void StreamProviderListDialog::jobFinished() -{ - NetworkJob *j=qobject_cast(sender()); - if (!j) { - return; - } - j->deleteLater(); - if (j!=job) { - return; - } - job=nullptr; - - if (0==tree->topLevelItemCount()) { - if (j->ok()) { - readProviders(j->actualJob()); - } else { - slotButtonClicked(Close); - MessageBox::error(parentWidget(), tr("Failed to download list of stream providers!")); - } - if (spinner) { - spinner->stop(); - } - if (msgOverlay) { - msgOverlay->setText(QString()); - } - } else { - QTreeWidgetItem *item=*(processItems.begin()); - if (j->ok()) { - statusText->setText(tr("Installing/updating %1").arg(item->text(0))); - QTemporaryFile temp("cantata_XXXXXX.streams"); - temp.setAutoRemove(true); - temp.open(); - temp.write(j->readAll()); - temp.close(); - if (!p->install(temp.fileName(), item->text(0), false)) { - MessageBox::error(this, tr("Failed to install '%1'").arg(item->text(0))); - setState(false); - } else { - item->setCheckState(0, Qt::Unchecked); - processItems.removeAll(item); - progress->setValue(progress->value()+1); - doNext(); - } - } else { - MessageBox::error(this, tr("Failed to download '%1'").arg(item->text(0))); - setState(false); - } - } -} - -void StreamProviderListDialog::itemChanged(QTreeWidgetItem *itm, int col) -{ - Q_UNUSED(col) - if (Qt::Checked==itm->checkState(0)) { - checkedItems.insert(itm); - } else { - checkedItems.remove(itm); - } - enableButton(User1, !checkedItems.isEmpty()); -} - -void StreamProviderListDialog::readProviders(QIODevice *dev) -{ - QMap categories; - QXmlStreamReader doc(dev); - int currentCat=-1; - while (!doc.atEnd()) { - doc.readNext(); - - if (doc.isStartElement()) { - if (QLatin1String("category")==doc.name()) { - currentCat=doc.attributes().value("type").toString().toInt(); - } else if (QLatin1String("provider")==doc.name()) { - QString name=doc.attributes().value("name").toString(); - if (!name.isEmpty() && currentCat>=0 && currentCatsetFlags(Qt::ItemIsEnabled); - QFont f=tree->font(); - f.setBold(true); - cat->setFont(0, f); - categories.insert(currentCat, cat); - } else { - cat=categories[currentCat]; - } - QTreeWidgetItem *prov=new QTreeWidgetItem(cat, QStringList() << name); - prov->setData(0, Role_Url, url); - prov->setData(0, Role_Md5, doc.attributes().value("md5").toString()); - prov->setData(0, Role_Updateable, false); - prov->setData(0, Role_Category, currentCat); - prov->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable); - prov->setCheckState(0, Qt::Unchecked); - cat->setExpanded(true); - } - } - } - } - updateView(); -} - -void StreamProviderListDialog::slotButtonClicked(int button) -{ - switch (button) { - case User2: - case User1: { - bool update=false; - bool install=false; - processItems.clear(); - if (User2==button) { - processItems.clear(); - for (int tl=0; tltopLevelItemCount(); ++tl) { - QTreeWidgetItem *tli=tree->topLevelItem(tl); - for (int c=0; cchildCount(); ++c) { - QTreeWidgetItem *ci=tli->child(c); - if (ci->data(0, Role_Updateable).toBool()) { - update=true; - processItems.append(ci); - } - } - } - } else { - for (QTreeWidgetItem *i: checkedItems) { - if (installedProviders.keys().contains(i->text(0))) { - update=true; - } else { - install=true; - } - processItems.append(i); - } - } - - if (install || update) { - setState(true); - doNext(); - } - break; - } - case Cancel: - if (MessageBox::Yes==MessageBox::questionYesNo(this, tr("Abort installation/update?"), tr("Abort"))) { - if (job) { - disconnect(job, SIGNAL(finished()), this, SLOT(jobFinished())); - job->cancelAndDelete(); - job=nullptr; - } - reject(); - // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!! - Dialog::slotButtonClicked(button); - } - break; - case Close: - reject(); - // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!! - Dialog::slotButtonClicked(button); - break; - default: - break; - } -} - -void StreamProviderListDialog::updateView(bool unCheck) -{ - QHash::ConstIterator end=installedProviders.end(); - int numUpdates=0; - - for (int tl=0; tltopLevelItemCount(); ++tl) { - QTreeWidgetItem *tli=tree->topLevelItem(tl); - for (int c=0; cchildCount(); ++c) { - bool update=false; - QTreeWidgetItem *ci=tli->child(c); - QHash::ConstIterator inst=installedProviders.find(ci->text(0)); - if (inst!=end) { - update=inst.value()!=ci->data(0, Role_Md5).toString(); - ci->setData(0, Qt::DecorationRole, update ? updateable : installed); - if (update) { - numUpdates++; - } - } else { - ci->setData(0, Qt::DecorationRole, def); - } - if (unCheck) { - ci->setCheckState(0, Qt::Unchecked); - } - ci->setData(0, Role_Updateable, update); - } - } - - updateText=0==numUpdates ? QString() : tr("%n Update(s) available", "", numUpdates); - setState(false); -} - -void StreamProviderListDialog::doNext() -{ - if (processItems.isEmpty()) { - accept(); - } else { - QTreeWidgetItem *item=*(processItems.begin()); - statusText->setText(tr("Downloading %1").arg(item->text(0))); - job=NetworkAccessManager::self()->get(QUrl(item->data(0, Role_Url).toString())); - connect(job, SIGNAL(finished()), this, SLOT(jobFinished())); - } -} - -void StreamProviderListDialog::setState(bool downloading) -{ - tree->setEnabled(!downloading); - progress->setVisible(downloading); - statusText->setVisible(downloading || !updateText.isEmpty()); - if (downloading) { - setButtons(Cancel); - progress->setRange(0, processItems.count()); - progress->setValue(0); - } else { - setButtons(User1|User2|Close); - setButtonText(User1, tr("Install/Update")); - enableButton(User1, !checkedItems.isEmpty()); - setButtonText(User2, tr("Update all updateable providers")); - enableButton(User2, !updateText.isEmpty()); - setDefaultButton(Close); - statusText->setText(updateText); - } -} - -#include "moc_streamproviderlistdialog.cpp" diff --git a/streams/streamproviderlistdialog.h b/streams/streamproviderlistdialog.h deleted file mode 100644 index 889bf5d56..000000000 --- a/streams/streamproviderlistdialog.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 STREAM_PROVIDER_LIST_DIALOG_H -#define STREAM_PROVIDER_LIST_DIALOG_H - -#include "support/dialog.h" -#include "support/icon.h" -#include -#include - -class NetworkJob; -class QTreeWidget; -class QTreeWidgetItem; -class QProgressBar; -class SqueezedTextLabel; -class StreamsSettings; -class Spinner; -class MessageOverlay; -class QIODevice; - -class StreamProviderListDialog : public Dialog -{ - Q_OBJECT - -public: - StreamProviderListDialog(StreamsSettings *parent); - ~StreamProviderListDialog() override; - void show(const QSet &installed); - -private Q_SLOTS: - void getProviderList(); - void jobFinished(); - void itemChanged(QTreeWidgetItem *itm, int col); - -private: - void readProviders(QIODevice *dev); - void slotButtonClicked(int button) override; - void updateView(bool unCheck=false); - void doNext(); - void setState(bool downloading); - -private: - QIcon def; - QIcon installed; - QIcon updateable; - StreamsSettings *p; - NetworkJob *job; - Spinner *spinner; - MessageOverlay *msgOverlay; - QTreeWidget *tree; - QProgressBar *progress; - QString updateText; - SqueezedTextLabel *statusText; - QHash installedProviders; - QList processItems; - QSet checkedItems; -}; - -#endif diff --git a/streams/streamspage.cpp b/streams/streamspage.cpp index d568b0fef..72f4029fd 100644 --- a/streams/streamspage.cpp +++ b/streams/streamspage.cpp @@ -23,7 +23,6 @@ #include "streamspage.h" #include "streamdialog.h" -#include "streamssettings.h" #include "mpd-interface/mpdconnection.h" #include "support/messagebox.h" #include "widgets/icons.h" @@ -35,10 +34,7 @@ #include "gui/settings.h" #include "widgets/menubutton.h" #include "widgets/itemview.h" -#include "widgets/servicestatuslabel.h" -#include "models/digitallyimported.h" #include "models/playqueuemodel.h" -#include "digitallyimportedsettings.h" #include #include #include @@ -97,7 +93,6 @@ void StreamsPage::addToFavourites() StreamsBrowsePage::StreamsBrowsePage(QWidget *p) : SinglePageWidget(p) - , settings(nullptr) { QColor iconCol=Utils::monoIconColor(); importAction = new Action(MonoIcon::icon(FontAwesome::arrowright, iconCol), tr("Import Streams Into Favorites"), this); @@ -111,7 +106,6 @@ StreamsBrowsePage::StreamsBrowsePage(QWidget *p) connect(view, SIGNAL(itemsSelected(bool)), SLOT(controlActions())); connect(addAction, SIGNAL(triggered()), this, SLOT(addStream())); connect(StreamsModel::self()->addBookmarkAct(), SIGNAL(triggered()), this, SLOT(addBookmark())); - connect(StreamsModel::self()->configureDiAct(), SIGNAL(triggered()), this, SLOT(configureDi())); connect(StreamsModel::self()->reloadAct(), SIGNAL(triggered()), this, SLOT(reload())); connect(editAction, SIGNAL(triggered()), this, SLOT(edit())); connect(importAction, SIGNAL(triggered()), this, SLOT(importXml())); @@ -122,10 +116,7 @@ StreamsBrowsePage::StreamsBrowsePage(QWidget *p) connect(StreamsModel::self(), SIGNAL(categoriesChanged()), view, SLOT(closeSearch())); connect(StreamsModel::self(), SIGNAL(favouritesLoaded()), SLOT(expandFavourites())); connect(StreamsModel::self(), SIGNAL(addedToFavourites(QString)), SLOT(addedToFavourites(QString))); - connect(DigitallyImported::self(), SIGNAL(loginStatus(bool,QString)), SLOT(updateDiStatus())); - connect(DigitallyImported::self(), SIGNAL(updated()), SLOT(updateDiStatus())); connect(view, SIGNAL(headerClicked(int)), SLOT(headerClicked(int))); - StreamsModel::self()->configureDiAct()->setEnabled(false); proxy.setSourceModel(StreamsModel::self()); view->setModel(&proxy); @@ -138,12 +129,8 @@ StreamsBrowsePage::StreamsBrowsePage(QWidget *p) view->load(config); MenuButton *menuButton=new MenuButton(this); - Action *configureAction=new Action(Icons::self()->configureIcon, tr("Configure"), this); - connect(configureAction, SIGNAL(triggered()), SLOT(configure())); menuButton->addAction(createViewMenu(QList() << ItemView::Mode_BasicTree << ItemView::Mode_SimpleTree << ItemView::Mode_DetailedTree << ItemView::Mode_List)); - menuButton->addAction(configureAction); - menuButton->addAction(StreamsModel::self()->configureDiAct()); menuButton->addSeparator(); menuButton->addAction(addAction); menuButton->addAction(StdActions::self()->removeAction); @@ -153,13 +140,9 @@ StreamsBrowsePage::StreamsBrowsePage(QWidget *p) menuButton->addAction(importAction); menuButton->addAction(exportAction); - diStatusLabel=new ServiceStatusLabel(this); - diStatusLabel->setText("DI", tr("Digitally Imported", "Service name")); - connect(diStatusLabel, SIGNAL(clicked()), SLOT(diSettings())); - updateDiStatus(); ToolButton *searchButton=new ToolButton(this); searchButton->setDefaultAction(searchAction); - init(ReplacePlayQueue, QList() << menuButton << diStatusLabel, QList() << searchButton); + init(ReplacePlayQueue, QList() << menuButton, QList() << searchButton); view->addAction(editAction); view->addAction(StdActions::self()->removeAction); @@ -218,36 +201,6 @@ void StreamsBrowsePage::itemDoubleClicked(const QModelIndex &index) } } -void StreamsBrowsePage::configure() -{ - if (!settings) { - settings=new StreamsSettings(this); - } - if (!settings->isVisible()) { - settings->load(); - } - settings->show(); - settings->raiseWindow(); -} - -void StreamsBrowsePage::configureDi() -{ - QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here... - if (1!=selected.count()) { - return; - } - - const StreamsModel::Item *item=static_cast(proxy.mapToSource(selected.first()).internalPointer()); - if (item->isCategory() && static_cast(item)->isDi()) { - diSettings(); - } -} - -void StreamsBrowsePage::diSettings() -{ - DigitallyImportedSettings(this).show(); -} - void StreamsBrowsePage::importXml() { QString fileName=QFileDialog::getOpenFileName(this, tr("Import Streams"), QDir::homePath(), @@ -554,23 +507,11 @@ void StreamsBrowsePage::controlActions() StdActions::self()->removeAction->setEnabled(item->isCategory() && item->parent && (item->parent->isBookmarks || (static_cast(item)->isBookmarks))); } - StreamsModel::self()->configureDiAct()->setEnabled(item->isCategory() && static_cast(item)->isDi()); - } else { - StreamsModel::self()->configureDiAct()->setEnabled(false); } StdActions::self()->replacePlayQueueAction->setEnabled(haveSelection && onlyStreamsSelected); } -void StreamsBrowsePage::updateDiStatus() -{ - if (DigitallyImported::self()->user().isEmpty() || DigitallyImported::self()->pass().isEmpty()) { - diStatusLabel->setVisible(false); - } else { - diStatusLabel->setStatus(DigitallyImported::self()->loggedIn()); - } -} - void StreamsBrowsePage::expandFavourites() { view->expand(proxy.mapFromSource(StreamsModel::self()->favouritesIndex()), true); diff --git a/streams/streamspage.h b/streams/streamspage.h index dd506fdfb..71564c0ad 100644 --- a/streams/streamspage.h +++ b/streams/streamspage.h @@ -35,8 +35,6 @@ class Action; class QAction; class NetworkReply; -class ServiceStatusLabel; -class StreamsSettings; struct StreamItem { @@ -67,9 +65,6 @@ public Q_SLOTS: void addToFavourites(const QList &items); private Q_SLOTS: - void configure(); - void configureDi(); - void diSettings(); void importXml(); void exportXml(); void addStream(); @@ -77,7 +72,6 @@ private Q_SLOTS: void reload(); void edit(); void itemDoubleClicked(const QModelIndex &index); - void updateDiStatus(); void expandFavourites(); void addedToFavourites(const QString &name); void tuneInResolved(); @@ -89,7 +83,6 @@ private: void addToFavourites(); private: - ServiceStatusLabel *diStatusLabel; Action *importAction; Action *exportAction; Action *addAction; @@ -97,7 +90,6 @@ private: Action *searchAction; StreamsProxyModel proxy; QSet resolveJobs; - StreamsSettings *settings; friend class StreamsPage; }; diff --git a/streams/streamssettings.cpp b/streams/streamssettings.cpp deleted file mode 100644 index edb2007e2..000000000 --- a/streams/streamssettings.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 "streamssettings.h" -#include "models/streamsmodel.h" -#include "streamproviderlistdialog.h" -#include "widgets/basicitemdelegate.h" -#include "widgets/icons.h" -#include "support/icon.h" -#include "tar.h" -#include "support/messagebox.h" -#include "support/utils.h" -#include "digitallyimportedsettings.h" -#include -#include -#include -#include -#include - -enum Roles { - KeyRole = Qt::UserRole, - BuiltInRole, - ConfigurableRole -}; - -static bool removeDir(const QString &d, const QStringList &types) -{ - QDir dir(d); - if (dir.exists()) { - QFileInfoList files=dir.entryInfoList(types, QDir::Files|QDir::NoDotAndDotDot); - for (const QFileInfo &file: files) { - if (!QFile::remove(file.absoluteFilePath())) { - return false; - } - } - return dir.rmdir(d); - } - return true; // Does not exist... -} - -StreamsSettings::StreamsSettings(QWidget *p) - : Dialog(p, "StreamsDialog", QSize(400, 500)) - , providerDialog(nullptr) -{ - setCaption(tr("Configure Streams")); - QWidget *mw=new QWidget(this); - setupUi(mw); - setMainWidget(mw); - categories->setItemDelegate(new BasicItemDelegate(categories)); - categories->setSortingEnabled(true); - int iSize=Icon::stdSize(QApplication::fontMetrics().height()*1.25); - QMenu *installMenu=new QMenu(this); - QAction *installFromFileAct=installMenu->addAction(tr("From File...")); - QAction *installFromWebAct=installMenu->addAction(tr("Download...")); - categories->setIconSize(QSize(iSize, iSize)); - connect(categories, SIGNAL(currentRowChanged(int)), SLOT(currentCategoryChanged(int))); - connect(installFromFileAct, SIGNAL(triggered()), this, SLOT(installFromFile())); - connect(installFromWebAct, SIGNAL(triggered()), this, SLOT(installFromWeb())); - - setButtons(Close|User1|User2|User3); - setButtonGuiItem(User1, GuiItem(tr("Configure Provider"))); - setButtonGuiItem(User2, GuiItem(tr("Install"))); - setButtonGuiItem(User3, GuiItem(tr("Remove"))); - setButtonMenu(User2, installMenu, InstantPopup); - enableButton(User3, false); - enableButton(User1, false); -} - -void StreamsSettings::load() -{ - QList cats=StreamsModel::self()->getCategories(); - QFont f(font()); - f.setItalic(true); - categories->clear(); - for (const StreamsModel::Category &cat: cats) { - QListWidgetItem *item=new QListWidgetItem(cat.name, categories); - item->setCheckState(cat.hidden ? Qt::Unchecked : Qt::Checked); - item->setData(KeyRole, cat.key); - item->setData(BuiltInRole, cat.builtin); - item->setData(ConfigurableRole, cat.configurable); - item->setIcon(cat.icon); - if (cat.builtin) { - item->setFont(f); - } - } -} - -void StreamsSettings::save() -{ - QSet disabled; - for (int i=0; icount(); ++i) { - QListWidgetItem *item=categories->item(i); - if (Qt::Unchecked==item->checkState()) { - disabled.insert(item->data(Qt::UserRole).toString()); - } - } - StreamsModel::self()->setHiddenCategories(disabled); -} - -void StreamsSettings::currentCategoryChanged(int row) -{ - bool enableRemove=false; - bool enableConfigure=false; - - if (row>=0) { - QListWidgetItem *item=categories->item(row); - enableRemove=!item->data(BuiltInRole).toBool(); - enableConfigure=item->data(ConfigurableRole).toBool(); - } - enableButton(User3, enableRemove); - enableButton(User1, enableConfigure); -} - -void StreamsSettings::installFromFile() -{ - QString fileName=QFileDialog::getOpenFileName(this, tr("Install Streams"), QDir::homePath(), tr("Cantata Streams (*.streams)")); - if (fileName.isEmpty()) { - return; - } - - QString name=QFileInfo(fileName).baseName(); - if (name.isEmpty()) { - return; - } - name=name.replace(Utils::constDirSep, "_"); - #ifdef Q_OS_WIN - name=name.replace("\\", "_"); - #endif - - if (get(name) && MessageBox::No==MessageBox::warningYesNo(this, tr("A category named '%1' already exists!\n\nOverwrite?").arg(name))) { - return; - } - install(fileName, name); -} - -void StreamsSettings::installFromWeb() -{ - if (!providerDialog) { - providerDialog=new StreamProviderListDialog(this); - } - - QSet installed; - for (int i=0; icount(); ++i) { - QListWidgetItem *item=categories->item(i); - installed.insert(item->text()); - } - - providerDialog->show(installed); - #ifdef Q_OS_MAC - // Under OSX when stream providers are installed/updated, and the dialog closed, it - // puts the pref dialog below the main window! This hack fixes this... - QTimer::singleShot(0, this, SLOT(raiseWindow())); - #endif -} - -bool StreamsSettings::install(const QString &fileName, const QString &name, bool showErrors) -{ - Tar tar(fileName); - if (!tar.open()) { - if (showErrors) { - MessageBox::error(this, tr("Failed to open package file.")); - } - return false; - } - - QMap files=tar.extract(QStringList() << StreamsModel::constXmlFile << StreamsModel::constCompressedXmlFile - << StreamsModel::constSettingsFile - << StreamsModel::constPngIcon << StreamsModel::constSvgIcon - << ".png" << ".svg"); - QString streamsName=files.contains(StreamsModel::constCompressedXmlFile) - ? StreamsModel::constCompressedXmlFile - : files.contains(StreamsModel::constXmlFile) - ? StreamsModel::constXmlFile - : StreamsModel::constSettingsFile; - QString iconName=files.contains(StreamsModel::constSvgIcon) ? StreamsModel::constSvgIcon : StreamsModel::constPngIcon; - QByteArray streamFile=files[streamsName]; - QByteArray icon=files[iconName]; - - if (streamFile.isEmpty()) { - if (showErrors) { - MessageBox::error(this, tr("Invalid file format!")); - } - return false; - } - - QString streamsDir=Utils::dataDir(StreamsModel::constSubDir, true); - QString dir=streamsDir+name; - if (!QDir(dir).exists() && !QDir(dir).mkpath(dir)) { - if (showErrors) { - MessageBox::error(this, tr("Failed to create stream category folder!")); - } - return false; - } - - QFile streamsFile(dir+Utils::constDirSep+streamsName); - if (!streamsFile.open(QIODevice::WriteOnly)) { - if (showErrors) { - MessageBox::error(this, tr("Failed to save stream list!")); - } - return false; - } - streamsFile.write(streamFile); - streamsFile.close(); - - QIcon icn; - if (!icon.isEmpty()) { - QFile iconFile(dir+Utils::constDirSep+iconName); - if (iconFile.open(QIODevice::WriteOnly)) { - iconFile.write(icon); - iconFile.close(); - icn.addFile(dir+Utils::constDirSep+iconName); - } - } - - // Write all other png and svg files... - QMap::ConstIterator it=files.constBegin(); - QMap::ConstIterator end=files.constEnd(); - for (; it!=end; ++it) { - if (it.key()!=iconName && (it.key().endsWith(".png") || it.key().endsWith(".svg"))) { - QFile f(dir+Utils::constDirSep+it.key()); - if (f.open(QIODevice::WriteOnly)) { - f.write(it.value()); - } - } - } - - StreamsModel::CategoryItem *cat=StreamsModel::self()->addInstalledProvider(name, icn, dir+Utils::constDirSep+streamsName, true); - if (!cat) { - if (showErrors) { - MessageBox::error(this, tr("Invalid file format!")); - } - return false; - } - QListWidgetItem *existing=get(name); - if (existing) { - delete existing; - } - - QListWidgetItem *item=new QListWidgetItem(name, categories); - item->setCheckState(Qt::Checked); - item->setData(KeyRole, cat->configName); - item->setData(BuiltInRole, false); - item->setData(ConfigurableRole, cat->isDi()); - item->setIcon(icn); - return true; -} - -void StreamsSettings::remove() -{ - int row=categories->currentRow(); - if (row<0) { - return; - } - - QListWidgetItem *item=categories->item(row); - if (!item->data(BuiltInRole).toBool() && MessageBox::No==MessageBox::warningYesNo(this, tr("Are you sure you wish to remove '%1'?").arg(item->text()))) { - return; - } - - QString dir=Utils::dataDir(StreamsModel::constSubDir); - if (!dir.isEmpty() && !removeDir(dir+item->text(), QStringList() << StreamsModel::constXmlFile << StreamsModel::constCompressedXmlFile - << StreamsModel::constSettingsFile << "*.png" << "*.svg")) { - MessageBox::error(this, tr("Failed to remove streams folder!")); - return; - } - - StreamsModel::self()->removeInstalledProvider(item->data(KeyRole).toString()); - delete item; -} - -void StreamsSettings::configure() -{ - int row=categories->currentRow(); - if (row<0) { - return; - } - - QListWidgetItem *item=categories->item(row); - if (!item->data(ConfigurableRole).toBool()) { - return; - } - - // TODO: Currently only digitally imported can be configured... - DigitallyImportedSettings(this).show(); -} - -void StreamsSettings::slotButtonClicked(int button) -{ - switch (button) { - case User1: - configure(); - break; - case User3: - remove(); - break; - case Close: - save(); - reject(); - // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!! - Dialog::slotButtonClicked(button); - break; - default: - break; - } -} - -void StreamsSettings::raiseWindow() -{ - Utils::raiseWindow(topLevelWidget()); -} - -QListWidgetItem * StreamsSettings::get(const QString &name) -{ - for (int i=0; icount(); ++i) { - QListWidgetItem *item=categories->item(i); - if (!item->data(BuiltInRole).toBool() && item->text()==name) { - return item; - } - } - return nullptr; -} - -#include "moc_streamssettings.cpp" diff --git a/streams/streamssettings.h b/streams/streamssettings.h deleted file mode 100644 index ef37dba6e..000000000 --- a/streams/streamssettings.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 STREAMS_SETTINGS_H -#define STREAMS_SETTINGS_H - -#include "ui_streamssettings.h" -#include "support/dialog.h" -class QListWidgetItem; -class StreamProviderListDialog; - -class StreamsSettings : public Dialog, private Ui::StreamsSettings -{ - Q_OBJECT - -public: - StreamsSettings(QWidget *p); - ~StreamsSettings() override { } - - void load(); - void save(); - -public Q_SLOTS: - void raiseWindow(); - -private Q_SLOTS: - void currentCategoryChanged(int row); - void installFromFile(); - void installFromWeb(); - -private: - void slotButtonClicked(int button) override; - void remove(); - void configure(); - bool install(const QString &fileName, const QString &name, bool showErrors=true); - QListWidgetItem * get(const QString &name); - -private: - friend class StreamProviderListDialog; - StreamProviderListDialog *providerDialog; -}; - -#endif diff --git a/streams/streamssettings.ui b/streams/streamssettings.ui deleted file mode 100644 index d138fc6c2..000000000 --- a/streams/streamssettings.ui +++ /dev/null @@ -1,40 +0,0 @@ - - - StreamsSettings - - - - 0 - - - - - Use the checkboxes below to configure the list of active providers. - - - true - - - - - - - - - - Built-in categories are shown in italic, and these cannot be removed. - - - - - - - - PlainNoteLabel - QLabel -
widgets/notelabel.h
-
-
- - -
diff --git a/streams/tar.cpp b/streams/tar.cpp deleted file mode 100644 index 660e90367..000000000 --- a/streams/tar.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 "tar.h" -#include "qtiocompressor/qtiocompressor.h" - -Tar::Tar(const QString &fileName) - : file(fileName) - , compressor(nullptr) - , dev(nullptr) -{ -} - -Tar::~Tar() -{ - delete compressor; -} - -bool Tar::open() -{ - if (!file.open(QIODevice::ReadOnly)) { - return false; - } - - // Check for gzip header... - QByteArray header=file.read(2); - bool isCompressed=((unsigned char)header[0])==0x1f && ((unsigned char)header[1])==0x8b; - file.seek(0); - - if (isCompressed) { - compressor=new QtIOCompressor(&file); - compressor->setStreamFormat(QtIOCompressor::GzipFormat); - if (!compressor->open(QIODevice::ReadOnly)) { - return false; - } - dev=compressor; - } else { - dev=&file; - } - return true; -} - -static const qint64 constHeaderLen=512; - -static qint64 roundUp(qint64 sz) -{ - return ((sz/constHeaderLen)*constHeaderLen)+((sz%constHeaderLen) ? constHeaderLen : 0); -} - -struct TarHeader -{ - TarHeader() : fileSize(0) { } - bool ok() const { return fileSize>0 && !fileName.isEmpty(); } - QString fileName; - qint64 fileSize; -}; - -static unsigned int octStrToInt(char *ch, unsigned int size) -{ - unsigned int val = 0; - while (size > 0){ - val = (val * 8) + (*ch - '0'); - ch++; - size--; - } - return val; -} - -static TarHeader readHeader(QIODevice *dev) -{ - TarHeader header; - char buffer[constHeaderLen]; - qint64 bytesRead=dev->read(buffer, constHeaderLen); - if (constHeaderLen==bytesRead && ('0'==buffer[156] || '\0'==buffer[156])) { - buffer[100]='\0'; - header.fileName=QFile::decodeName(buffer); - header.fileSize=octStrToInt(&buffer[124], 11); - } - return header; -} - -static QString getExt(const QString &fileName) -{ - int pos=fileName.lastIndexOf("."); - return -1!=pos ? fileName.mid(pos) : fileName; -} - -QMap Tar::extract(const QStringList &files) -{ - QMap data; - if (!dev) { - return data; - } - qint64 offset=0; - qint64 pos=0; - for (;;) { - TarHeader header=readHeader(dev); - if (header.ok()) { - pos+=constHeaderLen; - if (!data.contains(header.fileName) && (files.contains(header.fileName) || files.contains(getExt(header.fileName)))) { - data[header.fileName]=dev->read(header.fileSize); - pos+=header.fileSize; - } - offset+=constHeaderLen+header.fileSize; - offset=roundUp(offset); - if (dev->isSequential()) { - static const qint64 constSkipBlock=1024; - // Can't seek with QtIOCompressor - so fake this by reading and discarding - while (posread(toRead); - pos+=toRead; - } - } else { - dev->seek(offset); - } - } else { - break; - } - } - return data; -} diff --git a/streams/tar.h b/streams/tar.h deleted file mode 100644 index c635516a4..000000000 --- a/streams/tar.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 TAR_H -#define TAR_H - -#include -#include -#include -#include -#include -#include - -class QtIOCompressor; -class Tar -{ -public: - Tar(const QString &fileName); - ~Tar(); - - bool open(); - QMap extract(const QStringList &files); - -private: - QFile file; - QtIOCompressor *compressor; - QIODevice *dev; -}; - -#endif - diff --git a/widgets/servicestatuslabel.cpp b/widgets/servicestatuslabel.cpp deleted file mode 100644 index 706488e04..000000000 --- a/widgets/servicestatuslabel.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 "servicestatuslabel.h" -#include -#include -#include -#include - -ServiceStatusLabel::ServiceStatusLabel(QWidget *p) - : QLabel(p) - , pressed(false) -{ - QFont f(font()); - f.setBold(true); - setFont(f); -} - -void ServiceStatusLabel::setText(const QString &txt, const QString &name) -{ - QLabel::setText(txt); - onTooltip=tr("Logged into %1").arg(name); - offTooltip=tr("NOT logged into %1").arg(name); -} - -void ServiceStatusLabel::setStatus(bool on) -{ - setVisible(true); - setToolTip(on ? onTooltip : offTooltip); - QString col=on - ? palette().highlight().color().name() - : palette().color(QPalette::Disabled, QPalette::WindowText).name(); - - int margin=style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this); - if (margin<2) { - margin=2; - } - setStyleSheet(QString("QLabel { color : %1; border-radius: %4px; border: 2px solid %2; margin: %3px}") - .arg(col).arg(col).arg(margin).arg(margin*2)); -} - -void ServiceStatusLabel::mousePressEvent(QMouseEvent *ev) -{ - QLabel::mousePressEvent(ev); - pressed=true; -} - -void ServiceStatusLabel::mouseReleaseEvent(QMouseEvent *ev) -{ - QLabel::mouseReleaseEvent(ev); - if (pressed) { - pressed=false; - if (this==QApplication::widgetAt(QCursor::pos())) { - emit clicked(); - } - } -} - - -#include "moc_servicestatuslabel.cpp" diff --git a/widgets/servicestatuslabel.h b/widgets/servicestatuslabel.h deleted file mode 100644 index 9ef4d7a16..000000000 --- a/widgets/servicestatuslabel.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2020 Craig Drummond - * - * ---- - * - * 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 SERVICE_STATUS_LABEL_H -#define SERVICE_STATUS_LABEL_H - -#include - -class ServiceStatusLabel : public QLabel -{ - Q_OBJECT - -public: - ServiceStatusLabel(QWidget *p); - ~ServiceStatusLabel() override { } - - void setText(const QString &txt, const QString &name); - void setStatus(bool on); - -Q_SIGNALS: - void clicked(); - -private: - void mousePressEvent(QMouseEvent *ev) override; - void mouseReleaseEvent(QMouseEvent *ev) override; - -private: - bool pressed; - QString onTooltip; - QString offTooltip; -}; - -#endif