mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-18 04:19:55 +01:00
.pls parser. Fixes issue #302
This commit is contained in:
parent
1f8b0de49c
commit
211ae65e3d
@ -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
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
74
src/playlistparsers/plsparser.cpp
Normal file
74
src/playlistparsers/plsparser.cpp
Normal 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 {
|
||||
|
||||
}
|
34
src/playlistparsers/plsparser.h
Normal file
34
src/playlistparsers/plsparser.h
Normal 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
|
@ -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
6
tests/data/pls_one.pls
Normal 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
15
tests/data/pls_somafm.pls
Normal 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
|
@ -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
62
tests/plsparser_test.cpp
Normal 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());
|
||||
}
|
Loading…
Reference in New Issue
Block a user