mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-18 12:28:31 +01:00
Implement playlist saving for XSPF.
This commit is contained in:
parent
a59f5a3887
commit
1b76ead951
@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
#include "xspfparser.h"
|
#include "xspfparser.h"
|
||||||
|
|
||||||
|
#include <QDomDocument>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
|
#include <QRegExp>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
@ -137,5 +139,52 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void XSPFParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
|
void XSPFParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
|
||||||
|
QDomDocument doc;
|
||||||
|
QDomElement root = doc.createElement("playlist");
|
||||||
|
doc.appendChild(root);
|
||||||
|
QDomElement track_list = doc.createElement("trackList");
|
||||||
|
root.appendChild(track_list);
|
||||||
|
foreach (const Song& song, songs) {
|
||||||
|
QString url;
|
||||||
|
if (song.filetype() == Song::Type_Stream) {
|
||||||
|
url = song.filename();
|
||||||
|
} else {
|
||||||
|
url = QUrl::fromLocalFile(MakeRelativeTo(song.filename(), dir)).toString();
|
||||||
|
}
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
continue; // Skip empty items like Last.fm streams.
|
||||||
|
}
|
||||||
|
QDomElement track = doc.createElement("track");
|
||||||
|
track_list.appendChild(track);
|
||||||
|
MaybeAppendElementWithText("location", url, &doc, &track);
|
||||||
|
MaybeAppendElementWithText("creator", song.artist(), &doc, &track);
|
||||||
|
MaybeAppendElementWithText("album", song.album(), &doc, &track);
|
||||||
|
MaybeAppendElementWithText("title", song.title(), &doc, &track);
|
||||||
|
if (song.length() != -1) {
|
||||||
|
MaybeAppendElementWithText("duration", QString::number(song.length() * 1000), &doc, &track);
|
||||||
|
}
|
||||||
|
QString art = song.art_manual().isEmpty() ? song.art_automatic() : song.art_manual();
|
||||||
|
// Ignore images that are in our resource bundle.
|
||||||
|
if (!art.startsWith(":") && !art.isEmpty()) {
|
||||||
|
// Convert local files to URLs.
|
||||||
|
if (!art.contains(QRegExp("^\\w+://"))) {
|
||||||
|
art = QUrl::fromLocalFile(MakeRelativeTo(art, dir)).toString();
|
||||||
|
}
|
||||||
|
MaybeAppendElementWithText("image", art, &doc, &track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device->write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
|
device->write(doc.toByteArray(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void XSPFParser::MaybeAppendElementWithText(
|
||||||
|
const QString& element_name, const QString& text, QDomDocument* doc, QDomNode* parent) const {
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDomElement element = doc->createElement(element_name);
|
||||||
|
QDomText t = doc->createTextNode(text);
|
||||||
|
element.appendChild(t);
|
||||||
|
parent->appendChild(element);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
|
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
|
class QDomDocument;
|
||||||
|
class QDomNode;
|
||||||
|
|
||||||
class XSPFParser : public ParserBase {
|
class XSPFParser : public ParserBase {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -37,6 +40,8 @@ class XSPFParser : public ParserBase {
|
|||||||
bool ParseUntilElement(QXmlStreamReader* reader, const QString& element) const;
|
bool ParseUntilElement(QXmlStreamReader* reader, const QString& element) const;
|
||||||
void IgnoreElement(QXmlStreamReader* reader) const;
|
void IgnoreElement(QXmlStreamReader* reader) const;
|
||||||
Song ParseTrack(QXmlStreamReader* reader) const;
|
Song ParseTrack(QXmlStreamReader* reader) const;
|
||||||
|
void MaybeAppendElementWithText(
|
||||||
|
const QString& element, const QString& text, QDomDocument* doc, QDomNode* parent) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -15,12 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "test_utils.h"
|
#include "test_utils.h"
|
||||||
|
#include "gmock/gmock-matchers.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "playlistparsers/xspfparser.h"
|
#include "playlistparsers/xspfparser.h"
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
|
|
||||||
|
using ::testing::HasSubstr;
|
||||||
|
|
||||||
class XSPFParserTest : public ::testing::Test {
|
class XSPFParserTest : public ::testing::Test {
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -89,3 +92,24 @@ TEST_F(XSPFParserTest, IgnoresInvalidLength) {
|
|||||||
ASSERT_EQ(1, songs.length());
|
ASSERT_EQ(1, songs.length());
|
||||||
EXPECT_EQ(-1, songs[0].length());
|
EXPECT_EQ(-1, songs[0].length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(XSPFParserTest, SavesSong) {
|
||||||
|
QByteArray data;
|
||||||
|
QBuffer buffer(&data);
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
XSPFParser parser;
|
||||||
|
Song one;
|
||||||
|
one.set_filename("http://www.example.com/foo.mp3");
|
||||||
|
one.set_filetype(Song::Type_Stream);
|
||||||
|
one.set_title("foo");
|
||||||
|
one.set_length(123);
|
||||||
|
one.set_artist("bar");
|
||||||
|
SongList songs;
|
||||||
|
songs << one;
|
||||||
|
|
||||||
|
parser.Save(songs, &buffer);
|
||||||
|
EXPECT_THAT(data.constData(), HasSubstr("<location>http://www.example.com/foo.mp3</location>"));
|
||||||
|
EXPECT_THAT(data.constData(), HasSubstr("<duration>123000</duration>"));
|
||||||
|
EXPECT_THAT(data.constData(), HasSubstr("<title>foo</title>"));
|
||||||
|
EXPECT_THAT(data.constData(), HasSubstr("<creator>bar</creator>"));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user