From c760d26b68860f9a78844fe022c70a76cd80e35f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 18 Jun 2011 11:12:34 -0400 Subject: [PATCH] Listen along -- fix bug where catching up to the same track would stop audio. Also allow option to stop listening. --- src/libtomahawk/audio/audioengine.cpp | 30 ++++++--- src/libtomahawk/sourceplaylistinterface.cpp | 35 +++++++++-- src/libtomahawk/sourceplaylistinterface.h | 4 ++ src/sourcetree/sourcetreeview.cpp | 68 +++++++++++++++++---- src/sourcetree/sourcetreeview.h | 8 ++- 5 files changed, 114 insertions(+), 31 deletions(-) diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index b6c06f0af..400937551 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -111,14 +111,14 @@ AudioEngine::play() { m_mediaObject->play(); emit resumed(); - Tomahawk::InfoSystem::InfoCriteriaHash trackInfo; + Tomahawk::InfoSystem::InfoCriteriaHash trackInfo; - trackInfo["title"] = m_currentTrack->track(); - trackInfo["artist"] = m_currentTrack->artist()->name(); - trackInfo["album"] = m_currentTrack->album()->name(); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowResumed, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); + trackInfo["title"] = m_currentTrack->track(); + trackInfo["artist"] = m_currentTrack->artist()->name(); + trackInfo["album"] = m_currentTrack->album()->name(); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowResumed, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); } else loadNextTrack(); @@ -143,7 +143,8 @@ AudioEngine::stop() qDebug() << Q_FUNC_INFO; m_mediaObject->stop(); - + m_retryTimer.stop(); + setCurrentTrack( Tomahawk::result_ptr() ); emit stopped(); Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( @@ -155,6 +156,10 @@ void AudioEngine::previous() { qDebug() << Q_FUNC_INFO; + + if ( !m_playlist ) + return; + if ( m_playlist->skipRestrictions() == PlaylistInterface::NoSkip || m_playlist->skipRestrictions() == PlaylistInterface::NoSkipBackwards ) return; @@ -167,6 +172,10 @@ void AudioEngine::next() { qDebug() << Q_FUNC_INFO; + + if ( !m_playlist ) + return; + if ( m_playlist->skipRestrictions() == PlaylistInterface::NoSkip || m_playlist->skipRestrictions() == PlaylistInterface::NoSkipForwards ) return; @@ -178,6 +187,9 @@ AudioEngine::next() void AudioEngine::seek( int ms ) { + if ( !m_playlist ) + return; + if ( m_playlist->seekRestrictions() == PlaylistInterface::NoSeek ) return; @@ -345,7 +357,7 @@ AudioEngine::loadNextTrack() else { stop(); - if ( m_playlist->retryMode() == Tomahawk::PlaylistInterface::Retry ) + if ( m_playlist && m_playlist->retryMode() == Tomahawk::PlaylistInterface::Retry ) { m_retryTimer.setInterval( m_playlist->retryInterval() ); m_retryTimer.start(); diff --git a/src/libtomahawk/sourceplaylistinterface.cpp b/src/libtomahawk/sourceplaylistinterface.cpp index 27c30cf39..af197ea9d 100644 --- a/src/libtomahawk/sourceplaylistinterface.cpp +++ b/src/libtomahawk/sourceplaylistinterface.cpp @@ -28,6 +28,7 @@ using namespace Tomahawk; SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::source_ptr& source ) : PlaylistInterface( this ) , m_source( source ) + , m_currentItem( 0 ) , m_gotNextSong( false ) { connect( source.data(), SIGNAL( playbackStarted( const Tomahawk::query_ptr& ) ), SLOT( onSourcePlaybackStarted( const Tomahawk::query_ptr& ) ) ); @@ -39,15 +40,21 @@ SourcePlaylistInterface::siblingItem( int itemsAway ) { Q_UNUSED( itemsAway ); qDebug() << Q_FUNC_INFO; - if ( !m_gotNextSong || m_source.isNull() || m_source->currentTrack().isNull() || m_source->currentTrack()->results().isEmpty() ) + if ( m_source.isNull() || m_source->currentTrack().isNull() || m_source->currentTrack()->results().isEmpty() ) { qDebug() << Q_FUNC_INFO << " Results were empty for current track or source no longer valid"; + m_currentItem = Tomahawk::result_ptr(); + return m_currentItem; + } + else if ( !m_gotNextSong ) + { + qDebug() << Q_FUNC_INFO << " This song was already fetched"; return Tomahawk::result_ptr(); } m_gotNextSong = false; - - return m_source->currentTrack()->results().first(); + m_currentItem = m_source->currentTrack()->results().first(); + return m_currentItem; } @@ -55,15 +62,21 @@ Tomahawk::result_ptr SourcePlaylistInterface::nextItem() { qDebug() << Q_FUNC_INFO; - if ( !m_gotNextSong || m_source.isNull() || m_source->currentTrack().isNull() || m_source->currentTrack()->results().isEmpty() ) + if ( m_source.isNull() || m_source->currentTrack().isNull() || m_source->currentTrack()->results().isEmpty() ) { qDebug() << Q_FUNC_INFO << " Results were empty for current track or source no longer valid"; + m_currentItem = Tomahawk::result_ptr(); + return m_currentItem; + } + else if ( !m_gotNextSong ) + { + qDebug() << Q_FUNC_INFO << " This song was already fetched"; return Tomahawk::result_ptr(); } m_gotNextSong = false; - - return m_source->currentTrack()->results().first(); + m_currentItem = m_source->currentTrack()->results().first(); + return m_currentItem; } @@ -74,6 +87,16 @@ SourcePlaylistInterface::tracks() } +void +SourcePlaylistInterface::reset() +{ + if ( !m_currentItem.isNull() ) + m_gotNextSong = true; + else + m_gotNextSong = false; +} + + void SourcePlaylistInterface::onSourcePlaybackStarted( const Tomahawk::query_ptr& query ) { diff --git a/src/libtomahawk/sourceplaylistinterface.h b/src/libtomahawk/sourceplaylistinterface.h index b31df4773..f57fe5cd5 100644 --- a/src/libtomahawk/sourceplaylistinterface.h +++ b/src/libtomahawk/sourceplaylistinterface.h @@ -45,6 +45,7 @@ public: virtual Tomahawk::result_ptr siblingItem( int itemsAway ); virtual Tomahawk::result_ptr nextItem(); + virtual Tomahawk::result_ptr currentItem() { return m_currentItem; } virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual PlaylistInterface::SeekRestrictions seekRestrictions() const { return PlaylistInterface::NoSeek; } @@ -57,6 +58,8 @@ public: virtual Tomahawk::source_ptr source() const { return m_source; } + virtual void reset(); + public slots: virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} virtual void setShuffled( bool ) {} @@ -74,6 +77,7 @@ private slots: private: Tomahawk::source_ptr m_source; + Tomahawk::result_ptr m_currentItem; bool m_gotNextSong; }; diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 0adf981bd..ee36708b2 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -127,7 +127,7 @@ SourceTreeView::setupMenus() { m_playlistMenu.clear(); m_roPlaylistMenu.clear(); - m_followMenu.clear(); + m_latchMenu.clear(); bool readonly = true; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); @@ -142,6 +142,8 @@ SourceTreeView::setupMenus() } } + m_latchOnAction = m_latchMenu.addAction( tr( "&Listen Along" ) ); + if ( type == SourcesModel::Collection ) { CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); @@ -153,12 +155,13 @@ SourceTreeView::setupMenus() { SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi ); if ( !sourcepi->source().isNull() && sourcepi->source()->id() == source->id() ) - m_followAction = m_followMenu.addAction( tr( "&Catch Up" ) ); - else - m_followAction = m_followMenu.addAction( tr( "&Listen Along" ) ); + { + m_latchOnAction->setText( tr( "&Catch Up" ) ); + m_latchMenu.addSeparator(); + m_latchOffAction = m_latchMenu.addAction( tr( "&Stop Listening Along" ) ); + connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); + } } - else - m_followAction = m_followMenu.addAction( tr( "&Listen Along" ) ); } } @@ -192,7 +195,7 @@ SourceTreeView::setupMenus() connect( m_deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) ); connect( m_copyPlaylistAction, SIGNAL( triggered() ), SLOT( copyPlaylistLink() ) ); connect( m_addToLocalAction, SIGNAL( triggered() ), SLOT( addToLocal() ) ); - connect( m_followAction, SIGNAL( triggered() ), SLOT( follow() ) ); + connect( m_latchOnAction, SIGNAL( triggered() ), SLOT( latchOn() ) ); } @@ -329,7 +332,7 @@ void SourceTreeView::addToLocal() void -SourceTreeView::follow() +SourceTreeView::latchOn() { qDebug() << Q_FUNC_INFO; QModelIndex idx = m_contextMenuIndex; @@ -337,12 +340,51 @@ SourceTreeView::follow() return; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); - if( type == SourcesModel::Collection ) + if( type != SourcesModel::Collection ) + return; + + CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); + source_ptr source = item->source(); + PlaylistInterface* pi = AudioEngine::instance()->playlist(); + + if ( pi && dynamic_cast< SourcePlaylistInterface* >( pi ) ) { - CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); - source_ptr source = item->source(); - AudioEngine::instance()->playItem( source->getPlaylistInterface().data(), source->getPlaylistInterface()->nextItem() ); + SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi ); + if ( !sourcepi->source().isNull() && sourcepi->source()->id() == source->id() ) + { + //it's a catch-up -- if they're trying to catch-up in the same track, don't do anything + //so that you don't repeat the track and/or cause the retry timer to fire + if ( !AudioEngine::instance()->currentTrack().isNull() && + AudioEngine::instance()->currentTrack()->id() == sourcepi->currentItem()->id() ) + return; + } } + + AudioEngine::instance()->playItem( source->getPlaylistInterface().data(), source->getPlaylistInterface()->nextItem() ); +} + + +void +SourceTreeView::latchOff() +{ + qDebug() << Q_FUNC_INFO; + QModelIndex idx = m_contextMenuIndex; + if ( !idx.isValid() ) + return; + + SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); + if( type != SourcesModel::Collection ) + return; + + PlaylistInterface* pi = AudioEngine::instance()->playlist(); + if ( pi && dynamic_cast< SourcePlaylistInterface* >( pi ) ) + { + SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi ); + sourcepi->reset(); + } + + AudioEngine::instance()->stop(); + AudioEngine::instance()->setPlaylist( 0 ); } @@ -381,7 +423,7 @@ SourceTreeView::onCustomContextMenu( const QPoint& pos ) { CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); if ( !item->source()->isLocal() ) - m_followMenu.exec( mapToGlobal( pos ) ); + m_latchMenu.exec( mapToGlobal( pos ) ); } } diff --git a/src/sourcetree/sourcetreeview.h b/src/sourcetree/sourcetreeview.h index c53761e14..0824ae3c7 100644 --- a/src/sourcetree/sourcetreeview.h +++ b/src/sourcetree/sourcetreeview.h @@ -56,7 +56,8 @@ private slots: void copyPlaylistLink(); void addToLocal(); - void follow(); + void latchOn(); + void latchOff(); void onCustomContextMenu( const QPoint& pos ); protected: @@ -83,13 +84,14 @@ private: QMenu m_playlistMenu; QMenu m_roPlaylistMenu; - QMenu m_followMenu; + QMenu m_latchMenu; QAction* m_loadPlaylistAction; QAction* m_renamePlaylistAction; QAction* m_deletePlaylistAction; QAction* m_copyPlaylistAction; QAction* m_addToLocalAction; - QAction* m_followAction; + QAction* m_latchOnAction; + QAction* m_latchOffAction; bool m_dragging; QRect m_dropRect;