From 08b2bcc81689395f6ec2fc9e732f2ee6d1716f7d Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sat, 11 Dec 2010 10:35:07 +0000 Subject: [PATCH] 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 --- src/core/songloader.cpp | 2 +- src/playlist/playlistmanager.cpp | 5 ++-- src/playlistparsers/asxiniparser.cpp | 12 ++++++--- src/playlistparsers/asxiniparser.h | 2 +- src/playlistparsers/asxparser.cpp | 16 ++++++++---- src/playlistparsers/asxparser.h | 2 +- src/playlistparsers/m3uparser.cpp | 12 ++++++--- src/playlistparsers/m3uparser.h | 2 +- src/playlistparsers/parserbase.cpp | 35 ++++++++++++++++++++++++-- src/playlistparsers/parserbase.h | 15 ++++++++++- src/playlistparsers/playlistparser.cpp | 12 ++++----- src/playlistparsers/playlistparser.h | 3 ++- src/playlistparsers/plsparser.cpp | 10 ++++++-- src/playlistparsers/plsparser.h | 2 +- src/playlistparsers/xmlparser.cpp | 12 ++------- src/playlistparsers/xmlparser.h | 5 ++-- src/playlistparsers/xspfparser.cpp | 16 ++++++++---- src/playlistparsers/xspfparser.h | 2 +- src/ui/mainwindow.cpp | 3 +-- src/ui/mainwindow.h | 2 -- 20 files changed, 117 insertions(+), 53 deletions(-) diff --git a/src/core/songloader.cpp b/src/core/songloader.cpp index 59e88060c..38bc80673 100644 --- a/src/core/songloader.cpp +++ b/src/core/songloader.cpp @@ -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), diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index f67b9b999..f1c509b18 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -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); } diff --git a/src/playlistparsers/asxiniparser.cpp b/src/playlistparsers/asxiniparser.cpp index 4f1155945..5d7470198 100644 --- a/src/playlistparsers/asxiniparser.cpp +++ b/src/playlistparsers/asxiniparser.cpp @@ -20,8 +20,8 @@ #include #include -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; } } diff --git a/src/playlistparsers/asxiniparser.h b/src/playlistparsers/asxiniparser.h index 8f515757d..7a4bf0ad7 100644 --- a/src/playlistparsers/asxiniparser.h +++ b/src/playlistparsers/asxiniparser.h @@ -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"; } diff --git a/src/playlistparsers/asxparser.cpp b/src/playlistparsers/asxparser.cpp index 300147de1..363304089 100644 --- a/src/playlistparsers/asxparser.cpp +++ b/src/playlistparsers/asxparser.cpp @@ -26,8 +26,8 @@ #include #include -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()); diff --git a/src/playlistparsers/asxparser.h b/src/playlistparsers/asxparser.h index c6725e9f0..7732afa89 100644 --- a/src/playlistparsers/asxparser.h +++ b/src/playlistparsers/asxparser.h @@ -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"; } diff --git a/src/playlistparsers/m3uparser.cpp b/src/playlistparsers/m3uparser.cpp index 1745bf7d5..25f2c6d21 100644 --- a/src/playlistparsers/m3uparser.cpp +++ b/src/playlistparsers/m3uparser.cpp @@ -20,8 +20,8 @@ #include #include -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; diff --git a/src/playlistparsers/m3uparser.h b/src/playlistparsers/m3uparser.h index 12903e6e9..18c6b552f 100644 --- a/src/playlistparsers/m3uparser.h +++ b/src/playlistparsers/m3uparser.h @@ -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"; } diff --git a/src/playlistparsers/parserbase.cpp b/src/playlistparsers/parserbase.cpp index 49379fc39..0eb07a7ad 100644 --- a/src/playlistparsers/parserbase.cpp +++ b/src/playlistparsers/parserbase.cpp @@ -16,11 +16,15 @@ */ #include "parserbase.h" +#include "library/librarybackend.h" +#include "library/libraryquery.h" +#include "library/sqlrow.h" #include -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; +} diff --git a/src/playlistparsers/parserbase.h b/src/playlistparsers/parserbase.h index c8e95ec8d..e04a3a06b 100644 --- a/src/playlistparsers/parserbase.h +++ b/src/playlistparsers/parserbase.h @@ -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 diff --git a/src/playlistparsers/playlistparser.cpp b/src/playlistparsers/playlistparser.cpp index c2b5c5517..9ab9205ef 100644 --- a/src/playlistparsers/playlistparser.cpp +++ b/src/playlistparsers/playlistparser.cpp @@ -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 { diff --git a/src/playlistparsers/playlistparser.h b/src/playlistparsers/playlistparser.h index 64ccfb690..9fcd95bae 100644 --- a/src/playlistparsers/playlistparser.h +++ b/src/playlistparsers/playlistparser.h @@ -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; diff --git a/src/playlistparsers/plsparser.cpp b/src/playlistparsers/plsparser.cpp index 5ef020625..fbb21b87e 100644 --- a/src/playlistparsers/plsparser.cpp +++ b/src/playlistparsers/plsparser.cpp @@ -20,8 +20,8 @@ #include #include -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")) { diff --git a/src/playlistparsers/plsparser.h b/src/playlistparsers/plsparser.h index dcce104f2..77da55a3f 100644 --- a/src/playlistparsers/plsparser.h +++ b/src/playlistparsers/plsparser.h @@ -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"; } diff --git a/src/playlistparsers/xmlparser.cpp b/src/playlistparsers/xmlparser.cpp index a4fafdb91..48b08f95e 100644 --- a/src/playlistparsers/xmlparser.cpp +++ b/src/playlistparsers/xmlparser.cpp @@ -24,8 +24,8 @@ #include #include -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; -} diff --git a/src/playlistparsers/xmlparser.h b/src/playlistparsers/xmlparser.h index f7624c08b..15fe8d5b1 100644 --- a/src/playlistparsers/xmlparser.h +++ b/src/playlistparsers/xmlparser.h @@ -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) { diff --git a/src/playlistparsers/xspfparser.cpp b/src/playlistparsers/xspfparser.cpp index 9e1e18326..a4d48af39 100644 --- a/src/playlistparsers/xspfparser.cpp +++ b/src/playlistparsers/xspfparser.cpp @@ -24,8 +24,8 @@ #include #include -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); } } diff --git a/src/playlistparsers/xspfparser.h b/src/playlistparsers/xspfparser.h index 3a3549fe6..e77284954 100644 --- a/src/playlistparsers/xspfparser.h +++ b/src/playlistparsers/xspfparser.h @@ -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"; } diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 400e45f58..8bcf0d69b 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -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( diff --git a/src/ui/mainwindow.h b/src/ui/mainwindow.h index 03683a200..c53dc75d7 100644 --- a/src/ui/mainwindow.h +++ b/src/ui/mainwindow.h @@ -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_;