Fix remote-playlist load

This commit is contained in:
santigl 2017-03-16 23:23:32 -03:00 committed by John Maguire
parent 33aa38bbdd
commit f609bc793f
5 changed files with 110 additions and 16 deletions

View File

@ -34,6 +34,7 @@
#include "config.h" #include "config.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/player.h" #include "core/player.h"
#include "core/utilities.h"
#include "core/signalchecker.h" #include "core/signalchecker.h"
#include "core/song.h" #include "core/song.h"
#include "core/tagreaderclient.h" #include "core/tagreaderclient.h"
@ -115,6 +116,11 @@ SongLoader::Result SongLoader::Load(const QUrl& url) {
return Success; return Success;
} }
// It could be a playlist, we give it a shot.
if (LoadRemotePlaylist(url_)) {
return Success;
}
url_ = PodcastUrlLoader::FixPodcastUrl(url_); url_ = PodcastUrlLoader::FixPodcastUrl(url_);
preload_func_ = std::bind(&SongLoader::LoadRemote, this); preload_func_ = std::bind(&SongLoader::LoadRemote, this);
@ -144,10 +150,10 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString& filename) {
SongLoader::Result SongLoader::LoadAudioCD() { SongLoader::Result SongLoader::LoadAudioCD() {
#ifdef HAVE_AUDIOCD #ifdef HAVE_AUDIOCD
CddaSongLoader* cdda_song_loader = new CddaSongLoader; CddaSongLoader* cdda_song_loader = new CddaSongLoader;
connect(cdda_song_loader, SIGNAL(SongsDurationLoaded(SongList)), connect(cdda_song_loader, SIGNAL(SongsDurationLoaded(SongList)), this,
this, SLOT(AudioCDTracksLoadedSlot(SongList))); SLOT(AudioCDTracksLoadedSlot(SongList)));
connect(cdda_song_loader, SIGNAL(SongsMetadataLoaded(SongList)), connect(cdda_song_loader, SIGNAL(SongsMetadataLoaded(SongList)), this,
this, SLOT(AudioCDTracksTagsLoaded(SongList))); SLOT(AudioCDTracksTagsLoaded(SongList)));
cdda_song_loader->LoadSongs(); cdda_song_loader->LoadSongs();
return Success; return Success;
#else // HAVE_AUDIOCD #else // HAVE_AUDIOCD
@ -194,8 +200,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString& filename) {
} }
// It's not in the database, load it asynchronously. // It's not in the database, load it asynchronously.
preload_func_ = preload_func_ = std::bind(&SongLoader::LoadLocalAsync, this, filename);
std::bind(&SongLoader::LoadLocalAsync, this, filename);
return BlockingLoadRequired; return BlockingLoadRequired;
} }
@ -217,8 +222,8 @@ void SongLoader::LoadLocalAsync(const QString& filename) {
if (!parser) { if (!parser) {
// Check the file extension as well, maybe the magic failed, or it was a // Check the file extension as well, maybe the magic failed, or it was a
// basic M3U file which is just a plain list of filenames. // basic M3U file which is just a plain list of filenames.
parser = playlist_parser_-> parser = playlist_parser_->ParserForExtension(
ParserForExtension(QFileInfo(filename).suffix().toLower()); QFileInfo(filename).suffix().toLower());
} }
if (parser) { if (parser) {
@ -410,8 +415,7 @@ void SongLoader::LoadRemote() {
// Add a probe to the sink so we can capture the data if it's a playlist // Add a probe to the sink so we can capture the data if it's a playlist
GstPad* pad = gst_element_get_static_pad(fakesink, "sink"); GstPad* pad = gst_element_get_static_pad(fakesink, "sink");
gst_pad_add_probe( gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, &DataReady, this, NULL);
pad, GST_PAD_PROBE_TYPE_BUFFER, &DataReady, this, NULL);
gst_object_unref(pad); gst_object_unref(pad);
QEventLoop loop; QEventLoop loop;
@ -447,12 +451,11 @@ void SongLoader::TypeFound(GstElement*, uint, GstCaps* caps, void* self) {
instance->StopTypefindAsync(true); instance->StopTypefindAsync(true);
} }
GstPadProbeReturn SongLoader::DataReady( GstPadProbeReturn SongLoader::DataReady(GstPad*, GstPadProbeInfo* info,
GstPad*, GstPadProbeInfo* info, gpointer self) { gpointer self) {
SongLoader* instance = reinterpret_cast<SongLoader*>(self); SongLoader* instance = reinterpret_cast<SongLoader*>(self);
if (instance->state_ == Finished) if (instance->state_ == Finished) return GST_PAD_PROBE_OK;
return GST_PAD_PROBE_OK;
GstBuffer* buffer = gst_pad_probe_info_get_buffer(info); GstBuffer* buffer = gst_pad_probe_info_get_buffer(info);
GstMapInfo map; GstMapInfo map;
@ -617,3 +620,64 @@ void SongLoader::StopTypefindAsync(bool success) {
metaObject()->invokeMethod(this, "StopTypefind", Qt::QueuedConnection); metaObject()->invokeMethod(this, "StopTypefind", Qt::QueuedConnection);
} }
bool SongLoader::LoadRemotePlaylist(const QUrl& url) {
// This function makes a remote request for the given URL and, if its MIME
// type corresponds to a known playlist type, saves the content to a
// temporary file, loads it, and returns true.
// If the URL does not point to a playlist file we could handle,
// it returns false.
NetworkAccessManager manager;
QNetworkRequest req = QNetworkRequest(url);
// Getting headers:
QNetworkReply* reply = manager.head(req);
{
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
}
if (reply->error() != QNetworkReply::NoError) {
qLog(Debug) << url.toString() << reply->errorString();
return false;
}
// Now we check if there is a parser that can handle that MIME type.
QString mime_type =
reply->header(QNetworkRequest::ContentTypeHeader).toString();
ParserBase* parser = playlist_parser_->ParserForMimeType(mime_type);
if (parser == nullptr) {
qLog(Debug) << url.toString() << "seems to not be a playlist";
return false;
}
// We know it is a playlist!
// Getting its contents:
reply = manager.get(req);
{
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
}
if (reply->error() != QNetworkReply::NoError) {
qLog(Debug) << url.toString() << reply->errorString();
return false;
}
// Save them to a temporary file...
QString playlist_filename = Utilities::GetTemporaryFileName();
QFile file(playlist_filename);
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
qLog(Debug) << url.toString() << "with MIME" << mime_type << "saved to"
<< playlist_filename;
// ...and load it.
LoadPlaylist(parser, playlist_filename);
file.remove();
return true;
}

View File

@ -79,7 +79,7 @@ class SongLoader : public QObject {
void LoadMetadataBlocking(); void LoadMetadataBlocking();
Result LoadAudioCD(); Result LoadAudioCD();
signals: signals:
void AudioCDTracksLoaded(); void AudioCDTracksLoaded();
void LoadAudioCDFinished(bool success); void LoadAudioCDFinished(bool success);
void LoadRemoteFinished(); void LoadRemoteFinished();
@ -93,7 +93,12 @@ class SongLoader : public QObject {
#endif // HAVE_AUDIOCD #endif // HAVE_AUDIOCD
private: private:
enum State { WaitingForType, WaitingForMagic, WaitingForData, Finished, }; enum State {
WaitingForType,
WaitingForMagic,
WaitingForData,
Finished,
};
Result LoadLocal(const QString& filename); Result LoadLocal(const QString& filename);
void LoadLocalAsync(const QString& filename); void LoadLocalAsync(const QString& filename);
@ -105,6 +110,7 @@ class SongLoader : public QObject {
void AddAsRawStream(); void AddAsRawStream();
void LoadRemote(); void LoadRemote();
bool LoadRemotePlaylist(const QUrl& url);
// GStreamer callbacks // GStreamer callbacks
static void TypeFound(GstElement* typefind, uint probability, GstCaps* caps, static void TypeFound(GstElement* typefind, uint probability, GstCaps* caps,

View File

@ -53,6 +53,17 @@ QStringList PlaylistParser::file_extensions() const {
return ret; return ret;
} }
QStringList PlaylistParser::mime_types() const {
QStringList ret;
for (ParserBase* parser : parsers_) {
if (!parser->mime_type().isEmpty()) ret << parser->mime_type();
}
qStableSort(ret);
return ret;
}
QString PlaylistParser::filters() const { QString PlaylistParser::filters() const {
QStringList filters; QStringList filters;
QStringList all_extensions; QStringList all_extensions;
@ -91,6 +102,15 @@ ParserBase* PlaylistParser::ParserForExtension(const QString& suffix) const {
return nullptr; return nullptr;
} }
ParserBase* PlaylistParser::ParserForMimeType(const QString& mime_type) const {
for (ParserBase* p : parsers_) {
if (!p->mime_type().isEmpty() &&
(QString::compare(p->mime_type(), mime_type, Qt::CaseInsensitive) == 0))
return p;
}
return nullptr;
}
ParserBase* PlaylistParser::ParserForMagic(const QByteArray& data, ParserBase* PlaylistParser::ParserForMagic(const QByteArray& data,
const QString& mime_type) const { const QString& mime_type) const {
for (ParserBase* p : parsers_) { for (ParserBase* p : parsers_) {

View File

@ -38,12 +38,15 @@ class PlaylistParser : public QObject {
QStringList file_extensions() const; QStringList file_extensions() const;
QString filters() const; QString filters() const;
QStringList mime_types() const;
QString default_extension() const; QString default_extension() const;
QString default_filter() const; QString default_filter() const;
ParserBase* ParserForMagic(const QByteArray& data, ParserBase* ParserForMagic(const QByteArray& data,
const QString& mime_type = QString()) const; const QString& mime_type = QString()) const;
ParserBase* ParserForExtension(const QString& suffix) const; ParserBase* ParserForExtension(const QString& suffix) const;
ParserBase* ParserForMimeType(const QString& mime) const;
SongList LoadFromFile(const QString& filename) const; SongList LoadFromFile(const QString& filename) const;
SongList LoadFromDevice(QIODevice* device, SongList LoadFromDevice(QIODevice* device,

View File

@ -28,6 +28,7 @@ class PLSParser : public ParserBase {
QString name() const { return "PLS"; } QString name() const { return "PLS"; }
QStringList file_extensions() const { return QStringList() << "pls"; } QStringList file_extensions() const { return QStringList() << "pls"; }
QString mime_type() const { return "audio/x-scpls"; }
bool TryMagic(const QByteArray& data) const; bool TryMagic(const QByteArray& data) const;