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:
@@ -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()
|
||||||
{
|
{
|
||||||
|
@@ -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
|
||||||
|
@@ -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>
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user