.pls parser. Fixes issue #302

This commit is contained in:
David Sansome 2010-05-22 21:11:22 +00:00
parent 1f8b0de49c
commit 211ae65e3d
13 changed files with 236 additions and 35 deletions

View File

@ -6,6 +6,7 @@ set(SOURCES
m3uparser.cpp
parserbase.cpp
playlistparser.cpp
plsparser.cpp
xspfparser.cpp
)
@ -13,6 +14,7 @@ set(HEADERS
m3uparser.h
parserbase.h
playlistparser.h
plsparser.h
xspfparser.h
)

View File

@ -92,40 +92,6 @@ bool M3UParser::ParseMetadata(const QString& line, M3UParser::Metadata* metadata
return true;
}
bool M3UParser::ParseTrackLocation(const QString& line, const QDir& dir, Song* song) const {
if (line.contains(QRegExp("^[a-z]+://"))) {
// Looks like a url.
QUrl temp(line);
if (temp.isValid()) {
song->set_filename(temp.toString());
return true;
} else {
return false;
}
}
// Should be a local path.
if (QDir::isAbsolutePath(line)) {
// Absolute path.
// Fix windows \, eg. C:\foo -> C:/foo.
QString proper_path = QDir::fromNativeSeparators(line);
if (!QFile::exists(proper_path)) {
return false;
}
song->set_filename(proper_path);
} else {
// Relative path.
QString proper_path = QDir::fromNativeSeparators(line);
QString absolute_path = dir.absoluteFilePath(proper_path);
if (!QFile::exists(absolute_path)) {
return false;
}
song->set_filename(absolute_path);
}
song->InitFromFile(song->filename(), -1);
return true;
}
void M3UParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
// TODO
}

View File

@ -49,7 +49,6 @@ class M3UParser : public ParserBase {
};
bool ParseMetadata(const QString& line, Metadata* metadata) const;
bool ParseTrackLocation(const QString& line, const QDir& dir, Song* song) const;
FRIEND_TEST(M3UParserTest, ParsesMetadata);
FRIEND_TEST(M3UParserTest, ParsesTrackLocation);

View File

@ -16,7 +16,41 @@
#include "parserbase.h"
#include <QUrl>
ParserBase::ParserBase(QObject *parent)
: QObject(parent)
{
}
bool ParserBase::ParseTrackLocation(const QString& filename_or_url,
const QDir& dir, Song* song) const {
if (filename_or_url.contains(QRegExp("^[a-z]+://"))) {
// Looks like a url.
QUrl temp(filename_or_url);
if (temp.isValid()) {
song->set_filename(temp.toString());
return true;
} else {
return false;
}
}
// Should be a local path.
if (QDir::isAbsolutePath(filename_or_url)) {
// Absolute path.
// Fix windows \, eg. C:\foo -> C:/foo.
QString proper_path = QDir::fromNativeSeparators(filename_or_url);
if (!QFile::exists(proper_path)) {
return false;
}
song->set_filename(proper_path);
} else {
// Relative path.
QString proper_path = QDir::fromNativeSeparators(filename_or_url);
QString absolute_path = dir.absoluteFilePath(proper_path);
song->set_filename(absolute_path);
}
song->InitFromFile(song->filename(), -1);
return true;
}

View File

@ -32,6 +32,10 @@ public:
virtual SongList Load(QIODevice* device, const QDir& dir = QDir()) const = 0;
virtual void Save(const SongList& songs, QIODevice* device, const QDir& dir = QDir()) const = 0;
protected:
bool ParseTrackLocation(const QString& filename_or_url, const QDir& dir,
Song* song) const;
};
#endif // PARSERBASE_H

View File

@ -17,6 +17,7 @@
#include "playlistparser.h"
#include "xspfparser.h"
#include "m3uparser.h"
#include "plsparser.h"
#include <QtDebug>
@ -25,6 +26,7 @@ PlaylistParser::PlaylistParser(QObject *parent)
{
parsers_ << new M3UParser(this);
parsers_ << new XSPFParser(this);
parsers_ << new PLSParser(this);
}
QStringList PlaylistParser::file_extensions() const {

View File

@ -0,0 +1,74 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "plsparser.h"
#include <QTemporaryFile>
#include <QSettings>
#include <QtDebug>
PLSParser::PLSParser(QObject* parent)
: ParserBase(parent)
{
}
SongList PLSParser::Load(QIODevice *device, const QDir &dir) const {
QTemporaryFile temp_file;
temp_file.open();
temp_file.write(device->readAll());
temp_file.flush();
QSettings s(temp_file.fileName(), QSettings::IniFormat);
SongList ret;
// Use the first group, probably "playlist" but it doesn't matter
if (s.childGroups().isEmpty())
return ret;
s.beginGroup(s.childGroups()[0]);
// We try not to rely on NumberOfEntries (it might not be present), so go
// through each key in the file and look at ones that start with "File"
foreach (const QString& key, s.childKeys()) {
if (!key.toLower().startsWith("file"))
continue;
bool ok = false;
int n = key.mid(4).toInt(&ok); // 4 == "file".length
if (!ok)
continue;
QString filename = s.value(key).toString();
QString title = s.value("Title" + QString::number(n)).toString();
int length = s.value("Length" + QString::number(n)).toInt();
Song song;
song.set_title(title);
song.set_length(length);
if (!ParseTrackLocation(filename, dir, &song)) {
qWarning() << "Failed to parse location: " << filename;
} else {
ret << song;
}
}
return ret;
}
void PLSParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
}

View File

@ -0,0 +1,34 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PLSPARSER_H
#define PLSPARSER_H
#include "parserbase.h"
class PLSParser : public ParserBase {
Q_OBJECT
public:
PLSParser(QObject* parent = 0);
QStringList file_extensions() const { return QStringList() << "pls"; }
SongList Load(QIODevice* device, const QDir& dir = QDir()) const;
void Save(const SongList& songs, QIODevice* device, const QDir& dir = QDir()) const;
};
#endif // PLSPARSER_H

View File

@ -103,3 +103,4 @@ add_test_file(playlist_test.cpp true)
add_test_file(scopedtransaction_test.cpp false)
add_test_file(fileformats_test.cpp false)
add_test_file(mergedproxymodel_test.cpp false)
add_test_file(plsparser_test.cpp false)

6
tests/data/pls_one.pls Normal file
View File

@ -0,0 +1,6 @@
[playlist]
File1=filename with spaces.mp3
Title1=Title
Length1=123
NumberOfEntries=1
Version=2

15
tests/data/pls_somafm.pls Normal file
View File

@ -0,0 +1,15 @@
[playlist]
numberofentries=4
File1=http://streamer-dtc-aa05.somafm.com:80/stream/1018
Title1=SomaFM: Groove Salad (#1 128k mp3): A nicely chilled plate of ambient beats and grooves.
Length1=-1
File2=http://streamer-mtc-aa03.somafm.com:80/stream/1018
Title2=SomaFM: Groove Salad (#2 128k mp3): A nicely chilled plate of ambient beats and grooves.
Length2=-1
File3=http://streamer-ntc-aa04.somafm.com:80/stream/1018
Title3=SomaFM: Groove Salad (#3 128k mp3): A nicely chilled plate of ambient beats and grooves.
Length3=-1
File4=http://ice.somafm.com/groovesalad
Title4=SomaFM: Groove Salad (Firewall-friendly 128k mp3) A nicely chilled plate of ambient beats and grooves.
Length4=-1
Version=2

View File

@ -6,5 +6,7 @@
<file>beep.spx</file>
<file>beep.wav</file>
<file>beep.wma</file>
<file>pls_one.pls</file>
<file>pls_somafm.pls</file>
</qresource>
</RCC>

62
tests/plsparser_test.cpp Normal file
View File

@ -0,0 +1,62 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "test_utils.h"
#include "gtest/gtest.h"
#include "playlistparsers/plsparser.h"
#include <QBuffer>
#include <QFile>
#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
class PLSParserTest : public ::testing::Test {
protected:
shared_ptr<QFile> Open(const QString& filename) {
shared_ptr<QFile> ret(new QFile(":/testdata/" + filename));
if (!ret->open(QIODevice::ReadOnly))
ret.reset();
return ret;
}
PLSParser parser_;
};
TEST_F(PLSParserTest, ParseOneTrack) {
shared_ptr<QFile> file(Open("pls_one.pls"));
SongList songs = parser_.Load(file.get(), QDir("/relative/to/"));
ASSERT_EQ(1, songs.length());
EXPECT_EQ("/relative/to/filename with spaces.mp3", songs[0].filename());
EXPECT_EQ("Title", songs[0].title());
EXPECT_EQ(123, songs[0].length());
}
TEST_F(PLSParserTest, ParseSomaFM) {
shared_ptr<QFile> file(Open("pls_somafm.pls"));
SongList songs = parser_.Load(file.get());
ASSERT_EQ(4, songs.length());
EXPECT_EQ("http://streamer-dtc-aa05.somafm.com:80/stream/1018", songs[0].filename());
EXPECT_EQ("http://streamer-mtc-aa03.somafm.com:80/stream/1018", songs[1].filename());
EXPECT_EQ("http://streamer-ntc-aa04.somafm.com:80/stream/1018", songs[2].filename());
EXPECT_EQ("http://ice.somafm.com/groovesalad", songs[3].filename());
EXPECT_EQ(-1, songs[0].length());
EXPECT_EQ(Song::Type_Stream, songs[0].filetype());
}