Skip to content

Commit

Permalink
MythMusic: Add ice/shoutcast radio stream playback
Browse files Browse the repository at this point in the history
(cherry picked from commit 9268134)

Conflicts:

	mythplugins/mythmusic/mythmusic/metadata.cpp
	mythplugins/mythmusic/mythmusic/metadata.h
	mythplugins/mythmusic/mythmusic/musicplayer.h

Signed-off-by: Stuart Morgan <smorgan@mythtv.org>
  • Loading branch information
Paul Harrison authored and stuartm committed Jul 6, 2012
1 parent a32d5e3 commit cdde562
Show file tree
Hide file tree
Showing 23 changed files with 8,610 additions and 293 deletions.
25 changes: 24 additions & 1 deletion mythplugins/mythmusic/mythmusic/dbcheck.cpp
Expand Up @@ -11,7 +11,7 @@ using namespace std;
#include "mythtv/mythdb.h"
#include "mythtv/schemawizard.h"

const QString currentDatabaseVersion = "1019";
const QString currentDatabaseVersion = "1020";

static bool doUpgradeMusicDatabaseSchema(QString &dbver);

Expand Down Expand Up @@ -854,5 +854,28 @@ QString("ALTER DATABASE %1 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;")
return false;
}

if (dbver == "1019")
{
const QString updates[] = {
"DROP TABLE IF EXISTS music_radios;",
"CREATE TABLE music_radios ("
" intid INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,"
" station VARCHAR(128) NOT NULL,"
" channel VARCHAR(128) NOT NULL,"
" url VARCHAR(128) NOT NULL,"
" logourl VARCHAR(128) NOT NULL,"
" genre VARCHAR(128) NOT NULL,"
" metaformat VARCHAR(128) NOT NULL,"
" format VARCHAR(10) NOT NULL,"
" INDEX (station),"
" INDEX (channel)"
");",
""
};

if (!performActualUpdate(updates, "1020", dbver))
return false;
}

return true;
}
156 changes: 101 additions & 55 deletions mythplugins/mythmusic/mythmusic/decoderhandler.cpp
Expand Up @@ -27,13 +27,13 @@

QEvent::Type DecoderHandlerEvent::Ready = (QEvent::Type) QEvent::registerEventType();
QEvent::Type DecoderHandlerEvent::Meta = (QEvent::Type) QEvent::registerEventType();
QEvent::Type DecoderHandlerEvent::Info = (QEvent::Type) QEvent::registerEventType();
QEvent::Type DecoderHandlerEvent::BufferStatus = (QEvent::Type) QEvent::registerEventType();
QEvent::Type DecoderHandlerEvent::OperationStart = (QEvent::Type) QEvent::registerEventType();
QEvent::Type DecoderHandlerEvent::OperationStop = (QEvent::Type) QEvent::registerEventType();
QEvent::Type DecoderHandlerEvent::Error = (QEvent::Type) QEvent::registerEventType();

DecoderHandlerEvent::DecoderHandlerEvent(Type t, const Metadata &meta)
: MythEvent(t), m_msg(NULL), m_meta(NULL)
: MythEvent(t), m_msg(NULL), m_meta(NULL), m_available(0), m_maxSize(0)
{
m_meta = new Metadata(meta);
}
Expand All @@ -57,9 +57,18 @@ MythEvent* DecoderHandlerEvent::clone(void) const
if (m_meta)
result->m_meta = new Metadata(*m_meta);

result->m_available = m_available;
result->m_maxSize = m_maxSize;

return result;
}

void DecoderHandlerEvent::getBufferStatus(int *available, int *maxSize) const
{
*available = m_available;
*maxSize = m_maxSize;
}

/**********************************************************************/

DecoderIOFactory::DecoderIOFactory(DecoderHandler *parent)
Expand Down Expand Up @@ -88,11 +97,6 @@ void DecoderIOFactory::doFailed(const QString &message)
m_handler->doFailed(getUrl(), message);
}

void DecoderIOFactory::doInfo(const QString &message)
{
m_handler->doInfo(message);
}

void DecoderIOFactory::doOperationStart(const QString &name)
{
m_handler->doOperationStart(name);
Expand Down Expand Up @@ -201,7 +205,7 @@ void DecoderIOFactoryUrl::start(void)

m_started = false;

doOperationStart("Fetching remote file");
doOperationStart(tr("Fetching remote file"));

m_reply = m_accessManager->get(QNetworkRequest(getUrl()));

Expand Down Expand Up @@ -266,12 +270,6 @@ void DecoderIOFactoryUrl::readyRead(void)
m_reply->setReadBufferSize(DecoderIOFactory::DefaultPrebufferSize);
doStart();
}

#if 0
LOG(VB_GENERAL, LOG_DEBUG,
QString("DecoderIOFactoryUrl::readyRead file size: %1")
.arg(m_bytesWritten));
#endif
}

void DecoderIOFactoryUrl::doStart(void)
Expand Down Expand Up @@ -319,7 +317,19 @@ void DecoderHandler::start(Metadata *mdata)
else
url.setUrl(mdata->Filename());

bool result = createPlaylist(url);
createPlaylist(url);
}

void DecoderHandler::doStart(bool result)
{
doOperationStop();

QUrl url;
if (QFileInfo(m_meta->Filename()).isAbsolute())
url = QUrl::fromLocalFile(m_meta->Filename());
else
url.setUrl(m_meta->Filename());

if (m_state == LOADING && result)
{
for (int ii = 0; ii < m_playlist.size(); ii++)
Expand All @@ -329,7 +339,7 @@ void DecoderHandler::start(Metadata *mdata)
}
else
{
if (m_state != STOPPED)
if (m_state == STOPPED)
{
doFailed(url, "Could not get playlist");
}
Expand Down Expand Up @@ -428,16 +438,55 @@ void DecoderHandler::stop(void)
m_state = STOPPED;
}

void DecoderHandler::customEvent(QEvent *e)
void DecoderHandler::customEvent(QEvent *event)
{
if (class DecoderHandlerEvent *event = dynamic_cast<DecoderHandlerEvent*>(e))
if (DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event))
{
// Proxy all DecoderHandlerEvents
return dispatch(*event);
return dispatch(*dhe);
}
else if (event->type() == MythEvent::MythEventMessage)
{
MythEvent *me = (MythEvent *)event;
QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);

if (tokens.isEmpty())
return;

if (tokens[0] == "DOWNLOAD_FILE")
{
QStringList args = me->ExtraDataList();

if (tokens[1] == "UPDATE")
{
}
else if (tokens[1] == "FINISHED")
{
QString downloadUrl = args[0];
int fileSize = args[2].toInt();
int errorCode = args[4].toInt();
QString filename = args[1];

if ((errorCode != 0) || (fileSize == 0))
{
LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler: failed to download playlist from '%1'")
.arg(downloadUrl));
QUrl url(downloadUrl);
m_state = STOPPED;
doOperationStop();
doFailed(url, "Could not get playlist");
}
else
{
QUrl fileUrl(filename);
createPlaylistFromFile(fileUrl);
}
}
}
}
}

bool DecoderHandler::createPlaylist(const QUrl &url)
void DecoderHandler::createPlaylist(const QUrl &url)
{
QString extension = QFileInfo(url.path()).suffix();
LOG(VB_NETWORK, LOG_INFO,
Expand All @@ -447,15 +496,17 @@ bool DecoderHandler::createPlaylist(const QUrl &url)
if (extension == "pls" || extension == "m3u")
{
if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
return createPlaylistFromFile(url);
createPlaylistFromFile(url);
else
return createPlaylistFromRemoteUrl(url);
createPlaylistFromRemoteUrl(url);

return;
}

return createPlaylistForSingleFile(url);
createPlaylistForSingleFile(url);
}

bool DecoderHandler::createPlaylistForSingleFile(const QUrl &url)
void DecoderHandler::createPlaylistForSingleFile(const QUrl &url)
{
PlayListFileEntry *entry = new PlayListFileEntry;

Expand All @@ -466,49 +517,51 @@ bool DecoderHandler::createPlaylistForSingleFile(const QUrl &url)

m_playlist.add(entry);

return m_playlist.size() > 0;
doStart((m_playlist.size() > 0));
}

bool DecoderHandler::createPlaylistFromFile(const QUrl &url)
void DecoderHandler::createPlaylistFromFile(const QUrl &url)
{
QFile f(QFileInfo(url.path()).absolutePath() + "/" + QFileInfo(url.path()).fileName());
if (!f.open(QIODevice::ReadOnly))
return false;
return;
QTextStream stream(&f);

QString extension = QFileInfo(url.path()).suffix().toLower();

if (PlayListFile::parse(&m_playlist, &stream, extension) <= 0)
return false;
PlayListFile::parse(&m_playlist, &stream, extension);

return m_playlist.size() > 0;
doStart((m_playlist.size() > 0));
}

bool DecoderHandler::createPlaylistFromRemoteUrl(const QUrl &url)
void DecoderHandler::createPlaylistFromRemoteUrl(const QUrl &url)
{
LOG(VB_NETWORK, LOG_INFO,
QString("Retrieving playlist from '%1'").arg(url.toString()));

doOperationStart("Retrieving playlist");

QByteArray data;

if (!GetMythDownloadManager()->download(url.toString(), &data))
{
LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler:: Failed to download playlist from: %1").arg(url.toString()));
doOperationStop();
return false;
}

doOperationStop();

QTextStream stream(&data, QIODevice::ReadOnly);
doOperationStart(tr("Retrieving playlist"));

QString extension = QFileInfo(url.path()).suffix().toLower();
QString saveFilename = GetConfDir() + "/MythMusic/playlist." + extension;
GetMythDownloadManager()->queueDownload(url.toString(), saveFilename, this);

bool result = PlayListFile::parse(&m_playlist, &stream, extension) > 0;
//TODO should find a better way to do this
QTime time;
time.start();
while (m_state == LOADING)
{
if (time.elapsed() > 30000)
{
doOperationStop();
GetMythDownloadManager()->cancelDownload(url.toString());
LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler:: Timed out trying to download playlist from: %1")
.arg(url.toString()));
m_state = STOPPED;
}

return result;
qApp->processEvents();
usleep(500);
}
}

void DecoderHandler::doConnectDecoder(const QUrl &url, const QString &format)
Expand Down Expand Up @@ -538,18 +591,11 @@ void DecoderHandler::doConnectDecoder(const QUrl &url, const QString &format)
void DecoderHandler::doFailed(const QUrl &url, const QString &message)
{
LOG(VB_NETWORK, LOG_ERR,
QString("DecoderHandler: Unsupported file format: '%1' - %2")
.arg(url.toString()).arg(message));
QString("DecoderHandler error: '%1' - %2").arg(message).arg(url.toString()));
DecoderHandlerEvent ev(DecoderHandlerEvent::Error, new QString(message));
dispatch(ev);
}

void DecoderHandler::doInfo(const QString &message)
{
DecoderHandlerEvent ev(DecoderHandlerEvent::Info, new QString(message));
dispatch(ev);
}

void DecoderHandler::doOperationStart(const QString &name)
{
m_op = true;
Expand Down
24 changes: 15 additions & 9 deletions mythplugins/mythmusic/mythmusic/decoderhandler.h
Expand Up @@ -38,29 +38,36 @@ class DecoderHandlerEvent : public MythEvent
{
public:
DecoderHandlerEvent(Type t)
: MythEvent(t), m_msg(0), m_meta(0) {}
: MythEvent(t), m_msg(0), m_meta(0), m_available(0), m_maxSize(0) {}

DecoderHandlerEvent(Type t, QString *e)
: MythEvent(t), m_msg(e), m_meta(0) {}
: MythEvent(t), m_msg(e), m_meta(0), m_available(0), m_maxSize(0) {}

DecoderHandlerEvent(Type t, int available, int maxSize)
: MythEvent(t), m_msg(0), m_meta(0),
m_available(available), m_maxSize(maxSize) {}

DecoderHandlerEvent(Type t, const Metadata &m);
~DecoderHandlerEvent();

QString *getMessage(void) const { return m_msg; }
Metadata *getMetadata(void) const { return m_meta; }
void getBufferStatus(int *available, int *maxSize) const;

virtual MythEvent *clone(void) const;

static Type Ready;
static Type Meta;
static Type Info;
static Type BufferStatus;
static Type OperationStart;
static Type OperationStop;
static Type Error;

private:
QString *m_msg;
Metadata *m_meta;
int m_available;
int m_maxSize;
};

/** \brief Class for starting stream decoding.
Expand Down Expand Up @@ -105,13 +112,13 @@ class DecoderHandler : public QObject, public MythObservable
void doOperationStop(void);
void doConnectDecoder(const QUrl &url, const QString &format);
void doFailed(const QUrl &url, const QString &message);
void doInfo(const QString &message);
void doStart(bool result);

private:
bool createPlaylist(const QUrl &url);
bool createPlaylistForSingleFile(const QUrl &url);
bool createPlaylistFromFile(const QUrl &url);
bool createPlaylistFromRemoteUrl(const QUrl &url);
void createPlaylist(const QUrl &url);
void createPlaylistForSingleFile(const QUrl &url);
void createPlaylistFromFile(const QUrl &url);
void createPlaylistFromRemoteUrl(const QUrl &url);

bool haveIOFactory(void) { return m_io_factory != NULL; }
DecoderIOFactory *getIOFactory(void) { return m_io_factory; }
Expand Down Expand Up @@ -156,7 +163,6 @@ class DecoderIOFactory : public QObject, public MythObservable
void doConnectDecoder(const QString &format);
Decoder *getDecoder(void);
void doFailed(const QString &message);
void doInfo(const QString &message);
void doOperationStart(const QString &name);
void doOperationStop(void);
Metadata& getMetadata() { return m_meta; }
Expand Down
1 change: 0 additions & 1 deletion mythplugins/mythmusic/mythmusic/importmusic.cpp
Expand Up @@ -129,7 +129,6 @@ ImportMusicDialog::~ImportMusicDialog()

delete m_tracks;

// do we need to do a resync
if (m_somethingWasImported)
emit importFinished();
}
Expand Down

0 comments on commit cdde562

Please sign in to comment.