From 6635b084f7c18526bb3f0bd484c91d7b290762ab Mon Sep 17 00:00:00 2001 From: Stefan Derkits Date: Mon, 14 Jan 2013 22:40:26 +0100 Subject: [PATCH] Implement genreradio (still buggy & needs https://github.com/Horrendus/libechonest atm) --- .../dynamic/echonest/EchonestControl.cpp | 50 ++++++++++----- .../dynamic/echonest/EchonestControl.h | 6 +- .../dynamic/echonest/EchonestGenerator.cpp | 63 ++++++++++++++----- .../dynamic/echonest/EchonestGenerator.h | 9 ++- 4 files changed, 92 insertions(+), 36 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp index 61d25aaa1..a6319cd3c 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp @@ -35,7 +35,7 @@ QHash< QString, QStringList > Tomahawk::EchonestControl::s_suggestCache = QHash< QString, QStringList >(); -bool Tomahawk::EchonestControl::s_fetchingMoodsAndStyles = false; +bool Tomahawk::EchonestControl::s_fetchingMoodsStylesAndGenres = false; int Tomahawk::EchonestControl::s_stylePollCount = 0; @@ -418,11 +418,13 @@ Tomahawk::EchonestControl::updateWidgets() combo->hide(); m_match = QPointer< QWidget >( match ); m_input = QPointer< QWidget >( combo ); - } else if( selectedType() == "Mood" || selectedType() == "Style" ) { + } else if( selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" ) { if( selectedType() == "Mood" ) m_currentType = Echonest::DynamicPlaylist::Mood; - else + else if ( selectedType() == "Style" ) m_currentType = Echonest::DynamicPlaylist::Style; + else + m_currentType = Echonest::DynamicPlaylist::Genre; QLabel* match = new QLabel( tr( "is" ) ); @@ -440,7 +442,7 @@ Tomahawk::EchonestControl::updateWidgets() m_match = QPointer< QWidget >( match ); m_input = QPointer< QWidget >( combo ); - insertMoodsAndStyles(); + insertMoodsStylesAndGenres(); } else if( selectedType() == "Song Type" ) { m_currentType = Echonest::DynamicPlaylist::SongType; @@ -529,7 +531,7 @@ Tomahawk::EchonestControl::updateData() updateFromComboAndSlider(); } else if( selectedType() == "Danceability" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Artist Hotttnesss" || selectedType() == "Song Hotttnesss" ) { updateFromComboAndSlider( true ); - } else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "User Radio" ) { + } else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" || selectedType() == "User Radio" ) { updateFromLabelAndCombo(); } else if( selectedType() == "Sorting" ) { QComboBox* match = qobject_cast( m_match.data() ); @@ -635,7 +637,7 @@ Tomahawk::EchonestControl::updateWidgetsFromData() updateToComboAndSlider(); } else if( selectedType() == "Danceability" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Artist Hotttnesss" || selectedType() == "Song Hotttnesss" ) { updateToComboAndSlider( true ); - } else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style") { + } else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" ) { updateToLabelAndCombo(); } else if( selectedType() == "Sorting" ) { QComboBox* match = qobject_cast( m_match.data() ); @@ -896,7 +898,16 @@ Tomahawk::EchonestControl::calculateSummary() Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) ); QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower(); summary = tr( "in a %1 style" ).arg( text ); - } else if( selectedType() == "Song Type" ) { + } + else if ( selectedType() == "Genre" ) + { + Q_ASSERT( !m_input.isNull() ); + Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) ); + QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower(); + summary = tr( "where genre is %1" ).arg( text ); + } + else if ( selectedType() == "Song Type" ) + { Q_ASSERT( !m_input.isNull() ); Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) ); QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText(); @@ -916,13 +927,13 @@ Tomahawk::EchonestControl::calculateSummary() void -Tomahawk::EchonestControl::checkForMoodsOrStylesFetched() +Tomahawk::EchonestControl::checkForMoodsStylesOrGenresFetched() { - s_fetchingMoodsAndStyles = false; - if( selectedType() == "Mood" || selectedType() == "Style" ) { + s_fetchingMoodsStylesAndGenres = false; + if( selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" ) { QComboBox* cb = qobject_cast< QComboBox* >( m_input.data() ); if( cb && cb->count() == 0 ) { // got nothing, so lets populate - if( insertMoodsAndStyles() ) + if( insertMoodsStylesAndGenres() ) updateWidgetsFromData(); } } @@ -930,9 +941,16 @@ Tomahawk::EchonestControl::checkForMoodsOrStylesFetched() bool -Tomahawk::EchonestControl::insertMoodsAndStyles() +Tomahawk::EchonestControl::insertMoodsStylesAndGenres() { - QStringList src = selectedType() == "Mood" ? EchonestGenerator::moods() : EchonestGenerator::styles(); + QStringList src; + if ( selectedType() == "Mood" ) + src = EchonestGenerator::moods(); + else if ( selectedType() == "Style" ) + src = EchonestGenerator::styles(); + else + src = EchonestGenerator::genres(); + QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() ); if( !combo ) return false; @@ -942,9 +960,9 @@ Tomahawk::EchonestControl::insertMoodsAndStyles() } if( src.isEmpty() && !combo->count() ) { - if( s_stylePollCount <= 20 && !s_fetchingMoodsAndStyles ) { // try for 20s to get the styles... - s_fetchingMoodsAndStyles = true; - QTimer::singleShot( 1000, this, SLOT( checkForMoodsOrStylesFetched() ) ); + if( s_stylePollCount <= 20 && !s_fetchingMoodsStylesAndGenres ) { // try for 20s to get the styles... + s_fetchingMoodsStylesAndGenres = true; + QTimer::singleShot( 1000, this, SLOT( checkForMoodsStylesOrGenresFetched() ) ); } s_stylePollCount++; return false; diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h index d40a87c5e..4b47ca619 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h @@ -61,7 +61,7 @@ private slots: void artistTextEdited( const QString& ); void suggestFinished(); - void checkForMoodsOrStylesFetched(); + void checkForMoodsStylesOrGenresFetched(); private: void updateWidgets(); void updateWidgetsFromData(); @@ -70,7 +70,7 @@ private: void setupMinMaxWidgets( Echonest::DynamicPlaylist::PlaylistParam min, Echonest::DynamicPlaylist::PlaylistParam max, const QString& leftL, const QString& rightL, int maxRange ); void updateFromComboAndSlider( bool smooth = false ); void updateFromLabelAndCombo(); - bool insertMoodsAndStyles(); + bool insertMoodsStylesAndGenres(); void updateToComboAndSlider( bool smooth = false ); void updateToLabelAndCombo(); @@ -93,7 +93,7 @@ private: Echonest::DynamicPlaylist::PlaylistParamData m_data; QVariant m_cacheData; - static bool s_fetchingMoodsAndStyles; + static bool s_fetchingMoodsStylesAndGenres; static int s_stylePollCount; QSet< QNetworkReply* > m_suggestWorkers; diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 61e648188..e60a7322a 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -35,8 +35,10 @@ using namespace Tomahawk; QStringList EchonestGenerator::s_moods = QStringList(); QStringList EchonestGenerator::s_styles = QStringList(); +QStringList EchonestGenerator::s_genres = QStringList(); QNetworkReply* EchonestGenerator::s_moodsJob = 0; QNetworkReply* EchonestGenerator::s_stylesJob = 0; +QNetworkReply* EchonestGenerator::s_genresJob = 0; CatalogManager* EchonestGenerator::s_catalogs = 0; @@ -63,7 +65,7 @@ EchonestFactory::createControl( const QString& controlType ) QStringList EchonestFactory::typeSelectors() const { - QStringList types = QStringList() << "Artist" << "Artist Description" << "User Radio" << "Song" << "Mood" << "Style" << "Adventurousness" << "Variety" << "Tempo" << "Duration" << "Loudness" + QStringList types = QStringList() << "Artist" << "Artist Description" << "User Radio" << "Song" << "Genre" << "Mood" << "Style" << "Adventurousness" << "Variety" << "Tempo" << "Duration" << "Loudness" << "Danceability" << "Energy" << "Artist Familiarity" << "Artist Hotttnesss" << "Song Hotttnesss" << "Longitude" << "Latitude" << "Mode" << "Key" << "Sorting" << "Song Type"; @@ -126,7 +128,7 @@ EchonestGenerator::EchonestGenerator ( QObject* parent ) m_mode = OnDemand; m_logo.load( RESPATH "/images/echonest_logo.png" ); - loadStylesAndMoods(); + loadStylesMoodsAndGenres(); connect( s_catalogs, SIGNAL( catalogsUpdated() ), this, SLOT( knownCatalogsChanged() ) ); } @@ -470,12 +472,17 @@ EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& p /// 4. artist-radio: If all the artist entries are Similar To. If some were but not all, error out. /// 5. song-radio: If all the artist entries are Similar To. If some were but not all, error out. bool someCatalog = false; + bool genreType = false; foreach( const dyncontrol_ptr& control, m_controls ) { if ( control->selectedType() == "User Radio" ) someCatalog = true; + else if (control->selectedType() == "Genre" ) + genreType = true; } if( someCatalog ) params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::CatalogRadioType ) ); + else if ( genreType ) + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::GenreRadioType ) ); else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistType ) ) params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) ); else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistDescriptionType ) ) @@ -603,49 +610,52 @@ EchonestGenerator::sentenceSummary() } void -EchonestGenerator::loadStylesAndMoods() +EchonestGenerator::loadStylesMoodsAndGenres() { if( !s_styles.isEmpty() || !s_moods.isEmpty() ) return; - QFile dataFile( TomahawkUtils::appDataDir().absoluteFilePath( "echonest_stylesandmoods.dat" ) ); - if( !dataFile.exists() ) // load + QFile dataFile( TomahawkUtils::appDataDir().absoluteFilePath( "echonest_stylesmoodsandgenres.dat" ) ); //TODO: delete old file in migrate function + if( !dataFile.exists() ) // load TODO: re-fetch styles, moods & genres if file is older than X days? { s_stylesJob = Echonest::Artist::listTerms( "style" ); connect( s_stylesJob, SIGNAL( finished() ), this, SLOT( stylesReceived() ) ); s_moodsJob = Echonest::Artist::listTerms( "mood" ); connect( s_moodsJob, SIGNAL( finished() ), this, SLOT( moodsReceived() ) ); + s_genresJob = Echonest::Artist::fetchGenres(); + connect( s_genresJob, SIGNAL( finished() ), this, SLOT( genresReceived() ) ); } else { if( !dataFile.open( QIODevice::ReadOnly ) ) { - tLog() << "Failed to open for reading styles/moods db file:" << dataFile.fileName(); + tLog() << "Failed to open for reading styles/moods/genres db file:" << dataFile.fileName(); return; } QString allData = QString::fromUtf8( dataFile.readAll() ); QStringList parts = allData.split( "\n" ); - if( parts.size() != 2 ) + if( parts.size() != 3 ) { - tLog() << "Didn't get both moods and styles in file...:" << allData; + tLog() << "Didn't get moods, styles and genres in file...:" << allData; return; } s_moods = parts[ 0 ].split( "|" ); s_styles = parts[ 1 ].split( "|" ); + s_genres = parts[ 2 ].split( " | "); } } void -EchonestGenerator::saveStylesAndMoods() +EchonestGenerator::saveStylesMoodsAndGenres() { - QFile dataFile( TomahawkUtils::appDataDir().absoluteFilePath( "echonest_stylesandmoods.dat" ) ); + QFile dataFile( TomahawkUtils::appDataDir().absoluteFilePath( "echonest_stylesmoodsandgenres.dat" ) ); if( !dataFile.open( QIODevice::WriteOnly ) ) { tLog() << "Failed to open styles and moods data file for saving:" << dataFile.errorString() << dataFile.fileName(); return; } - QByteArray data = QString( "%1\n%2" ).arg( s_moods.join( "|" ) ).arg( s_styles.join( "|" ) ).toUtf8(); + QByteArray data = QString( "%1\n%2\n%3" ).arg( s_moods.join( "|" ) ).arg( s_styles.join( "|" ) ).arg( s_genres.join( "|" ) ).toUtf8(); dataFile.write( data ); } @@ -671,8 +681,8 @@ EchonestGenerator::moodsReceived() } s_moodsJob = 0; - if( !s_styles.isEmpty() ) - saveStylesAndMoods(); + if( !s_styles.isEmpty() && !s_genres.isEmpty() ) + saveStylesMoodsAndGenres(); } @@ -696,6 +706,29 @@ EchonestGenerator::stylesReceived() } s_stylesJob = 0; - if( !s_moods.isEmpty() ) - saveStylesAndMoods(); + if( !s_moods.isEmpty() && !s_styles.isEmpty() ) + saveStylesMoodsAndGenres(); +} + +QStringList +EchonestGenerator::genres() +{ + return s_genres; +} + +void +EchonestGenerator::genresReceived() +{ + QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); + Q_ASSERT( r ); + + try { + s_genres = Echonest::Artist::parseGenreList( r ).toList(); + } catch( Echonest::ParseError& e ) { + qWarning() << "Echonest failed to parse genres list"; + } + s_genresJob = 0; + + if( !s_moods.isEmpty() && !s_styles.isEmpty() ) + saveStylesMoodsAndGenres(); } diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h index 993ea1766..7e0822add 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h @@ -82,6 +82,7 @@ public: static QStringList styles(); static QStringList moods(); + static QStringList genres(); static QStringList userCatalogs(); static QByteArray catalogId( const QString& collectionId ); @@ -99,6 +100,7 @@ private slots: void stylesReceived(); void moodsReceived(); + void genresReceived(); void knownCatalogsChanged(); void songLookupFinished(); @@ -110,16 +112,18 @@ private: Echonest::DynamicPlaylist::ArtistTypeEnum appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error ); bool onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error ); - void loadStylesAndMoods(); - void saveStylesAndMoods(); + void loadStylesMoodsAndGenres(); + void saveStylesMoodsAndGenres(); Echonest::DynamicPlaylist* m_dynPlaylist; QPixmap m_logo; static QStringList s_styles; static QStringList s_moods; + static QStringList s_genres; static QNetworkReply* s_stylesJob; static QNetworkReply* s_moodsJob; + static QNetworkReply* s_genresJob; static CatalogManager* s_catalogs; @@ -131,3 +135,4 @@ private: }; #endif +