mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-03-25 02:09:48 +01:00
Initial last.fm loved track syncing
This commit is contained in:
parent
a70f14e7ab
commit
26509493a2
@ -459,14 +459,19 @@ Query::checkResults()
|
||||
|
||||
|
||||
bool
|
||||
Query::equals( const Tomahawk::query_ptr& other ) const
|
||||
Query::equals( const Tomahawk::query_ptr& other, bool ignoreCase ) const
|
||||
{
|
||||
if ( other.isNull() )
|
||||
return false;
|
||||
|
||||
return ( artist() == other->artist() &&
|
||||
album() == other->album() &&
|
||||
track() == other->track() );
|
||||
if ( ignoreCase )
|
||||
return ( artist().toLower() == other->artist().toLower() &&
|
||||
album().toLower() == other->album().toLower() &&
|
||||
track().toLower() == other->track().toLower() );
|
||||
else
|
||||
return ( artist() == other->artist() &&
|
||||
album() == other->album() &&
|
||||
track() == other->track() );
|
||||
}
|
||||
|
||||
|
||||
@ -502,7 +507,7 @@ Query::howSimilar( const Tomahawk::result_ptr& r )
|
||||
Q_ASSERT( !r->album().isNull() );
|
||||
if ( r->artist().isNull() || r->album().isNull() )
|
||||
return 0.0;
|
||||
|
||||
|
||||
// result values
|
||||
const QString rArtistname = r->artist()->sortname();
|
||||
const QString rAlbumname = r->album()->sortname();
|
||||
|
@ -122,7 +122,7 @@ public:
|
||||
void setAlbumPos( unsigned int albumpos ) { m_albumpos = albumpos; }
|
||||
void setDiscNumber( unsigned int discnumber ) { m_discnumber = discnumber; }
|
||||
|
||||
bool equals( const Tomahawk::query_ptr& other ) const;
|
||||
bool equals( const Tomahawk::query_ptr& other, bool ignoreCase = false ) const;
|
||||
|
||||
QVariant toVariant() const;
|
||||
QString toString() const;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <attica/content.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
|
||||
namespace Tomahawk {
|
||||
class ExternalResolverGui;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
|
||||
* Copyright 2010-2012, Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -22,11 +22,16 @@
|
||||
#include "LastFmAccount.h"
|
||||
#include "database/Database.h"
|
||||
#include "database/DatabaseCommand_LogPlayback.h"
|
||||
#include <database/DatabaseCommand_LoadSocialActions.h>
|
||||
#include <database/DatabaseCommand_SocialAction.h>
|
||||
#include "utils/TomahawkUtils.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "lastfm/ws.h"
|
||||
#include "lastfm/User.h"
|
||||
#include "lastfm/XmlQuery.h"
|
||||
#include "utils/Closure.h"
|
||||
|
||||
#include <lastfm/ws.h>
|
||||
#include <lastfm/User.h>
|
||||
#include <lastfm/XmlQuery.h>
|
||||
#include <lastfm/Track.h>
|
||||
|
||||
using namespace Tomahawk::Accounts;
|
||||
|
||||
@ -36,6 +41,8 @@ LastFmConfig::LastFmConfig( LastFmAccount* account )
|
||||
, m_account( account )
|
||||
, m_page( 1 )
|
||||
, m_lastTimeStamp( 0 )
|
||||
, m_totalLovedPages( -1 )
|
||||
, m_doneFetchingLoved( false )
|
||||
{
|
||||
m_ui = new Ui_LastFmConfig;
|
||||
m_ui->setupUi( this );
|
||||
@ -48,6 +55,7 @@ LastFmConfig::LastFmConfig( LastFmAccount* account )
|
||||
|
||||
connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), SLOT( testLogin() ) );
|
||||
connect( m_ui->importHistory, SIGNAL( clicked( bool ) ), SLOT( loadHistory() ) );
|
||||
connect( m_ui->syncLovedTracks, SIGNAL( clicked( bool ) ), SLOT( syncLovedTracks() ) );
|
||||
|
||||
connect( m_ui->username, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
|
||||
connect( m_ui->password, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
|
||||
@ -141,7 +149,7 @@ LastFmConfig::onHistoryLoaded()
|
||||
foreach ( lastfm::XmlQuery e, lfm.children( "track" ) )
|
||||
{
|
||||
// tDebug() << "Found:" << e.children( "artist" ).first()["name"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt();
|
||||
Tomahawk::query_ptr query = Query::get( e.children( "artist" ).first()["name"].text(), e["name"].text(), QString(), QString(), false );
|
||||
Tomahawk::query_ptr query = Tomahawk::Query::get( e.children( "artist" ).first()["name"].text(), e["name"].text(), QString(), QString(), false );
|
||||
if ( query.isNull() )
|
||||
continue;
|
||||
|
||||
@ -237,3 +245,149 @@ LastFmConfig::onLastFmFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LastFmConfig::syncLovedTracks( uint page )
|
||||
{
|
||||
QNetworkReply* reply = lastfm::User( username() ).getLovedTracks( 200, page );
|
||||
|
||||
m_ui->progressBar->show();
|
||||
|
||||
NewClosure( reply, SIGNAL( finished() ), this, SLOT( onLovedFinished( QNetworkReply* ) ), reply );
|
||||
|
||||
DatabaseCommand_LoadSocialActions* cmd = new DatabaseCommand_LoadSocialActions( "Love", SourceList::instance()->getLocal() );
|
||||
connect( cmd, SIGNAL( done( DatabaseCommand_LoadSocialActions::TrackActions ) ), this, SLOT( localLovedLoaded( DatabaseCommand_LoadSocialActions::TrackActions ) ) );
|
||||
|
||||
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LastFmConfig::onLovedFinished( QNetworkReply* reply )
|
||||
{
|
||||
Q_ASSERT( reply );
|
||||
|
||||
try
|
||||
{
|
||||
lastfm::XmlQuery lfm;
|
||||
lfm.parse( reply->readAll() );
|
||||
|
||||
if ( !lfm.children( "lovedtracks" ).isEmpty() )
|
||||
{
|
||||
lastfm::XmlQuery loved = lfm.children( "lovedtracks" ).first();
|
||||
|
||||
const int thisPage = loved.attribute( "page" ).toInt();
|
||||
|
||||
if ( m_totalLovedPages < 0 )
|
||||
{
|
||||
m_totalLovedPages = loved.attribute( "totalPages" ).toInt();
|
||||
m_ui->progressBar->setMaximum( m_totalLovedPages );
|
||||
}
|
||||
|
||||
m_ui->progressBar->setValue( thisPage );
|
||||
foreach ( lastfm::XmlQuery e, loved.children( "track" ) )
|
||||
{
|
||||
// tDebug() << "Found:" << e.children( "artist" ).first()["name"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt();
|
||||
Tomahawk::query_ptr query = Tomahawk::Query::get( e.children( "artist" ).first()["name"].text(), e["name"].text(), QString(), QString(), false );
|
||||
if ( query.isNull() )
|
||||
continue;
|
||||
|
||||
m_lastfmLoved.insert( query );
|
||||
}
|
||||
|
||||
|
||||
if ( thisPage == m_totalLovedPages )
|
||||
{
|
||||
m_doneFetchingLoved = true;
|
||||
|
||||
if ( !m_localLoved.isEmpty() )
|
||||
syncLoved();
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
syncLovedTracks( thisPage + 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( lastfm::ws::ParseError e )
|
||||
{
|
||||
tDebug() << "XmlQuery error:" << e.message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool trackEquality( const Tomahawk::query_ptr& first, const Tomahawk::query_ptr& second )
|
||||
{
|
||||
return first->equals( second, true );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LastFmConfig::localLovedLoaded( DatabaseCommand_LoadSocialActions::TrackActions tracks )
|
||||
{
|
||||
m_localLoved = tracks;
|
||||
|
||||
if ( m_doneFetchingLoved )
|
||||
syncLoved();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LastFmConfig::syncLoved()
|
||||
{
|
||||
QSet< Tomahawk::query_ptr > localToLove, lastFmToLove, lastFmToUnlove;
|
||||
|
||||
const QSet< Tomahawk::query_ptr > myLoved = m_localLoved.keys().toSet();
|
||||
|
||||
foreach ( const Tomahawk::query_ptr& lastfmLoved, m_lastfmLoved )
|
||||
{
|
||||
QSet< Tomahawk::query_ptr >::const_iterator iter = std::find_if( myLoved.begin(), myLoved.end(), boost::bind( &trackEquality, _1, boost::ref( lastfmLoved ) ) );
|
||||
if ( iter == myLoved.constEnd() )
|
||||
{
|
||||
// qDebug() << "Found last.fm loved track that we didn't have loved locally:" << lastfmLoved->track() << lastfmLoved->artist();
|
||||
localToLove << lastfmLoved;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( const Tomahawk::query_ptr& localLoved, myLoved )
|
||||
{
|
||||
QSet< Tomahawk::query_ptr >::const_iterator iter = std::find_if( m_lastfmLoved.begin(), m_lastfmLoved.end(), boost::bind( &trackEquality, _1, boost::ref( localLoved ) ) );
|
||||
|
||||
// If we unloved it locally, but it's still loved on last.fm, unlove it
|
||||
if ( m_localLoved[ localLoved ].value.toString() == "false" && iter != m_lastfmLoved.constEnd() )
|
||||
lastFmToUnlove << localLoved;
|
||||
|
||||
// If we loved it locally but not loved on last.fm, love it
|
||||
if ( m_localLoved[ localLoved ].value.toString() == "true" && iter == m_lastfmLoved.constEnd() )
|
||||
lastFmToLove << localLoved;
|
||||
}
|
||||
|
||||
foreach ( const Tomahawk::query_ptr& track, localToLove )
|
||||
{
|
||||
// Don't use the infosystem as we don't want to tweet a few hundred times :)
|
||||
DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction( track, QString( "Love" ), QString( "true" ) );
|
||||
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
|
||||
}
|
||||
|
||||
lastFmToLove.unite( lastFmToUnlove );
|
||||
|
||||
foreach ( const Tomahawk::query_ptr& track, lastFmToLove )
|
||||
{
|
||||
lastfm::MutableTrack lfmTrack;
|
||||
lfmTrack.stamp();
|
||||
|
||||
lfmTrack.setTitle( track->track() );
|
||||
lfmTrack.setArtist( track->artist() );
|
||||
lfmTrack.setSource( lastfm::Track::Player );
|
||||
|
||||
if ( lastFmToUnlove.contains( track ) )
|
||||
lfmTrack.unlove();
|
||||
else
|
||||
lfmTrack.love();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
|
||||
* Copyright 2010-2012, Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -19,7 +19,12 @@
|
||||
#ifndef LASTFMCONFIG_H
|
||||
#define LASTFMCONFIG_H
|
||||
|
||||
#include "Query.h"
|
||||
#include "database/DatabaseCommand_LoadSocialActions.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSet>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class Ui_LastFmConfig;
|
||||
|
||||
@ -45,19 +50,31 @@ public slots:
|
||||
|
||||
private slots:
|
||||
void enableButton();
|
||||
|
||||
|
||||
void loadHistory();
|
||||
void onHistoryLoaded();
|
||||
|
||||
void syncLovedTracks() { syncLovedTracks( 1 ); }
|
||||
void syncLovedTracks( uint page );
|
||||
void onLovedFinished( QNetworkReply* reply );
|
||||
void localLovedLoaded( DatabaseCommand_LoadSocialActions::TrackActions );
|
||||
|
||||
signals:
|
||||
void sizeHintChanged();
|
||||
|
||||
private:
|
||||
void syncLoved();
|
||||
|
||||
LastFmAccount* m_account;
|
||||
Ui_LastFmConfig* m_ui;
|
||||
|
||||
|
||||
unsigned int m_page;
|
||||
unsigned int m_lastTimeStamp;
|
||||
|
||||
int m_totalLovedPages;
|
||||
bool m_doneFetchingLoved;
|
||||
QSet< Tomahawk::query_ptr > m_lastfmLoved;
|
||||
DatabaseCommand_LoadSocialActions::TrackActions m_localLoved;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../../resources.qrc">:/data/images/lastfm-icon.png</pixmap>
|
||||
<pixmap>:/data/images/lastfm-icon.png</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
@ -84,6 +84,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="syncLovedTracks">
|
||||
<property name="text">
|
||||
<string>Synchronize Loved Tracks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
|
@ -37,40 +37,74 @@ DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi )
|
||||
|
||||
TomahawkSqlQuery query = dbi->newquery();
|
||||
|
||||
QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id();
|
||||
|
||||
int artid = dbi->artistId( m_artist, false );
|
||||
if( artid < 1 )
|
||||
return;
|
||||
|
||||
int trkid = dbi->trackId( artid, m_track, false );
|
||||
if( trkid < 1 )
|
||||
return;
|
||||
|
||||
QString whereToken;
|
||||
whereToken = QString( "WHERE id IS %1" ).arg( trkid );
|
||||
|
||||
QString sql = QString(
|
||||
"SELECT k, v, timestamp, source "
|
||||
"FROM social_attributes %1 "
|
||||
"ORDER BY timestamp ASC" ).arg( whereToken );
|
||||
|
||||
query.prepare( sql );
|
||||
query.exec();
|
||||
|
||||
QList< Tomahawk::SocialAction > allSocialActions;
|
||||
while ( query.next() )
|
||||
if ( m_actionOnly.isNull() )
|
||||
{
|
||||
Tomahawk::SocialAction action;
|
||||
action.action = query.value( 0 ); // action
|
||||
action.value = query.value( 1 ); // comment
|
||||
action.timestamp = query.value( 2 ); // timestamp
|
||||
action.source = SourceList::instance()->get( query.value( 3 ).toInt() ); // source
|
||||
|
||||
if ( !action.source.isNull() )
|
||||
allSocialActions.append( action );
|
||||
}
|
||||
// Load for just specified track
|
||||
int artid = dbi->artistId( m_artist, false );
|
||||
if( artid < 1 )
|
||||
return;
|
||||
|
||||
m_query->setAllSocialActions( allSocialActions );
|
||||
int trkid = dbi->trackId( artid, m_track, false );
|
||||
if( trkid < 1 )
|
||||
return;
|
||||
|
||||
QString whereToken;
|
||||
whereToken = QString( "WHERE id IS %1" ).arg( trkid );
|
||||
|
||||
QString sql = QString(
|
||||
"SELECT k, v, timestamp, source "
|
||||
"FROM social_attributes %1 "
|
||||
"ORDER BY timestamp ASC" ).arg( whereToken );
|
||||
|
||||
query.prepare( sql );
|
||||
query.exec();
|
||||
|
||||
QList< Tomahawk::SocialAction > allSocialActions;
|
||||
while ( query.next() )
|
||||
{
|
||||
Tomahawk::SocialAction action;
|
||||
action.action = query.value( 0 ); // action
|
||||
action.value = query.value( 1 ); // comment
|
||||
action.timestamp = query.value( 2 ); // timestamp
|
||||
action.source = SourceList::instance()->get( query.value( 3 ).toInt() ); // source
|
||||
|
||||
if ( !action.source.isNull() )
|
||||
allSocialActions.append( action );
|
||||
}
|
||||
|
||||
m_query->setAllSocialActions( allSocialActions );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load all tracks with this social action
|
||||
const QString srcStr = source()->isLocal() ? "IS NULL" : QString( "=%1" ).arg( source()->id() );
|
||||
|
||||
query.prepare( QString( "SELECT id, v, timestamp FROM social_attributes WHERE source %1 AND k = ? " ).arg( srcStr ) );
|
||||
query.addBindValue( m_actionOnly );
|
||||
|
||||
query.exec();
|
||||
|
||||
DatabaseCommand_LoadSocialActions::TrackActions trackActions;
|
||||
while ( query.next() )
|
||||
{
|
||||
const QVariantMap track = dbi->track( query.value( 0 ).toInt() );
|
||||
if ( track.value( "artist" ).toString().isEmpty() || track.value( "name" ).toString().isEmpty() )
|
||||
continue;
|
||||
|
||||
const QVariantMap artist = dbi->artist( track.value( "artist" ).toInt() );
|
||||
|
||||
const query_ptr trackQuery = Query::get( artist.value( "name" ).toString(), track.value( "name" ).toString(), QString(), QString(), false );
|
||||
|
||||
Tomahawk::SocialAction action;
|
||||
action.action = m_actionOnly; // action
|
||||
action.value = query.value( 1 ); // comment
|
||||
action.timestamp = query.value( 2 ); // timestamp
|
||||
action.source = source(); // source
|
||||
|
||||
trackActions[ trackQuery ] = action;
|
||||
}
|
||||
|
||||
emit done( trackActions );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2011, Christopher Reichert <creichert07@gmail.com>
|
||||
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -45,6 +46,7 @@ class DLLEXPORT DatabaseCommand_LoadSocialActions : public DatabaseCommand
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
typedef QMap<Tomahawk::query_ptr,Tomahawk::SocialAction> TrackActions;
|
||||
/**
|
||||
* \brief Default constructor for DatabaseCommand_LoadSocialActions.
|
||||
*
|
||||
@ -69,6 +71,16 @@ public:
|
||||
setTrack( query->track() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all tracks with a specific social action
|
||||
*/
|
||||
explicit DatabaseCommand_LoadSocialActions( const QString& action, const Tomahawk::source_ptr& source, QObject* parent = 0 )
|
||||
: DatabaseCommand( parent ), m_actionOnly( action )
|
||||
{
|
||||
setSource( source );
|
||||
qRegisterMetaType<TrackActions>( "DatabaseCommand_LoadSocialAction::TrackActions" );
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the name of this database command.
|
||||
* \return QString containing the database command name 'loadsocialaction'.
|
||||
@ -115,20 +127,20 @@ public:
|
||||
void setTrack( const QString& s ) { m_track = s; }
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
* \brief Emitted when the database command has finished the Query successfully
|
||||
*
|
||||
* \param QList of all social actions
|
||||
* \see QList
|
||||
* All loaded social actions for each track found, for queries that generate all tracks
|
||||
* with matching actions.
|
||||
*/
|
||||
void done( QList< Tomahawk::SocialAction >& allSocialActions );
|
||||
void done( DatabaseCommand_LoadSocialActions::TrackActions actionsForTracks );
|
||||
|
||||
private:
|
||||
Tomahawk::query_ptr m_query;
|
||||
QString m_artist;
|
||||
QString m_track;
|
||||
QString m_actionOnly;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE( DatabaseCommand_LoadSocialActions::TrackActions )
|
||||
|
||||
#endif // DATABASECOMMAND_LOADSOCIALACTIONS_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user