mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-31 11:35:24 +01:00
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:
parent
d5f8d2f9af
commit
08b2bcc816
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"; }
|
||||
|
@ -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());
|
||||
|
@ -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"; }
|
||||
|
@ -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;
|
||||
|
@ -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"; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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")) {
|
||||
|
@ -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"; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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"; }
|
||||
|
@ -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(
|
||||
|
@ -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_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user