Only start internal HTTP server when required, and stop 1 second after last Cantata stream is removed.

BUG: 369
This commit is contained in:
craig.p.drummond
2014-01-02 19:05:06 +00:00
committed by craig.p.drummond
parent 16b3ed8e25
commit 3a07ab4e6d
9 changed files with 153 additions and 77 deletions

View File

@@ -247,7 +247,7 @@ set(CANTATA_MOC_HDRS
widgets/playqueueview.h widgets/groupedview.h widgets/actionitemdelegate.h widgets/coverwidget.h widgets/volumeslider.h
widgets/genrecombo.h widgets/toolbar.h widgets/searchwidget.h widgets/messageoverlay.h widgets/servicestatuslabel.h
network/networkaccessmanager.h
http/httpserversettings.h http/httpsocket.h
http/httpserver.h http/httpsocket.h
context/togglelist.h context/ultimatelyrics.h context/ultimatelyricsprovider.h context/lyricsdialog.h
context/contextwidget.h context/artistview.h context/albumview.h context/songview.h context/view.h context/contextengine.h
context/wikipediaengine.h context/wikipediasettings.h context/othersettings.h context/lastfmengine.h context/metaengine.h

View File

@@ -21,7 +21,9 @@
12. When adding files to playqueue, only add in batches of up to 2000 songs.
This 'chunk size' may be altered by setting mpdListSize in Cantata's config
file - see README for details.
13. Add 'Remove dusplicates' functionality to play queue and play lists.
13. Add 'Remove duplicates' functionality to play queue and play lists.
14. Only start internal HTTP server when required, and stop 1 second after
last Cantata stream is removed.
1.2.2
-----

View File

@@ -30,6 +30,7 @@
#include "thread.h"
#include <QFile>
#include <QUrl>
#include <QTimer>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
@@ -72,51 +73,72 @@ HttpServer * HttpServer::self()
#endif
}
void HttpServer::stop()
HttpServer::HttpServer()
: QObject(0)
, thread(0)
, socket(0)
, closeTimer(0)
{
if (socket) {
socket->terminate();
socket=0;
}
if (thread) {
thread->stop();
thread=0;
}
connect(MPDConnection::self(), SIGNAL(socketAddress(QString)), this, SLOT(mpdAddress(QString)));
connect(MPDConnection::self(), SIGNAL(cantataStreams(QList<Song>,bool)), this, SLOT(cantataStreams(QList<Song>,bool)));
connect(MPDConnection::self(), SIGNAL(cantataStreams(QStringList)), this, SLOT(cantataStreams(QStringList)));
connect(MPDConnection::self(), SIGNAL(removedIds(QSet<qint32>)), this, SLOT(removedIds(QSet<qint32>)));
}
bool HttpServer::readConfig()
bool HttpServer::start()
{
QString iface=Settings::self()->httpInterface();
if (closeTimer) {
DBUG << "stop close timer";
closeTimer->stop();
}
if (socket && socket->isListening() && iface==socket->configuredInterface()) {
if (socket) {
DBUG << "already open";
return true;
}
if (socket) {
socket->terminate();
socket=0;
}
if (thread) {
thread->stop();
thread=0;
}
DBUG << "open new socket";
quint16 prevPort=Settings::self()->httpAllocatedPort();
thread=new Thread("HttpServer");
socket=new HttpSocket(iface, prevPort);
bool newThread=0==thread;
if (newThread) {
thread=new Thread("HttpServer");
}
socket=new HttpSocket(Settings::self()->httpInterface(), prevPort);
socket->mpdAddress(mpdAddr);
connect(this, SIGNAL(terminateSocket()), socket, SLOT(terminate()), Qt::QueuedConnection);
if (socket->serverPort()!=prevPort) {
Settings::self()->saveHttpAllocatedPort(socket->serverPort());
}
socket->moveToThread(thread);
thread->start();
return socket->isListening();
bool started=socket->isListening();
if (newThread) {
thread->start();
}
return started;
}
bool HttpServer::isAlive() const
void HttpServer::stop()
{
return socket && socket->isListening();
if (socket) {
DBUG;
emit terminateSocket();
socket=0;
}
}
void HttpServer::readConfig()
{
QString iface=Settings::self()->httpInterface();
if (socket && socket->isListening() && iface==socket->configuredInterface()) {
return;
}
bool wasStarted=0!=socket;
stop();
if (wasStarted) {
start();
}
}
QString HttpServer::address() const
@@ -127,13 +149,13 @@ QString HttpServer::address() const
bool HttpServer::isOurs(const QString &url) const
{
return isAlive() ? url.startsWith(address()+"/") : false;
return 0!=socket ? url.startsWith(address()+"/") : false;
}
QByteArray HttpServer::encodeUrl(const Song &s) const
QByteArray HttpServer::encodeUrl(const Song &s)
{
DBUG << "song" << s.file << isAlive();
if (!isAlive()) {
if (!start()) {
return QByteArray();
}
#if QT_VERSION < 0x050000
@@ -186,7 +208,7 @@ QByteArray HttpServer::encodeUrl(const Song &s) const
return url.toEncoded();
}
QByteArray HttpServer::encodeUrl(const QString &file) const
QByteArray HttpServer::encodeUrl(const QString &file)
{
Song s;
#ifdef Q_OS_WIN
@@ -284,3 +306,57 @@ Song HttpServer::decodeUrl(const QUrl &url) const
return s;
}
void HttpServer::startCloseTimer()
{
if (!closeTimer) {
closeTimer=new QTimer(this);
closeTimer->setSingleShot(true);
connect(closeTimer, SIGNAL(timeout()), this, SLOT(stop()));
}
DBUG;
closeTimer->start(1000);
}
void HttpServer::mpdAddress(const QString &a)
{
mpdAddr=a;
}
void HttpServer::cantataStreams(const QStringList &files)
{
DBUG << files;
foreach (const QString &f, files) {
Song s=HttpServer::self()->decodeUrl(f);
if (s.isCantataStream() || s.isCdda()) {
start();
break;
}
}
}
void HttpServer::cantataStreams(const QList<Song> &songs, bool isUpdate)
{
DBUG << isUpdate << songs.count();
if (!isUpdate) {
streamIds.clear();
}
foreach (const Song &s, songs) {
streamIds.insert(s.id);
}
if (streamIds.isEmpty()) {
startCloseTimer();
} else {
start();
}
}
void HttpServer::removedIds(const QSet<qint32> &ids)
{
streamIds+=ids;
if (streamIds.isEmpty()) {
startCloseTimer();
}
}

View File

@@ -24,39 +24,60 @@
#ifndef _HTTP_SERVER_H
#define _HTTP_SERVER_H
#include <qglobal.h>
#include <QObject>
#include <QByteArray>
#include <QSet>
#include "song.h"
#include "config.h"
class HttpSocket;
class Thread;
class QUrl;
class QTimer;
class HttpServer
class HttpServer : public QObject
{
Q_OBJECT
public:
static void enableDebug();
static bool debugEnabled();
static HttpServer * self();
HttpServer() : thread(0), socket(0) { }
HttpServer();
virtual ~HttpServer() { }
void stop();
bool isAlive() const;
bool readConfig();
bool start();
bool isAlive() const { return true; } // Started on-demamnd!
void readConfig();
QString address() const;
bool isOurs(const QString &url) const;
QByteArray encodeUrl(const Song &s) const;
QByteArray encodeUrl(const QString &file) const;
QByteArray encodeUrl(const Song &s);
QByteArray encodeUrl(const QString &file);
Song decodeUrl(const QUrl &url) const;
Song decodeUrl(const QString &file) const;
public Q_SLOTS:
void stop();
private Q_SLOTS:
void startCloseTimer();
void mpdAddress(const QString &a);
void cantataStreams(const QStringList &files);
void cantataStreams(const QList<Song> &songs, bool isUpdate);
void removedIds(const QSet<qint32> &ids);
Q_SIGNALS:
void terminateSocket();
private:
Thread *thread;
HttpSocket *socket;
QString mpdAddr;
QSet<qint32> streamIds; // Currently playing MPD stream IDs
QTimer *closeTimer;
};
#endif

View File

@@ -26,7 +26,6 @@
#include "localize.h"
#include "httpserver.h"
#include <QComboBox>
#include <QTimer>
#include <QNetworkInterface>
static int isIfaceType(const QNetworkInterface &iface, const QString &prefix)
@@ -64,7 +63,6 @@ HttpServerSettings::HttpServerSettings(QWidget *p)
{
setupUi(this);
initInterfaces(httpInterface);
updateStatus();
}
void HttpServerSettings::load()
@@ -87,14 +85,4 @@ void HttpServerSettings::save()
{
Settings::self()->saveHttpInterface(httpInterface->itemData(httpInterface->currentIndex()).toString());
HttpServer::self()->readConfig();
QTimer::singleShot(250, this, SLOT(updateStatus()));
}
void HttpServerSettings::updateStatus()
{
if (HttpServer::self()->isAlive()) {
status->setText(HttpServer::self()->address());
} else {
status->setText(i18n("Inactive"));
}
}

View File

@@ -28,7 +28,6 @@
class HttpServerSettings : public QWidget, private Ui::HttpServerSettings
{
Q_OBJECT
public:
HttpServerSettings(QWidget *p);
virtual ~HttpServerSettings() { }
@@ -36,9 +35,6 @@ public:
void load();
void save();
bool haveMultipleInterfaces() const;
private Q_SLOTS:
void updateStatus();
};
#endif

View File

@@ -32,22 +32,6 @@
<item row="1" column="1">
<widget class="QComboBox" name="httpInterface"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3b">
<property name="text">
<string>Current URL:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="status">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@@ -259,6 +259,8 @@ HttpSocket::HttpSocket(const QString &iface, quint16 port)
ifaceAddress=QLatin1String("127.0.0.1");
}
DBUG << isListening() << ifaceAddress << serverPort();
connect(MPDConnection::self(), SIGNAL(socketAddress(QString)), this, SLOT(mpdAddress(QString)));
connect(MPDConnection::self(), SIGNAL(cantataStreams(QList<Song>,bool)), this, SLOT(cantataStreams(QList<Song>,bool)));
connect(MPDConnection::self(), SIGNAL(cantataStreams(QStringList)), this, SLOT(cantataStreams(QStringList)));
@@ -293,7 +295,12 @@ bool HttpSocket::openPort(const QHostAddress &a, quint16 p)
void HttpSocket::terminate()
{
if (terminated) {
return;
}
DBUG;
terminated=true;
close();
deleteLater();
}

View File

@@ -41,11 +41,14 @@ public:
HttpSocket(const QString &iface, quint16 port);
virtual ~HttpSocket() { }
void terminate();
void incomingConnection(int socket);
QString address() const { return ifaceAddress; }
QString configuredInterface() { return cfgInterface; }
public Q_SLOTS:
void terminate();
void mpdAddress(const QString &a);
private:
bool openPort(const QHostAddress &a, quint16 p);
bool isCantataStream(const QString &file) const;
@@ -54,7 +57,6 @@ private:
private Q_SLOTS:
void readClient();
void discardClient();
void mpdAddress(const QString &a);
void cantataStreams(const QStringList &files);
void cantataStreams(const QList<Song> &songs, bool isUpdate);
void removedIds(const QSet<qint32> &ids);