Always write URLs in XML based playlists, instead of URLs of relative paths. Also load playlist items from the library if possible. Fixes issue #1054

This commit is contained in:
David Sansome 2010-12-11 10:35:07 +00:00
parent d5f8d2f9af
commit 08b2bcc816
20 changed files with 117 additions and 53 deletions

View File

@ -38,7 +38,7 @@ const int SongLoader::kDefaultTimeout = 5000;
SongLoader::SongLoader(LibraryBackendInterface* library, QObject *parent)
: QObject(parent),
timeout_timer_(new QTimer(this)),
playlist_parser_(new PlaylistParser(this)),
playlist_parser_(new PlaylistParser(library, this)),
timeout_(kDefaultTimeout),
state_(WaitingForType),
success_(false),

View File

@ -36,7 +36,7 @@ PlaylistManager::PlaylistManager(TaskManager* task_manager, QObject *parent)
playlist_backend_(NULL),
library_backend_(NULL),
sequence_(NULL),
parser_(new PlaylistParser(this)),
parser_(NULL),
current_(-1),
active_(-1)
{
@ -54,6 +54,7 @@ void PlaylistManager::Init(LibraryBackend* library_backend,
library_backend_ = library_backend;
playlist_backend_ = playlist_backend;
sequence_ = sequence;
parser_ = new PlaylistParser(library_backend, this);
connect(library_backend_, SIGNAL(SongsDiscovered(SongList)), SLOT(SongsDiscovered(SongList)));
connect(library_backend_, SIGNAL(SongsStatisticsChanged(SongList)), SLOT(SongsDiscovered(SongList)));
@ -101,7 +102,7 @@ void PlaylistManager::New(const QString& name, const SongList& songs) {
qFatal("Couldn't create playlist");
Playlist* playlist = AddPlaylist(id, name);
playlist->InsertSongs(songs);
playlist->InsertSongsOrLibraryItems(songs);
SetCurrentPlaylist(id);
}

View File

@ -20,8 +20,8 @@
#include <QTextStream>
#include <QtDebug>
AsxIniParser::AsxIniParser(QObject* parent)
: ParserBase(parent)
AsxIniParser::AsxIniParser(LibraryBackendInterface* library, QObject* parent)
: ParserBase(library, parent)
{
}
@ -42,7 +42,13 @@ SongList AsxIniParser::Load(QIODevice *device, const QDir &dir) const {
Song song;
if (!ParseTrackLocation(value, dir, &song))
qWarning() << "Failed to parse location: " << value;
ret << song;
// Load the song from the library if it's there.
Song library_song = LoadLibrarySong(song.filename());
if (library_song.is_valid())
ret << library_song;
else
ret << song;
}
}

View File

@ -24,7 +24,7 @@ class AsxIniParser : public ParserBase {
Q_OBJECT
public:
AsxIniParser(QObject* parent = 0);
AsxIniParser(LibraryBackendInterface* library, QObject* parent = 0);
QString name() const { return "ASX/INI"; }
QStringList file_extensions() const { return QStringList() << "asxini"; }

View File

@ -26,8 +26,8 @@
#include <QXmlStreamReader>
#include <QtDebug>
ASXParser::ASXParser(QObject* parent)
: XMLParser(parent)
ASXParser::ASXParser(LibraryBackendInterface* library, QObject* parent)
: XMLParser(library, parent)
{
}
@ -59,7 +59,7 @@ SongList ASXParser::Load(QIODevice *device, const QDir&) const {
while (!reader.atEnd() && ParseUntilElement(&reader, "entry")) {
Song song = ParseTrack(&reader);
if (song.is_valid()) {
if (!song.is_valid()) {
ret << song;
}
}
@ -83,6 +83,12 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const {
if (!QFile::exists(filename)) {
return Song();
}
// Load the song from the library if it's there.
Song library_song = LoadLibrarySong(filename);
if (library_song.is_valid())
return library_song;
song.InitFromFile(filename, -1);
return song;
} else {
@ -112,7 +118,7 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const {
return song;
}
void ASXParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
void ASXParser::Save(const SongList& songs, QIODevice* device, const QDir&) const {
QXmlStreamWriter writer(device);
writer.setAutoFormatting(true);
writer.writeStartDocument();
@ -124,7 +130,7 @@ void ASXParser::Save(const SongList &songs, QIODevice *device, const QDir &dir)
writer.writeTextElement("title", song.title());
{
StreamElement ref("ref", &writer);
writer.writeAttribute("href", MakeRelativeTo(song.filename(), dir));
writer.writeAttribute("href", MakeUrl(song.filename()));
}
if (!song.artist().isEmpty()) {
writer.writeTextElement("author", song.artist());

View File

@ -24,7 +24,7 @@ class ASXParser : public XMLParser {
Q_OBJECT
public:
ASXParser(QObject* parent = 0);
ASXParser(LibraryBackendInterface* library, QObject* parent = 0);
QString name() const { return "ASX"; }
QStringList file_extensions() const { return QStringList() << "asx"; }

View File

@ -20,8 +20,8 @@
#include <QBuffer>
#include <QtDebug>
M3UParser::M3UParser(QObject* parent)
: ParserBase(parent)
M3UParser::M3UParser(LibraryBackendInterface* library, QObject* parent)
: ParserBase(library, parent)
{
}
@ -67,7 +67,13 @@ SongList M3UParser::Load(QIODevice* device, const QDir& dir) const {
if (!ParseTrackLocation(line, dir, &song)) {
qWarning() << "Failed to parse location: " << line;
} else {
ret << song;
// Load the song from the library if it's there.
Song library_song = LoadLibrarySong(song.filename());
if (library_song.is_valid())
ret << library_song;
else
ret << song;
current_metadata.artist.clear();
current_metadata.title.clear();
current_metadata.length = -1;

View File

@ -28,7 +28,7 @@ class M3UParser : public ParserBase {
Q_OBJECT
public:
M3UParser(QObject* parent = 0);
M3UParser(LibraryBackendInterface* library, QObject* parent = 0);
QString name() const { return "M3U"; }
QStringList file_extensions() const { return QStringList() << "m3u" << "m3u8"; }

View File

@ -16,11 +16,15 @@
*/
#include "parserbase.h"
#include "library/librarybackend.h"
#include "library/libraryquery.h"
#include "library/sqlrow.h"
#include <QUrl>
ParserBase::ParserBase(QObject *parent)
: QObject(parent)
ParserBase::ParserBase(LibraryBackendInterface* library, QObject *parent)
: QObject(parent),
library_(library)
{
}
@ -67,3 +71,30 @@ QString ParserBase::MakeRelativeTo(const QString& filename_or_url,
}
return filename_or_url;
}
QString ParserBase::MakeUrl(const QString& filename_or_url) const {
if (filename_or_url.contains(QRegExp("^[a-z]+://"))) {
return filename_or_url;
}
return QUrl::fromLocalFile(filename_or_url).toString();
}
Song ParserBase::LoadLibrarySong(const QString& filename_or_url) const {
QFileInfo info;
if (filename_or_url.contains("://"))
info.setFile(QUrl(filename_or_url).path());
else
info.setFile(filename_or_url);
LibraryQuery query;
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddWhere("filename", info.canonicalFilePath());
Song song;
if (library_->ExecQuery(&query) && query.Next()) {
song.InitFromQuery(query);
}
return song;
}

View File

@ -23,11 +23,13 @@
#include "core/song.h"
class LibraryBackendInterface;
class ParserBase : public QObject {
Q_OBJECT
public:
ParserBase(QObject *parent = 0);
ParserBase(LibraryBackendInterface* library, QObject *parent = 0);
virtual QString name() const = 0;
virtual QStringList file_extensions() const = 0;
@ -47,6 +49,17 @@ protected:
// Takes a URL, relative path or absolute path, and in the case of absolute
// paths makes them relative to dir if they are subdirectories.
QString MakeRelativeTo(const QString& filename_or_url, const QDir& dir) const;
// Takes a URL or absolute path and returns a URL
QString MakeUrl(const QString& filename_or_url) const;
// Converts the URL or path to a canonical path and searches the library for
// a song with that path. If one is found, returns it, otherwise returns an
// invalid song.
Song LoadLibrarySong(const QString& filename_or_url) const;
private:
LibraryBackendInterface* library_;
};
#endif // PARSERBASE_H

View File

@ -26,15 +26,15 @@
const int PlaylistParser::kMagicSize = 512;
PlaylistParser::PlaylistParser(QObject *parent)
PlaylistParser::PlaylistParser(LibraryBackendInterface* library, QObject *parent)
: QObject(parent)
{
default_parser_ = new XSPFParser(this);
parsers_ << new M3UParser(this);
default_parser_ = new XSPFParser(library, this);
parsers_ << new M3UParser(library, this);
parsers_ << default_parser_;
parsers_ << new PLSParser(this);
parsers_ << new ASXParser(this);
parsers_ << new AsxIniParser(this);
parsers_ << new PLSParser(library, this);
parsers_ << new ASXParser(library, this);
parsers_ << new AsxIniParser(library, this);
}
QStringList PlaylistParser::file_extensions() const {

View File

@ -23,12 +23,13 @@
#include "core/song.h"
class ParserBase;
class LibraryBackendInterface;
class PlaylistParser : public QObject {
Q_OBJECT
public:
PlaylistParser(QObject *parent = 0);
PlaylistParser(LibraryBackendInterface* library, QObject* parent = 0);
static const int kMagicSize;

View File

@ -20,8 +20,8 @@
#include <QTextStream>
#include <QtDebug>
PLSParser::PLSParser(QObject* parent)
: ParserBase(parent)
PLSParser::PLSParser(LibraryBackendInterface* library, QObject* parent)
: ParserBase(library, parent)
{
}
@ -41,6 +41,12 @@ SongList PLSParser::Load(QIODevice *device, const QDir &dir) const {
if (key.startsWith("file")) {
if (!ParseTrackLocation(value, dir, &songs[n]))
qWarning() << "Failed to parse location: " << value;
// Load the song from the library if it's there.
Song library_song = LoadLibrarySong(songs[n].filename());
if (library_song.is_valid())
songs[n] = library_song;
} else if (key.startsWith("title")) {
songs[n].set_title(value);
} else if (key.startsWith("length")) {

View File

@ -24,7 +24,7 @@ class PLSParser : public ParserBase {
Q_OBJECT
public:
PLSParser(QObject* parent = 0);
PLSParser(LibraryBackendInterface* library, QObject* parent = 0);
QString name() const { return "PLS"; }
QStringList file_extensions() const { return QStringList() << "pls"; }

View File

@ -24,8 +24,8 @@
#include <QUrl>
#include <QXmlStreamReader>
XMLParser::XMLParser(QObject* parent)
: ParserBase(parent) {
XMLParser::XMLParser(LibraryBackendInterface* library, QObject* parent)
: ParserBase(library, parent) {
}
bool XMLParser::ParseUntilElement(QXmlStreamReader* reader, const QString& name) const {
@ -60,11 +60,3 @@ void XMLParser::IgnoreElement(QXmlStreamReader* reader) const {
}
}
}
QString XMLParser::MakeRelativeTo(const QString& filename_or_url, const QDir& dir) const {
QString file = ParserBase::MakeRelativeTo(filename_or_url, dir);
if (!file.contains(QRegExp("^[a-z]+://"))) {
return QUrl::fromLocalFile(file).toString();
}
return file;
}

View File

@ -30,12 +30,11 @@ class QDomNode;
class XMLParser : public ParserBase {
protected:
XMLParser(QObject* parent);
XMLParser(LibraryBackendInterface* library, QObject* parent);
bool ParseUntilElement(QXmlStreamReader* reader, const QString& element) const;
void IgnoreElement(QXmlStreamReader* reader) const;
QString MakeRelativeTo(const QString& filename, const QDir& dir) const;
class StreamElement : public boost::noncopyable {
public:
StreamElement(const QString& name, QXmlStreamWriter* stream) : stream_(stream) {

View File

@ -24,8 +24,8 @@
#include <QUrl>
#include <QXmlStreamReader>
XSPFParser::XSPFParser(QObject* parent)
: XMLParser(parent)
XSPFParser::XSPFParser(LibraryBackendInterface* library, QObject* parent)
: XMLParser(library, parent)
{
}
@ -63,6 +63,12 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const {
if (!QFile::exists(filename)) {
return Song();
}
// Load the song from the library if it's there.
Song library_song = LoadLibrarySong(filename);
if (library_song.is_valid())
return library_song;
song.InitFromFile(filename, -1);
return song;
} else {
@ -104,7 +110,7 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const {
return song;
}
void XSPFParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
void XSPFParser::Save(const SongList& songs, QIODevice* device, const QDir&) const {
QXmlStreamWriter writer(device);
writer.writeStartDocument();
StreamElement playlist("playlist", &writer);
@ -114,7 +120,7 @@ void XSPFParser::Save(const SongList &songs, QIODevice *device, const QDir &dir)
StreamElement tracklist("trackList", &writer);
foreach (const Song& song, songs) {
StreamElement track("track", &writer);
writer.writeTextElement("location", MakeRelativeTo(song.filename(), dir));
writer.writeTextElement("location", MakeUrl(song.filename()));
writer.writeTextElement("title", song.title());
if (!song.artist().isEmpty()) {
writer.writeTextElement("creator", song.artist());
@ -130,7 +136,7 @@ void XSPFParser::Save(const SongList &songs, QIODevice *device, const QDir &dir)
// Ignore images that are in our resource bundle.
if (!art.startsWith(":") && !art.isEmpty()) {
// Convert local files to URLs.
art = MakeRelativeTo(art, dir);
art = MakeUrl(art);
writer.writeTextElement("image", art);
}
}

View File

@ -29,7 +29,7 @@ class XSPFParser : public XMLParser {
Q_OBJECT
public:
XSPFParser(QObject* parent = 0);
XSPFParser(LibraryBackendInterface* library, QObject* parent = 0);
QString name() const { return "XSPF"; }
QStringList file_extensions() const { return QStringList() << "xspf"; }

View File

@ -135,7 +135,6 @@ MainWindow::MainWindow(Engine::Type engine, QWidget *parent)
radio_model_(NULL),
playlist_backend_(NULL),
playlists_(new PlaylistManager(task_manager_, this)),
playlist_parser_(new PlaylistParser(this)),
player_(NULL),
library_(NULL),
global_shortcuts_(new GlobalShortcuts(this)),
@ -1275,7 +1274,7 @@ void MainWindow::AddFile() {
// Last used directory
QString directory = settings_.value("add_media_path", QDir::currentPath()).toString();
PlaylistParser parser;
PlaylistParser parser(library_->backend());
// Show dialog
QStringList file_names = QFileDialog::getOpenFileNames(

View File

@ -54,7 +54,6 @@ class OSD;
class Player;
class PlaylistBackend;
class PlaylistManager;
class PlaylistParser;
class QueueManager;
class RadioItem;
class RadioModel;
@ -225,7 +224,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
RadioModel* radio_model_;
PlaylistBackend* playlist_backend_;
PlaylistManager* playlists_;
PlaylistParser* playlist_parser_;
Player* player_;
Library* library_;
GlobalShortcuts* global_shortcuts_;