1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-05 05:37:29 +02:00

* Allow to locally import Last.fm playback history. Works but needs discussion.

This commit is contained in:
Christian Muehlhaeuser
2012-05-22 10:43:39 +02:00
parent 3f5d219549
commit 2ee66a3cda
5 changed files with 152 additions and 31 deletions

View File

@@ -17,15 +17,20 @@
*/ */
#include "LastFmConfig.h" #include "LastFmConfig.h"
#include "ui_LastFmConfig.h"
#include "LastFmAccount.h" #include "LastFmAccount.h"
#include <utils/TomahawkUtils.h> #include "database/Database.h"
#include "ui_LastFmConfig.h" #include "database/DatabaseCommand_LogPlayback.h"
#include "utils/TomahawkUtils.h"
#include "utils/Logger.h"
#include "lastfm/ws.h" #include "lastfm/ws.h"
#include "lastfm/User"
#include "lastfm/XmlQuery" #include "lastfm/XmlQuery"
using namespace Tomahawk::Accounts; using namespace Tomahawk::Accounts;
LastFmConfig::LastFmConfig( LastFmAccount* account ) LastFmConfig::LastFmConfig( LastFmAccount* account )
: QWidget( 0 ) : QWidget( 0 )
, m_account( account ) , m_account( account )
@@ -33,18 +38,17 @@ LastFmConfig::LastFmConfig( LastFmAccount* account )
m_ui = new Ui_LastFmConfig; m_ui = new Ui_LastFmConfig;
m_ui->setupUi( this ); m_ui->setupUi( this );
m_ui->progressBar->hide();
m_ui->username->setText( m_account->username() ); m_ui->username->setText( m_account->username() );
m_ui->password->setText( m_account->password() ); m_ui->password->setText( m_account->password() );
m_ui->enable->setChecked( m_account->scrobble() ); m_ui->enable->setChecked( m_account->scrobble() );
connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), this, SLOT( testLogin( bool ) ) ); connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), SLOT( testLogin() ) );
connect( m_ui->importHistory, SIGNAL( clicked( bool ) ), SLOT( loadHistory() ) );
connect( m_ui->username, SIGNAL( textChanged( QString ) ), this, SLOT( enableButton() ) ); connect( m_ui->username, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
connect( m_ui->password, SIGNAL( textChanged( QString ) ), this, SLOT( enableButton() ) ); connect( m_ui->password, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
// #ifdef Q_WS_MAC // FIXME
// m_ui->testLogin->setVisible( false );
// #endif
} }
@@ -70,10 +74,10 @@ LastFmConfig::username() const
void void
LastFmConfig::testLogin(bool ) LastFmConfig::testLogin()
{ {
m_ui->testLogin->setEnabled( false ); m_ui->testLogin->setEnabled( false );
m_ui->testLogin->setText( "Testing..." ); m_ui->testLogin->setText( tr( "Testing..." ) );
QString authToken = TomahawkUtils::md5( ( m_ui->username->text().toLower() + TomahawkUtils::md5( m_ui->password->text().toUtf8() ) ).toUtf8() ); QString authToken = TomahawkUtils::md5( ( m_ui->username->text().toLower() + TomahawkUtils::md5( m_ui->password->text().toUtf8() ) ).toUtf8() );
@@ -100,6 +104,79 @@ LastFmConfig::enableButton()
} }
void
LastFmConfig::loadHistory( int page )
{
if ( page == 1 )
{
m_ui->importHistory->setText( tr( "Importing History..." ) );
m_ui->importHistory->setEnabled( false );
m_ui->progressBar->show();
}
QNetworkReply* reply = lastfm::User( m_ui->username->text().toLower() ).getRecentTracks( 200, page );
connect( reply, SIGNAL( finished() ), SLOT( onHistoryLoaded() ) );
}
void
LastFmConfig::onHistoryLoaded()
{
bool finished = false;
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
try
{
lastfm::XmlQuery lfm = reply->readAll();
foreach ( lastfm::XmlQuery e, lfm.children( "track" ) )
{
tDebug() << "Found:" << e["artist"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt();
Tomahawk::query_ptr query = Query::get( e["artist"].text(), e["name"].text(), QString(), QString(), false );
uint timeStamp = e["date"].attribute( "uts" ).toUInt();
DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( query, DatabaseCommand_LogPlayback::Finished, timeStamp );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
}
if ( !lfm.children( "recenttracks" ).isEmpty() )
{
lastfm::XmlQuery stats = lfm.children( "recenttracks" ).first();
int page = stats.attribute( "page" ).toInt();
int total = stats.attribute( "totalPages" ).toInt();
tDebug() << "page:" << page << "total:" << total;
m_ui->progressBar->setMaximum( total );
m_ui->progressBar->setValue( page );
if ( page < total )
loadHistory( ++page );
else
finished = true;
}
else
finished = true;
}
catch( lastfm::ws::ParseError e )
{
tDebug() << "XmlQuery error:" << e.what();
finished = true;
}
if ( finished )
{
if ( m_ui->progressBar->value() != m_ui->progressBar->maximum() )
m_ui->importHistory->setText( tr( "History Incomplete. Retry" ) );
else
m_ui->importHistory->setText( tr( "Import Playback History" ) );
m_ui->importHistory->setEnabled( true );
}
}
void void
LastFmConfig::onLastFmFinished() LastFmConfig::onLastFmFinished()
{ {

View File

@@ -24,6 +24,7 @@
class Ui_LastFmConfig; class Ui_LastFmConfig;
namespace Tomahawk { namespace Tomahawk {
namespace Accounts { namespace Accounts {
class LastFmAccount; class LastFmAccount;
@@ -39,18 +40,22 @@ public:
bool scrobble() const; bool scrobble() const;
public slots: public slots:
void testLogin( bool ); void testLogin();
void onLastFmFinished(); void onLastFmFinished();
private slots: private slots:
void enableButton(); void enableButton();
void loadHistory( int page = 1 );
void onHistoryLoaded();
private: private:
LastFmAccount* m_account; LastFmAccount* m_account;
Ui_LastFmConfig* m_ui; Ui_LastFmConfig* m_ui;
}; };
} }
} }
#endif // LASTFMCONFIG_H #endif // LASTFMCONFIG_H

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>220</height> <height>253</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -77,6 +77,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="importHistory">
<property name="text">
<string>Import Playback History</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>

View File

@@ -37,31 +37,33 @@ using namespace Tomahawk;
void void
DatabaseCommand_LogPlayback::postCommitHook() DatabaseCommand_LogPlayback::postCommitHook()
{ {
if ( !m_query.isNull() )
return;
connect( this, SIGNAL( trackPlaying( Tomahawk::query_ptr, unsigned int ) ), connect( this, SIGNAL( trackPlaying( Tomahawk::query_ptr, unsigned int ) ),
source().data(), SLOT( onPlaybackStarted( Tomahawk::query_ptr, unsigned int ) ), Qt::QueuedConnection ); source().data(), SLOT( onPlaybackStarted( Tomahawk::query_ptr, unsigned int ) ), Qt::QueuedConnection );
connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ), connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ),
source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection ); source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection );
Tomahawk::query_ptr q; if ( !m_result.isNull() && m_query.isNull() )
if ( !m_result.isNull() )
{ {
q = m_result->toQuery(); m_query = m_result->toQuery();
} }
else else
{ {
// do not auto resolve this track // do not auto resolve this track
q = Tomahawk::Query::get( m_artist, m_track, QString() ); m_query = Tomahawk::Query::get( m_artist, m_track, QString() );
} }
q->setPlayedBy( source(), m_playtime ); m_query->setPlayedBy( source(), m_playtime );
if ( m_action == Finished ) if ( m_action == Finished )
{ {
emit trackPlayed( q ); emit trackPlayed( m_query );
} }
// if the play time is more than 10 minutes in the past, ignore // if the play time is more than 10 minutes in the past, ignore
else if ( m_action == Started && QDateTime::fromTime_t( playtime() ).secsTo( QDateTime::currentDateTime() ) < STARTED_THRESHOLD ) else if ( m_action == Started && QDateTime::fromTime_t( playtime() ).secsTo( QDateTime::currentDateTime() ) < STARTED_THRESHOLD )
{ {
emit trackPlaying( q, m_trackDuration ); emit trackPlaying( m_query, m_trackDuration );
} }
if ( source()->isLocal() ) if ( source()->isLocal() )
@@ -78,25 +80,36 @@ DatabaseCommand_LogPlayback::exec( DatabaseImpl* dbi )
if ( m_action != Finished ) if ( m_action != Finished )
return; return;
if ( m_secsPlayed < FINISHED_THRESHOLD ) if ( m_secsPlayed < FINISHED_THRESHOLD && m_trackDuration > 0 )
return;
if ( m_artist.isEmpty() || m_track.isEmpty() )
return; return;
TomahawkSqlQuery query = dbi->newquery();
query.prepare( "INSERT INTO playback_log(source, track, playtime, secs_played) "
"VALUES (?, ?, ?, ?)" );
QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id(); QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id();
qDebug() << "Logging playback of" << m_artist << "-" << m_track << "for source" << srcid; TomahawkSqlQuery query = dbi->newquery();
if ( !m_query.isNull() )
{
query.prepare( QString( "SELECT * FROM playback_log WHERE source %1 AND playtime = %2" ).arg( srcid.isNull() ? "IS NULL" : srcid.toString() ).arg( m_playtime ) );
query.exec();
if ( query.next() )
{
tDebug() << "Ignoring dupe playback log for source" << srcid << "with timestamp" << m_playtime;
return;
}
}
// tDebug() << "Logging playback of" << m_artist << "-" << m_track << "for source" << srcid << "- timestamp:" << m_playtime;
query.prepare( "INSERT INTO playback_log(source, track, playtime, secs_played) VALUES (?, ?, ?, ?)" );
query.bindValue( 0, srcid ); query.bindValue( 0, srcid );
// If there's no artist, becuase it's a resolver result with bad metadata for example, don't save it // If there's no artist, because it's a resolver result with bad metadata for example, don't save it
bool autoCreate = m_artist.isEmpty(); int artid = dbi->artistId( m_artist, true );
int artid = dbi->artistId( m_artist, autoCreate );
if( artid < 1 ) if( artid < 1 )
return; return;
autoCreate = true; // artistId overwrites autoCreate (reference) int trkid = dbi->trackId( artid, m_track, true );
int trkid = dbi->trackId( artid, m_track, autoCreate );
if( trkid < 1 ) if( trkid < 1 )
return; return;

View File

@@ -51,6 +51,17 @@ public:
: DatabaseCommandLoggable( parent ), m_playtime( 0 ), m_secsPlayed( 0 ), m_trackDuration( 0 ) : DatabaseCommandLoggable( parent ), m_playtime( 0 ), m_secsPlayed( 0 ), m_trackDuration( 0 )
{} {}
explicit DatabaseCommand_LogPlayback( const Tomahawk::query_ptr& query, Action action, uint timeStamp, QObject* parent = 0 )
: DatabaseCommandLoggable( parent ), m_query( query ), m_secsPlayed( 0 ), m_action( action )
{
m_playtime = timeStamp;
m_trackDuration = 0;
setSource( SourceList::instance()->getLocal() );
setArtist( query->artist() );
setTrack( query->track() );
}
explicit DatabaseCommand_LogPlayback( const Tomahawk::result_ptr& result, Action action, unsigned int secsPlayed = 0, QObject* parent = 0 ) explicit DatabaseCommand_LogPlayback( const Tomahawk::result_ptr& result, Action action, unsigned int secsPlayed = 0, QObject* parent = 0 )
: DatabaseCommandLoggable( parent ), m_result( result ), m_secsPlayed( secsPlayed ), m_action( action ) : DatabaseCommandLoggable( parent ), m_result( result ), m_secsPlayed( secsPlayed ), m_action( action )
{ {
@@ -96,6 +107,7 @@ signals:
private: private:
Tomahawk::result_ptr m_result; Tomahawk::result_ptr m_result;
Tomahawk::query_ptr m_query;
QString m_artist; QString m_artist;
QString m_track; QString m_track;