/* * Cantata * * Copyright (c) 2011-2012 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "streamfetcher.h" #include "networkaccessmanager.h" #include "mpdconnection.h" #include "httpserver.h" #include #include #include #include static const int constMaxRedirects = 3; static const int constMaxData = 12 * 1024; static QString parsePlaylist(const QByteArray &data, const QString &key, const QStringList &handlers) { QStringList lines=QString(data).split('\n', QString::SkipEmptyParts); foreach (QString line, lines) { if (line.startsWith(key, Qt::CaseInsensitive)) { foreach (const QString &handler, handlers) { QString protocol(handler+QLatin1String("://")); int index=line.indexOf(protocol, Qt::CaseInsensitive); if (index>-1 && index<7) { line.remove('\n'); line.remove('\r'); return line.mid(index+1); } } } } return QString(); } static QString parseExt3Mu(const QByteArray &data, const QStringList &handlers) { QStringList lines=QString(data).split(QRegExp(QLatin1String("(\r\n|\n|\r)")), QString::SkipEmptyParts); foreach (QString line, lines) { foreach (const QString &handler, handlers) { QString protocol(handler+QLatin1String("://")); if (line.startsWith(protocol, Qt::CaseInsensitive)) { line.remove('\n'); line.remove('\r'); return line; } } } return QString(); } static QString parseAsx(const QByteArray &data, const QStringList &handlers) { QStringList lines=QString(data).split(QRegExp(QLatin1String("(\r\n|\n|\r|/>)")), QString::SkipEmptyParts); foreach (QString line, lines) { int ref=line.indexOf(QLatin1String("10 && !strncasecmp(data.constData(), "[playlist]", 10)) { return parsePlaylist(data, QLatin1String("File"), handlers); } else if (data.length()>7 && (!strncasecmp(data.constData(), "#EXTM3U", 7) || !strncasecmp(data.constData(), "http://", 7))) { return parseExt3Mu(data, handlers); } else if (data.length()>5 && !strncasecmp(data.constData(), "11 && !strncasecmp(data.constData(), "[reference]", 11)) { return parsePlaylist(data, QLatin1String("Ref"), handlers); } else if (data.length()>5 && !strncasecmp(data.constData(), "isOurs(current)) { data.clear(); job=manager->get(u); connect(job, SIGNAL(readyRead()), this, SLOT(dataReady())); return; } else { done.append(current); } } if (todo.isEmpty() && !done.isEmpty()) { emit result(done, row); } } void StreamFetcher::cancel() { todo.clear(); done.clear(); row=0; data.clear(); current=QString(); if (job) { disconnect(job, SIGNAL(readyRead()), this, SLOT(dataReady())); job->abort(); } job=0; } void StreamFetcher::urlHandlers(const QStringList &uh) { handlers=uh; handlers.removeAll("file"); } void StreamFetcher::dataReady() { data+=job->readAll(); if (data.count()>constMaxData) { QNetworkReply *thisJob=job; disconnect(thisJob, SIGNAL(readyRead()), this, SLOT(dataReady())); jobFinished(thisJob); thisJob->abort(); } } void StreamFetcher::jobFinished(QNetworkReply *reply) { // We only handle 1 job at a time! if (reply==job) { if (!reply->error()) { QVariant redirect = reply->header(QNetworkRequest::LocationHeader); if (redirect.isValid() && ++redirectsdeleteLater(); current=redirect.toString(); data.clear(); job=manager->get(current); connect(job, SIGNAL(readyRead()), this, SLOT(dataReady())); } else { QString u=parse(data, handlers); if (u.isEmpty() || u==current) { done.append(current); } else if (u.startsWith(QLatin1String("http://")) && ++redirectsget(u); connect(job, SIGNAL(readyRead()), this, SLOT(dataReady())); } else { done.append(u); } } } else { done.append(current); } doNext(); } }