PageRenderTime 206ms CodeModel.GetById 20ms app.highlight 67ms RepoModel.GetById 61ms app.codeStats 0ms

/src/libtomahawk/playlist/dynamic/DynamicModel.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 350 lines | 257 code | 70 blank | 23 comment | 67 complexity | 931e0f95cb4f041b9ba8f42537657a7d MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
  4 *   Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
  5 *
  6 *   Tomahawk is free software: you can redistribute it and/or modify
  7 *   it under the terms of the GNU General Public License as published by
  8 *   the Free Software Foundation, either version 3 of the License, or
  9 *   (at your option) any later version.
 10 *
 11 *   Tomahawk is distributed in the hope that it will be useful,
 12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14 *   GNU General Public License for more details.
 15 *
 16 *   You should have received a copy of the GNU General Public License
 17 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 18 */
 19
 20#include "playlist/dynamic/DynamicModel.h"
 21
 22#include "DynamicPlaylist.h"
 23#include "GeneratorInterface.h"
 24#include "Pipeline.h"
 25#include "Query.h"
 26#include "Source.h"
 27#include "audio/AudioEngine.h"
 28
 29#include "utils/Logger.h"
 30
 31using namespace Tomahawk;
 32
 33
 34DynamicModel::DynamicModel( QObject* parent )
 35    : PlaylistModel( parent )
 36    , m_onDemandRunning( false )
 37    , m_changeOnNext( false )
 38    , m_searchingForNext( false )
 39    , m_filterUnresolvable( true )
 40    , m_startingAfterFailed( false )
 41    , m_currentAttempts( 0 )
 42    , m_lastResolvedRow( 0 )
 43{
 44
 45}
 46
 47
 48DynamicModel::~DynamicModel()
 49{
 50
 51}
 52
 53
 54void
 55DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool loadEntries )
 56{
 57    Q_UNUSED( loadEntries );
 58
 59    if ( !m_playlist.isNull() )
 60    {
 61        disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
 62    }
 63    const int oldCount = rowCount( QModelIndex() );
 64
 65    m_playlist = playlist;
 66
 67    m_deduper.clear();
 68    if ( m_playlist->mode() == OnDemand )
 69        setFilterUnresolvable( true );
 70
 71    connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
 72    PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static );
 73
 74    if ( m_playlist->mode() == OnDemand && oldCount != rowCount( QModelIndex() ) )
 75        emit itemCountChanged( rowCount( QModelIndex() ) );
 76}
 77
 78
 79QString
 80DynamicModel::description() const
 81{
 82    if ( !m_playlist.isNull() && !m_playlist->generator().isNull() )
 83        return m_playlist->generator()->sentenceSummary();
 84    else
 85        return QString();
 86}
 87
 88
 89void
 90DynamicModel::startOnDemand()
 91{
 92    connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
 93
 94    m_playlist->generator()->startOnDemand();
 95
 96    m_onDemandRunning = true;
 97}
 98
 99
100void
101DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query )
102{
103    if ( m_onDemandRunning )
104    {
105        bool isDuplicate = false;
106        for ( int i = 0; i < m_deduper.size(); i++ )
107        {
108            if ( m_deduper[ i ].first == query->track()->track() && m_deduper[ i ].second == query->track()->artist() )
109                isDuplicate = true;
110        }
111        if ( isDuplicate )
112        {
113            m_playlist->generator()->fetchNext();
114
115            return;
116        }
117        else
118        {
119            m_deduper.append( QPair< QString, QString >( query->track()->track(), query->track()->artist() ) );
120        }
121
122        connect( query.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolveFinished( bool ) ) );
123
124        m_waitingFor << query.data();
125        appendQuery( query );
126    }
127}
128
129
130void
131DynamicModel::stopOnDemand( bool stopPlaying )
132{
133    m_onDemandRunning = false;
134    if ( stopPlaying )
135        AudioEngine::instance()->stop();
136
137    disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
138}
139
140
141void
142DynamicModel::changeStation()
143{
144    if ( m_onDemandRunning )
145        m_changeOnNext = true;
146    else // if we're not running, just start
147        m_playlist->generator()->startOnDemand();
148}
149
150
151void
152DynamicModel::trackResolveFinished( bool success )
153{
154    Q_UNUSED( success );
155
156    Query* q = qobject_cast<Query*>( sender() );
157
158    tDebug() << "Got resolveFinished in DynamicModel" << q->track()->toString();
159    if ( !m_waitingFor.contains( q ) )
160        return;
161
162    if ( !q->playable() )
163    {
164        tDebug() << "Got not playable or resolved track:" << q->track()->toString() << m_lastResolvedRow << m_currentAttempts;
165        m_currentAttempts++;
166
167        int curAttempts = m_startingAfterFailed ? m_currentAttempts - 20 : m_currentAttempts; // if we just failed, m_currentAttempts includes those failures
168        if( curAttempts < 20 ) {
169            qDebug() << "FETCHING MORE!";
170            m_playlist->generator()->fetchNext();
171        } else {
172            m_startingAfterFailed = true;
173            emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
174        }
175    }
176    else
177    {
178        qDebug() << "Got successful resolved track:" << q->track()->toString() << m_lastResolvedRow << m_currentAttempts;
179
180        if ( m_currentAttempts > 0 ) {
181            qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;
182            emit collapseFromTo( m_lastResolvedRow, m_currentAttempts );
183        }
184        m_currentAttempts = 0;
185        m_searchingForNext = false;
186
187        emit checkForOverflow();
188    }
189    m_waitingFor.removeAll( q );
190}
191
192
193void
194DynamicModel::newTrackLoading()
195{
196    qDebug() << "Got NEW TRACK LOADING signal";
197    if ( m_changeOnNext )
198    { // reset instead of getting the next one
199        m_lastResolvedRow = rowCount( QModelIndex() );
200        m_searchingForNext = true;
201        m_playlist->generator()->startOnDemand();
202    }
203    else if ( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext )
204    { // if we're in dynamic mode and we're also currently idle
205        m_lastResolvedRow = rowCount( QModelIndex() );
206        m_searchingForNext = true;
207        qDebug() << "IDLE fetching new track!";
208        m_playlist->generator()->fetchNext();
209    }
210}
211
212
213void
214DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo )
215{
216    if ( m_filterUnresolvable && m_playlist->mode() == OnDemand )
217    { // wait till we get them resolved (for previewing stations)
218        m_limitResolvedTo = limitResolvedTo;
219        filterUnresolved( entries );
220    }
221    else
222    {
223        addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old
224
225        if ( m_playlist->mode() == OnDemand )
226        {
227            m_lastResolvedRow = rowCount( QModelIndex() );
228        }
229    }
230    if ( m_playlist->mode() == OnDemand && entries.isEmpty() )
231        emit trackGenerationFailure( tr( "Failed to generate preview with the desired filters" ) );
232}
233
234
235void
236DynamicModel::filterUnresolved( const QList< query_ptr >& entries )
237{
238    m_toResolveList = entries;
239
240    foreach ( const query_ptr& q, entries )
241        connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) );
242
243    Pipeline::instance()->resolve( entries, true );
244}
245
246
247void
248DynamicModel::filteringTrackResolved( bool successful )
249{
250    // arg, we don't have the query_ptr, just the Query
251    Query* q = qobject_cast< Query* >( sender() );
252    Q_ASSERT( q );
253
254    // if meantime the user began the station, abort
255    qDebug() << "Got filtering resolved finished for track, was it successful?:" << q->track()->toString() << successful << q->playable();
256    if ( m_onDemandRunning )
257    {
258        m_toResolveList.clear();
259        m_resolvedList.clear();
260
261        return;
262    }
263
264    query_ptr realptr;
265    foreach ( const query_ptr& qptr, m_toResolveList )
266    {
267        if ( qptr.data() == q )
268        {
269            realptr = qptr;
270            break;
271        }
272    }
273    if( realptr.isNull() ) // we already finished
274        return;
275
276    m_toResolveList.removeAll( realptr );
277
278    if ( realptr->playable() )
279    {
280        m_resolvedList << realptr;
281
282        // append and update internal lastResolvedRow
283        addToPlaylist( QList< query_ptr >() << realptr, false );
284        if ( m_playlist->mode() == OnDemand )
285        {
286            m_lastResolvedRow = rowCount( QModelIndex() );
287        }
288
289        if ( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo )
290        { // done
291            m_toResolveList.clear();
292            m_resolvedList.clear();
293
294        }
295    }
296    else
297    {
298        qDebug() << "Got unsuccessful resolve request for this track" << realptr->track()->toString();
299    }
300
301    if ( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
302        emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
303}
304
305
306void
307DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst )
308{
309    if ( clearFirst )
310        clear();
311
312    foreach ( const query_ptr& q, entries )
313        m_deduper.append( QPair< QString, QString >( q->track()->track(), q->track()->artist() ) );
314
315    if ( m_playlist->author()->isLocal() && m_playlist->mode() == Static )
316    {
317        m_playlist->addEntries( entries );
318    }
319    else
320    {
321        // read-only, so add tracks only in the GUI, not to the playlist itself
322        appendQueries( entries );
323    }
324
325    emit tracksAdded();
326}
327
328
329void
330DynamicModel::removeIndex( const QModelIndex& idx, bool moreToCome )
331{
332    if ( m_playlist->mode() == Static && isReadOnly() )
333        return;
334
335    qDebug() << Q_FUNC_INFO << "DYNAMIC MODEL REMOVIN!" << moreToCome << ( idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) );
336    if ( m_playlist->mode() == OnDemand )
337    {
338        if ( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) )
339        { // if the user is manually removing the last one, re-add as we're a station
340            newTrackLoading();
341        }
342        PlayableModel::removeIndex( idx );
343    }
344    else
345        PlaylistModel::removeIndex( idx, moreToCome );
346    // don't call onPlaylistChanged.
347
348    if( !moreToCome )
349        m_lastResolvedRow = rowCount( QModelIndex() );
350}