Files
cantata/web/app/playqueueapi.cpp
Craig Drummond 605af484f7 Initial import of *VERY* incomplete, and not fully functional Cantata
webapp. Mainly used to test SQLite backend for storing MPD db.
2015-06-01 22:57:49 +01:00

255 lines
9.3 KiB
C++

/*
* Cantata Web
*
* Copyright (c) 2015 Craig Drummond <craig.p.drummond@gmail.com>
*
* ----
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "playqueueapi.h"
#include "db/mpdlibrarydb.h"
#include "http/httprequest.h"
#include "http/httpresponse.h"
#include "httpserver.h"
#include "mpd-interface/mpdconnection.h"
#include "mpd-interface/mpdparseutils.h"
#include "streams/streamfetcher.h"
#include "support/localize.h"
#include <QDebug>
#define DBUG if (ApiHandler::debugEnabled()) qWarning() << metaObject()->className() << (void *)this << __FUNCTION__
static QString streamText(const Song &song, const QString &trackTitle, bool useName=true)
{
if (song.album.isEmpty() && song.albumArtist().isEmpty()) {
QString songName=song.name();
return song.title.isEmpty() && songName.isEmpty()
? song.file
: !useName || songName.isEmpty()
? song.title
: song.title.isEmpty()
? songName
: (song.title + " - " + songName);
} else if (!song.title.isEmpty() && !song.artist.isEmpty()) {
QString name=song.name();
return song.artist + " - " + (!useName || name.isEmpty()
? song.title
: song.title.isEmpty()
? name
: (song.title + " - " + name));
} else {
return trackTitle;
}
}
static QVariant toVariant(const QList<Song> &songs)
{
QVariantList list;
int lastKey=Song::Null_Key;
int lastAlbumIndex=0;
int albumDuration=0;
int totalDuration=0;
for (int i=0; i<songs.count(); ++i) {
Song song=songs.at(i);
QVariantMap map;
song.setKey(MPDParseUtils::Loc_PlayQueue);
QString title;
QString track;
bool stream=song.isStandardStream();
bool isEmpty=song.title.isEmpty() && song.artist.isEmpty() && !song.file.isEmpty();
QString trackTitle=isEmpty
? song.file
: song.diffArtist()
? song.artistSong()
: song.title;
if (0==i || lastKey!=song.key) {
if (stream) {
int nextKey=i+1<songs.count() ? Song(songs.at(i+1)).setKey(MPDParseUtils::Loc_PlayQueue) : Song::Null_Key;
if (nextKey!=song.key && !song.name().isEmpty()) {
title=song.name();
track=streamText(song, trackTitle, false);
} else {
title=song.isCdda() ? i18n("Audio CD") : i18n("Streams");
track=streamText(song, trackTitle);
}
} else if (isEmpty) {
title=Song::unknown();
track=trackTitle;
} else if (song.album.isEmpty()) {
title=song.artistOrComposer();
track=song.trackAndTitleStr();
} else {
if (song.isFromOnlineService()) {
title=Song::displayAlbum(song.album, Song::albumYear(song));
} else {
title=song.artistOrComposer()+QLatin1String(" - ")+Song::displayAlbum(song.album, Song::albumYear(song));
}
while (title.contains(") (")) {
title=title.replace(") (", ", ");
}
track=song.trackAndTitleStr();
}
} else {
if (stream) {
track=streamText(song, trackTitle);
} else {
track=song.trackAndTitleStr();
}
}
if (!title.isEmpty()) {
map["title"]=title;
map["albumId"]=song.albumId();
map["artistId"]=song.artistOrComposer();
}
map["track"]=track;
map["duration"]=song.time;
map["key"]=song.key;
map["id"]=song.id;
list.append(map);
if (0!=i && lastKey!=song.key) {
QVariantMap album=list[lastAlbumIndex].toMap();
album["albumDuration"]=albumDuration;
list[lastAlbumIndex]=album;
lastAlbumIndex=i;
albumDuration=0;
}
lastKey=song.key;
totalDuration+=song.time;
albumDuration+=song.time;
}
if (!list.isEmpty() && lastAlbumIndex!=songs.length()-1) {
QVariantMap album=list[lastAlbumIndex].toMap();
album["albumDuration"]=albumDuration;
list[lastAlbumIndex]=album;
}
QVariantMap map;
map["tracks"]=list;
map["count"]=list.size();
map["duration"]=totalDuration;
return map;
}
PlayQueueApi::PlayQueueApi(QObject *p)
: ApiHandler(p)
, fetcher(0)
{
connect(this, SIGNAL(add(QStringList,bool,quint8)), MPDConnection::self(), SLOT(add(QStringList,bool,quint8)));
connect(this, SIGNAL(loadPlaylist(QString,bool)), MPDConnection::self(), SLOT(loadPlaylist(QString,bool)));
connect(this, SIGNAL(playListInfo()), MPDConnection::self(), SLOT(playListInfo()));
connect(this, SIGNAL(clear()), MPDConnection::self(), SLOT(clear()));
connect(this, SIGNAL(prev()), MPDConnection::self(), SLOT(goToPrevious()));
connect(this, SIGNAL(play()), MPDConnection::self(), SLOT(play()));
connect(this, SIGNAL(pause(bool)), MPDConnection::self(), SLOT(setPause(bool)));
connect(this, SIGNAL(next()), MPDConnection::self(), SLOT(goToNext()));
connect(MPDConnection::self(), SIGNAL(playlistUpdated(QList<Song>,bool)), this, SLOT(playlistUpdated(QList<Song>,bool)));
}
HttpRequestHandler::HandleStatus PlayQueueApi::handle(HttpRequest *request, HttpResponse *response)
{
const QByteArray &path=request->path();
DBUG << path;
if (HttpRequest::Method_Get==request->method() && path=="/api/v1/playqueue") {
emit playListInfo();
awaitResponse(response);
return Status_Handling;
} else if (HttpRequest::Method_Delete==request->method() && path=="/api/v1/playqueue") {
emit clear();
} else if (HttpRequest::Method_Post==request->method() && path=="/api/v1/playqueue") {
QString url=request->parameter("url");
bool play="true"==request->parameter("play");
QStringList files;
DBUG << "params" << url;
if (url.isEmpty()) {
QString genre=request->parameter("genre");
QString artistId=request->parameter("artistId");
QString albumId=request->parameter("albumId");
QString sort=request->parameter("sort");
DBUG << "other params" << artistId << albumId << genre << sort;
QList<Song> songs=MpdLibraryDb::self()->getTracks(artistId, albumId, genre, sort);
foreach (const Song &s, songs) {
files.append(s.file);
}
} else {
files << url;
}
bool isStream=!url.isEmpty() && request->parameter("stream")=="true";
bool isPlaylist=!url.isEmpty() && request->parameter("playlist")=="true";
if (isStream) {
if (!fetcher) {
fetcher=new StreamFetcher(this);
connect(fetcher, SIGNAL(result(QStringList,int,bool,quint8)), this, SLOT(streamResult(QStringList,int,bool,quint8)));
fetcher->get(files, 0, true, 0);
}
awaitResponse(response);
return Status_Handling;
} else if (isPlaylist) {
if (!files.isEmpty()) {
emit loadPlaylist(url, play);
}
setResponse(response, !files.isEmpty());
} else {
if (!files.isEmpty()) {
emit add(files, play, 0);
}
setResponse(response, !files.isEmpty());
}
} else if (HttpRequest::Method_Post==request->method() && path=="/api/v1/playqueue/control") {
QString cmd=request->parameter("cmd");
if ("prev"==cmd) {
emit prev();
} else if ("play"==cmd) {
emit play();
} else if("pause"==cmd) {
emit pause(true);
} else if ("next"==cmd) {
emit next();
} else {
return Status_BadRequest;
}
} else {
return Status_BadRequest;
}
return Status_Handled;
}
void PlayQueueApi::streamResult(const QStringList &items, int insertRow, bool replace, quint8 priority)
{
Q_UNUSED(insertRow)
Q_UNUSED(replace)
Q_UNUSED(priority)
DBUG << awaitingResponse();
if (awaitingResponse()) {
emit add(items, true, 0);
responseReceived(true);
}
}
void PlayQueueApi::playlistUpdated(const QList<Song> &songs, bool isComplete)
{
DBUG << isComplete << awaitingResponse();
if (isComplete && awaitingResponse()) {
responseReceived(toVariant(songs));
}
}