diff options
| author | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2017-06-14 20:24:58 +0000 |
|---|---|---|
| committer | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2017-06-14 20:24:58 +0000 |
| commit | b2576ddc181d3758e3a57d9cac871ee7d7e2ef06 (patch) | |
| tree | 1dfd6331a91f153d4a693a0aa32a774e2e53e2ea | |
| parent | 9e3bcc8653f5e0500e9d0e4ee0c953e8bbf4ea4c (diff) | |
| download | qmmp-b2576ddc181d3758e3a57d9cac871ee7d7e2ef06.tar.gz qmmp-b2576ddc181d3758e3a57d9cac871ee7d7e2ef06.tar.bz2 qmmp-b2576ddc181d3758e3a57d9cac871ee7d7e2ef06.zip | |
improved playlist formats support
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@7237 90c681e8-e032-0410-971d-27865f9a5e38
| -rw-r--r-- | src/app/builtincommandlineoption.cpp | 6 | ||||
| -rw-r--r-- | src/plugins/PlayListFormats/m3u/m3uplaylistformat.cpp | 49 | ||||
| -rw-r--r-- | src/plugins/PlayListFormats/m3u/m3uplaylistformat.h | 4 | ||||
| -rw-r--r-- | src/plugins/PlayListFormats/pls/plsplaylistformat.cpp | 113 | ||||
| -rw-r--r-- | src/plugins/PlayListFormats/pls/plsplaylistformat.h | 4 | ||||
| -rw-r--r-- | src/plugins/PlayListFormats/xspf/xspfplaylistformat.cpp | 67 | ||||
| -rw-r--r-- | src/plugins/PlayListFormats/xspf/xspfplaylistformat.h | 4 | ||||
| -rw-r--r-- | src/qmmpui/addurldialog.cpp | 36 | ||||
| -rw-r--r-- | src/qmmpui/addurldialog_p.h | 5 | ||||
| -rw-r--r-- | src/qmmpui/fileloader.cpp | 109 | ||||
| -rw-r--r-- | src/qmmpui/fileloader_p.h | 8 | ||||
| -rw-r--r-- | src/qmmpui/playlistdownloader.cpp | 21 | ||||
| -rw-r--r-- | src/qmmpui/playlistdownloader.h | 23 | ||||
| -rw-r--r-- | src/qmmpui/playlistformat.h | 8 | ||||
| -rw-r--r-- | src/qmmpui/playlistmodel.cpp | 32 | ||||
| -rw-r--r-- | src/qmmpui/playlistmodel.h | 2 | ||||
| -rw-r--r-- | src/qmmpui/playlistparser.cpp | 73 | ||||
| -rw-r--r-- | src/qmmpui/playlistparser.h | 15 | ||||
| -rw-r--r-- | src/qmmpui/playlisttrack.cpp | 22 | ||||
| -rw-r--r-- | src/qmmpui/playlisttrack.h | 5 |
20 files changed, 424 insertions, 182 deletions
diff --git a/src/app/builtincommandlineoption.cpp b/src/app/builtincommandlineoption.cpp index 30bad21a1..1cc93df44 100644 --- a/src/app/builtincommandlineoption.cpp +++ b/src/app/builtincommandlineoption.cpp @@ -147,10 +147,8 @@ QString BuiltinCommandLineOption::executeCommand(const QString &option_string, if(!remote_pls_list.isEmpty()) { PlayListDownloader *downloader = new PlayListDownloader(this); - connect(downloader, SIGNAL(done(QStringList)), m_model, SLOT(add(QStringList))); - connect(downloader, SIGNAL(done(QStringList)), downloader, SLOT(deleteLater())); - connect(downloader, SIGNAL(error(QString)), downloader, SLOT(deleteLater())); - downloader->start(remote_pls_list.at(0)); + connect(downloader, SIGNAL(finished(bool,QString)), downloader, SLOT(deleteLater())); + downloader->start(remote_pls_list.at(0), m_model); } } else if (option_string == "--play" || option_string == "-p") diff --git a/src/plugins/PlayListFormats/m3u/m3uplaylistformat.cpp b/src/plugins/PlayListFormats/m3u/m3uplaylistformat.cpp index 87bd2eaf8..783f3c518 100644 --- a/src/plugins/PlayListFormats/m3u/m3uplaylistformat.cpp +++ b/src/plugins/PlayListFormats/m3u/m3uplaylistformat.cpp @@ -32,34 +32,57 @@ const PlayListFormatProperties M3UPlaylistFormat::properties() const return p; } -QStringList M3UPlaylistFormat::decode(const QString & contents) +QList<PlayListTrack *> M3UPlaylistFormat::decode(const QByteArray &contents) { - QStringList out; - QStringList splitted = contents.split("\n"); + QList<PlayListTrack*> out; + QStringList splitted = QString::fromUtf8(contents).split("\n"); if(splitted.isEmpty()) - return QStringList(); + return out; + + QRegExp extInfRegExp("#EXTINF:(-{0,1}\\d+),(.*) - (.*)"); + int length = 0; + QString artist, title; + bool hasExtInf = false; foreach(QString str, splitted) { - str = str.trimmed (); - if (str.startsWith("#EXTM3U") || str.startsWith("#EXTINF:") || str.isEmpty()) - continue;//TODO: Let's skip it for now.. - else if (str.startsWith("#") || str.isEmpty()) + str = str.trimmed(); + if(str.startsWith("#EXTM3U") || str.isEmpty()) continue; - else - out << str; + + if(extInfRegExp.indexIn(str) > -1) + { + length = extInfRegExp.cap(1).toInt(); + artist = extInfRegExp.cap(2); + title = extInfRegExp.cap(3); + hasExtInf = true; + } + + if(str.startsWith("#")) + continue; + + out << new PlayListTrack(); + out.last()->insert(Qmmp::URL, str); + + if(hasExtInf) + { + out.last()->setLength(length); + out.last()->insert(Qmmp::ARTIST, artist); + out.last()->insert(Qmmp::TITLE, title); + hasExtInf = false; + } } return out; } -QString M3UPlaylistFormat::encode(const QList<PlayListTrack*> & contents, const QString &path) +QByteArray M3UPlaylistFormat::encode(const QList<PlayListTrack*> &contents, const QString &path) { QStringList out; out << QString("#EXTM3U"); MetaDataFormatter formatter("%if(%p,%p - %t,%t)%if(%p|%t,,%f)"); QString m3uDir = QFileInfo(path).canonicalPath(); - foreach(PlayListTrack* f,contents) + foreach(PlayListTrack* f, contents) { QString info = "#EXTINF:" + QString::number(f->length()) + "," + formatter.format(f); out.append(info); @@ -75,7 +98,7 @@ QString M3UPlaylistFormat::encode(const QList<PlayListTrack*> & contents, const else out.append(f->url()); } - return out.join("\n"); + return out.join("\n").toUtf8(); } Q_EXPORT_PLUGIN2(m3uplaylistformat,M3UPlaylistFormat) diff --git a/src/plugins/PlayListFormats/m3u/m3uplaylistformat.h b/src/plugins/PlayListFormats/m3u/m3uplaylistformat.h index 379ae063e..dd859c807 100644 --- a/src/plugins/PlayListFormats/m3u/m3uplaylistformat.h +++ b/src/plugins/PlayListFormats/m3u/m3uplaylistformat.h @@ -36,8 +36,8 @@ class M3UPlaylistFormat : public QObject, public PlayListFormat Q_INTERFACES(PlayListFormat) public: const PlayListFormatProperties properties() const; - QStringList decode(const QString& contents); - QString encode(const QList<PlayListTrack*>& contents, const QString &path); + QList<PlayListTrack*> decode(const QByteArray &contents); + QByteArray encode(const QList<PlayListTrack*>& contents, const QString &path); }; diff --git a/src/plugins/PlayListFormats/pls/plsplaylistformat.cpp b/src/plugins/PlayListFormats/pls/plsplaylistformat.cpp index ac4954567..a4822c423 100644 --- a/src/plugins/PlayListFormats/pls/plsplaylistformat.cpp +++ b/src/plugins/PlayListFormats/pls/plsplaylistformat.cpp @@ -18,8 +18,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include <QFileInfo> #include <QtPlugin> +#include <QRegExp> +#include <qmmpui/metadataformatter.h> #include "plsplaylistformat.h" const PlayListFormatProperties PLSPlaylistFormat::properties() const @@ -31,57 +32,111 @@ const PlayListFormatProperties PLSPlaylistFormat::properties() const return p; } -QStringList PLSPlaylistFormat::decode(const QString & contents) +QList<PlayListTrack *> PLSPlaylistFormat::decode(const QByteArray &contents) { - QStringList out; - QStringList splitted = contents.split("\n"); - if (!splitted.isEmpty()) + QList<PlayListTrack *> out; + QStringList splitted = QString::fromUtf8(contents).split("\n"); + + if(splitted.isEmpty()) { - if (splitted.takeAt(0).toLower().contains("[playlist]")) + qWarning("PLSPlaylistFormat: error parsing PLS format"); + return out; + } + + if(!splitted.takeFirst().toLower().startsWith("[playlist]")) + { + qWarning("PLSPlaylistFormat: unknown playlist format"); + return out; + } + + QRegExp fileRegExp("^File(\\d+)=(.+)"); + QRegExp fullTitleRegExp("^Title(\\d+)=(.+) - (.+)"); + QRegExp titleRegExp("^Title(\\d+)=(.+)"); + QRegExp lengthRegExp("^Length(\\d+)=(-{0,1}\\d+)"); + + int number = 0; + bool error = false; + + foreach (QString line, splitted) + { + if(fileRegExp.indexIn(line) > -1) { - foreach(QString str, splitted) + if((number = fileRegExp.cap(1).toInt()) > 0) { - if (str.startsWith("File")) - { - QString unverified = str.remove(0,str.indexOf(QChar('=')) + 1); - unverified = unverified.trimmed(); - if (unverified.startsWith("http://")) - { - out << unverified; - } - else /*if (QFileInfo(unverified).exists())*/ - out << QFileInfo(unverified).absoluteFilePath(); - /*else - qWarning("File %s does not exist", qPrintable(unverified));*/ - } + while(number > out.count()) + out << new PlayListTrack(); + out[number - 1]->insert(Qmmp::URL, fileRegExp.cap(2)); } - return out; + else + error = true; + } + else if(fullTitleRegExp.indexIn(line) > -1) + { + if((number = fullTitleRegExp.cap(1).toInt()) > 0) + { + while(number > out.count()) + out << new PlayListTrack(); + out[number - 1]->insert(Qmmp::ARTIST, fullTitleRegExp.cap(2)); + out[number - 1]->insert(Qmmp::TITLE, fullTitleRegExp.cap(3)); + } + else + error = true; + } + else if(titleRegExp.indexIn(line) > -1) + { + if((number = titleRegExp.cap(1).toInt()) > 0) + { + while(number > out.count()) + out << new PlayListTrack(); + out[number - 1]->insert(Qmmp::TITLE, titleRegExp.cap(2)); + } + else + error = true; + } + else if(lengthRegExp.indexIn(line) > -1) + { + if((number = lengthRegExp.cap(1).toInt()) > 0) + { + while(number > out.count()) + out << new PlayListTrack(); + out[number - 1]->setLength(lengthRegExp.cap(2).toInt()); + } + else + error = true; + } + + if(error) + { + qWarning("PLSPlaylistFormat: error while parsing line: '%s'", qPrintable(line)); + qDeleteAll(out); + out.clear(); + break; } } - else - qWarning("Error parsing PLS format"); - return QStringList(); + return out; } -QString PLSPlaylistFormat::encode(const QList<PlayListTrack *> & contents, const QString &path) +QByteArray PLSPlaylistFormat::encode(const QList<PlayListTrack *> &contents, const QString &path) { Q_UNUSED(path); + MetaDataFormatter formatter("%if(%p,%p - %t,%t)%if(%p|%t,,%f)"); QStringList out; out << QString("[playlist]"); int counter = 1; - foreach(PlayListTrack* f,contents) + foreach(PlayListTrack *f, contents) { QString begin = "File" + QString::number(counter) + "="; out.append(begin + f->url()); begin = "Title" + QString::number(counter) + "="; - out.append(begin + f->value(Qmmp::TITLE)); + out.append(begin + formatter.format(f)); begin = "Length" + QString::number(counter) + "="; out.append(begin + QString::number(f->length())); - counter ++; + counter++; } out << "NumberOfEntries=" + QString::number(contents.count()); - return out.join("\n"); + out << "Version=2"; + return out.join("\n").toUtf8(); } Q_EXPORT_PLUGIN2(plsplaylistformat, PLSPlaylistFormat) diff --git a/src/plugins/PlayListFormats/pls/plsplaylistformat.h b/src/plugins/PlayListFormats/pls/plsplaylistformat.h index 7fed6d810..79bebf91c 100644 --- a/src/plugins/PlayListFormats/pls/plsplaylistformat.h +++ b/src/plugins/PlayListFormats/pls/plsplaylistformat.h @@ -36,8 +36,8 @@ class PLSPlaylistFormat : public QObject, public PlayListFormat Q_INTERFACES(PlayListFormat) public: const PlayListFormatProperties properties() const; - QStringList decode(const QString& contents); - QString encode(const QList<PlayListTrack*>& contents, const QString &path); + QList<PlayListTrack*> decode(const QByteArray& contents); + QByteArray encode(const QList<PlayListTrack*> &contents, const QString &path); }; diff --git a/src/plugins/PlayListFormats/xspf/xspfplaylistformat.cpp b/src/plugins/PlayListFormats/xspf/xspfplaylistformat.cpp index d5ab21fb5..93d691d3c 100644 --- a/src/plugins/PlayListFormats/xspf/xspfplaylistformat.cpp +++ b/src/plugins/PlayListFormats/xspf/xspfplaylistformat.cpp @@ -37,11 +37,11 @@ const PlayListFormatProperties XSPFPlaylistFormat::XSPFPlaylistFormat::propertie return p; } -QStringList XSPFPlaylistFormat::decode(const QString & contents) +QList<PlayListTrack*> XSPFPlaylistFormat::decode(const QByteArray &contents) { - QStringList out; + QList<PlayListTrack*> out; QString currentTag; - QString contents_copy = contents; + QString contents_copy = QString::fromUtf8(contents); //remove control symbols to avoid xml errors for(int i = 0; i < contents_copy.size(); ++i) @@ -54,24 +54,47 @@ QStringList XSPFPlaylistFormat::decode(const QString & contents) } QXmlStreamReader xml(contents_copy); - while(!xml.atEnd()) + while(!xml.atEnd() || !xml.hasError()) { xml.readNext(); if (xml.isStartElement()) { currentTag = xml.name().toString(); - + if(currentTag == "track") + out << new PlayListTrack(); } else if (xml.isCharacters() && !xml.isWhitespace()) { - if (currentTag == "location") - { + if(out.isEmpty()) + continue; + if(currentTag == "location") + { QUrl url(xml.text().toString()); if (url.scheme() == "file") //remove scheme for local files only - out << QUrl::fromPercentEncoding(url.toString().toLatin1()).remove("file://"); + out.last()->insert(Qmmp::URL, QUrl::fromPercentEncoding(url.toString().toLatin1()).remove("file://")); else - out << QUrl::fromPercentEncoding(url.toString().toLatin1()); + out.last()->insert(Qmmp::URL, QUrl::fromPercentEncoding(url.toString().toLatin1())); + } + else if(currentTag == "title") + { + out.last()->insert(Qmmp::TITLE, xml.text().toString()); + } + else if(currentTag == "creator") + { + out.last()->insert(Qmmp::ARTIST, xml.text().toString()); + } + else if(currentTag == "annotation") + { + out.last()->insert(Qmmp::COMMENT, xml.text().toString()); + } + else if(currentTag == "album") + { + out.last()->insert(Qmmp::ALBUM, xml.text().toString()); + } + else if(currentTag == "meta" && xml.attributes().value("rel") == "year") + { + out.last()->insert(Qmmp::YEAR, xml.text().toString()); } else xml.skipCurrentElement(); @@ -88,10 +111,10 @@ QStringList XSPFPlaylistFormat::decode(const QString & contents) // Needs more work - it's better use libSpiff there and put it as plugin. -QString XSPFPlaylistFormat::encode(const QList<PlayListTrack*> & files, const QString &path) +QByteArray XSPFPlaylistFormat::encode(const QList<PlayListTrack*> &files, const QString &path) { - Q_UNUSED(path); - QString out; + QString xspfDir = QFileInfo(path).canonicalPath(); + QByteArray out; QXmlStreamWriter xml(&out); xml.setCodec("UTF-8"); xml.setAutoFormatting(true); @@ -103,16 +126,27 @@ QString XSPFPlaylistFormat::encode(const QList<PlayListTrack*> & files, const QS xml.writeStartElement("trackList"); int counter = 1; - foreach(PlayListTrack* f,files) + foreach(PlayListTrack* f, files) { xml.writeStartElement("track"); QString url; if (f->url().contains("://")) + { url = QUrl::toPercentEncoding(f->url(), ":/"); - else //append protocol - url = QUrl::toPercentEncoding(QString("file://") + - QFileInfo(f->url()).absoluteFilePath(), ":/"); + } + else if(f->url().startsWith(xspfDir)) //relative path + { + QString p = f->url(); + p.remove(0, xspfDir.size()); + if(p.startsWith("/")) + p.remove(0, 1); + url = QUrl::toPercentEncoding(p, ":/"); + } + else //absolute path + { + url = QUrl::toPercentEncoding(QLatin1String("file://") + f->url(), ":/"); + } xml.writeTextElement("location", url); xml.writeTextElement("title", f->value(Qmmp::TITLE)); @@ -133,7 +167,6 @@ QString XSPFPlaylistFormat::encode(const QList<PlayListTrack*> & files, const QS xml.writeEndElement(); //playlist xml.writeEndDocument(); return out; - } Q_EXPORT_PLUGIN2(xspfplaylistformat,XSPFPlaylistFormat) diff --git a/src/plugins/PlayListFormats/xspf/xspfplaylistformat.h b/src/plugins/PlayListFormats/xspf/xspfplaylistformat.h index d3b03d3ca..dbd1325d5 100644 --- a/src/plugins/PlayListFormats/xspf/xspfplaylistformat.h +++ b/src/plugins/PlayListFormats/xspf/xspfplaylistformat.h @@ -36,8 +36,8 @@ class XSPFPlaylistFormat : public QObject, public PlayListFormat Q_INTERFACES(PlayListFormat) public: const PlayListFormatProperties properties() const; - QStringList decode(const QString& contents); - QString encode(const QList<PlayListTrack*>& contents, const QString &path); + QList<PlayListTrack*> decode(const QByteArray &contents); + QByteArray encode(const QList<PlayListTrack*> &contents, const QString &path); }; #endif diff --git a/src/qmmpui/addurldialog.cpp b/src/qmmpui/addurldialog.cpp index 0bfef038a..9af61b516 100644 --- a/src/qmmpui/addurldialog.cpp +++ b/src/qmmpui/addurldialog.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2006-2012 by Ilya Kotov * + * Copyright (C) 2006-2017 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -43,8 +43,8 @@ AddUrlDialog::AddUrlDialog(QWidget *parent) : QDialog(parent) m_history = settings.value("URLDialog/history").toStringList(); urlComboBox->addItems(m_history); m_downloader = new PlayListDownloader(this); - connect(m_downloader, SIGNAL(done(QStringList)), SLOT(add(QStringList))); - connect(m_downloader, SIGNAL(error(QString)), SLOT(showError(QString))); + connect(m_downloader, SIGNAL(finished(bool,QString)), SLOT(onFinished(bool,QString))); + if(QmmpUiSettings::instance()->useClipboard()) { QUrl url(QApplication::clipboard()->text().trimmed()); @@ -74,6 +74,19 @@ void AddUrlDialog::popup(QWidget* parent, PlayListModel* model) m_instance->raise(); } +void AddUrlDialog::onFinished(bool ok, const QString &message) +{ + if(ok) + { + QDialog::accept(); + } + else + { + QMessageBox::warning(this, tr("Error"), message); + addButton->setEnabled(true); + } +} + void AddUrlDialog::accept() { addButton->setEnabled(false); @@ -98,9 +111,9 @@ void AddUrlDialog::accept() m_history.removeAll(s); m_history.prepend(s); - if (s.startsWith("http://")) //try to download playlist + if (s.startsWith("http://") || s.startsWith("https://")) //try to download playlist { - m_downloader->start(QUrl(s)); + m_downloader->start(QUrl(s), m_model); return; } m_model->add(s); @@ -111,16 +124,3 @@ void AddUrlDialog::setModel(PlayListModel *m) { m_model = m; } - -void AddUrlDialog::add(const QStringList &urls) -{ - addButton->setEnabled(true); - m_model->add(urls); - QDialog::accept(); -} - -void AddUrlDialog::showError(const QString &message) -{ - QMessageBox::warning(this, tr("Error"), message); - addButton->setEnabled(true); -} diff --git a/src/qmmpui/addurldialog_p.h b/src/qmmpui/addurldialog_p.h index 5835b3fbf..962c7fe38 100644 --- a/src/qmmpui/addurldialog_p.h +++ b/src/qmmpui/addurldialog_p.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2006-2012 by Ilya Kotov * + * Copyright (C) 2006-2017 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -41,8 +41,7 @@ public: static void popup(QWidget* parent ,PlayListModel*); private slots: - void add(const QStringList &urls); - void showError(const QString &message); + void onFinished(bool ok, const QString &message); private: AddUrlDialog(QWidget *parent); diff --git a/src/qmmpui/fileloader.cpp b/src/qmmpui/fileloader.cpp index 0b0958423..e42226a4d 100644 --- a/src/qmmpui/fileloader.cpp +++ b/src/qmmpui/fileloader.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2006-2015 by Ilya Kotov * + * Copyright (C) 2006-2017 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -26,6 +26,7 @@ #include "fileloader_p.h" #include "qmmpuisettings.h" #include "playlistitem.h" +#include "playlistparser.h" #include "playlisttrack.h" FileLoader::FileLoader(QObject *parent) : QThread(parent) @@ -53,6 +54,62 @@ QList<PlayListTrack *> FileLoader::processFile(const QString &path, QStringList return tracks; } +void FileLoader::insertPlayList(const QString &fmt, const QByteArray &contents, PlayListItem *before) +{ + QList<PlayListTrack *> tracks = PlayListParser::loadPlaylist(fmt, contents); + + while (!tracks.isEmpty() && !m_finished) + { + PlayListTrack *t = tracks.takeFirst(); + QList <FileInfo *> infoList = MetaDataManager::instance()->createPlayList(t->url(), m_use_meta); + if(infoList.count() != 1) //invalid or unsupported track + { + qDeleteAll(infoList); + infoList.clear(); + delete t; + continue; + } + + FileInfo *info = infoList.first(); + if(!info->metaData(Qmmp::ALBUM).isEmpty() && !info->metaData(Qmmp::ARTIST).isEmpty()) + t->updateMetaData(infoList.first()); + + emit newTracksToInsert(before, QList<PlayListTrack *>() << t); + delete info; + } + //clear remaining tracks + qDeleteAll(tracks); + tracks.clear(); +} + +void FileLoader::insertPlayList(const QString &path, PlayListItem *before) +{ + QList<PlayListTrack *> tracks = PlayListParser::loadPlaylist(path); + + while (!tracks.isEmpty() && !m_finished) + { + PlayListTrack *t = tracks.takeFirst(); + QList <FileInfo *> infoList = MetaDataManager::instance()->createPlayList(t->url(), m_use_meta); + if(infoList.count() != 1) //invalid or unsupported track + { + qDeleteAll(infoList); + infoList.clear(); + delete t; + continue; + } + + FileInfo *info = infoList.first(); + if(!info->metaData(Qmmp::ALBUM).isEmpty() && !info->metaData(Qmmp::ARTIST).isEmpty()) + t->updateMetaData(infoList.first()); + + emit newTracksToInsert(before, QList<PlayListTrack *>() << t); + delete info; + } + //clear remaining tracks + qDeleteAll(tracks); + tracks.clear(); +} + void FileLoader::addDirectory(const QString& s, PlayListItem *before) { QList<PlayListTrack *> tracks; @@ -127,20 +184,31 @@ void FileLoader::run() PlayListItem *before = i.before; QString path = i.path; - QFileInfo info(path); - - if(info.isDir()) + if(!path.isEmpty()) { - addDirectory(path, before); + QFileInfo info(path); - } - else if(info.isFile() || path.contains("://")) - { - QList<PlayListTrack *> tracks = processFile(path); - if(!tracks.isEmpty()) + if(info.isDir()) + { + addDirectory(path, before); + + } + else if(info.isFile() && PlayListParser::isPlayList(path)) { - emit newTracksToInsert(before, tracks); + insertPlayList(path, before); } + else if(info.isFile() || path.contains("://")) + { + QList<PlayListTrack *> tracks = processFile(path); + if(!tracks.isEmpty()) + { + emit newTracksToInsert(before, tracks); + } + } + } + else if(!i.playListContent.isEmpty() && !i.playListFormat.isEmpty()) + { + insertPlayList(i.playListFormat, i.playListContent, before); } m_mutex.lock(); @@ -163,6 +231,25 @@ void FileLoader::add(const QStringList &paths) insert(0, paths); } +void FileLoader::addPlayList(const QString &fmt, const QByteArray &data) +{ + m_mutex.lock(); + LoaderTask task; + task.before = 0; + task.playListFormat = fmt; + task.playListContent = data; + m_tasks.append(task); + m_mutex.unlock(); + if(!isRunning()) + { + MetaDataManager::instance()->prepareForAnotherThread(); + PlayListParser::loadFormats(); + m_filters = MetaDataManager::instance()->nameFilters(); + m_use_meta = m_settings->useMetadata(); + } + start(QThread::IdlePriority); +} + void FileLoader::insert(PlayListItem *before, const QString &path) { insert(before, QStringList() << path); diff --git a/src/qmmpui/fileloader_p.h b/src/qmmpui/fileloader_p.h index 1dd0a823b..58f771ff7 100644 --- a/src/qmmpui/fileloader_p.h +++ b/src/qmmpui/fileloader_p.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2006-2015 by Ilya Kotov * + * Copyright (C) 2006-2017 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -61,10 +61,12 @@ public: * Sets files/directories to load */ void add(const QStringList &paths); + void addPlayList(const QString &fmt, const QByteArray &data); void insert(PlayListItem *before, const QString &path); void insert(PlayListItem *before, const QStringList &paths); + public slots: /*! * Removes files and directories from queue and waits until thread is finished @@ -81,6 +83,8 @@ signals: private: void run(); QList<PlayListTrack*> processFile(const QString &path, QStringList *ignoredPaths = 0); + void insertPlayList(const QString &fmt, const QByteArray &contents, PlayListItem *before); + void insertPlayList(const QString &path, PlayListItem *before); void addDirectory(const QString &s, PlayListItem *before = 0); bool checkRestrictFilters(const QFileInfo &info); bool checkExcludeFilters(const QFileInfo &info); @@ -89,6 +93,8 @@ private: { QString path; PlayListItem *before; + QString playListFormat; + QByteArray playListContent; }; QQueue <LoaderTask> m_tasks; QStringList m_filters; diff --git a/src/qmmpui/playlistdownloader.cpp b/src/qmmpui/playlistdownloader.cpp index a88f18460..951877258 100644 --- a/src/qmmpui/playlistdownloader.cpp +++ b/src/qmmpui/playlistdownloader.cpp @@ -46,11 +46,13 @@ PlayListDownloader::PlayListDownloader(QObject *parent) : QObject(parent) } } -void PlayListDownloader::start(const QUrl &url) +void PlayListDownloader::start(const QUrl &url, PlayListModel *model) { + m_model = model; if(!PlayListParser::findByUrl(url)) //is it playlist? { - emit done(QStringList() << QString::fromLatin1(url.toEncoded())); //just send initial URL + m_model->add(url.toString()); + emit finished(true); return; } m_url = url; @@ -71,7 +73,7 @@ void PlayListDownloader::readResponse(QNetworkReply *reply) if(reply->error() != QNetworkReply::NoError) { - emit error(reply->errorString() + " (" + reply->error() + ")"); + emit finished(false, reply->errorString() + " (" + reply->error() + ")"); reply->deleteLater(); return; } @@ -93,6 +95,13 @@ void PlayListDownloader::readResponse(QNetworkReply *reply) if(reply == m_getReply) { m_getReply = 0; + + if(m_model.isNull()) + { + emit finished(true); + return; + } + QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); qDebug("PlayListDownloader: content type: %s", qPrintable(contentType)); PlayListFormat *fmt = PlayListParser::findByMime(contentType); @@ -100,12 +109,12 @@ void PlayListDownloader::readResponse(QNetworkReply *reply) fmt = PlayListParser::findByUrl(m_url); if(fmt) { - QStringList list = fmt->decode(QString::fromUtf8(reply->readAll())); - emit done(list); + m_model->loadPlaylist(fmt->properties().shortName, reply->readAll()); + emit finished(true); } else { - emit error(tr("Unsupported playlist format")); + emit finished(false, tr("Unsupported playlist format")); } } reply->deleteLater(); diff --git a/src/qmmpui/playlistdownloader.h b/src/qmmpui/playlistdownloader.h index df0a612db..ef044ab6c 100644 --- a/src/qmmpui/playlistdownloader.h +++ b/src/qmmpui/playlistdownloader.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012 by Ilya Kotov * + * Copyright (C) 2012-2017 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -23,6 +23,8 @@ #include <QObject> #include <QUrl> +#include <QPointer> +#include "playlistmodel.h" class QNetworkAccessManager; class QNetworkReply; @@ -41,23 +43,19 @@ public: signals: /*! - * Emitted when downloading is finished without errors. - * @param urls A list of extracted URLs or argument of the \b PlayListDownloader::start() - * function if remote URL doesn't contain playlist. - */ - void done(const QStringList &urls); - /*! - * Emitted when downloading is finished with error. + * Emitted when downloading is finished. + * @param ok Result of downloading (if an error occurs, \b ok is set to \b false; otherwise \b ok is set to \b true). * @param message Error message. */ - void error(const QString &message); + void finished(bool ok, const QString &message = QString()); public slots: /*! - * Starts playlist downloading - * @param url URL of remote playlist + * Starts playlist downloading. + * @param url URL of remote playlist. + * @param model Destination playlist model. */ - void start(const QUrl &url); + void start(const QUrl &url, PlayListModel *model); private slots: void readResponse(QNetworkReply *reply); @@ -67,6 +65,7 @@ private: QUrl m_redirect_url, m_url; QNetworkReply *m_getReply; QByteArray m_ua; + QPointer<PlayListModel> m_model; }; #endif // PLAYLISTDOWNLOADER_H diff --git a/src/qmmpui/playlistformat.h b/src/qmmpui/playlistformat.h index 06927cfbf..16e23a57d 100644 --- a/src/qmmpui/playlistformat.h +++ b/src/qmmpui/playlistformat.h @@ -53,13 +53,13 @@ public: * Takes raw contents of playlist file, should return string list of * ready file pathes to fill the playlist. */ - virtual QStringList decode(const QString& contents) = 0; + virtual QList<PlayListTrack*> decode(const QByteArray &contents) = 0; /*! - * Takes the list of AbstractPlaylistItem objects, should return string of + * Takes the list of \b PlayListTrack objects, should return content of * encoded playlist file. - * @param dir Playlist file path (May be used to adjust playlist content). + * @param path Playlist file path (May be used to adjust playlist content). */ - virtual QString encode(const QList<PlayListTrack*>& contents, const QString &path) = 0; + virtual QByteArray encode(const QList<PlayListTrack*> &contents, const QString &path) = 0; }; Q_DECLARE_INTERFACE(PlayListFormat,"PlayListFormat/1.0") diff --git a/src/qmmpui/playlistmodel.cpp b/src/qmmpui/playlistmodel.cpp index 20e82650e..7fe8daacb 100644 --- a/src/qmmpui/playlistmodel.cpp +++ b/src/qmmpui/playlistmodel.cpp @@ -143,26 +143,12 @@ void PlayListModel::add(QList<PlayListTrack *> tracks) void PlayListModel::add(const QString &path) { - QStringList paths = PlayListParser::loadPlaylist(path); - if(paths.isEmpty()) - m_loader->add(path); - else - m_loader->add(paths); + m_loader->add(path); } void PlayListModel::add(const QStringList &paths) { - QStringList urls, pl_urls; - foreach(QString path, paths) - { - pl_urls = PlayListParser::loadPlaylist(path); //is it playlist? - if(pl_urls.isEmpty()) - urls.append(path); - else - urls.append(pl_urls); - - } - m_loader->add(urls); + m_loader->add(paths); } void PlayListModel::insert(int index, PlayListTrack *track) @@ -242,11 +228,7 @@ void PlayListModel::insert(int index, const QStringList &paths) else { PlayListItem *before = m_container->item(index); - - QStringList list = paths; - foreach (QString path, paths) - list.append(PlayListParser::loadPlaylist(path)); - m_loader->insert(before, list); + m_loader->insert(before, paths); } } @@ -945,8 +927,12 @@ void PlayListModel::doCurrentVisibleRequest() void PlayListModel::loadPlaylist(const QString &f_name) { - QStringList list = PlayListParser::loadPlaylist(f_name); - m_loader->add(list); + m_loader->add(f_name); +} + +void PlayListModel::loadPlaylist(const QString &fmt, const QByteArray &data) +{ + m_loader->addPlayList(fmt, data); } void PlayListModel::savePlaylist(const QString &f_name) diff --git a/src/qmmpui/playlistmodel.h b/src/qmmpui/playlistmodel.h index f7d393162..37fe5497d 100644 --- a/src/qmmpui/playlistmodel.h +++ b/src/qmmpui/playlistmodel.h @@ -287,6 +287,8 @@ public: * Loads playlist with \b f_name name. */ void loadPlaylist(const QString& f_name); + + void loadPlaylist(const QString &fmt, const QByteArray &data); /*! * Saves current songs to the playlist with \b f_name name. */ diff --git a/src/qmmpui/playlistparser.cpp b/src/qmmpui/playlistparser.cpp index 70c3c3fe9..736a400cc 100644 --- a/src/qmmpui/playlistparser.cpp +++ b/src/qmmpui/playlistparser.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2014 by Ilya Kotov * + * Copyright (C) 2008-2017 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -23,7 +23,6 @@ #include <QList> #include <QDir> #include <QApplication> -#include <QTextStream> #include <qmmp/qmmp.h> #include "playlistformat.h" #include "playlistparser.h" @@ -32,13 +31,13 @@ QList<PlayListFormat*> *PlayListParser::m_formats = 0; QList<PlayListFormat *> PlayListParser::formats() { - checkFormats(); + loadFormats(); return *m_formats; } QStringList PlayListParser::nameFilters() { - checkFormats(); + loadFormats(); QStringList filters; foreach(PlayListFormat* format, *m_formats) { @@ -47,9 +46,20 @@ QStringList PlayListParser::nameFilters() return filters; } +bool PlayListParser::isPlayList(const QString &url) +{ + foreach (QString filter, nameFilters()) + { + QRegExp r(filter, Qt::CaseInsensitive, QRegExp::Wildcard); + if(r.exactMatch(url)) + return true; + } + return false; +} + PlayListFormat *PlayListParser::findByMime(const QString &mime) { - checkFormats(); + loadFormats(); foreach(PlayListFormat* format, *m_formats) { if(format->properties().contentTypes.contains(mime)) @@ -60,7 +70,7 @@ PlayListFormat *PlayListParser::findByMime(const QString &mime) PlayListFormat *PlayListParser::findByPath(const QString &filePath) { - checkFormats(); + loadFormats(); foreach(PlayListFormat* format, *m_formats) { foreach(QString filter, format->properties().filters) @@ -89,50 +99,65 @@ void PlayListParser::savePlayList(QList<PlayListTrack *> tracks, const QString & QFile file(f_name); if (file.open(QIODevice::WriteOnly)) { - QTextStream ts(&file); - ts << prs->encode(tracks, QFileInfo(f_name).canonicalFilePath()); + file.write(prs->encode(tracks, QFileInfo(f_name).canonicalFilePath())); file.close(); } else qWarning("PlayListParser: unable to save playlist, error: %s", qPrintable(file.errorString())); } -QStringList PlayListParser::loadPlaylist(const QString &f_name) +QList<PlayListTrack *> PlayListParser::loadPlaylist(const QString &f_name) { - QStringList list; if(!QFile::exists(f_name)) - return list; + return QList<PlayListTrack *>(); PlayListFormat* prs = PlayListParser::findByPath(f_name); if(!prs) - return list; + return QList<PlayListTrack *>(); QFile file(f_name); if (!file.open(QIODevice::ReadOnly)) { qWarning("PlayListParser: unable to open playlist, error: %s", qPrintable(file.errorString())); - return list; + return QList<PlayListTrack *>(); } - list = prs->decode(QTextStream(&file).readAll()); - if(list.isEmpty()) + QList <PlayListTrack*> tracks = prs->decode(file.readAll()); + + if(tracks.isEmpty()) + { qWarning("PlayListParser: error opening %s",qPrintable(f_name)); + return tracks; + } - for (int i = 0; i < list.size(); ++i) + QString url; + foreach (PlayListTrack *t, tracks) { - if(list.at(i).contains("://")) + url = t->value(Qmmp::URL); + + if(url.contains("://")) continue; - if (QFileInfo(list.at(i)).isRelative()) - list[i].prepend(QFileInfo(f_name).canonicalPath () + "/"); + if(QFileInfo(url).isRelative()) + url.prepend(QFileInfo(f_name).canonicalPath () + "/"); - list[i].replace("\\","/"); - list[i].replace("//","/"); + url.replace("\\","/"); + url.replace("//","/"); + t->insert(Qmmp::URL, url); + } + return tracks; +} + +QList<PlayListTrack *> PlayListParser::loadPlaylist(const QString &fmt, const QByteArray &contents) +{ + foreach (PlayListFormat *p, *m_formats) + { + if(p->properties().shortName == fmt) + return p->decode(contents); } - file.close(); - return list; + return QList<PlayListTrack *>(); } -void PlayListParser::checkFormats() +void PlayListParser::loadFormats() { if (m_formats) return; diff --git a/src/qmmpui/playlistparser.h b/src/qmmpui/playlistparser.h index c8e779b84..c171116e0 100644 --- a/src/qmmpui/playlistparser.h +++ b/src/qmmpui/playlistparser.h @@ -39,6 +39,9 @@ public: * Returns a list of the supported files name filters, i.e. "*.m3u *.pls" */ static QStringList nameFilters(); + + static bool isPlayList(const QString &url); + /*! * Returns PlayListFormat pointer which supports mime type \b mime * or \b 0 if mime type \b mime is unsupported @@ -59,17 +62,23 @@ public: * @param tracks A list of tracks. * @param f_name File name the playlist. */ - static void savePlayList(QList <PlayListTrack *> tracks, const QString &f_name); + static void savePlayList(QList<PlayListTrack *> tracks, const QString &f_name); /*! * Loads playlist from file \b f_name * @param f_name File name. * @return A list of URLs or file paths. */ - static QStringList loadPlaylist(const QString &f_name); + static QList<PlayListTrack *> loadPlaylist(const QString &f_name); + + static QList<PlayListTrack *> loadPlaylist(const QString &fmt, const QByteArray &contents); + /*! + * Loads all playlist plugins. Should be called before usage from another thread. + */ + static void loadFormats(); private: PlayListParser(){} - static void checkFormats(); + static QList<PlayListFormat*> *m_formats; diff --git a/src/qmmpui/playlisttrack.cpp b/src/qmmpui/playlisttrack.cpp index fc9c139e9..917e95399 100644 --- a/src/qmmpui/playlisttrack.cpp +++ b/src/qmmpui/playlisttrack.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2015 by Ilya Kotov * + * Copyright (C) 2008-2017 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -59,7 +59,7 @@ PlayListTrack::PlayListTrack(FileInfo *info) : QMap<Qmmp::MetaData, QString>(in m_track_index = -1; m_settings = QmmpUiSettings::instance(); m_helper = MetaDataHelper::instance(); - setLength(m_length = info->length()); + m_length = info->length(); insert(Qmmp::URL, info->path()); m_refCount = 0; m_sheduledForDeletion = false; @@ -78,17 +78,23 @@ void PlayListTrack::updateMetaData(const QMap <Qmmp::MetaData, QString> &metaDat formatGroup(); } +void PlayListTrack::updateMetaData(FileInfo *info) +{ + m_length = info->length(); + QMap <Qmmp::MetaData, QString>::operator =(info->metaData()); + insert(Qmmp::URL, info->path()); + m_formattedTitles.clear(); + m_formattedLength.clear(); + formatGroup(); +} + void PlayListTrack::updateMetaData() { QList <FileInfo *> list = MetaDataManager::instance()->createPlayList(value(Qmmp::URL)); if(!list.isEmpty() && !list.at(0)->path().contains("://")) { FileInfo *info = list.at(0); - m_length = info->length(); - QMap <Qmmp::MetaData, QString>::operator =(info->metaData()); - insert(Qmmp::URL, info->path()); - m_formattedTitles.clear(); - formatGroup(); + updateMetaData(info); } qDeleteAll(list); } @@ -223,7 +229,7 @@ qint64 PlayListTrack::length() const void PlayListTrack::setLength(qint64 length) { - m_length = length; + m_length = qMax(length, 0LL); m_formattedLength.clear(); } diff --git a/src/qmmpui/playlisttrack.h b/src/qmmpui/playlisttrack.h index 2c832f616..97674f0fd 100644 --- a/src/qmmpui/playlisttrack.h +++ b/src/qmmpui/playlisttrack.h @@ -84,6 +84,11 @@ public: */ void updateMetaData(const QMap <Qmmp::MetaData, QString> &metaData); /*! + * Updates current metadata. + * @param info Media file information. + */ + void updateMetaData(FileInfo *info); + /*! * Gets new metadata from file (works for local files only). */ void updateMetaData(); |
