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 "core/logging.h"
#include "core/player.h"
#include "core/utilities.h"
#include "core/signalchecker.h"
#include "core/song.h"
#include "core/tagreaderclient.h"
@ -115,6 +116,11 @@ SongLoader::Result SongLoader::Load(const QUrl& url) {
return Success;
}
// It could be a playlist, we give it a shot.
if (LoadRemotePlaylist(url_)) {
return Success;
}
url_ = PodcastUrlLoader::FixPodcastUrl(url_);
preload_func_ = std::bind(&SongLoader::LoadRemote, this);
@ -144,10 +150,10 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString& filename) {
SongLoader::Result SongLoader::LoadAudioCD() {
#ifdef HAVE_AUDIOCD
CddaSongLoader* cdda_song_loader = new CddaSongLoader;
connect(cdda_song_loader, SIGNAL(SongsDurationLoaded(SongList)),
this, SLOT(AudioCDTracksLoadedSlot(SongList)));
connect(cdda_song_loader, SIGNAL(SongsMetadataLoaded(SongList)),
this, SLOT(AudioCDTracksTagsLoaded(SongList)));
connect(cdda_song_loader, SIGNAL(SongsDurationLoaded(SongList)), this,
SLOT(AudioCDTracksLoadedSlot(SongList)));
connect(cdda_song_loader, SIGNAL(SongsMetadataLoaded(SongList)), this,
SLOT(AudioCDTracksTagsLoaded(SongList)));
cdda_song_loader->LoadSongs();
return Success;
#else // HAVE_AUDIOCD
@ -194,8 +200,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString& filename) {
}
// It's not in the database, load it asynchronously.
preload_func_ =
std::bind(&SongLoader::LoadLocalAsync, this, filename);
preload_func_ = std::bind(&SongLoader::LoadLocalAsync, this, filename);
return BlockingLoadRequired;
}
@ -217,8 +222,8 @@ void SongLoader::LoadLocalAsync(const QString& filename) {
if (!parser) {
// 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.
parser = playlist_parser_->
ParserForExtension(QFileInfo(filename).suffix().toLower());
parser = playlist_parser_->ParserForExtension(
QFileInfo(filename).suffix().toLower());
}
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
GstPad* pad = gst_element_get_static_pad(fakesink, "sink");
gst_pad_add_probe(
pad, GST_PAD_PROBE_TYPE_BUFFER, &DataReady, this, NULL);
gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, &DataReady, this, NULL);
gst_object_unref(pad);
QEventLoop loop;
@ -447,12 +451,11 @@ void SongLoader::TypeFound(GstElement*, uint, GstCaps* caps, void* self) {
instance->StopTypefindAsync(true);
}
GstPadProbeReturn SongLoader::DataReady(
GstPad*, GstPadProbeInfo* info, gpointer self) {
GstPadProbeReturn SongLoader::DataReady(GstPad*, GstPadProbeInfo* info,
gpointer self) {
SongLoader* instance = reinterpret_cast<SongLoader*>(self);
if (instance->state_ == Finished)
return GST_PAD_PROBE_OK;
if (instance->state_ == Finished) return GST_PAD_PROBE_OK;
GstBuffer* buffer = gst_pad_probe_info_get_buffer(info);
GstMapInfo map;
@ -617,3 +620,64 @@ void SongLoader::StopTypefindAsync(bool success) {
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();
Result LoadAudioCD();
signals:
signals:
void AudioCDTracksLoaded();
void LoadAudioCDFinished(bool success);
void LoadRemoteFinished();
@ -93,7 +93,12 @@ class SongLoader : public QObject {
#endif // HAVE_AUDIOCD
private:
enum State { WaitingForType, WaitingForMagic, WaitingForData, Finished, };
enum State {
WaitingForType,
WaitingForMagic,
WaitingForData,
Finished,
};
Result LoadLocal(const QString& filename);
void LoadLocalAsync(const QString& filename);
@ -105,6 +110,7 @@ class SongLoader : public QObject {
void AddAsRawStream();
void LoadRemote();
bool LoadRemotePlaylist(const QUrl& url);
// GStreamer callbacks
static void TypeFound(GstElement* typefind, uint probability, GstCaps* caps,

View File

@ -53,6 +53,17 @@ QStringList PlaylistParser::file_extensions() const {
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 {
QStringList filters;
QStringList all_extensions;
@ -91,6 +102,15 @@ ParserBase* PlaylistParser::ParserForExtension(const QString& suffix) const {
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,
const QString& mime_type) const {
for (ParserBase* p : parsers_) {

View File

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

View File

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