If listallinfo fails, usr lsinfo recursively

BUG: 379
This commit is contained in:
craig.p.drummond
2014-01-08 19:43:05 +00:00
committed by craig.p.drummond
parent 9029248314
commit cf2e5fbe76
8 changed files with 74 additions and 21 deletions

View File

@@ -30,8 +30,8 @@
HTTP server, set alwaysUseHttp to true in Cantata's config file. Refer to
README for more details.
16. Add CMake option to disable building of internal HTTP server.
17. If listallinfo fails with MPD 0.18.x then prompt user to check MPD's
max_output_buffer_size setting.
17. If listallinfo fails, then attempt to retrieve music listing via lsinfo
calls on each folder.
18. Add CMake option to disable streams, dynamic, and online services. Refer to
INSTALL file for details.
19. Don't use QKeySequence::Delete to detect delete key event for play queue,

9
README
View File

@@ -361,6 +361,14 @@ alwaysUseHttp=<Boolean>
from media devices, or when connected to MPD via a standard socket.
Default is false.
alwaysUseLsInfo=<Boolean>
By default, Cantata uses MPD's listallinfo command to retrieve the whole
music collection. This can fail (especially with MPD 0.18.x which needs
more memory), and if it does Cantata will fall back to calling
"lsinfo <dir>" for each directory. Setting this config item to true will
cause Cantata to always use this alternative method.
Default is false.
e.g.
[General]
iconTheme=oxygen
@@ -373,6 +381,7 @@ undoSteps=20
mpdPoll=true
mpdListSize=5000
alwaysUseHttp=true
alwaysUseLsInfo=true
7. CUE Files

View File

@@ -863,6 +863,11 @@ QString Settings::lang()
}
#endif
bool Settings::alwaysUseLsInfo()
{
return GET_BOOL("alwaysUseLsInfo", false);
}
void Settings::removeConnectionDetails(const QString &v)
{
if (v==currentConnection()) {

View File

@@ -209,6 +209,7 @@ public:
#ifndef ENABLE_KDE_SUPPORT
QString lang();
#endif
bool alwaysUseLsInfo();
void removeConnectionDetails(const QString &v);
void saveConnectionDetails(const MPDConnectionDetails &v);

View File

@@ -26,6 +26,7 @@
#include "mpdconnection.h"
#include "mpdparseutils.h"
#include "musiclibraryitemroot.h"
#include "mpduser.h"
#include "localize.h"
#include "utils.h"
@@ -53,6 +54,7 @@ void MPDConnection::enableDebug()
static const int constSocketCommsTimeout=2000;
static const int constMaxReadAttempts=4;
static int maxFilesPerAddCommand=10000;
static bool alwaysUseLsInfo=false;
#ifdef ENABLE_KDE_SUPPORT
K_GLOBAL_STATIC(MPDConnection, conn)
@@ -213,6 +215,7 @@ MPDConnection::MPDConnection()
connect(PowerManagement::self(), SIGNAL(resuming()), this, SLOT(reconnect()));
#endif
maxFilesPerAddCommand=Settings::self()->mpdListSize();
alwaysUseLsInfo=Settings::self()->alwaysUseLsInfo();
}
MPDConnection::~MPDConnection()
@@ -530,11 +533,11 @@ MPDConnection::Response MPDConnection::sendCommand(const QByteArray &command, bo
}
} else if (!response.getError(command).isEmpty()) {
emit error(i18n("MPD reported the following error: %1", response.getError(command)));
} else if ("listallinfo"==command && ver>=MPD_MAKE_VERSION(0,18,0)) {
} /*else if ("listallinfo"==command && ver>=MPD_MAKE_VERSION(0,18,0)) {
disconnectFromMPD();
emit stateChanged(false);
emit error(i18n("Failed to load library. Please increase \"max_output_buffer_size\" in MPD's config file."));
} else {
} */ else {
disconnectFromMPD();
emit stateChanged(false);
emit error(i18n("Failed to send command. Disconnected from %1", details.description()), true);
@@ -1207,24 +1210,29 @@ void MPDConnection::update()
* Database commands
*/
/**
* Get all files in the playlist with detailed info (artist, album,
* title, time etc).
*/
void MPDConnection::loadLibrary()
{
emit updatingLibrary();
Response response=sendCommand("listallinfo");
Response response=alwaysUseLsInfo ? Response(false) : sendCommand("listallinfo", false);
MusicLibraryItemRoot *root=0;
if (response.ok) {
emit musicLibraryUpdated(MPDParseUtils::parseLibraryItems(response.data, details.dir, ver), dbUpdate);
root = new MusicLibraryItemRoot;
MPDParseUtils::parseLibraryItems(response.data, details.dir, ver, root);
} else { // MPD >=0.18 can fail listallinfo for large DBs, so get info dir by dir...
root = new MusicLibraryItemRoot;
if (!listDirInfo("/", root)) {
delete root;
root=0;
}
}
if (root) {
root->applyGrouping();
emit musicLibraryUpdated(root, dbUpdate);
}
emit updatedLibrary();
}
/**
* Get all the files and dir in the mpdmusic dir.
*
*/
void MPDConnection::loadFolders()
{
emit updatingFileList();
@@ -1451,6 +1459,24 @@ void MPDConnection::toggleStopAfterCurrent(bool afterCurrent)
}
}
bool MPDConnection::listDirInfo(const QString &dir, MusicLibraryItemRoot *root)
{
bool topLevel="/"==dir;
Response response=sendCommand(topLevel ? "lsinfo /" : ("lsinfo "+encodeName(dir)));
if (response.ok) {
QSet<QString> childDirs;
MPDParseUtils::parseLibraryItems(response.data, details.dir, ver, root, !topLevel, &childDirs);
foreach (const QString &child, childDirs) {
if (!listDirInfo(child, root)) {
return false;
}
}
return true;
} else {
return false;
}
}
MpdSocket::MpdSocket(QObject *parent)
: QObject(parent)
, tcp(0)

View File

@@ -337,6 +337,7 @@ private:
void parseIdleReturn(const QByteArray &data);
bool doMoveInPlaylist(const QString &name, const QList<quint32> &items, quint32 pos, quint32 size);
void toggleStopAfterCurrent(bool afterCurrent);
bool listDirInfo(const QString &dir, MusicLibraryItemRoot *root);
private:
Thread *thread;

View File

@@ -409,10 +409,10 @@ void MPDParseUtils::setGroupMultiple(bool g)
groupMultipleArtists=g;
}
MusicLibraryItemRoot * MPDParseUtils::parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion)
void MPDParseUtils::parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion,
MusicLibraryItemRoot *rootItem, bool parsePlaylists, QSet<QString> *childDirs)
{
bool canSplitCue=mpdVersion>=MPD_MAKE_VERSION(0,17,0);
MusicLibraryItemRoot * const rootItem = new MusicLibraryItemRoot;
QByteArray currentItem;
QList<QByteArray> lines = data.split('\n');
int amountOfLines = lines.size();
@@ -422,9 +422,13 @@ MusicLibraryItemRoot * MPDParseUtils::parseLibraryItems(const QByteArray &data,
QString unknown=i18n("Unknown");
for (int i = 0; i < amountOfLines; i++) {
currentItem += lines.at(i);
QByteArray line=lines.at(i);
if (childDirs && line.startsWith("directory: ")) {
childDirs->insert(QString::fromUtf8(line.remove(0, 11)));
}
currentItem += line;
currentItem += "\n";
if (i == lines.size() - 1 || lines.at(i + 1).startsWith("file:") || lines.at(i + 1).startsWith("playlist:")) {
if (i == amountOfLines - 1 || lines.at(i + 1).startsWith("file:") || lines.at(i + 1).startsWith("playlist:")) {
Song currentSong = parseSong(currentItem, false);
currentItem.clear();
if (currentSong.file.isEmpty()) {
@@ -432,6 +436,11 @@ MusicLibraryItemRoot * MPDParseUtils::parseLibraryItems(const QByteArray &data,
}
if (Song::Playlist==currentSong.type) {
// lsinfo / will return all stored playlists - but this is deprecated.
if (!parsePlaylists) {
continue;
}
MusicLibraryItemAlbum *prevAlbum=albumItem;
QString prevSongFile=songItem ? songItem->file() : QString();
QList<Song> cueSongs; // List of songs from cue file
@@ -603,10 +612,10 @@ MusicLibraryItemRoot * MPDParseUtils::parseLibraryItems(const QByteArray &data,
albumItem->addGenre(currentSong.genre);
artistItem->addGenre(currentSong.genre);
rootItem->addGenre(currentSong.genre);
} else if (childDirs) {
}
}
rootItem->applyGrouping();
return rootItem;
}
DirViewItemRoot * MPDParseUtils::parseDirViewItems(const QByteArray &data)

View File

@@ -28,6 +28,7 @@
#define MPD_PARSE_UTILS_H
#include <QString>
#include <QSet>
struct Song;
class Playlist;
@@ -60,7 +61,8 @@ namespace MPDParseUtils
extern void setGroupSingle(bool g);
extern bool groupMultiple();
extern void setGroupMultiple(bool g);
extern MusicLibraryItemRoot * parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion);
extern void parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion,
MusicLibraryItemRoot *rootItem, bool parsePlaylists=true, QSet<QString> *childDirs=0);
extern DirViewItemRoot * parseDirViewItems(const QByteArray &data);
extern QList<Output> parseOuputs(const QByteArray &data);
extern QString addStreamName(const QString &url, const QString &name);