PageRenderTime 104ms CodeModel.GetById 41ms app.highlight 30ms RepoModel.GetById 30ms app.codeStats 0ms

/thirdparty/liblastfm2/src/radio/RadioTuner.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 219 lines | 148 code | 37 blank | 34 comment | 14 complexity | efe1f41755406f2abab737a26d632eb5 MD5 | raw file
  1/*
  2   Copyright 2009 Last.fm Ltd. 
  3      - Primarily authored by Max Howell, Jono Cole and Doug Mansell
  4
  5   This file is part of liblastfm.
  6
  7   liblastfm is free software: you can redistribute it and/or modify
  8   it under the terms of the GNU General Public License as published by
  9   the Free Software Foundation, either version 3 of the License, or
 10   (at your option) any later version.
 11
 12   liblastfm is distributed in the hope that it will be useful,
 13   but WITHOUT ANY WARRANTY; without even the implied warranty of
 14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15   GNU General Public License for more details.
 16
 17   You should have received a copy of the GNU General Public License
 18   along with liblastfm.  If not, see <http://www.gnu.org/licenses/>.
 19*/
 20
 21#include <QTimer>
 22
 23#include "RadioTuner.h"
 24#include "../core/XmlQuery.h"
 25#include "../types/Xspf.h"
 26#include "../ws/ws.h"
 27
 28using namespace lastfm;
 29
 30//TODO skips left
 31//TODO multiple locations for the same track
 32//TODO set rtp flag in getPlaylist (whether user is scrobbling this radio session or not)
 33
 34// limit the number of retries following empty playlists:
 35#define MAX_TUNING_ATTEMPTS 3
 36
 37
 38RadioTuner::RadioTuner( const RadioStation& station )
 39     : m_retry_counter( 0 ), m_fetchingPlaylist( false ), m_requestedPlaylist(false)
 40{
 41    m_twoSecondTimer = new QTimer( this );
 42    m_twoSecondTimer->setSingleShot( true );
 43    connect( m_twoSecondTimer, SIGNAL(timeout()), SLOT(onTwoSecondTimeout()));
 44
 45    qDebug() << station.url();
 46
 47    //Empty RadioStation implies that the radio
 48    //should tune to the previous station.
 49    if( station.url().isEmpty() )
 50    {
 51        fetchFiveMoreTracks();
 52    }
 53    else
 54    {
 55        QMap<QString, QString> map;
 56        map["method"] = "radio.tune";
 57        map["station"] = station.url();
 58        map["additional_info"] = "1";
 59        connect( ws::post(map), SIGNAL(finished()), SLOT(onTuneReturn()) );
 60    }
 61}
 62
 63void
 64RadioTuner::retune( const RadioStation& station )
 65{
 66    m_playlistQueue.clear();
 67    m_retuneStation = station;
 68
 69    qDebug() << station.url();
 70}
 71
 72
 73void
 74RadioTuner::onTuneReturn()
 75{
 76    try {
 77        XmlQuery lfm = qobject_cast<QNetworkReply*>(sender())->readAll();
 78        emit title( lfm["station"]["name"].text() );
 79
 80        qDebug() << lfm;
 81
 82        emit supportsDisco( lfm["station"]["supportsdiscovery"].text() == "1" );
 83        fetchFiveMoreTracks();
 84    }
 85    catch (ws::ParseError& e)
 86    {
 87        emit error( e.enumValue(), e.message() );
 88    }
 89}
 90
 91
 92void
 93RadioTuner::fetchFiveMoreTracks()
 94{
 95    if ( !m_retuneStation.url().isEmpty() )
 96    {
 97        // We have been asked to retune so do it now
 98        QMap<QString, QString> map;
 99        map["method"] = "radio.tune";
100        map["station"] = m_retuneStation.url();
101        map["additional_info"] = "1";
102
103        QNetworkReply* reply = ws::post(map);
104        connect( reply, SIGNAL(finished()), SLOT(onTuneReturn()) );
105
106        m_retuneStation = RadioStation();
107        m_twoSecondTimer->stop();
108    }
109    else
110    {
111        if ( !m_twoSecondTimer->isActive() )
112        {
113            //TODO check documentation, I figure this needs a session key
114            QMap<QString, QString> map;
115            map["method"] = "radio.getPlaylist";
116            map["additional_info"] = "1";
117            map["rtp"] = "1"; // see above
118            connect( ws::post( map ), SIGNAL(finished()), SLOT(onGetPlaylistReturn()) );
119            m_fetchingPlaylist = true;
120        }
121        else
122            m_requestedPlaylist = true;
123    }
124}
125
126
127bool
128RadioTuner::tryAgain()
129{
130    qDebug() << "Bad response count" << m_retry_counter;
131    
132    if (++m_retry_counter > MAX_TUNING_ATTEMPTS)
133        return false;
134    fetchFiveMoreTracks();
135    return true;
136}
137
138
139void
140RadioTuner::onGetPlaylistReturn()
141{   
142    // We shouldn't request another playlist for 2 seconds because we'll get the same one
143    // in a different order. This QTimer will block until it has finished. If one or more
144    // playlists have been requested in the meantime, it will fetch one on timeout
145    m_twoSecondTimer->start( 2000 );
146
147    // This will block us fetching two playlists at once
148    m_fetchingPlaylist = false;
149
150    try {
151        XmlQuery lfm = qobject_cast<QNetworkReply*>(sender())->readAll();
152        emit title( lfm["playlist"]["title"].text() );
153
154        qDebug() << lfm;
155
156        Xspf* xspf = new Xspf( lfm["playlist"], this );
157        connect( xspf, SIGNAL(expired()), SLOT(onXspfExpired()) );
158
159        if ( xspf->isEmpty() )
160        {
161            // give up after too many empty playlists  :(
162            if (!tryAgain())
163                emit error( ws::NotEnoughContent, tr("Not enough content") );
164        }
165        else
166        {
167            m_retry_counter = 0;
168            m_playlistQueue << xspf;
169            emit trackAvailable();
170        }
171    }
172    catch (ws::ParseError& e) 
173    {
174        qWarning() << e.what();
175        emit error( e.enumValue(), e.message() );
176    }
177}
178
179void
180RadioTuner::onTwoSecondTimeout()
181{
182    if (m_requestedPlaylist)
183    {
184        m_requestedPlaylist = false;
185        fetchFiveMoreTracks();
186    }
187}
188
189void
190RadioTuner::onXspfExpired()
191{
192    int index = m_playlistQueue.indexOf( static_cast<Xspf*>(sender()) );
193    if ( index != -1 )
194        m_playlistQueue.takeAt( index )->deleteLater();
195}
196
197Track
198RadioTuner::takeNextTrack()
199{
200    if ( m_playlistQueue.isEmpty() )
201    {
202        // If there are no tracks here and we're not fetching tracks
203        // it's probably because the playlist expired so fetch more now
204        if ( !m_fetchingPlaylist )
205            fetchFiveMoreTracks();
206
207        return Track();
208    }
209
210    Track result = m_playlistQueue[0]->takeFirst();
211
212    if ( m_playlistQueue[0]->isEmpty() )
213        m_playlistQueue.removeFirst();
214
215    if ( m_playlistQueue.isEmpty() )
216        fetchFiveMoreTracks();
217
218    return result;
219}