1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-06 14:16:32 +02:00

Initial work on Spotify account

Merge branch 'master' into spotifyAccount

Conflicts:
	src/libtomahawk/CMakeLists.txt
This commit is contained in:
Leo Franchi
2012-03-09 17:56:38 -05:00
160 changed files with 2693 additions and 1415 deletions

View File

@@ -1,5 +1,5 @@
PROJECT( tomahawk ) PROJECT( tomahawk )
CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) CMAKE_MINIMUM_REQUIRED( VERSION 2.8.6 )
SET( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" ) SET( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" )
IF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 ) IF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 )
@@ -15,11 +15,13 @@ SET( TOMAHAWK_APPLICATION_NAME "Tomahawk" )
SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" ) SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 ) SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 3 ) SET( TOMAHAWK_VERSION_MINOR 4 )
SET( TOMAHAWK_VERSION_PATCH 99 ) SET( TOMAHAWK_VERSION_PATCH 99 )
#SET( TOMAHAWK_VERSION_RC 0 ) #SET( TOMAHAWK_VERSION_RC 0 )
# enforce proper symbol exporting on all platforms
add_definitions( "-fvisibility=hidden" )
# build options # build options
option(BUILD_GUI "Build Tomahawk with GUI" ON) option(BUILD_GUI "Build Tomahawk with GUI" ON)
@@ -120,19 +122,6 @@ macro_log_feature(LIBJREEN_FOUND "Jreen" "Qt XMPP Library" "https://github.com/e
macro_optional_find_package(QTweetLib) macro_optional_find_package(QTweetLib)
macro_log_feature(QTWEETLIB_FOUND "QTweetLib" "Qt Twitter Library" "https://github.com/minimoog/QTweetLib" FALSE "" "QTweetLib is needed for the Twitter SIP plugin.\n") macro_log_feature(QTWEETLIB_FOUND "QTweetLib" "Qt Twitter Library" "https://github.com/minimoog/QTweetLib" FALSE "" "QTweetLib is needed for the Twitter SIP plugin.\n")
IF( NOT QuaZip_FOUND )
add_subdirectory( ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip )
SET( QuaZip_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/libtomahawk/thirdparty/quazip )
SET( QuaZip_LIBRARY quazip )
SET( QuaZip_LIBRARIES ${QuaZip_LIBRARY} )
SET( QuaZip_FOUND true )
macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically. Building internal copy" "http://quazip.sourceforge.net/" FALSE "" "")
# copy headers to build/quazip so we can use proper includes inside the code
FILE( COPY ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip/quazip/ DESTINATION ${CMAKE_BINARY_DIR}/libtomahawk/thirdparty/quazip )
ENDIF()
# required # required
#While we distribute our own liblastfm2, don't need to look for it #While we distribute our own liblastfm2, don't need to look for it
#macro_optional_find_package(LibLastFm 0.3.3) #macro_optional_find_package(LibLastFm 0.3.3)

View File

@@ -1,3 +1,39 @@
Version 0.4.0:
* Added visual notification for database indexing job.
* Fixed icons not appearing in resolvers list.
* Fixed various UI glitches and stray error messages in stations.
* Fixed bug where album page would resolve bottom-to-top.
* Fixed bug where Footnotes would not update when changing selected album in Album View.
* Fixed dragging albums and artists from charts, album, and artist views.
* Fixed bug where filter text would be one step behind filter value.
* Fixed bug where resolvers would enable themselves after auto-updating.
* Fixed occasional crash when dropping tracks onto New Station item.
* Added jump-to-current-track support for search results page.
* Fixed out of sync Show/Hide menu items on OS X when hidden with cmd-h.
* Fixed non-resolving tracks when dragging from album view.
* Fixed /Volumes directory not showing up on OS X.
* Fixed fetching album covers for albums with special characters.
* Show errors and continue gracefully when resolved audio is not available.
* Fixed various crashes on exit.
* Added basic command-line options for playback control.
* Bumped up web api timeouts to allow web clients to finish resolving.
* Added filename suggestion when exporting a playlist.
* Cleaned up highlighting of artist names in album view.
* Cleaned up alignment of playlist items.
* Fixed potential crash when searching.
* Added support for disc number.
* Added SoundCloudWall.com charts.
* Added ability to "lock on" to a user when listening along, to skip along.
* Fixed bug where loved tracks would be refreshed much too often.
* Fixed startup crash on OS X.
* Fixed some font size issues.
* Sped up Tomahawk startup by moving chart loading into a separate thread.
* Added support for parsing Grooveshark and Tinysong tracks and playlists.
* Reorganized sidebar to follow more logical item groupings.
* Added artist and album results to global searches.
* Fixed style and contrast issues when using GTK styles.
* Fixed paths to artwork when using MPRIS2 interface.
Version 0.3.3: Version 0.3.3:
* Automatically load Super Collection tracks when no official release * Automatically load Super Collection tracks when no official release
information is available. information is available.

2
README
View File

@@ -39,6 +39,7 @@ Dependencies
The following dependencies are optional, but recommended: The following dependencies are optional, but recommended:
Attica 0.2.0 - ftp://ftp.kde.org/pub/kde/stable/attica/ Attica 0.2.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
QuaZip 0.4.3 - http://quazip.sourceforge.net/
Jreen 1.0.1 - https://github.com/euroelessar/jreen Jreen 1.0.1 - https://github.com/euroelessar/jreen
QTweetLib 0.3.0 - https://github.com/minimoog/QTweetLib QTweetLib 0.3.0 - https://github.com/minimoog/QTweetLib
@@ -46,6 +47,5 @@ Dependencies
MiniUPnP 1.6 - http://miniupnp.free.fr/ MiniUPnP 1.6 - http://miniupnp.free.fr/
liblastfm 0.4.0 - http://github.com/jonocole/liblastfm/ liblastfm 0.4.0 - http://github.com/jonocole/liblastfm/
QuaZip 0.4.3 - http://quazip.sourceforge.net/
Enjoy! Enjoy!

View File

@@ -491,7 +491,7 @@ def FindVLCPlugin(name):
FixBinary(binary) FixBinary(binary)
for plugin in VLC_PLUGINS: for plugin in VLC_PLUGINS:
FixVLCPlugin(FindVLCPlugin(plugin), '.') FixVLCPlugin(FindVLCPlugin(plugin), '../Frameworks/vlc/plugins')
for plugin in TOMAHAWK_PLUGINS: for plugin in TOMAHAWK_PLUGINS:
FixPlugin(plugin, '../MacOS') FixPlugin(plugin, '../MacOS')

BIN
data/images/grooveshark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -133,7 +133,9 @@
<file>data/images/headphones-bigger.png</file> <file>data/images/headphones-bigger.png</file>
<file>data/images/no-album-no-case.png</file> <file>data/images/no-album-no-case.png</file>
<file>data/images/rdio.png</file> <file>data/images/rdio.png</file>
<file>data/images/grooveshark.png</file>
<file>data/images/lastfm-icon.png</file> <file>data/images/lastfm-icon.png</file>
<file>data/sql/dbmigrate-27_to_28.sql</file> <file>data/sql/dbmigrate-27_to_28.sql</file>
<file>data/images/process-stop.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -34,10 +34,10 @@
#define PADDING_BETWEEN_STARS 2 #define PADDING_BETWEEN_STARS 2
#define STAR_SIZE 12 #define STAR_SIZE 12
#ifdef Q_WS_MAC #ifdef Q_OS_MAC
#define TOPLEVEL_ACCOUNT_HEIGHT 72 #define ROW_HEIGHT_MULTIPLIER 4.9
#else #else
#define TOPLEVEL_ACCOUNT_HEIGHT 68 #define ROW_HEIGHT_MULTIPLIER 5.7
#endif #endif
#define ICONSIZE 40 #define ICONSIZE 40
@@ -52,6 +52,7 @@ using namespace Accounts;
AccountDelegate::AccountDelegate( QObject* parent ) AccountDelegate::AccountDelegate( QObject* parent )
: QStyledItemDelegate ( parent ) : QStyledItemDelegate ( parent )
, m_accountRowHeight( -1 )
{ {
m_defaultCover.load( RESPATH "images/sipplugin-online.png" ); m_defaultCover.load( RESPATH "images/sipplugin-online.png" );
@@ -75,20 +76,31 @@ AccountDelegate::AccountDelegate( QObject* parent )
QSize QSize
AccountDelegate::sizeHint( const QStyleOptionViewItem&, const QModelIndex& index ) const AccountDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{ {
AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() ); AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() );
if ( m_accountRowHeight < 0 )
{
// Haven't calculated normal item height yet, do it once and save it
QStyleOptionViewItemV4 opt( option );
initStyleOption( &opt, index );
m_accountRowHeight = ROW_HEIGHT_MULTIPLIER * opt.fontMetrics.height();
}
if ( rowType == AccountModel::TopLevelAccount || rowType == AccountModel::UniqueFactory || rowType == AccountModel::CustomAccount ) if ( rowType == AccountModel::TopLevelAccount || rowType == AccountModel::UniqueFactory || rowType == AccountModel::CustomAccount )
return QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT ); {
return QSize( 200, m_accountRowHeight );
}
else if ( rowType == AccountModel::TopLevelFactory ) else if ( rowType == AccountModel::TopLevelFactory )
{ {
// Make more space for each account we have to show. // Make more space for each account we have to show.
AccountFactory* fac = qobject_cast< AccountFactory* >( index.data( AccountModel::AccountData ).value< QObject* >() ); AccountFactory* fac = qobject_cast< AccountFactory* >( index.data( AccountModel::AccountData ).value< QObject* >() );
if ( fac->isUnique() ) if ( fac->isUnique() )
return QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT ); return QSize( 200, m_accountRowHeight );
const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >(); const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >();
const QSize s = QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT + 12 * accts.size()-1 ); const QSize s = QSize( 200, m_accountRowHeight + 12 * accts.size()-1 );
if ( s != m_sizeHints[ index ] ) if ( s != m_sizeHints[ index ] )
const_cast< AccountDelegate* >( this )->sizeHintChanged( index ); // FU KTHBBQ const_cast< AccountDelegate* >( this )->sizeHintChanged( index ); // FU KTHBBQ
@@ -200,7 +212,6 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option,
{ {
painter->save(); painter->save();
painter->setFont( installFont ); painter->setFont( installFont );
int oldRightEdge = rightEdge;
rightEdge = drawAccountList( painter, opt, accts, rightEdge ); rightEdge = drawAccountList( painter, opt, accts, rightEdge );
painter->restore(); painter->restore();

View File

@@ -64,6 +64,7 @@ private:
mutable QHash< QPersistentModelIndex, QRect > m_cachedStarRects; mutable QHash< QPersistentModelIndex, QRect > m_cachedStarRects;
mutable QHash< QPersistentModelIndex, QRect > m_cachedConfigRects; mutable QHash< QPersistentModelIndex, QRect > m_cachedConfigRects;
mutable QHash< QPersistentModelIndex, QSize > m_sizeHints; mutable QHash< QPersistentModelIndex, QSize > m_sizeHints;
mutable int m_accountRowHeight;
}; };
} }

View File

@@ -19,7 +19,8 @@
#include "AccountFactoryWrapper.h" #include "AccountFactoryWrapper.h"
#include "accounts/Account.h" #include "accounts/Account.h"
#include <accounts/AccountManager.h> #include "accounts/AccountManager.h"
#include "guihelpers.h"
#include "AccountFactoryWrapperDelegate.h" #include "AccountFactoryWrapperDelegate.h"
#include "delegateconfigwrapper.h" #include "delegateconfigwrapper.h"
#include "ui_AccountFactoryWrapper.h" #include "ui_AccountFactoryWrapper.h"
@@ -29,7 +30,6 @@ AccountFactoryWrapper::AccountFactoryWrapper( AccountFactory* factory, QWidget*
: QDialog( parent, Qt::Sheet ) : QDialog( parent, Qt::Sheet )
, m_factory( factory ) , m_factory( factory )
, m_ui( new Ui_AccountFactoryWrapper ) , m_ui( new Ui_AccountFactoryWrapper )
, m_createAccount( false )
{ {
m_ui->setupUi( this ); m_ui->setupUi( this );
@@ -92,40 +92,10 @@ AccountFactoryWrapper::load()
void void
AccountFactoryWrapper::openAccountConfig( Account* account ) AccountFactoryWrapper::openAccountConfig( Account* account )
{ {
if( account->configurationWidget() ) TomahawkUtils::openAccountConfig( account, this, false );
{
#ifndef Q_WS_MAC
DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this );
QWeakPointer< DelegateConfigWrapper > watcher( &dialog );
int ret = dialog.exec();
if( !watcher.isNull() && ret == QDialog::Accepted )
{
// send changed config to resolver
account->saveConfig();
}
#else
// on osx a sheet needs to be non-modal
DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this, Qt::Sheet );
dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) );
connect( dialog, SIGNAL( finished( int ) ), this, SLOT( accountConfigClosed( int ) ) );
dialog->show();
#endif
}
} }
void
AccountFactoryWrapper::accountConfigClosed( int value )
{
if( value == QDialog::Accepted )
{
DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() );
Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() );
account->saveConfig();
}
}
void void
AccountFactoryWrapper::removeAccount( Tomahawk::Accounts::Account* acct ) AccountFactoryWrapper::removeAccount( Tomahawk::Accounts::Account* acct )
{ {
@@ -139,9 +109,7 @@ AccountFactoryWrapper::buttonClicked( QAbstractButton* button )
{ {
if ( button == m_addButton ) if ( button == m_addButton )
{ {
m_createAccount = true; TomahawkUtils::createAccountFromFactory( m_factory, this );
emit createAccount( m_factory );
return;
} }
else else
reject(); reject();

View File

@@ -42,14 +42,8 @@ public:
explicit AccountFactoryWrapper( Tomahawk::Accounts::AccountFactory* factory, QWidget* parent = 0 ); explicit AccountFactoryWrapper( Tomahawk::Accounts::AccountFactory* factory, QWidget* parent = 0 );
bool doCreateAccount() const { return m_createAccount; }
signals:
void createAccount( Tomahawk::Accounts::AccountFactory* factory );
public slots: public slots:
void openAccountConfig( Tomahawk::Accounts::Account* ); void openAccountConfig( Tomahawk::Accounts::Account* );
void accountConfigClosed( int value );
void removeAccount( Tomahawk::Accounts::Account* ); void removeAccount( Tomahawk::Accounts::Account* );
private slots: private slots:
@@ -60,7 +54,6 @@ private:
Tomahawk::Accounts::AccountFactory* m_factory; Tomahawk::Accounts::AccountFactory* m_factory;
Ui_AccountFactoryWrapper* m_ui; Ui_AccountFactoryWrapper* m_ui;
QPushButton* m_addButton; QPushButton* m_addButton;
bool m_createAccount;
}; };
#endif // ACCOUNTFACTORYWRAPPER_H #endif // ACCOUNTFACTORYWRAPPER_H

View File

@@ -58,4 +58,7 @@ if (APPLE)
FILE(COPY ${CMAKE_SOURCE_DIR}/admin/mac/sparkle_pub.pem FILE(COPY ${CMAKE_SOURCE_DIR}/admin/mac/sparkle_pub.pem
DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/Resources") DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/Resources")
FILE(COPY /usr/bin/SetFile DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
FILE(COPY /usr/bin/GetFileInfo DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
endif (APPLE) endif (APPLE)

View File

@@ -70,63 +70,21 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
breakpad/BreakPad.cpp breakpad/BreakPad.cpp
utils/guihelpers.cpp
tomahawktrayicon.cpp tomahawktrayicon.cpp
audiocontrols.cpp audiocontrols.cpp
settingsdialog.cpp settingsdialog.cpp
diagnosticsdialog.cpp diagnosticsdialog.cpp
AccountDelegate.cpp AccountDelegate.cpp
settingslistdelegate.cpp settingslistdelegate.cpp
delegateconfigwrapper.cpp
tomahawkwindow.cpp tomahawkwindow.cpp
LoadXSPFDialog.cpp LoadXSPFDialog.cpp
AccountFactoryWrapper.cpp AccountFactoryWrapper.cpp
AccountFactoryWrapperDelegate.cpp AccountFactoryWrapperDelegate.cpp
) )
SET( tomahawkHeaders ${tomahawkHeaders}
tomahawkapp.h
web/api_v1.h
musicscanner.h
scanmanager.h
ubuntuunityhack.h
shortcuthandler.h
)
IF(LIBLASTFM_FOUND)
SET(tomahawkHeaders ${tomahawkHeaders}
scrobbler.h
)
ENDIF(LIBLASTFM_FOUND)
SET( tomahawkHeadersGui ${tomahawkHeadersGui}
sourcetree/sourcesmodel.h
sourcetree/sourcesproxymodel.h
sourcetree/sourcetreeview.h
sourcetree/sourcedelegate.h
sourcetree/animationhelper.h
sourcetree/items/sourcetreeitem.h
sourcetree/items/sourceitem.h
sourcetree/items/playlistitems.h
sourcetree/items/categoryitems.h
sourcetree/items/genericpageitems.h
sourcetree/items/temporarypageitem.h
sourcetree/items/groupitem.h
sourcetree/items/historyitem.h
tomahawktrayicon.h
audiocontrols.h
settingsdialog.h
diagnosticsdialog.h
AccountDelegate.h
settingslistdelegate.h
delegateconfigwrapper.h
tomahawkwindow.h
LoadXSPFDialog.h
AccountFactoryWrapper.h
AccountFactoryWrapperDelegate.h
)
SET( tomahawkUI ${tomahawkUI} SET( tomahawkUI ${tomahawkUI}
tomahawkwindow.ui tomahawkwindow.ui
diagnosticsdialog.ui diagnosticsdialog.ui
@@ -180,17 +138,11 @@ ENDIF( UNIX )
IF( APPLE ) IF( APPLE )
INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/thirdparty/SPMediaKeyTap ) INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/thirdparty/SPMediaKeyTap )
SET( tomahawkHeaders ${tomahawkHeaders} mac/tomahawkapp_mac.h mac/macshortcuthandler.h )
SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp ) SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp )
IF(HAVE_SPARKLE)
SET( tomahawkHeaders ${tomahawkHeaders} ${SPARKLE}/Headers )
ENDIF(HAVE_SPARKLE)
ENDIF( APPLE ) ENDIF( APPLE )
IF(GLOOX_FOUND) IF(GLOOX_FOUND)
INCLUDE_DIRECTORIES( ${GLOOX_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( ${GLOOX_INCLUDE_DIR} )
SET( tomahawkHeaders ${tomahawkHeaders} xmppbot/xmppbot.h )
SET( tomahawkSources ${tomahawkSources} xmppbot/xmppbot.cpp ) SET( tomahawkSources ${tomahawkSources} xmppbot/xmppbot.cpp )
ENDIF(GLOOX_FOUND) ENDIF(GLOOX_FOUND)
@@ -206,17 +158,16 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake ) include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake )
SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ${trans_outfile}) SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${trans_outfile})
IF( BUILD_GUI ) IF( BUILD_GUI )
LIST(APPEND tomahawkHeaders ${tomahawkHeadersGui})
LIST(APPEND tomahawkSources ${tomahawkSourcesGui}) LIST(APPEND tomahawkSources ${tomahawkSourcesGui})
qt4_wrap_ui( tomahawkUI_H ${tomahawkUI} ) qt4_wrap_ui( tomahawkUI_H ${tomahawkUI} )
ENDIF() ENDIF()
kde4_add_app_icon( tomahawkSources "${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon-*.png" ) kde4_add_app_icon( tomahawkSources "${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon-*.png" )
qt4_add_resources( RC_SRCS "../resources.qrc" ) qt4_add_resources( RC_SRCS "../resources.qrc" )
qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} )
SET( final_src ${final_src} ${tomahawkUI_H} ${tomahawkMoc} ${tomahawkSources} ${RC_SRCS} ) SET( final_src ${final_src} ${tomahawkUI_H} ${tomahawkMoc} ${tomahawkSources} ${RC_SRCS} )
IF( UNIX AND NOT APPLE ) IF( UNIX AND NOT APPLE )
@@ -224,13 +175,14 @@ IF( UNIX AND NOT APPLE )
ENDIF( UNIX AND NOT APPLE ) ENDIF( UNIX AND NOT APPLE )
IF( APPLE ) IF( APPLE )
ADD_EXECUTABLE( tomahawk MACOSX_BUNDLE ${final_src} ) ADD_EXECUTABLE( tomahawk MACOSX_BUNDLE ${final_src} )
SET_TARGET_PROPERTIES(tomahawk PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_BINARY_DIR}/Info.plist" SET_TARGET_PROPERTIES(tomahawk PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_BINARY_DIR}/Info.plist")
)
ENDIF( APPLE ) ENDIF( APPLE )
IF( WIN32 ) IF( WIN32 )
ADD_EXECUTABLE( tomahawk WIN32 ${final_src} ) ADD_EXECUTABLE( tomahawk WIN32 ${final_src} )
ENDIF( WIN32 ) ENDIF( WIN32 )
SET_TARGET_PROPERTIES(tomahawk PROPERTIES AUTOMOC TRUE)
MESSAGE( STATUS "OS_SPECIFIC_LINK_LIBRARIES: ${OS_SPECIFIC_LINK_LIBRARIES}" ) MESSAGE( STATUS "OS_SPECIFIC_LINK_LIBRARIES: ${OS_SPECIFIC_LINK_LIBRARIES}" )
SET(LINK_LIBRARIES "") SET(LINK_LIBRARIES "")

View File

@@ -100,8 +100,6 @@ AudioControls::AudioControls( QWidget* parent )
m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve ); m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve );
ui->seekSlider->setTimeLine( &m_sliderTimeLine ); ui->seekSlider->setTimeLine( &m_sliderTimeLine );
m_defaultCover = QPixmap( RESPATH "images/no-album-no-case.png" ).scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
connect( &m_phononTickCheckTimer, SIGNAL( timeout() ), SLOT( phononTickCheckTimeout() ) ); connect( &m_phononTickCheckTimer, SIGNAL( timeout() ), SLOT( phononTickCheckTimeout() ) );
connect( &m_sliderTimeLine, SIGNAL( frameChanged( int ) ), ui->seekSlider, SLOT( setValue( int ) ) ); connect( &m_sliderTimeLine, SIGNAL( frameChanged( int ) ), ui->seekSlider, SLOT( setValue( int ) ) );
@@ -264,14 +262,14 @@ AudioControls::onAlbumCoverUpdated()
void void
AudioControls::setAlbumCover() AudioControls::setAlbumCover()
{ {
if ( !m_currentTrack->album()->cover().isNull() ) if ( !m_currentTrack->album()->cover( ui->coverImage->size() ).isNull() )
{ {
QPixmap cover; QPixmap cover;
cover.loadFromData( m_currentTrack->album()->cover() ); cover = m_currentTrack->album()->cover( ui->coverImage->size() );
ui->coverImage->setPixmap( cover.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) ); ui->coverImage->setPixmap( cover );
} }
else else
ui->coverImage->setPixmap( m_defaultCover ); ui->coverImage->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, ui->coverImage->size() ) );
} }
@@ -279,10 +277,16 @@ void
AudioControls::onSocialActionsLoaded() AudioControls::onSocialActionsLoaded()
{ {
Query* query = qobject_cast< Query* >( sender() ); Query* query = qobject_cast< Query* >( sender() );
if ( !query || query != m_currentTrack->toQuery().data() ) if ( !query )
return; return;
query_ptr currentQuery = m_currentTrack->toQuery();
if ( query->artist() == currentQuery->artist() &&
query->track() == currentQuery->track() &&
query->album() == currentQuery->album() )
{
setSocialActions(); setSocialActions();
}
} }

View File

@@ -91,8 +91,6 @@ private:
Ui::AudioControls *ui; Ui::AudioControls *ui;
QPixmap m_defaultCover;
Tomahawk::result_ptr m_currentTrack; Tomahawk::result_ptr m_currentTrack;
Tomahawk::PlaylistInterface::RepeatMode m_repeatMode; Tomahawk::PlaylistInterface::RepeatMode m_repeatMode;
bool m_shuffled; bool m_shuffled;

View File

@@ -0,0 +1,114 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "delegateconfigwrapper.h"
DelegateConfigWrapper::DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags )
: QDialog( parent, flags )
, m_widget( conf )
, m_deleted( false )
{
m_widget->setWindowFlags( Qt::Sheet );
#ifdef Q_WS_MAC
m_widget->setVisible( true );
#endif
setWindowTitle( title );
QVBoxLayout* v = new QVBoxLayout( this );
v->setContentsMargins( 0, 0, 0, 0 );
v->addWidget( m_widget );
m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this );
m_okButton = m_buttons->button( QDialogButtonBox::Ok );
connect( m_buttons, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( closed( QAbstractButton* ) ) );
connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) );
v->addWidget( m_buttons );
setLayout( v );
#ifdef Q_WS_MAC
setSizeGripEnabled( false );
setMinimumSize( sizeHint() );
setMaximumSize( sizeHint() ); // to remove the resize grip on osx this is the only way
if( conf->metaObject()->indexOfSignal( "sizeHintChanged()" ) > -1 )
connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) );
#else
m_widget->setVisible( true );
#endif
}
void
DelegateConfigWrapper::setShowDelete( bool del )
{
if ( del )
m_deleteButton = m_buttons->addButton( tr( "Delete Account" ), QDialogButtonBox::DestructiveRole );
}
void
DelegateConfigWrapper::toggleOkButton( bool dataError )
{
// if dataError is True we want to set the button enabled to false
m_okButton->setEnabled( !dataError );
}
void
DelegateConfigWrapper::closed( QAbstractButton* b )
{
// let the config widget live to see another day
layout()->removeWidget( m_widget );
m_widget->setParent( 0 );
m_widget->setVisible( false );
QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() );
if ( buttons->standardButton( b ) == QDialogButtonBox::Ok )
done( QDialog::Accepted );
else if ( b == m_deleteButton )
{
m_deleted = true;
emit closedWithDelete();
reject();
}
else
done( QDialog::Rejected );
}
void
DelegateConfigWrapper::rejected()
{
layout()->removeWidget( m_widget );
m_widget->setParent( 0 );
m_widget->setVisible( false );
}
void
DelegateConfigWrapper::updateSizeHint()
{
hide();
setSizeGripEnabled( false );
setMinimumSize( sizeHint() );
setMaximumSize( sizeHint() );
show();
}

View File

@@ -28,91 +28,23 @@ class DelegateConfigWrapper : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 ) : QDialog( parent, flags ), m_widget( conf ), m_deleted( false ) DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 );
{
m_widget->setWindowFlags( Qt::Sheet );
#ifdef Q_WS_MAC
m_widget->setVisible( true );
#endif
setWindowTitle( title );
QVBoxLayout* v = new QVBoxLayout( this );
v->setContentsMargins( 0, 0, 0, 0 );
v->addWidget( m_widget );
m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this );
m_okButton = m_buttons->button( QDialogButtonBox::Ok );
connect( m_buttons, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( closed( QAbstractButton* ) ) );
connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) );
v->addWidget( m_buttons );
setLayout( v );
#ifdef Q_WS_MAC
setSizeGripEnabled( false );
setMinimumSize( sizeHint() );
setMaximumSize( sizeHint() ); // to remove the resize grip on osx this is the only way
if( conf->metaObject()->indexOfSignal( "sizeHintChanged()" ) > -1 )
connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) );
#else
m_widget->setVisible( true );
#endif
}
~DelegateConfigWrapper() {} ~DelegateConfigWrapper() {}
void setShowDelete( bool del ) void setShowDelete( bool del );
{
if ( del )
m_deleteButton = m_buttons->addButton( tr( "Delete Account" ), QDialogButtonBox::DestructiveRole );
}
bool deleted() const { return m_deleted; } bool deleted() const { return m_deleted; }
public slots: public slots:
void toggleOkButton( bool dataError ) void toggleOkButton( bool dataError )
{ ;
// if dataError is True we want to set the button enabled to false void closed( QAbstractButton* b );
m_okButton->setEnabled( !dataError );
}
void closed( QAbstractButton* b )
{
// let the config widget live to see another day
layout()->removeWidget( m_widget );
m_widget->setParent( 0 );
m_widget->setVisible( false );
QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() );
if( buttons->standardButton( b ) == QDialogButtonBox::Ok )
done( QDialog::Accepted );
else if ( b == m_deleteButton )
{
m_deleted = true;
emit closedWithDelete();
reject();
}
else
done( QDialog::Rejected );
}
// we get a rejected() signal emitted if the user presses escape (and no clicked() signal ) // we get a rejected() signal emitted if the user presses escape (and no clicked() signal )
void rejected() void rejected();
{
layout()->removeWidget( m_widget );
m_widget->setParent( 0 );
m_widget->setVisible( false );
}
void updateSizeHint() void updateSizeHint();
{
hide();
setSizeGripEnabled( false );
setMinimumSize( sizeHint() );
setMaximumSize( sizeHint() );
show();
}
signals: signals:
void closedWithDelete(); void closedWithDelete();

View File

@@ -78,8 +78,11 @@ AtticaManager::loadPixmapsFromCache()
} }
QPixmap* icon = new QPixmap( cacheDir.absoluteFilePath( file ) ); QPixmap* icon = new QPixmap( cacheDir.absoluteFilePath( file ) );
if ( !icon->isNull() )
{
m_resolverStates[ info.baseName() ].pixmap = icon; m_resolverStates[ info.baseName() ].pixmap = icon;
} }
}
} }
@@ -95,14 +98,21 @@ AtticaManager::savePixmapsToCache()
foreach( const QString& id, m_resolverStates.keys() ) foreach( const QString& id, m_resolverStates.keys() )
{ {
if ( !m_resolverStates[ id ].pixmap ) if ( !m_resolverStates[ id ].pixmap || !m_resolverStates[ id ].pixmapDirty )
continue; continue;
const QString filename = cacheDir.absoluteFilePath( QString( "%1.png" ).arg( id ) ); const QString filename = cacheDir.absoluteFilePath( QString( "%1.png" ).arg( id ) );
if ( !m_resolverStates[ id ].pixmap->save( filename ) ) QFile f( filename );
if ( !f.open( QIODevice::WriteOnly ) )
{ {
tLog() << "Failed to open cache file for writing:" << filename; tLog() << "Failed to open cache file for writing:" << filename;
continue; }
else
{
if ( !m_resolverStates[ id ].pixmap->save( &f ) )
{
tLog() << "Failed to save pixmap into opened file for writing:" << filename;
}
} }
} }
} }
@@ -315,6 +325,7 @@ AtticaManager::resolverIconFetched()
QPixmap* icon = new QPixmap; QPixmap* icon = new QPixmap;
icon->loadFromData( data ); icon->loadFromData( data );
m_resolverStates[ resolverId ].pixmap = icon; m_resolverStates[ resolverId ].pixmap = icon;
m_resolverStates[ resolverId ].pixmapDirty = true;
} }
@@ -389,7 +400,7 @@ AtticaManager::upgradeResolver( const Content& resolver )
emit resolverStateChanged( resolver.id() ); emit resolverStateChanged( resolver.id() );
uninstallResolver( resolver ); uninstallResolver( resolver );
installResolver( resolver ); installResolver( resolver, false );
} }
@@ -447,6 +458,7 @@ AtticaManager::payloadFetched()
// Do the install / add to tomahawk // Do the install / add to tomahawk
Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, true ); Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, true );
Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver ); Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver );
TomahawkSettings::instance()->addAccount( resolver->accountId() );
} }
m_resolverStates[ resolverId ].state = Installed; m_resolverStates[ resolverId ].state = Installed;

View File

@@ -26,16 +26,12 @@
#include <QPixmap> #include <QPixmap>
#include "dllmacro.h" #include "dllmacro.h"
#include "accounts/Account.h"
#include <attica/provider.h> #include <attica/provider.h>
#include <attica/providermanager.h> #include <attica/providermanager.h>
#include <attica/content.h> #include <attica/content.h>
namespace Tomahawk {
namespace Accounts {
class Account;
}
}
class DLLEXPORT AtticaManager : public QObject class DLLEXPORT AtticaManager : public QObject
{ {
@@ -56,9 +52,12 @@ public:
ResolverState state; ResolverState state;
QPixmap* pixmap; QPixmap* pixmap;
// internal
bool pixmapDirty;
Resolver( const QString& v, const QString& path, int userR, ResolverState s ) Resolver( const QString& v, const QString& path, int userR, ResolverState s )
: version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ) {} : version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), pixmapDirty( false ) {}
Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ) {} Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), pixmapDirty( false ) {}
}; };
typedef QHash< QString, AtticaManager::Resolver > StateHash; typedef QHash< QString, AtticaManager::Resolver > StateHash;
@@ -137,6 +136,19 @@ private:
static AtticaManager* s_instance; static AtticaManager* s_instance;
}; };
class DLLEXPORT CustomAtticaAccount : public Tomahawk::Accounts::Account
{
Q_OBJECT
public:
virtual ~CustomAtticaAccount() {}
virtual Attica::Content atticaContent() const = 0;
protected:
// No, you can't.
CustomAtticaAccount( const QString& id ) : Tomahawk::Accounts::Account( id ) {}
};
Q_DECLARE_METATYPE( Attica::Content ); Q_DECLARE_METATYPE( Attica::Content );
#endif // ATTICAMANAGER_H #endif // ATTICAMANAGER_H

View File

@@ -35,9 +35,12 @@ set( libGuiSources
jobview/JobStatusView.cpp jobview/JobStatusView.cpp
jobview/JobStatusModel.cpp jobview/JobStatusModel.cpp
jobview/JobStatusDelegate.cpp jobview/JobStatusDelegate.cpp
jobview/JobStatusItem.cpp
jobview/PipelineStatusItem.cpp jobview/PipelineStatusItem.cpp
jobview/TransferStatusItem.cpp jobview/TransferStatusItem.cpp
jobview/LatchedStatusItem.cpp jobview/LatchedStatusItem.cpp
jobview/ErrorStatusMessage.cpp
jobview/IndexingJobItem.cpp
infobar/infobar.cpp infobar/infobar.cpp
@@ -113,6 +116,7 @@ set( libGuiSources
utils/proxystyle.cpp utils/proxystyle.cpp
utils/tomahawkutilsgui.cpp utils/tomahawkutilsgui.cpp
widgets/animatedcounterlabel.cpp
widgets/checkdirtree.cpp widgets/checkdirtree.cpp
widgets/querylabel.cpp widgets/querylabel.cpp
widgets/imagebutton.cpp widgets/imagebutton.cpp
@@ -145,137 +149,6 @@ IF(QCA2_FOUND)
set( libGuiSources ${libGuiSources} utils/groovesharkparser.cpp ) set( libGuiSources ${libGuiSources} utils/groovesharkparser.cpp )
ENDIF(QCA2_FOUND) ENDIF(QCA2_FOUND)
set( libGuiHeaders
actioncollection.h
contextmenu.h
dropjob.h
viewmanager.h
globalactionmanager.h
LatchManager.h
TomahawkSettingsGui.h
context/ContextPage.h
context/ContextWidget.h
context/pages/TopTracksContext.h
context/pages/RelatedArtistsContext.h
context/pages/WikipediaContext.h
context/pages/WebContext.h
infobar/infobar.h
playlist/topbar/topbar.h
playlist/topbar/clearbutton.h
playlist/topbar/searchlineedit.h
playlist/topbar/lineedit.h
playlist/topbar/lineedit_p.h
playlist/topbar/searchbutton.h
playlist/treemodel.h
playlist/treeproxymodel.h
playlist/treeproxymodelplaylistinterface.h
playlist/treeheader.h
playlist/treeitemdelegate.h
playlist/collectionproxymodel.h
playlist/collectionproxymodelplaylistinterface.h
playlist/collectionflatmodel.h
playlist/collectionview.h
playlist/playlistmodel.h
playlist/playlistproxymodel.h
playlist/playlistproxymodelplaylistinterface.h
playlist/playlistview.h
playlist/playlistitemdelegate.h
playlist/queueproxymodel.h
playlist/queueproxymodelplaylistinterface.h
playlist/queueview.h
playlist/trackmodel.h
playlist/trackmodelitem.h
playlist/trackproxymodel.h
playlist/trackproxymodelplaylistinterface.h
playlist/trackview.h
playlist/trackheader.h
playlist/treemodelitem.h
playlist/albumitem.h
playlist/albummodel.h
playlist/albumproxymodel.h
playlist/albumproxymodelplaylistinterface.h
playlist/albumitemdelegate.h
playlist/albumview.h
playlist/artistview.h
playlist/customplaylistview.h
playlist/ViewHeader.h
playlist/dynamic/DynamicPlaylist.h
playlist/dynamic/GeneratorInterface.h
playlist/dynamic/DynamicView.h
playlist/dynamic/DynamicModel.h
playlist/dynamic/echonest/EchonestGenerator.h
playlist/dynamic/echonest/EchonestControl.h
playlist/dynamic/echonest/EchonestSteerer.h
playlist/dynamic/widgets/DynamicWidget.h
playlist/dynamic/widgets/DynamicControlWrapper.h
playlist/dynamic/widgets/DynamicControlList.h
playlist/dynamic/widgets/ReadOrWriteWidget.h
playlist/dynamic/widgets/MiscControlWidgets.h
playlist/dynamic/widgets/CollapsibleControls.h
playlist/dynamic/widgets/DynamicSetupWidget.h
playlist/dynamic/widgets/LoadingSpinner.h
ExternalResolverGui.h
resolvers/scriptresolver.h
resolvers/qtscriptresolver.h
utils/widgetdragfilter.h
utils/xspfgenerator.h
utils/jspfloader.h
utils/spotifyparser.h
utils/m3uloader.h
utils/itunesparser.h
utils/rdioparser.h
utils/shortenedlinkparser.h
utils/dropjobnotifier.h
widgets/checkdirtree.h
widgets/querylabel.h
widgets/animatedcounterlabel.h
widgets/imagebutton.h
widgets/animatedsplitter.h
widgets/elidedlabel.h
widgets/newplaylistwidget.h
widgets/searchwidget.h
widgets/SeekSlider.h
widgets/playlisttypeselectordlg.h
widgets/welcomewidget.h
widgets/whatshotwidget.h
widgets/whatshotwidget_p.h
widgets/ChartDataLoader.h
widgets/RecentlyPlayedPlaylistsModel.h
widgets/RecentPlaylistsModel.h
widgets/OverlayButton.h
widgets/overlaywidget.h
widgets/HeaderLabel.h
widgets/HeaderWidget.h
widgets/combobox.h
widgets/ToggleButton.h
widgets/SocialPlaylistWidget.h
widgets/infowidgets/sourceinfowidget.h
widgets/infowidgets/ArtistInfoWidget.h
widgets/infowidgets/ArtistInfoWidget_p.h
widgets/infowidgets/AlbumInfoWidget.h
widgets/Breadcrumb.h
widgets/BreadcrumbButton.h
jobview/JobStatusView.h
jobview/JobStatusModel.h
jobview/JobStatusDelegate.h
jobview/JobStatusItem.h
jobview/PipelineStatusItem.h
jobview/TransferStatusItem.h
jobview/LatchedStatusItem.h
thirdparty/Qocoa/qsearchfield.h
)
IF(QCA2_FOUND) IF(QCA2_FOUND)
set( libGuiHeaders ${libGuiHeaders} utils/groovesharkparser.h ) set( libGuiHeaders ${libGuiHeaders} utils/groovesharkparser.h )
ENDIF(QCA2_FOUND) ENDIF(QCA2_FOUND)
@@ -291,6 +164,7 @@ set( libSources
album.cpp album.cpp
albumplaylistinterface.cpp albumplaylistinterface.cpp
collection.cpp collection.cpp
functimeout.cpp
playlist.cpp playlist.cpp
playlistplaylistinterface.cpp playlistplaylistinterface.cpp
resolver.cpp resolver.cpp
@@ -423,156 +297,6 @@ set( libSources
thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp
) )
set( libHeaders
tomahawksettings.h
sourcelist.h
pipeline.h
functimeout.h
playlistinterface.h
aclsystem.h
collection.h
query.h
resolver.h
ExternalResolver.h
result.h
source.h
sourceplaylistinterface.h
artist.h
artistplaylistinterface.h
album.h
albumplaylistinterface.h
playlist.h
playlistplaylistinterface.h
viewpage.h
accounts/Account.h
accounts/AccountManager.h
accounts/AccountModel.h
accounts/AccountModelFilterProxy.h
accounts/ResolverAccount.h
accounts/LastFmAccount.h
accounts/LastFmConfig.h
accounts/SpotifyAccount.h
EchonestCatalogSynchronizer.h
sip/SipPlugin.h
sip/SipHandler.h
sip/sipinfo.h
audio/audioengine.h
database/database.h
database/fuzzyindex.h
database/databaseworker.h
database/databaseimpl.h
database/databaseresolver.h
database/databasecommand.h
database/databasecommandloggable.h
database/databasecommand_resolve.h
database/databasecommand_allartists.h
database/databasecommand_allalbums.h
database/databasecommand_alltracks.h
database/databasecommand_addfiles.h
database/databasecommand_deletefiles.h
database/databasecommand_dirmtimes.h
database/databasecommand_filemtimes.h
database/databasecommand_loadfiles.h
database/databasecommand_logplayback.h
database/databasecommand_addsource.h
database/databasecommand_sourceoffline.h
database/databasecommand_collectionstats.h
database/databasecommand_loadplaylistentries.h
database/databasecommand_modifyplaylist.h
database/databasecommand_playbackhistory.h
database/databasecommand_setplaylistrevision.h
database/databasecommand_loadallplaylists.h
database/databasecommand_loadallsortedplaylists.h
database/databasecommand_loadallsources.h
database/databasecommand_createplaylist.h
database/databasecommand_deleteplaylist.h
database/databasecommand_renameplaylist.h
database/databasecommand_loadops.h
database/databasecommand_updatesearchindex.h
database/databasecollection.h
database/localcollection.h
database/databasecommand_setdynamicplaylistrevision.h
database/databasecommand_createdynamicplaylist.h
database/databasecommand_loaddynamicplaylist.h
database/databasecommand_loaddynamicplaylistentries.h
database/databasecommand_deletedynamicplaylist.h
database/databasecommand_loadallautoplaylists.h
database/databasecommand_loadallstations.h
database/databasecommand_addclientauth.h
database/databasecommand_clientauthvalid.h
database/databasecommand_socialaction.h
database/databasecommand_loadsocialactions.h
database/databasecommand_genericselect.h
database/databasecommand_setcollectionattributes.h
database/databasecommand_collectionattributes.h
database/databasecommand_trackattributes.h
database/databasecommand_settrackattributes.h
infosystem/infosystem.h
infosystem/infosystem.h
infosystem/infosystemworker.h
infosystem/infosystemcache.h
infosystem/infoplugins/generic/echonestplugin.h
infosystem/infoplugins/generic/lastfmplugin.h
infosystem/infoplugins/generic/chartsplugin.h
infosystem/infoplugins/generic/spotifyPlugin.h
infosystem/infoplugins/generic/hypemPlugin.h
infosystem/infoplugins/generic/musixmatchplugin.h
infosystem/infoplugins/generic/musicbrainzPlugin.h
infosystem/infoplugins/generic/RoviPlugin.h
network/bufferiodevice.h
network/msgprocessor.h
network/remotecollection.h
network/streamconnection.h
network/dbsyncconnection.h
network/servent.h
network/connection.h
network/controlconnection.h
network/portfwdthread.h
playlist/PlaylistUpdaterInterface.h
playlist/dynamic/DynamicPlaylist.h
playlist/dynamic/GeneratorInterface.h
playlist/dynamic/GeneratorFactory.h
playlist/XspfUpdater.h
playlist/dynamic/database/DatabaseGenerator.h
playlist/dynamic/database/DatabaseControl.h
playlist/dynamic/DynamicControl.h
thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.h
utils/tomahawkutilsgui.h
utils/xspfloader.h
utils/qnr_iodevicestream.h
)
set( libHeaders_NoMOC
viewpage.h
accounts/Account.h
infosystem/infoplugins/unix/imageconverter.h
taghandlers/tag.h
taghandlers/apetag.h
taghandlers/asftag.h
taghandlers/id3v1tag.h
taghandlers/id3v2tag.h
taghandlers/mp4tag.h
taghandlers/oggtag.h
utils/tomahawkutils.h
)
set( libUI ${libUI} set( libUI ${libUI}
widgets/playlisttypeselectordlg.ui widgets/playlisttypeselectordlg.ui
widgets/newplaylistwidget.ui widgets/newplaylistwidget.ui
@@ -665,9 +389,7 @@ IF( APPLE )
utils/tomahawkutils_mac.mm utils/tomahawkutils_mac.mm
thirdparty/Qocoa/qsearchfield_mac.mm ) thirdparty/Qocoa/qsearchfield_mac.mm )
SET( libHeaders ${libHeaders} SET_SOURCE_FILES_PROPERTIES(utils/tomahawkutils_mac.mm PROPERTIES COMPILE_FLAGS "-fvisibility=default")
infosystem/infoplugins/mac/adium.h
infosystem/infoplugins/mac/adiumplugin.h )
SET( OS_SPECIFIC_LINK_LIBRARIES SET( OS_SPECIFIC_LINK_LIBRARIES
${OS_SPECIFIC_LINK_LIBRARIES} ${OS_SPECIFIC_LINK_LIBRARIES}
@@ -689,15 +411,14 @@ ENDIF(LIBLASTFM_FOUND)
IF(BUILD_GUI) IF(BUILD_GUI)
LIST(APPEND libSources ${libGuiSources} ) LIST(APPEND libSources ${libGuiSources} )
LIST(APPEND libHeaders ${libGuiHeaders} )
ENDIF() ENDIF()
qt4_wrap_ui( libUI_H ${libUI} ) qt4_wrap_ui( libUI_H ${libUI} )
qt4_wrap_cpp( libMoc ${libHeaders} )
SET( libSources ${libSources} ${libUI_H} ${libHeaders_NoMOC} ) SET( libSources ${libSources} ${libUI_H} )
ADD_LIBRARY( tomahawklib SHARED ${libSources} ${libMoc} ) ADD_LIBRARY( tomahawklib SHARED ${libSources})
set_target_properties(tomahawklib PROPERTIES AUTOMOC TRUE)
IF(QCA2_FOUND) IF(QCA2_FOUND)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} ) SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} )

View File

@@ -55,6 +55,13 @@ Account::Account( const QString& accountId )
loadFromConfig( accountId ); loadFromConfig( accountId );
} }
Account::~Account()
{
sync();
}
QWidget* QWidget*
Account::configurationWidget() Account::configurationWidget()
{ {
@@ -192,7 +199,7 @@ Account::types() const
types |= SipType; types |= SipType;
if ( m_types.contains( "ResolverType" ) ) if ( m_types.contains( "ResolverType" ) )
types |= ResolverType; types |= ResolverType;
if ( m_types.contains( "StatusPushTypeType" ) ) if ( m_types.contains( "StatusPushType" ) )
types |= StatusPushType; types |= StatusPushType;
return types; return types;

View File

@@ -73,7 +73,7 @@ public:
enum ConnectionState { Disconnected, Connecting, Connected, Disconnecting }; enum ConnectionState { Disconnected, Connecting, Connected, Disconnecting };
explicit Account( const QString &accountId ); explicit Account( const QString &accountId );
virtual ~Account() {} virtual ~Account();
QString accountServiceName() const { QMutexLocker locker( &m_mutex ); return m_accountServiceName; } // e.g. "Twitter", "Last.fm" QString accountServiceName() const { QMutexLocker locker( &m_mutex ); return m_accountServiceName; } // e.g. "Twitter", "Last.fm"
QString accountFriendlyName() const { QMutexLocker locker( &m_mutex ); return m_accountFriendlyName; } // e.g. screen name on the service, JID, etc. QString accountFriendlyName() const { QMutexLocker locker( &m_mutex ); return m_accountFriendlyName; } // e.g. screen name on the service, JID, etc.

View File

@@ -182,7 +182,7 @@ AccountManager::loadPluginFactory( const QString& path )
void void
AccountManager::enableAccount( Account* account ) AccountManager::enableAccount( Account* account )
{ {
if ( account->isAuthenticated() ) if ( account->enabled() )
return; return;
account->authenticate(); account->authenticate();
@@ -197,7 +197,7 @@ AccountManager::enableAccount( Account* account )
void void
AccountManager::disableAccount( Account* account ) AccountManager::disableAccount( Account* account )
{ {
if ( !account->isAuthenticated() ) if ( !account->enabled() )
return; return;
account->deauthenticate(); account->deauthenticate();
@@ -215,8 +215,6 @@ AccountManager::connectAll()
{ {
acc->authenticate(); acc->authenticate();
m_enabledAccounts << acc; m_enabledAccounts << acc;
// if ( acc->types() & Accounts::SipType && acc->sipPlugin() )
// acc->sipPlugin()->connectPlugin();
} }
m_connected = true; m_connected = true;

View File

@@ -29,6 +29,7 @@
using namespace Tomahawk; using namespace Tomahawk;
using namespace Accounts; using namespace Accounts;
#define ACCOUNTMODEL_DEBUG 0
AccountModel::AccountModel( QObject* parent ) AccountModel::AccountModel( QObject* parent )
: QAbstractListModel( parent ) : QAbstractListModel( parent )
{ {
@@ -52,6 +53,11 @@ AccountModel::loadData()
// Add all factories // Add all factories
QList< AccountFactory* > factories = AccountManager::instance()->factories(); QList< AccountFactory* > factories = AccountManager::instance()->factories();
QList< Account* > allAccounts = AccountManager::instance()->accounts(); QList< Account* > allAccounts = AccountManager::instance()->accounts();
#if ACCOUNTMODEL_DEBUG
qDebug() << "All accounts:";
foreach ( Account* acct, allAccounts )
qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId();
#endif
foreach ( AccountFactory* fac, factories ) foreach ( AccountFactory* fac, factories )
{ {
if ( !fac->allowUserCreation() ) if ( !fac->allowUserCreation() )
@@ -76,16 +82,28 @@ AccountModel::loadData()
if ( AtticaManager::instance()->hasCustomAccountForAttica( content.id() ) ) if ( AtticaManager::instance()->hasCustomAccountForAttica( content.id() ) )
{ {
Account* acct = AtticaManager::instance()->customAccountForAttica( content.id() ); Account* acct = AtticaManager::instance()->customAccountForAttica( content.id() );
Q_ASSERT( acct );
if ( acct )
{
m_accounts << new AccountModelNode( acct ); m_accounts << new AccountModelNode( acct );
allAccounts.removeAll( acct ); const int removed = allAccounts.removeAll( acct );
#if ACCOUNTMODEL_DEBUG
qDebug() << "Removed custom account from misc accounts list, found:" << removed;
qDebug() << "All accounts after remove:";
foreach ( Account* acct, allAccounts )
qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId(); // All other accounts we haven't dealt with yet
#endif
}
} else } else
{ {
m_accounts << new AccountModelNode( content ); m_accounts << new AccountModelNode( content );
foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) ) foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) )
{ {
// qDebug() << "Found ResolverAccount" << acct->accountFriendlyName();
if ( AtticaResolverAccount* resolver = qobject_cast< AtticaResolverAccount* >( acct ) ) if ( AtticaResolverAccount* resolver = qobject_cast< AtticaResolverAccount* >( acct ) )
{ {
// qDebug() << "Which is an attica resolver with id:" << resolver->atticaId();
if ( resolver->atticaId() == content.id() ) if ( resolver->atticaId() == content.id() )
{ {
allAccounts.removeAll( acct ); allAccounts.removeAll( acct );
@@ -95,9 +113,14 @@ AccountModel::loadData()
} }
} }
// All other accounts we haven't dealt with yet #if ACCOUNTMODEL_DEBUG
qDebug() << "All accounts left:";
foreach ( Account* acct, allAccounts )
qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId(); // All other accounts we haven't dealt with yet
#endif
foreach ( Account* acct, allAccounts ) foreach ( Account* acct, allAccounts )
{ {
qDebug() << "Resolver is left over:" << acct->accountFriendlyName();
Q_ASSERT( !qobject_cast< AtticaResolverAccount* >( acct ) ); // This should be caught above in the attica list Q_ASSERT( !qobject_cast< AtticaResolverAccount* >( acct ) ); // This should be caught above in the attica list
if ( qobject_cast< ResolverAccount* >( acct ) && !qobject_cast< AtticaResolverAccount* >( acct ) ) if ( qobject_cast< ResolverAccount* >( acct ) && !qobject_cast< AtticaResolverAccount* >( acct ) )
@@ -314,6 +337,12 @@ AccountModel::data( const QModelIndex& index, int role ) const
Q_ASSERT( node->factory ); Q_ASSERT( node->factory );
Account* account = node->customAccount; Account* account = node->customAccount;
// This is sort of ugly. CustomAccounts are pure Account*, but we know that
// some might also be linked to attica resolvers (not always). If that is the case
// they have a Attica::Content set on the node, so we use that to display some
// extra metadata and rating
const bool hasAttica = !node->atticaContent.id().isEmpty();
switch ( role ) switch ( role )
{ {
case Qt::DisplayRole: case Qt::DisplayRole:
@@ -324,9 +353,15 @@ AccountModel::data( const QModelIndex& index, int role ) const
return ShippedWithTomahawk; return ShippedWithTomahawk;
case Qt::ToolTipRole: case Qt::ToolTipRole:
case DescriptionRole: case DescriptionRole:
return node->factory->description(); return hasAttica ? node->atticaContent.description() : node->factory->description();
case CanRateRole: case CanRateRole:
return false; return hasAttica;
case AuthorRole:
return hasAttica ? node->atticaContent.author() : QString();
case RatingRole:
return hasAttica ? node->atticaContent.rating() / 20 : 0; // rating is out of 100
case DownloadCounterRole:
return hasAttica ? node->atticaContent.downloads() : QVariant();
case RowTypeRole: case RowTypeRole:
return CustomAccount; return CustomAccount;
case AccountData: case AccountData:
@@ -364,15 +399,24 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role
switch ( node->type ) switch ( node->type )
{ {
case AccountModelNode::UniqueFactoryType: case AccountModelNode::UniqueFactoryType:
{
const Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() );
if ( node->accounts.isEmpty() ) if ( node->accounts.isEmpty() )
{ {
Q_ASSERT( state == Qt::Checked ); // How could we have a checked unique factory w/ no account??
// No account for this unique factory, create it // No account for this unique factory, create it
// Don't add it to node->accounts here, slot attached to accountmanager::accountcreated will do it for us // Don't add it to node->accounts here, slot attached to accountmanager::accountcreated will do it for us
acct = node->factory->createAccount(); acct = node->factory->createAccount();
AccountManager::instance()->addAccount( acct ); AccountManager::instance()->addAccount( acct );
TomahawkSettings::instance()->addAccount( acct->accountId() ); TomahawkSettings::instance()->addAccount( acct->accountId() );
} }
else
{
Q_ASSERT( node->accounts.size() == 1 );
acct = node->accounts.first();
}
break; break;
}
case AccountModelNode::AtticaType: case AccountModelNode::AtticaType:
{ {
// This may or may not be installed. if it's not installed yet, install it, then go ahead and enable it // This may or may not be installed. if it's not installed yet, install it, then go ahead and enable it
@@ -397,6 +441,12 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role
} }
else else
{ {
if ( m_waitingForAtticaInstall.contains( resolver.id() ) )
{
// in progress, ignore
return true;
}
qDebug() << "Kicked off fetch+install, now waiting"; qDebug() << "Kicked off fetch+install, now waiting";
m_waitingForAtticaInstall.insert( resolver.id() ); m_waitingForAtticaInstall.insert( resolver.id() );

View File

@@ -22,6 +22,7 @@
#include "Account.h" #include "Account.h"
#include "AccountManager.h" #include "AccountManager.h"
#include "ResolverAccount.h" #include "ResolverAccount.h"
#include "AtticaManager.h"
#include <attica/content.h> #include <attica/content.h>
@@ -86,7 +87,7 @@ struct AccountModelNode {
{ {
if ( AccountManager::instance()->factoryForAccount( acct ) == fac ) if ( AccountManager::instance()->factoryForAccount( acct ) == fac )
{ {
qDebug() << "Found account for factory:" << acct->accountFriendlyName(); // qDebug() << "Found account for factory:" << acct->accountFriendlyName();
accounts.append( acct ); accounts.append( acct );
} }
} }
@@ -97,7 +98,7 @@ struct AccountModelNode {
init(); init();
atticaContent = cnt; atticaContent = cnt;
qDebug() << "Creating attica model node for resolver:" << cnt.id(); // qDebug() << "Creating attica model node for resolver:" << cnt.id();
foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) ) foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) )
{ {
@@ -105,7 +106,7 @@ struct AccountModelNode {
{ {
if ( resolver->atticaId() == atticaContent.id() ) if ( resolver->atticaId() == atticaContent.id() )
{ {
qDebug() << "found atticaaccount :" << resolver->accountFriendlyName(); // qDebug() << "found atticaaccount :" << resolver->accountFriendlyName();
atticaAccount = resolver; atticaAccount = resolver;
break; break;
} }
@@ -124,6 +125,9 @@ struct AccountModelNode {
init(); init();
customAccount = account; customAccount = account;
factory = AccountManager::instance()->factoryForAccount( account ); factory = AccountManager::instance()->factoryForAccount( account );
if ( CustomAtticaAccount* customAtticaAccount = qobject_cast< CustomAtticaAccount* >( account ) )
atticaContent = customAtticaAccount->atticaContent();
} }
void init() void init()

View File

@@ -52,7 +52,7 @@ LastFmAccountFactory::icon() const
LastFmAccount::LastFmAccount( const QString& accountId ) LastFmAccount::LastFmAccount( const QString& accountId )
: Account( accountId ) : CustomAtticaAccount( accountId )
{ {
m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) ); m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) );
@@ -86,13 +86,19 @@ LastFmAccount::authenticate()
const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" ); const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" );
const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
if ( state == AtticaManager::Installed ) qDebug() << "Last.FM account authenticating...";
if ( m_resolver.isNull() && state == AtticaManager::Installed )
{ {
hookupResolver(); hookupResolver();
} else }
else if ( m_resolver.isNull() )
{ {
AtticaManager::instance()->installResolver( res, false ); AtticaManager::instance()->installResolver( res, false );
} }
else
{
m_resolver.data()->start();
}
emit connectionStateChanged( connectionState() ); emit connectionStateChanged( connectionState() );
} }
@@ -101,7 +107,7 @@ LastFmAccount::authenticate()
void void
LastFmAccount::deauthenticate() LastFmAccount::deauthenticate()
{ {
if ( m_resolver.data()->running() ) if ( !m_resolver.isNull() && m_resolver.data()->running() )
m_resolver.data()->stop(); m_resolver.data()->stop();
emit connectionStateChanged( connectionState() ); emit connectionStateChanged( connectionState() );
@@ -121,7 +127,7 @@ LastFmAccount::configurationWidget()
Account::ConnectionState Account::ConnectionState
LastFmAccount::connectionState() const LastFmAccount::connectionState() const
{ {
return m_authenticated ? Account::Connected : Account::Disconnected; return (!m_resolver.isNull() && m_resolver.data()->running()) ? Account::Connected : Account::Disconnected;
} }
@@ -141,7 +147,7 @@ LastFmAccount::infoPlugin()
bool bool
LastFmAccount::isAuthenticated() const LastFmAccount::isAuthenticated() const
{ {
return m_authenticated; return !m_resolver.isNull() && m_resolver.data()->running();
} }
@@ -169,7 +175,7 @@ LastFmAccount::password() const
void void
LastFmAccount::setPassword( const QString& password ) LastFmAccount::setPassword( const QString& password )
{ {
QVariantHash creds; QVariantHash creds = credentials();
creds[ "password" ] = password; creds[ "password" ] = password;
setCredentials( creds ); setCredentials( creds );
} }
@@ -184,7 +190,7 @@ LastFmAccount::sessionKey() const
void void
LastFmAccount::setSessionKey( const QString& sessionkey ) LastFmAccount::setSessionKey( const QString& sessionkey )
{ {
QVariantHash creds; QVariantHash creds = credentials();
creds[ "sessionkey" ] = sessionkey; creds[ "sessionkey" ] = sessionkey;
setCredentials( creds ); setCredentials( creds );
} }
@@ -200,7 +206,7 @@ LastFmAccount::username() const
void void
LastFmAccount::setUsername( const QString& username ) LastFmAccount::setUsername( const QString& username )
{ {
QVariantHash creds; QVariantHash creds = credentials();
creds[ "username" ] = username; creds[ "username" ] = username;
setCredentials( creds ); setCredentials( creds );
} }
@@ -253,3 +259,10 @@ LastFmAccount::hookupResolver()
m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) ); m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) );
connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) );
} }
Attica::Content
LastFmAccount::atticaContent() const
{
return AtticaManager::instance()->resolverForId( "lastfm" );
}

View File

@@ -20,6 +20,9 @@
#define LASTFMACCOUNT_H #define LASTFMACCOUNT_H
#include "accounts/Account.h" #include "accounts/Account.h"
#include "AtticaManager.h"
#include <attica/content.h>
#include <QObject> #include <QObject>
@@ -58,7 +61,7 @@ private:
* but the user can install the attica resolver on-demand. So we take care of both there. * but the user can install the attica resolver on-demand. So we take care of both there.
* *
*/ */
class LastFmAccount : public Account class LastFmAccount : public CustomAtticaAccount
{ {
Q_OBJECT Q_OBJECT
public: public:
@@ -88,6 +91,8 @@ public:
bool scrobble() const; bool scrobble() const;
void setScrobble( bool scrobble ); void setScrobble( bool scrobble );
Attica::Content atticaContent() const;
private slots: private slots:
void resolverInstalled( const QString& resolverId ); void resolverInstalled( const QString& resolverId );
@@ -95,7 +100,6 @@ private slots:
private: private:
void hookupResolver(); void hookupResolver();
bool m_authenticated;
QWeakPointer<Tomahawk::ExternalResolverGui> m_resolver; QWeakPointer<Tomahawk::ExternalResolverGui> m_resolver;
QWeakPointer<Tomahawk::InfoSystem::LastFmPlugin> m_infoPlugin; QWeakPointer<Tomahawk::InfoSystem::LastFmPlugin> m_infoPlugin;
QWeakPointer<LastFmConfig> m_configWidget; QWeakPointer<LastFmConfig> m_configWidget;

View File

@@ -48,6 +48,7 @@ ResolverAccountFactory::createAccount( const QString& accountId )
Account* Account*
ResolverAccountFactory::createFromPath( const QString& path, bool isAttica ) ResolverAccountFactory::createFromPath( const QString& path, bool isAttica )
{ {
qDebug() << "Creating ResolverAccount from path:" << path << "is attica" << isAttica;
if ( isAttica ) if ( isAttica )
{ {
QFileInfo info( path ); QFileInfo info( path );
@@ -200,6 +201,8 @@ ResolverAccount::resolverChanged()
AtticaResolverAccount::AtticaResolverAccount( const QString& accountId ) AtticaResolverAccount::AtticaResolverAccount( const QString& accountId )
: ResolverAccount( accountId ) : ResolverAccount( accountId )
{ {
TomahawkSettings::instance()->setValue( QString( "accounts/%1/atticaresolver" ).arg( accountId ), true );
m_atticaId = configuration().value( "atticaId" ).toString(); m_atticaId = configuration().value( "atticaId" ).toString();
loadIcon(); loadIcon();
} }
@@ -209,9 +212,11 @@ AtticaResolverAccount::AtticaResolverAccount( const QString& accountId, const QS
, m_atticaId( atticaId ) , m_atticaId( atticaId )
{ {
QVariantHash conf = configuration(); QVariantHash conf = configuration();
conf[ "atticaid" ] = atticaId; conf[ "atticaId" ] = atticaId;
setConfiguration( conf ); setConfiguration( conf );
TomahawkSettings::instance()->setValue( QString( "accounts/%1/atticaresolver" ).arg( accountId ), true );
loadIcon(); loadIcon();
} }

View File

@@ -35,7 +35,7 @@ public:
ResolverAccountFactory() {} ResolverAccountFactory() {}
virtual ~ResolverAccountFactory() {} virtual ~ResolverAccountFactory() {}
virtual Account* createAccount(const QString& accountId = QString()); virtual Account* createAccount( const QString& accountId = QString() );
virtual QString factoryId() const { return "resolveraccount"; } virtual QString factoryId() const { return "resolveraccount"; }
virtual QString description() const { return QString(); } virtual QString description() const { return QString(); }
virtual QString prettyName() const { return QString(); } // Internal, not displayed virtual QString prettyName() const { return QString(); } // Internal, not displayed

View File

@@ -22,20 +22,48 @@
#include "PlaylistUpdaterInterface.h" #include "PlaylistUpdaterInterface.h"
#include "sourcelist.h" #include "sourcelist.h"
#include <QPixmap>
using namespace Tomahawk; using namespace Tomahawk;
using namespace Accounts; using namespace Accounts;
SpotifyResolverAccount::SpotifyResolverAccount() static QPixmap* s_icon = 0;
Account*
SpotifyAccountFactory::createAccount( const QString& accountId )
{ {
qDebug() << Q_FUNC_INFO; return new SpotifyAccount( accountId );
}
SpotifyResolverAccount::~SpotifyResolverAccount()
{
qDebug() << Q_FUNC_INFO;
} }
QPixmap
SpotifyAccountFactory::icon() const
{
if ( !s_icon )
s_icon = new QPixmap( RESPATH "images/spotify-logo.png" );
return *s_icon;
}
SpotifyAccount::SpotifyAccount( const QString& accountId )
: ResolverAccount( accountId )
{
}
QPixmap
SpotifyAccount::icon() const
{
if ( !s_icon )
s_icon = new QPixmap( RESPATH "images/spotify-logo.png" );
return *s_icon;
}
void void
SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks) SpotifyAccount::addPlaylist( const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks )
{ {
Sync sync; Sync sync;
sync.id_ = qid; sync.id_ = qid;
@@ -56,11 +84,12 @@ SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QL
sync.uuid = pl->guid(); sync.uuid = pl->guid();
m_syncPlaylists.append( sync ); m_syncPlaylists.append( sync );
} }
else{ else
{
qDebug() << Q_FUNC_INFO << "Found playlist"; qDebug() << Q_FUNC_INFO << "Found playlist";
if( index != -1 && !tracks.isEmpty()) if ( index != -1 && !tracks.isEmpty())
{ {
qDebug() << Q_FUNC_INFO << "Got pl" << m_syncPlaylists[ index ].playlist->guid(); qDebug() << Q_FUNC_INFO << "Got pl" << m_syncPlaylists[ index ].playlist->guid();
@@ -71,11 +100,14 @@ SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QL
qDebug() << Q_FUNC_INFO << "tracks" << currTracks; qDebug() << Q_FUNC_INFO << "tracks" << currTracks;
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( currTracks, tracks ); bool changed = false;
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( currTracks, tracks, changed );
if ( changed )
{
QList<Tomahawk::plentry_ptr> el = m_syncPlaylists[ index ].playlist->entriesFromQueries( mergedTracks, true ); QList<Tomahawk::plentry_ptr> el = m_syncPlaylists[ index ].playlist->entriesFromQueries( mergedTracks, true );
m_syncPlaylists[ index ].playlist->createNewRevision( uuid(), m_syncPlaylists[ index ].playlist->currentrevision(), el ); m_syncPlaylists[ index ].playlist->createNewRevision( uuid(), m_syncPlaylists[ index ].playlist->currentrevision(), el );
}
} }
} }
@@ -84,9 +116,9 @@ SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QL
bool operator==(SpotifyResolverAccount::Sync one, SpotifyResolverAccount::Sync two) bool operator==( SpotifyAccount::Sync one, SpotifyAccount::Sync two )
{ {
if(one.id_ == two.id_) if( one.id_ == two.id_ )
return true; return true;
return false; return false;
} }

View File

@@ -23,6 +23,7 @@
#include "utils/tomahawkutils.h" #include "utils/tomahawkutils.h"
#include "sourcelist.h" #include "sourcelist.h"
#include "ResolverAccount.h" #include "ResolverAccount.h"
class QTimer; class QTimer;
namespace Tomahawk { namespace Tomahawk {
@@ -31,24 +32,40 @@ class ExternalResolverGui;
namespace Accounts { namespace Accounts {
class SpotifyResolverAccount : public QObject // : public AccountFactory
class SpotifyAccountFactory : public AccountFactory
{ {
Q_OBJECT Q_OBJECT
public: public:
SpotifyResolverAccount(); SpotifyAccountFactory() {}
virtual ~SpotifyResolverAccount();
/*virtual Account* createAccount(const QString& accountId = QString()); virtual Account* createAccount( const QString& accountId = QString() );
virtual QString description() const { return tr( "Play and sync your playlists with Spotify" ); } virtual QString description() const { return tr( "Play music from and sync your playlists with Spotify" ); }
virtual QString factoryId() const { return "spotifyaccount"; } virtual QString factoryId() const { return "spotifyaccount"; }
virtual QString prettyName() const { return "Spotify"; } virtual QString prettyName() const { return "Spotify"; }
virtual AccountTypes types() const { return AccountTypes( ResolverType ); } virtual AccountTypes types() const { return AccountTypes( ResolverType ); }
virtual bool allowUserCreation() const { return false; } virtual bool allowUserCreation() const { return false; }
virtual QPixmap icon() const { return m_icon; } virtual QPixmap icon() const;
virtual bool isUnique() const { return true; } virtual bool isUnique() const { return true; }
*/
void addPlaylist(const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks); };
class SpotifyAccount : public ResolverAccount
{
Q_OBJECT
public:
SpotifyAccount( const QString& accountId );
virtual ~SpotifyAccount() {}
virtual QPixmap icon() const;
virtual QWidget* aclWidget() { return 0; }
virtual InfoSystem::InfoPlugin* infoPlugin() { return 0; }
virtual SipPlugin* sipPlugin() { return 0; }
void addPlaylist( const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks );
struct Sync { struct Sync {
QString id_; QString id_;
QString uuid; QString uuid;

View File

@@ -22,7 +22,6 @@
#include "albumplaylistinterface.h" #include "albumplaylistinterface.h"
#include "database/database.h" #include "database/database.h"
#include "database/databaseimpl.h" #include "database/databaseimpl.h"
#include "database/databasecommand_alltracks.h"
#include "query.h" #include "query.h"
#include "utils/logger.h" #include "utils/logger.h"
@@ -32,12 +31,16 @@ using namespace Tomahawk;
Album::~Album() Album::~Album()
{ {
delete m_cover;
} }
album_ptr album_ptr
Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate ) Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate )
{ {
if ( !Database::instance() || !Database::instance()->impl() )
return album_ptr();
int albid = Database::instance()->impl()->albumId( artist->id(), name, autoCreate ); int albid = Database::instance()->impl()->albumId( artist->id(), name, autoCreate );
if ( albid < 1 && autoCreate ) if ( albid < 1 && autoCreate )
return album_ptr(); return album_ptr();
@@ -71,6 +74,7 @@ Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr&
, m_id( id ) , m_id( id )
, m_name( name ) , m_name( name )
, m_artist( artist ) , m_artist( artist )
, m_cover( 0 )
, m_infoLoaded( false ) , m_infoLoaded( false )
{ {
connect( Tomahawk::InfoSystem::InfoSystem::instance(), connect( Tomahawk::InfoSystem::InfoSystem::instance(),
@@ -97,11 +101,14 @@ Album::artist() const
} }
QByteArray #ifndef ENABLE_HEADLESS
Album::cover() const QPixmap
Album::cover( const QSize& size, bool forceLoad ) const
{ {
if ( !m_infoLoaded ) if ( !m_infoLoaded )
{ {
if ( !forceLoad )
return QPixmap();
m_uuid = uuid(); m_uuid = uuid();
Tomahawk::InfoSystem::InfoStringHash trackInfo; Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -117,8 +124,31 @@ Album::cover() const
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
} }
return m_cover; if ( !m_cover && !m_coverBuffer.isEmpty() )
{
m_cover = new QPixmap();
m_cover->loadFromData( m_coverBuffer );
}
if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
{
if ( m_coverCache.contains( size.width() ) )
{
return m_coverCache.value( size.width() );
}
QPixmap scaledCover;
scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_coverCache.insert( size.width(), scaledCover );
return scaledCover;
}
if ( m_cover )
return *m_cover;
else
return QPixmap();
} }
#endif
void void
@@ -137,7 +167,7 @@ Album::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria
const QByteArray ba = returnedData["imgbytes"].toByteArray(); const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() ) if ( ba.length() )
{ {
m_cover = ba; m_coverBuffer = ba;
} }
} }

View File

@@ -19,8 +19,13 @@
#ifndef TOMAHAWKALBUM_H #ifndef TOMAHAWKALBUM_H
#define TOMAHAWKALBUM_H #define TOMAHAWKALBUM_H
#include "config.h"
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QSharedPointer> #include <QtCore/QSharedPointer>
#ifndef ENABLE_HEADLESS
#include <QtGui/QPixmap>
#endif
#include "typedefs.h" #include "typedefs.h"
#include "playlistinterface.h" #include "playlistinterface.h"
@@ -44,7 +49,9 @@ public:
unsigned int id() const { return m_id; } unsigned int id() const { return m_id; }
QString name() const { return m_name; } QString name() const { return m_name; }
artist_ptr artist() const; artist_ptr artist() const;
QByteArray cover() const; #ifndef ENABLE_HEADLESS
QPixmap cover( const QSize& size, bool forceLoad = true ) const;
#endif
bool infoLoaded() const { return m_infoLoaded; } bool infoLoaded() const { return m_infoLoaded; }
Tomahawk::playlistinterface_ptr playlistInterface(); Tomahawk::playlistinterface_ptr playlistInterface();
@@ -64,10 +71,13 @@ private:
unsigned int m_id; unsigned int m_id;
QString m_name; QString m_name;
artist_ptr m_artist; artist_ptr m_artist;
QByteArray m_cover; QByteArray m_coverBuffer;
mutable QPixmap* m_cover;
bool m_infoLoaded; bool m_infoLoaded;
mutable QString m_uuid; mutable QString m_uuid;
mutable QHash< int, QPixmap > m_coverCache;
Tomahawk::playlistinterface_ptr m_playlistInterface; Tomahawk::playlistinterface_ptr m_playlistInterface;
}; };

View File

@@ -68,7 +68,6 @@ signals:
void nextTrackReady(); void nextTrackReady();
private: private:
Q_DISABLE_COPY( AlbumPlaylistInterface )
AlbumPlaylistInterface(); AlbumPlaylistInterface();
QList< Tomahawk::query_ptr > m_queries; QList< Tomahawk::query_ptr > m_queries;

View File

@@ -31,12 +31,16 @@ using namespace Tomahawk;
Artist::~Artist() Artist::~Artist()
{ {
delete m_cover;
} }
artist_ptr artist_ptr
Artist::get( const QString& name, bool autoCreate ) Artist::get( const QString& name, bool autoCreate )
{ {
if ( !Database::instance() || !Database::instance()->impl() )
return artist_ptr();
int artid = Database::instance()->impl()->artistId( name, autoCreate ); int artid = Database::instance()->impl()->artistId( name, autoCreate );
if ( artid < 1 && autoCreate ) if ( artid < 1 && autoCreate )
return artist_ptr(); return artist_ptr();
@@ -69,6 +73,7 @@ Artist::Artist( unsigned int id, const QString& name )
: QObject() : QObject()
, m_id( id ) , m_id( id )
, m_name( name ) , m_name( name )
, m_cover( 0 )
, m_infoLoaded( false ) , m_infoLoaded( false )
{ {
m_sortname = DatabaseImpl::sortname( name, true ); m_sortname = DatabaseImpl::sortname( name, true );
@@ -89,11 +94,14 @@ Artist::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks )
} }
QByteArray #ifndef ENABLE_HEADLESS
Artist::cover() const QPixmap
Artist::cover( const QSize& size, bool forceLoad ) const
{ {
if ( !m_infoLoaded ) if ( !m_infoLoaded )
{ {
if ( !forceLoad )
return QPixmap();
m_uuid = uuid(); m_uuid = uuid();
Tomahawk::InfoSystem::InfoStringHash trackInfo; Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -108,8 +116,31 @@ Artist::cover() const
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
} }
return m_cover; if ( !m_cover && !m_coverBuffer.isEmpty() )
{
m_cover = new QPixmap();
m_cover->loadFromData( m_coverBuffer );
}
if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
{
if ( m_coverCache.contains( size.width() ) )
{
return m_coverCache.value( size.width() );
}
QPixmap scaledCover;
scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_coverCache.insert( size.width(), scaledCover );
return scaledCover;
}
if ( m_cover )
return *m_cover;
else
return QPixmap();
} }
#endif
void void
@@ -128,7 +159,7 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari
const QByteArray ba = returnedData["imgbytes"].toByteArray(); const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() ) if ( ba.length() )
{ {
m_cover = ba; m_coverBuffer = ba;
} }
} }

View File

@@ -19,8 +19,13 @@
#ifndef TOMAHAWKARTIST_H #ifndef TOMAHAWKARTIST_H
#define TOMAHAWKARTIST_H #define TOMAHAWKARTIST_H
#include "config.h"
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QSharedPointer> #include <QtCore/QSharedPointer>
#ifndef ENABLE_HEADLESS
#include <QtGui/QPixmap>
#endif
#include "typedefs.h" #include "typedefs.h"
#include "dllmacro.h" #include "dllmacro.h"
@@ -43,7 +48,9 @@ public:
unsigned int id() const { return m_id; } unsigned int id() const { return m_id; }
QString name() const { return m_name; } QString name() const { return m_name; }
QString sortname() const { return m_sortname; } QString sortname() const { return m_sortname; }
QByteArray cover() const; #ifndef ENABLE_HEADLESS
QPixmap cover( const QSize& size, bool forceLoad = true ) const;
#endif
bool infoLoaded() const { return m_infoLoaded; } bool infoLoaded() const { return m_infoLoaded; }
Tomahawk::playlistinterface_ptr playlistInterface(); Tomahawk::playlistinterface_ptr playlistInterface();
@@ -63,10 +70,13 @@ private:
unsigned int m_id; unsigned int m_id;
QString m_name; QString m_name;
QString m_sortname; QString m_sortname;
QByteArray m_cover; QByteArray m_coverBuffer;
mutable QPixmap* m_cover;
bool m_infoLoaded; bool m_infoLoaded;
mutable QString m_uuid; mutable QString m_uuid;
mutable QHash< int, QPixmap > m_coverCache;
Tomahawk::playlistinterface_ptr m_playlistInterface; Tomahawk::playlistinterface_ptr m_playlistInterface;
}; };

View File

@@ -90,7 +90,7 @@ ArtistPlaylistInterface::tracks()
cmd->setSortOrder( DatabaseCommand_AllTracks::Album ); cmd->setSortOrder( DatabaseCommand_AllTracks::Album );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ), connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) ); m_artist.data(), SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
} }

View File

@@ -18,6 +18,8 @@
#include "audioengine.h" #include "audioengine.h"
#include "config.h"
#include <QtCore/QUrl> #include <QtCore/QUrl>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
@@ -328,13 +330,15 @@ AudioEngine::sendWaitingNotificationSlot() const
void void
AudioEngine::sendNowPlayingNotification() AudioEngine::sendNowPlayingNotification()
{ {
#ifndef ENABLE_HEADLESS
if ( m_currentTrack->album().isNull() || m_currentTrack->album()->infoLoaded() ) if ( m_currentTrack->album().isNull() || m_currentTrack->album()->infoLoaded() )
onNowPlayingInfoReady(); onNowPlayingInfoReady();
else else
{ {
connect( m_currentTrack->album().data(), SIGNAL( updated() ), SLOT( onNowPlayingInfoReady() ), Qt::UniqueConnection ); connect( m_currentTrack->album().data(), SIGNAL( updated() ), SLOT( onNowPlayingInfoReady() ), Qt::UniqueConnection );
m_currentTrack->album()->cover(); m_currentTrack->album()->cover( QSize( 0, 0 ) );
} }
#endif
} }
@@ -357,9 +361,11 @@ AudioEngine::onNowPlayingInfoReady()
if ( !m_currentTrack->album().isNull() ) if ( !m_currentTrack->album().isNull() )
{ {
#ifndef ENABLE_HEADLESS
QImage cover; QImage cover;
cover.loadFromData( m_currentTrack->album()->cover() ); cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage();
playInfo["image"] = QVariant( cover ); playInfo["image"] = QVariant( cover );
#endif
} }
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo(
@@ -584,10 +590,10 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState )
if ( newState == Phonon::ErrorState ) if ( newState == Phonon::ErrorState )
{ {
stop();
tLog() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType(); tLog() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType();
emit error( UnknownError ); emit error( UnknownError );
stop();
return; return;
} }
if ( newState == Phonon::PlayingState ) if ( newState == Phonon::PlayingState )

View File

@@ -88,6 +88,7 @@ ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
m_sigmap->setMapping( m_loveAction, ActionLove ); m_sigmap->setMapping( m_loveAction, ActionLove );
connect( queries.first().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) ); connect( queries.first().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
m_queries.first()->loadSocialActions();
onSocialActionsLoaded(); onSocialActionsLoaded();
} }

View File

@@ -169,7 +169,6 @@ DatabaseCommand_AllAlbums::execForCollection( DatabaseImpl* dbi )
void void
DatabaseCommand_AllAlbums::exec( DatabaseImpl* dbi ) DatabaseCommand_AllAlbums::exec( DatabaseImpl* dbi )
{ {
return;
if ( !m_artist.isNull() ) if ( !m_artist.isNull() )
{ {
execForArtist( dbi ); execForArtist( dbi );

View File

@@ -20,6 +20,7 @@
#include "artist.h" #include "artist.h"
#include "album.h" #include "album.h"
#include "pipeline.h"
#include "sourcelist.h" #include "sourcelist.h"
#include "utils/logger.h" #include "utils/logger.h"
@@ -30,10 +31,14 @@ DatabaseCommand_Resolve::DatabaseCommand_Resolve( const query_ptr& query )
: DatabaseCommand() : DatabaseCommand()
, m_query( query ) , m_query( query )
{ {
Q_ASSERT( Pipeline::instance()->isRunning() );
} }
DatabaseCommand_Resolve::~DatabaseCommand_Resolve() DatabaseCommand_Resolve::~DatabaseCommand_Resolve()
{} {
}
void void
DatabaseCommand_Resolve::exec( DatabaseImpl* lib ) DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
@@ -76,11 +81,9 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
typedef QPair<int, float> scorepair_t; typedef QPair<int, float> scorepair_t;
// STEP 1 // STEP 1
QList< QPair<int, float> > artists = lib->searchTable( "artist", m_query->artist() ); QList< QPair<int, float> > tracks = lib->search( m_query );
QList< QPair<int, float> > tracks = lib->searchTable( "track", m_query->track() );
QList< QPair<int, float> > albums = lib->searchTable( "album", m_query->album() );
if ( artists.length() == 0 || tracks.length() == 0 ) if ( tracks.length() == 0 )
{ {
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track(); qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
emit results( m_query->id(), res ); emit results( m_query->id(), res );
@@ -90,13 +93,10 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
// STEP 2 // STEP 2
TomahawkSqlQuery files_query = lib->newquery(); TomahawkSqlQuery files_query = lib->newquery();
QStringList artsl, trksl; QStringList trksl;
for ( int k = 0; k < artists.count(); k++ )
artsl.append( QString::number( artists.at( k ).first ) );
for ( int k = 0; k < tracks.count(); k++ ) for ( int k = 0; k < tracks.count(); k++ )
trksl.append( QString::number( tracks.at( k ).first ) ); trksl.append( QString::number( tracks.at( k ).first ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) ); QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString sql = QString( "SELECT " QString sql = QString( "SELECT "
@@ -119,8 +119,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
"artist.id = file_join.artist AND " "artist.id = file_join.artist AND "
"track.id = file_join.track AND " "track.id = file_join.track AND "
"file.id = file_join.file AND " "file.id = file_join.file AND "
"(%1 AND %2)" ) "(%1)" )
.arg( artsToken )
.arg( trksToken ); .arg( trksToken );
files_query.prepare( sql ); files_query.prepare( sql );
@@ -196,27 +195,9 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
typedef QPair<int, float> scorepair_t; typedef QPair<int, float> scorepair_t;
// STEP 1 // STEP 1
QList< QPair<int, float> > artistPairs = lib->searchTable( "artist", m_query->fullTextQuery(), 20 ); QList< QPair<int, float> > trackPairs = lib->search( m_query );
QList< QPair<int, float> > trackPairs = lib->searchTable( "track", m_query->fullTextQuery(), 20 ); QList< QPair<int, float> > albumPairs = lib->searchAlbum( m_query, 20 );
QList< QPair<int, float> > albumPairs = lib->searchTable( "album", m_query->fullTextQuery(), 20 );
foreach ( const scorepair_t& artistPair, artistPairs )
{
TomahawkSqlQuery query = lib->newquery();
QString sql = QString( "SELECT name FROM artist WHERE id = %1" ).arg( artistPair.first );
query.prepare( sql );
query.exec();
QList<Tomahawk::artist_ptr> artistList;
while ( query.next() )
{
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistPair.first, query.value( 0 ).toString() );
artistList << artist;
}
emit artists( m_query->id(), artistList );
}
foreach ( const scorepair_t& albumPair, albumPairs ) foreach ( const scorepair_t& albumPair, albumPairs )
{ {
TomahawkSqlQuery query = lib->newquery(); TomahawkSqlQuery query = lib->newquery();
@@ -236,9 +217,9 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
emit albums( m_query->id(), albumList ); emit albums( m_query->id(), albumList );
} }
if ( artistPairs.length() == 0 && trackPairs.length() == 0 && albumPairs.length() == 0 ) if ( trackPairs.length() == 0 )
{ {
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track(); qDebug() << "No candidates found in first pass, aborting resolve" << m_query->fullTextQuery();
emit results( m_query->id(), res ); emit results( m_query->id(), res );
return; return;
} }
@@ -246,18 +227,11 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
// STEP 2 // STEP 2
TomahawkSqlQuery files_query = lib->newquery(); TomahawkSqlQuery files_query = lib->newquery();
QStringList artsl, trksl, albsl; QStringList trksl;
for ( int k = 0; k < artistPairs.count(); k++ )
artsl.append( QString::number( artistPairs.at( k ).first ) );
for ( int k = 0; k < trackPairs.count(); k++ ) for ( int k = 0; k < trackPairs.count(); k++ )
trksl.append( QString::number( trackPairs.at( k ).first ) ); trksl.append( QString::number( trackPairs.at( k ).first ) );
for ( int k = 0; k < albumPairs.count(); k++ )
albsl.append( QString::number( albumPairs.at( k ).first ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) ); QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString albsToken = QString( "file_join.album IN (%1)" ).arg( albsl.join( "," ) );
QString sql = QString( "SELECT " QString sql = QString( "SELECT "
"url, mtime, size, md5, mimetype, duration, bitrate, " //0 "url, mtime, size, md5, mimetype, duration, bitrate, " //0
"file_join.artist, file_join.album, file_join.track, " //7 "file_join.artist, file_join.album, file_join.track, " //7
@@ -279,7 +253,7 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
"track.id = file_join.track AND " "track.id = file_join.track AND "
"file.id = file_join.file AND " "file.id = file_join.file AND "
"%1" ) "%1" )
.arg( trackPairs.length() > 0 ? trksToken : QString( "0" ) ); .arg( trksl.length() > 0 ? trksToken : QString( "0" ) );
files_query.prepare( sql ); files_query.prepare( sql );
files_query.exec(); files_query.exec();

View File

@@ -21,32 +21,26 @@
#include "databaseimpl.h" #include "databaseimpl.h"
#include "tomahawksqlquery.h" #include "tomahawksqlquery.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "jobview/IndexingJobItem.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include <QSqlRecord>
DatabaseCommand_UpdateSearchIndex::DatabaseCommand_UpdateSearchIndex() DatabaseCommand_UpdateSearchIndex::DatabaseCommand_UpdateSearchIndex()
: DatabaseCommand() : DatabaseCommand()
, m_statusJob( new IndexingJobItem )
{ {
tLog() << Q_FUNC_INFO << "Updating index."; tLog() << Q_FUNC_INFO << "Updating index.";
JobStatusView::instance()->model()->addJob( m_statusJob );
} }
void DatabaseCommand_UpdateSearchIndex::~DatabaseCommand_UpdateSearchIndex()
DatabaseCommand_UpdateSearchIndex::indexTable( DatabaseImpl* db, const QString& table )
{ {
qDebug() << Q_FUNC_INFO; m_statusJob->done();
TomahawkSqlQuery query = db->newquery();
qDebug() << "Building index for" << table;
query.exec( QString( "SELECT id, name FROM %1" ).arg( table ) );
QMap< unsigned int, QString > fields;
while ( query.next() )
{
fields.insert( query.value( 0 ).toUInt(), query.value( 1 ).toString() );
}
db->m_fuzzyIndex->appendFields( table, fields );
qDebug() << "Building index for" << table << "finished.";
} }
@@ -55,9 +49,35 @@ DatabaseCommand_UpdateSearchIndex::exec( DatabaseImpl* db )
{ {
db->m_fuzzyIndex->beginIndexing(); db->m_fuzzyIndex->beginIndexing();
indexTable( db, "artist" ); QMap< unsigned int, QMap< QString, QString > > data;
indexTable( db, "album" ); TomahawkSqlQuery q = db->newquery();
indexTable( db, "track" );
q.exec( "SELECT track.id, track.name, artist.name, artist.id FROM track, artist WHERE artist.id = track.artist" );
while ( q.next() )
{
QMap< QString, QString > track;
track.insert( "track", q.value( 1 ).toString() );
track.insert( "artist", q.value( 2 ).toString() );
track.insert( "artistid", q.value( 3 ).toString() );
data.insert( q.value( 0 ).toUInt(), track );
}
db->m_fuzzyIndex->appendFields( data );
data.clear();
q.exec( "SELECT album.id, album.name FROM album" );
while ( q.next() )
{
QMap< QString, QString > album;
album.insert( "album", q.value( 1 ).toString() );
data.insert( q.value( 0 ).toUInt(), album );
}
db->m_fuzzyIndex->appendFields( data );
qDebug() << "Building index finished.";
db->m_fuzzyIndex->endIndexing(); db->m_fuzzyIndex->endIndexing();
} }

View File

@@ -22,23 +22,21 @@
#include "databasecommand.h" #include "databasecommand.h"
#include "dllmacro.h" #include "dllmacro.h"
class IndexingJobItem;
class DLLEXPORT DatabaseCommand_UpdateSearchIndex : public DatabaseCommand class DLLEXPORT DatabaseCommand_UpdateSearchIndex : public DatabaseCommand
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit DatabaseCommand_UpdateSearchIndex(); explicit DatabaseCommand_UpdateSearchIndex();
virtual ~DatabaseCommand_UpdateSearchIndex();
virtual QString commandname() const { return "updatesearchindex"; } virtual QString commandname() const { return "updatesearchindex"; }
virtual bool doesMutates() const { return true; } virtual bool doesMutates() const { return true; }
virtual void exec( DatabaseImpl* db ); virtual void exec( DatabaseImpl* db );
signals:
void indexUpdated();
private: private:
void indexTable( DatabaseImpl* db, const QString& table ); IndexingJobItem* m_statusJob;
QString table;
}; };
#endif // DATABASECOMMAND_UPDATESEARCHINDEX_H #endif // DATABASECOMMAND_UPDATESEARCHINDEX_H

View File

@@ -79,6 +79,9 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )
query.exec( "UPDATE source SET isonline = 'false'" ); query.exec( "UPDATE source SET isonline = 'false'" );
m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated ); m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated );
if ( schemaUpdated )
QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
tDebug( LOGVERBOSE ) << "Loaded index:" << t.elapsed(); tDebug( LOGVERBOSE ) << "Loaded index:" << t.elapsed();
if ( qApp->arguments().contains( "--dumpdb" ) ) if ( qApp->arguments().contains( "--dumpdb" ) )
@@ -405,13 +408,36 @@ DatabaseImpl::albumId( int artistid, const QString& name_orig, bool autoCreate )
QList< QPair<int, float> > QList< QPair<int, float> >
DatabaseImpl::searchTable( const QString& table, const QString& name, uint limit ) DatabaseImpl::search( const Tomahawk::query_ptr& query, uint limit )
{ {
QList< QPair<int, float> > resultslist; QList< QPair<int, float> > resultslist;
if ( table != "artist" && table != "track" && table != "album" )
QMap< int, float > resultsmap = m_fuzzyIndex->search( query );
foreach ( int i, resultsmap.keys() )
{
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
}
qSort( resultslist.begin(), resultslist.end(), DatabaseImpl::scorepairSorter );
if ( !limit )
return resultslist; return resultslist;
QMap< int, float > resultsmap = m_fuzzyIndex->search( table, name ); QList< QPair<int, float> > resultscapped;
for ( int i = 0; i < (int)limit && i < resultsmap.count(); i++ )
{
resultscapped << resultslist.at( i );
}
return resultscapped;
}
QList< QPair<int, float> >
DatabaseImpl::searchAlbum( const Tomahawk::query_ptr& query, uint limit )
{
QList< QPair<int, float> > resultslist;
QMap< int, float > resultsmap = m_fuzzyIndex->searchAlbum( query );
foreach ( int i, resultsmap.keys() ) foreach ( int i, resultsmap.keys() )
{ {
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) ); resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
@@ -696,3 +722,11 @@ DatabaseImpl::openDatabase( const QString& dbname )
return schemaUpdated; return schemaUpdated;
} }
void
DatabaseImpl::updateIndex()
{
DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex();
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}

View File

@@ -56,7 +56,8 @@ public:
int trackId( int artistid, const QString& name_orig, bool autoCreate ); int trackId( int artistid, const QString& name_orig, bool autoCreate );
int albumId( int artistid, const QString& name_orig, bool autoCreate ); int albumId( int artistid, const QString& name_orig, bool autoCreate );
QList< QPair<int, float> > searchTable( const QString& table, const QString& name, uint limit = 0 ); QList< QPair<int, float> > search( const Tomahawk::query_ptr& query, uint limit = 0 );
QList< QPair<int, float> > searchAlbum( const Tomahawk::query_ptr& query, uint limit = 0 );
QList< int > getTrackFids( int tid ); QList< int > getTrackFids( int tid );
static QString sortname( const QString& str, bool replaceArticle = false ); static QString sortname( const QString& str, bool replaceArticle = false );
@@ -79,7 +80,8 @@ public:
signals: signals:
void indexReady(); void indexReady();
public slots: private slots:
void updateIndex();
private: private:
QString cleanSql( const QString& sql ); QString cleanSql( const QString& sql );

View File

@@ -22,12 +22,14 @@
#include <QTime> #include <QTime>
#include <CLucene.h> #include <CLucene.h>
#include <CLucene/queryParser/MultiFieldQueryParser.h>
#include "databaseimpl.h" #include "databaseimpl.h"
#include "utils/tomahawkutils.h" #include "utils/tomahawkutils.h"
#include "utils/logger.h" #include "utils/logger.h"
using namespace lucene::analysis; using namespace lucene::analysis;
using namespace lucene::analysis::standard;
using namespace lucene::document; using namespace lucene::document;
using namespace lucene::store; using namespace lucene::store;
using namespace lucene::index; using namespace lucene::index;
@@ -83,7 +85,7 @@ FuzzyIndex::beginIndexing()
} }
qDebug() << "Creating new index writer."; qDebug() << "Creating new index writer.";
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, true ); IndexWriter luceneWriter( m_luceneDir, m_analyzer, true );
} }
catch( CLuceneError& error ) catch( CLuceneError& error )
{ {
@@ -102,38 +104,55 @@ FuzzyIndex::endIndexing()
void void
FuzzyIndex::appendFields( const QString& table, const QMap< unsigned int, QString >& fields ) FuzzyIndex::appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData )
{ {
try try
{ {
qDebug() << "Appending to index:" << fields.count(); tDebug() << "Appending to index:" << trackData.count();
bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ); bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() );
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, create ); IndexWriter luceneWriter( m_luceneDir, m_analyzer, create );
Document doc; Document doc;
QMapIterator< unsigned int, QString > it( fields ); QMapIterator< unsigned int, QMap< QString, QString > > it( trackData );
while ( it.hasNext() ) while ( it.hasNext() )
{ {
it.next(); it.next();
unsigned int id = it.key(); unsigned int id = it.key();
QString name = it.value(); QMap< QString, QString > values = it.value();
if ( values.contains( "track" ) )
{ {
Field* field = _CLNEW Field( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str(), doc.add( *( _CLNEW Field( _T( "fulltext" ), DatabaseImpl::sortname( QString( "%1 %2" ).arg( values.value( "artist" ) ).arg( values.value( "track" ) ) ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_UNTOKENIZED ); Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *field );
}
{ doc.add( *( _CLNEW Field( _T( "track" ), DatabaseImpl::sortname( values.value( "track" ) ).toStdWString().c_str(),
Field* field = _CLNEW Field( _T( "id" ), QString::number( id ).toStdWString().c_str(), Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
Field::STORE_YES | Field::INDEX_NO );
doc.add( *field ); doc.add( *( _CLNEW Field( _T( "artist" ), DatabaseImpl::sortname( values.value( "artist" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artistid" ), values.value( "artistid" ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
doc.add( *( _CLNEW Field( _T( "trackid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
} }
else if ( values.contains( "album" ) )
{
doc.add( *( _CLNEW Field( _T( "album" ), DatabaseImpl::sortname( values.value( "album" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "albumid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else
Q_ASSERT( false );
luceneWriter.addDocument( &doc ); luceneWriter.addDocument( &doc );
doc.clear(); doc.clear();
} }
luceneWriter.optimize();
luceneWriter.close(); luceneWriter.close();
} }
catch( CLuceneError& error ) catch( CLuceneError& error )
@@ -152,7 +171,7 @@ FuzzyIndex::loadLuceneIndex()
QMap< int, float > QMap< int, float >
FuzzyIndex::search( const QString& table, const QString& name ) FuzzyIndex::search( const Tomahawk::query_ptr& query )
{ {
QMutexLocker lock( &m_mutex ); QMutexLocker lock( &m_mutex );
@@ -171,33 +190,112 @@ FuzzyIndex::search( const QString& table, const QString& name )
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader ); m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
} }
if ( name.isEmpty() ) float minScore;
return resultsmap; const TCHAR** fields = 0;
MultiFieldQueryParser parser( fields, m_analyzer );
BooleanQuery* qry = _CLNEW BooleanQuery();
SimpleAnalyzer analyzer; if ( query->isFullTextQuery() )
QueryParser parser( table.toStdWString().c_str(), m_analyzer ); {
Hits* hits = 0; QString escapedQuery = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
FuzzyQuery* qry = _CLNEW FuzzyQuery( _CLNEW Term( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str() ) ); Term* term = _CLNEW Term( _T( "track" ), escapedQuery.toStdWString().c_str() );
hits = m_luceneSearcher->search( qry ); Query* fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
term = _CLNEW Term( _T( "artist" ), escapedQuery.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
term = _CLNEW Term( _T( "fulltext" ), escapedQuery.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
minScore = 0.00;
}
else
{
QString track = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->track() ).toStdWString().c_str() ) );
QString artist = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->artist() ).toStdWString().c_str() ) );
// QString album = QString::fromWCharArray( parser.escape( query->album().toStdWString().c_str() ) );
Term* term = _CLNEW Term( _T( "track" ), track.toStdWString().c_str() );
Query* fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::MUST );
term = _CLNEW Term( _T( "artist" ), artist.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::MUST );
minScore = 0.05;
}
Hits* hits = m_luceneSearcher->search( qry );
for ( uint i = 0; i < hits->length(); i++ ) for ( uint i = 0; i < hits->length(); i++ )
{ {
Document* d = &hits->doc( i ); Document* d = &hits->doc( i );
float score = hits->score( i ); float score = hits->score( i );
int id = QString::fromWCharArray( d->get( _T( "id" ) ) ).toInt(); int id = QString::fromWCharArray( d->get( _T( "trackid" ) ) ).toInt();
QString result = QString::fromWCharArray( d->get( table.toStdWString().c_str() ) );
if ( DatabaseImpl::sortname( result ) == DatabaseImpl::sortname( name ) ) if ( score > minScore )
score = 1.0;
else
score = qMin( score, (float)0.99 );
if ( score > 0.05 )
{ {
resultsmap.insert( id, score ); resultsmap.insert( id, score );
// qDebug() << "Hitres:" << result << id << score << table << name; // tDebug() << "Index hit:" << id << score << QString::fromWCharArray( ((Query*)qry)->toString() );
}
}
delete hits;
delete qry;
}
catch( CLuceneError& error )
{
tDebug() << "Caught CLucene error:" << error.what();
Q_ASSERT( false );
}
return resultsmap;
}
QMap< int, float >
FuzzyIndex::searchAlbum( const Tomahawk::query_ptr& query )
{
Q_ASSERT( query->isFullTextQuery() );
QMutexLocker lock( &m_mutex );
QMap< int, float > resultsmap;
try
{
if ( !m_luceneReader )
{
if ( !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ) )
{
qDebug() << Q_FUNC_INFO << "index didn't exist.";
return resultsmap;
}
m_luceneReader = IndexReader::open( m_luceneDir );
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
}
QueryParser parser( _T( "album" ), m_analyzer );
QString escapedName = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
Query* qry = _CLNEW FuzzyQuery( _CLNEW Term( _T( "album" ), escapedName.toStdWString().c_str() ) );
Hits* hits = m_luceneSearcher->search( qry );
for ( uint i = 0; i < hits->length(); i++ )
{
Document* d = &hits->doc( i );
float score = hits->score( i );
int id = QString::fromWCharArray( d->get( _T( "albumid" ) ) ).toInt();
if ( score > 0.30 )
{
resultsmap.insert( id, score );
// tDebug() << "Index hit:" << id << score;
} }
} }

View File

@@ -25,6 +25,8 @@
#include <QString> #include <QString>
#include <QMutex> #include <QMutex>
#include "query.h"
namespace lucene namespace lucene
{ {
namespace analysis namespace analysis
@@ -58,7 +60,7 @@ public:
void beginIndexing(); void beginIndexing();
void endIndexing(); void endIndexing();
void appendFields( const QString& table, const QMap< unsigned int, QString >& fields ); void appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData );
signals: signals:
void indexReady(); void indexReady();
@@ -66,7 +68,8 @@ signals:
public slots: public slots:
void loadLuceneIndex(); void loadLuceneIndex();
QMap< int, float > search( const QString& table, const QString& name ); QMap< int, float > search( const Tomahawk::query_ptr& query );
QMap< int, float > searchAlbum( const Tomahawk::query_ptr& query );
private: private:
DatabaseImpl& m_db; DatabaseImpl& m_db;

View File

@@ -35,6 +35,9 @@
#include "utils/xspfloader.h" #include "utils/xspfloader.h"
#include "jobview/JobStatusView.h" #include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h" #include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#include "pipeline.h"
#ifdef QCA2_FOUND #ifdef QCA2_FOUND
#include "utils/groovesharkparser.h" #include "utils/groovesharkparser.h"
#endif //QCA2_FOUND #endif //QCA2_FOUND
@@ -43,7 +46,7 @@
using namespace Tomahawk; using namespace Tomahawk;
bool DropJob::s_canParseSpotifyPlaylists = false; bool DropJob::s_canParseSpotifyPlaylists = false;
static QString s_dropJobInfoId = "dropjob";
DropJob::DropJob( QObject *parent ) DropJob::DropJob( QObject *parent )
: QObject( parent ) : QObject( parent )
@@ -691,6 +694,67 @@ DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
} }
void
DropJob::tracksFromDB( const QList< query_ptr >& tracks )
{
// Tracks that we get from databasecommand_alltracks are resolved only against the database and explicitly marked
// as finished. if the source they resolve to is offline they will not resolve against any resolver.
// explicitly resolve them if they fall in that case first
foreach( const query_ptr& track, tracks )
{
if ( !track->playable() && !track->solved() && track->results().size() ) // we have offline results
{
track->setResolveFinished( false );
Pipeline::instance()->resolve( track );
}
}
album_ptr albumPtr;
artist_ptr artistPtr;
if ( Tomahawk::Album* album = qobject_cast< Tomahawk::Album* >( sender() ) )
{
foreach ( const album_ptr& ptr, m_albumsToKeep )
if ( ptr.data() == album )
{
albumPtr = ptr;
m_albumsToKeep.remove( ptr );
}
}
else if ( Tomahawk::Artist* artist = qobject_cast< Tomahawk::Artist* >( sender() ) )
{
foreach ( const artist_ptr& ptr, m_artistsToKeep )
if ( ptr.data() == artist )
{
artistPtr = ptr;
m_artistsToKeep.remove( ptr );
}
}
// If we have no tracks, this means no sources in our network have the give request (artist or album)
// Since we really do want to try to drop them, we ask the infosystem as well.
if ( tracks.isEmpty() )
{
if ( !albumPtr.isNull() && !albumPtr->artist().isNull() )
{
Q_ASSERT( artistPtr.isNull() );
--m_queryCount; // This query is done. New query is infosystem query
getAlbumFromInfoystem( albumPtr->artist()->name(), albumPtr->name() );
}
else if ( !artistPtr.isNull() )
{
Q_ASSERT( albumPtr.isNull() );
--m_queryCount;
getTopTen( artistPtr->name() );
}
}
else
{
onTracksAdded( tracks );
}
}
void void
DropJob::removeDuplicates() DropJob::removeDuplicates()
{ {
@@ -699,10 +763,18 @@ DropJob::removeDuplicates()
{ {
bool contains = false; bool contains = false;
foreach( const Tomahawk::query_ptr &tmpItem, list ) foreach( const Tomahawk::query_ptr &tmpItem, list )
{
if ( item->album() == tmpItem->album() if ( item->album() == tmpItem->album()
&& item->artist() == tmpItem->artist() && item->artist() == tmpItem->artist()
&& item->track() == tmpItem->track() ) && item->track() == tmpItem->track() )
{
if ( item->playable() && !tmpItem->playable() )
list.replace( list.indexOf( tmpItem ), item );
contains = true; contains = true;
break;
}
}
if ( !contains ) if ( !contains )
list.append( item ); list.append( item );
} }
@@ -733,28 +805,31 @@ DropJob::removeRemoteSources()
void void
DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{ {
if ( requestData.caller == "changeme" ) if ( requestData.caller == s_dropJobInfoId )
{ {
Tomahawk::InfoSystem::InfoStringHash artistInfo; const Tomahawk::InfoSystem::InfoStringHash info = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
artistInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); const QString artist = info["artist"];
const QString album = info["album"];
QString artist = artistInfo["artist"]; qDebug() << "Got requestData response for artist" << artist << "and album:" << album << output;
qDebug() << "Got requestData response for artist" << artist << output;
QList< query_ptr > results; QList< query_ptr > results;
int i = 0; int i = 0;
foreach ( const QVariant& title, output.toMap().value( "tracks" ).toList() ) foreach ( const QVariant& title, output.toMap().value( "tracks" ).toList() )
{ {
qDebug() << "got title" << title;
results << Query::get( artist, title.toString(), QString(), uuid() ); results << Query::get( artist, title.toString(), QString(), uuid() );
if ( ++i == 10 ) // Only getting top ten for now. Would make sense to make it configurable if ( ++i == 10 ) // Only getting top ten for now. Would make sense to make it configurable
break; break;
} }
if ( results.isEmpty() )
{
const QString which = album.isEmpty() ? "artist" : "album";
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "No tracks found for given %1" ).arg( which ), 5 ) );
}
onTracksAdded( results ); onTracksAdded( results );
} }
} }
@@ -766,8 +841,10 @@ DropJob::getArtist( const QString &artist )
artist_ptr artistPtr = Artist::get( artist ); artist_ptr artistPtr = Artist::get( artist );
if ( artistPtr->playlistInterface()->tracks().isEmpty() ) if ( artistPtr->playlistInterface()->tracks().isEmpty() )
{ {
m_artistsToKeep.insert( artistPtr );
connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ), connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) ); SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++; m_queryCount++;
return QList< query_ptr >(); return QList< query_ptr >();
} }
@@ -787,9 +864,14 @@ DropJob::getAlbum(const QString &artist, const QString &album)
if ( albumPtr->playlistInterface()->tracks().isEmpty() ) if ( albumPtr->playlistInterface()->tracks().isEmpty() )
{ {
// For albums that don't exist until this moment, we are the main shared pointer holding on.
// fetching the tracks is asynchronous, so the resulting signal is queued. when we go out of scope we delete
// the artist_ptr which means we never get the signal delivered. so we hold on to the album pointer till we're done
m_albumsToKeep.insert( albumPtr );
m_dropJob = new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album ); m_dropJob = new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album );
connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ), connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) ); SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
JobStatusView::instance()->model()->addJob( m_dropJob ); JobStatusView::instance()->model()->addJob( m_dropJob );
m_queryCount++; m_queryCount++;
@@ -811,7 +893,7 @@ DropJob::getTopTen( const QString &artist )
artistInfo["artist"] = artist; artistInfo["artist"] = artist;
Tomahawk::InfoSystem::InfoRequestData requestData; Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = "changeme"; requestData.caller = s_dropJobInfoId;
requestData.customData = QVariantMap(); requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
@@ -821,3 +903,28 @@ DropJob::getTopTen( const QString &artist )
m_queryCount++; m_queryCount++;
} }
void
DropJob::getAlbumFromInfoystem( const QString& artist, const QString& album )
{
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
Tomahawk::InfoSystem::InfoStringHash artistInfo;
artistInfo["artist"] = artist;
artistInfo["album"] = album;
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = s_dropJobInfoId;
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
m_queryCount++;
}

View File

@@ -23,6 +23,8 @@
#include "query.h" #include "query.h"
#include "infosystem/infosystem.h" #include "infosystem/infosystem.h"
#include "utils/xspfloader.h"
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
#include <QMimeData> #include <QMimeData>
@@ -120,9 +122,9 @@ private slots:
void onTracksAdded( const QList<Tomahawk::query_ptr>& ); void onTracksAdded( const QList<Tomahawk::query_ptr>& );
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void tracksFromDB( const QList< Tomahawk::query_ptr >& );
private: private:
/// handle parsing mime data /// handle parsing mime data
void handleAllUrls( const QString& urls ); void handleAllUrls( const QString& urls );
void handleTrackUrls( const QString& urls ); void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d ); QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
@@ -135,6 +137,7 @@ private:
QList< Tomahawk::query_ptr > getAlbum( const QString& artist, const QString& album ); QList< Tomahawk::query_ptr > getAlbum( const QString& artist, const QString& album );
void getTopTen( const QString& artist ); void getTopTen( const QString& artist );
void getAlbumFromInfoystem( const QString& artist, const QString& album );
void removeDuplicates(); void removeDuplicates();
void removeRemoteSources(); void removeRemoteSources();
@@ -151,6 +154,8 @@ private:
Tomahawk::DropJobNotifier* m_dropJob; Tomahawk::DropJobNotifier* m_dropJob;
QList< Tomahawk::query_ptr > m_resultList; QList< Tomahawk::query_ptr > m_resultList;
QSet< Tomahawk::album_ptr > m_albumsToKeep;
QSet< Tomahawk::artist_ptr > m_artistsToKeep;
static bool s_canParseSpotifyPlaylists; static bool s_canParseSpotifyPlaylists;
}; };

View File

@@ -0,0 +1,47 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "functimeout.h"
#include <QTimer>
using namespace Tomahawk;
FuncTimeout::FuncTimeout( int ms, boost::function< void() > func, QObject* besafe )
: m_func( func )
, m_watch( QWeakPointer< QObject >( besafe ) )
{
//qDebug() << Q_FUNC_INFO;
QTimer::singleShot( ms, this, SLOT( exec() ) );
}
FuncTimeout::~FuncTimeout()
{
}
void FuncTimeout::exec()
{
if( !m_watch.isNull() )
m_func();
this->deleteLater();
}

View File

@@ -20,7 +20,6 @@
#define FUNCTIMEOUT_H #define FUNCTIMEOUT_H
#include <QObject> #include <QObject>
#include <QTimer>
#include <QWeakPointer> #include <QWeakPointer>
#include "boost/function.hpp" #include "boost/function.hpp"
@@ -43,26 +42,12 @@ class DLLEXPORT FuncTimeout : public QObject
Q_OBJECT Q_OBJECT
public: public:
FuncTimeout( int ms, boost::function<void()> func, QObject* besafe ) FuncTimeout( int ms, boost::function<void()> func, QObject* besafe );
: m_func( func )
, m_watch( QWeakPointer< QObject >( besafe ) )
{
//qDebug() << Q_FUNC_INFO;
QTimer::singleShot( ms, this, SLOT( exec() ) );
};
~FuncTimeout() ~FuncTimeout();
{
//qDebug() << Q_FUNC_INFO;
};
public slots: public slots:
void exec() void exec();
{
if( !m_watch.isNull() )
m_func();
this->deleteLater();
};
private: private:
boost::function<void()> m_func; boost::function<void()> m_func;

View File

@@ -45,9 +45,9 @@ ChartsPlugin::ChartsPlugin()
, m_chartsFetchJobs( 0 ) , m_chartsFetchJobs( 0 )
{ {
/// Add resources here /// Add resources here
m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall.com"; m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall";
/// If you add resource, update version aswell /// If you add resource, update version aswell
m_chartVersion = "1.0"; m_chartVersion = "2.1";
m_supportedGetTypes << InfoChart << InfoChartCapabilities; m_supportedGetTypes << InfoChart << InfoChartCapabilities;
} }
@@ -332,7 +332,9 @@ ChartsPlugin::chartTypes()
if( source == "itunes" ){ if( source == "itunes" ){
chartName = "iTunes"; chartName = "iTunes";
} }
if( source == "soundcloudwall" ){
chartName = "SoundCloudWall";
}
if( source == "wearehunted" ){ if( source == "wearehunted" ){
chartName = "WeAreHunted"; chartName = "WeAreHunted";
} }

View File

@@ -451,8 +451,15 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
QString artistName = criteria["artist"]; QString artistName = criteria["artist"];
QString albumName = criteria["album"]; QString albumName = criteria["album"];
QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); imgurl.addQueryItem( "method", "album.imageredirect" );
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addEncodedQueryItem( "album", QUrl::toPercentEncoding( albumName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "large" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
QNetworkReply* reply = TomahawkUtils::nam()->get( req ); QNetworkReply* reply = TomahawkUtils::nam()->get( req );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
@@ -464,8 +471,14 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
{ {
QString artistName = criteria["artist"]; QString artistName = criteria["artist"];
QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
QNetworkRequest req( imgurl.arg( artistName ) ); imgurl.addQueryItem( "method", "artist.imageredirect" );
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "large" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
QNetworkReply* reply = TomahawkUtils::nam()->get( req ); QNetworkReply* reply = TomahawkUtils::nam()->get( req );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
@@ -693,14 +706,14 @@ LastFmPlugin::artistImagesReturned()
void void
LastFmPlugin::settingsChanged() LastFmPlugin::settingsChanged()
{ {
if ( !m_scrobbler && m_account->enabled() ) if ( !m_scrobbler && m_account->scrobble() )
{ // can simply create the scrobbler { // can simply create the scrobbler
lastfm::ws::Username = m_account->username(); lastfm::ws::Username = m_account->username();
m_pw = m_account->password(); m_pw = m_account->password();
createScrobbler(); createScrobbler();
} }
else if ( m_scrobbler && !m_account->enabled() ) else if ( m_scrobbler && !m_account->scrobble() )
{ {
delete m_scrobbler; delete m_scrobbler;
m_scrobbler = 0; m_scrobbler = 0;
@@ -748,7 +761,7 @@ LastFmPlugin::onAuthenticated()
m_account->setSessionKey( lastfm::ws::SessionKey.toLatin1() ); m_account->setSessionKey( lastfm::ws::SessionKey.toLatin1() );
// qDebug() << "Got session key from last.fm"; // qDebug() << "Got session key from last.fm";
if ( m_account->enabled() ) if ( m_account->scrobble() )
m_scrobbler = new lastfm::Audioscrobbler( "thk" ); m_scrobbler = new lastfm::Audioscrobbler( "thk" );
} }
} }

View File

@@ -43,7 +43,6 @@ SpotifyPlugin::SpotifyPlugin()
: InfoPlugin() : InfoPlugin()
, m_chartsFetchJobs( 0 ) , m_chartsFetchJobs( 0 )
{ {
m_supportedGetTypes << InfoChart << InfoChartCapabilities; m_supportedGetTypes << InfoChart << InfoChartCapabilities;
} }
@@ -71,7 +70,6 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
switch ( requestData.type ) switch ( requestData.type )
{ {
case InfoChart: case InfoChart:
@@ -87,6 +85,7 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
case InfoChartCapabilities: case InfoChartCapabilities:
fetchChartCapabilities( requestData ); fetchChartCapabilities( requestData );
break; break;
default: default:
dataError( requestData ); dataError( requestData );
} }
@@ -110,7 +109,6 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!"; tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!";
dataError( requestData ); dataError( requestData );
return; return;
} }
/// Set the criterias for current chart /// Set the criterias for current chart
criteria["chart_id"] = hash["chart_id"]; criteria["chart_id"] = hash["chart_id"];
@@ -118,6 +116,8 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
emit getCachedInfo( criteria, 86400000 /* Expire chart cache in 1 day */, requestData ); emit getCachedInfo( criteria, 86400000 /* Expire chart cache in 1 day */, requestData );
} }
void void
SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
{ {
@@ -132,12 +132,12 @@ SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData req
emit getCachedInfo( criteria, 604800000, requestData ); emit getCachedInfo( criteria, 604800000, requestData );
} }
void void
SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
{ {
switch ( requestData.type ) switch ( requestData.type )
{ {
case InfoChart: case InfoChart:
{ {
/// Fetch the chart, we need source and id /// Fetch the chart, we need source and id
@@ -149,8 +149,6 @@ SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, To
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
return; return;
} }
case InfoChartCapabilities: case InfoChartCapabilities:
{ {
@@ -212,9 +210,8 @@ SpotifyPlugin::chartTypes()
} }
QVariantMap charts; QVariantMap charts;
foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() ) foreach( QVariant geos, chartObj.value( "Charts" ).toList().takeLast().toMap().value( "geo" ).toList() )
{ {
const QString geo = geos.toMap().value( "name" ).toString(); const QString geo = geos.toMap().value( "name" ).toString();
const QString geoId = geos.toMap().value( "id" ).toString(); const QString geoId = geos.toMap().value( "id" ).toString();
QString country; QString country;
@@ -225,7 +222,6 @@ SpotifyPlugin::chartTypes()
country = geo; country = geo;
else else
{ {
QLocale l( QString( "en_%1" ).arg( geo ) ); QLocale l( QString( "en_%1" ).arg( geo ) );
country = Tomahawk::CountryUtils::fullCountryFromCode( geo ); country = Tomahawk::CountryUtils::fullCountryFromCode( geo );
@@ -240,7 +236,7 @@ SpotifyPlugin::chartTypes()
} }
QList< InfoStringHash > chart_types; QList< InfoStringHash > chart_types;
foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() ) foreach( QVariant types, chartObj.value( "Charts" ).toList().takeFirst().toMap().value( "types" ).toList() )
{ {
QString type = types.toMap().value( "id" ).toString(); QString type = types.toMap().value( "id" ).toString();
QString label = types.toMap().value( "name" ).toString(); QString label = types.toMap().value( "name" ).toString();
@@ -251,18 +247,15 @@ SpotifyPlugin::chartTypes()
c[ "type" ] = type; c[ "type" ] = type;
chart_types.append( c ); chart_types.append( c );
} }
charts.insert( country.toUtf8(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) ); charts.insert( country.toUtf8(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) );
} }
QVariantMap defaultMap; QVariantMap defaultMap;
defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums"; defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums";
m_allChartsMap[ "defaults" ] = defaultMap; m_allChartsMap[ "defaults" ] = defaultMap;
m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) ); m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) );
} }
else else
{ {
@@ -281,13 +274,12 @@ SpotifyPlugin::chartTypes()
} }
m_cachedRequests.clear(); m_cachedRequests.clear();
} }
} }
void void
SpotifyPlugin::chartReturned() SpotifyPlugin::chartReturned()
{ {
/// Chart request returned something! Woho /// Chart request returned something! Woho
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() ); QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
QString url = reply->url().toString(); QString url = reply->url().toString();
@@ -318,14 +310,13 @@ SpotifyPlugin::chartReturned()
else else
setChartType( None ); setChartType( None );
foreach(QVariant result, res.value("toplist").toMap().value("result").toList() ) foreach( QVariant result, res.value( "toplist" ).toMap().value( "result" ).toList() )
{ {
QString title, artist; QString title, artist;
QVariantMap chartMap = result.toMap(); QVariantMap chartMap = result.toMap();
if ( !chartMap.isEmpty() ) if ( !chartMap.isEmpty() )
{ {
title = chartMap.value( "title" ).toString(); title = chartMap.value( "title" ).toString();
artist = chartMap.value( "artist" ).toString(); artist = chartMap.value( "artist" ).toString();
@@ -341,7 +332,6 @@ SpotifyPlugin::chartReturned()
if( chartType() == Album ) if( chartType() == Album )
{ {
InfoStringHash pair; InfoStringHash pair;
pair["artist"] = artist; pair["artist"] = artist;
pair["album"] = title; pair["album"] = title;
@@ -351,10 +341,8 @@ SpotifyPlugin::chartReturned()
if( chartType() == Artist ) if( chartType() == Artist )
{ {
top_artists << chartMap.value( "name" ).toString(); top_artists << chartMap.value( "name" ).toString();
qDebug() << "SpotifyChart type is artist"; qDebug() << "SpotifyChart type is artist";
} }
} }
} }
@@ -393,5 +381,4 @@ SpotifyPlugin::chartReturned()
} }
else else
qDebug() << "Network error in fetching chart:" << reply->url().toString(); qDebug() << "Network error in fetching chart:" << reply->url().toString();
} }

View File

@@ -42,8 +42,6 @@ MprisPlugin::MprisPlugin()
: InfoPlugin() : InfoPlugin()
, m_coverTempFile( 0 ) , m_coverTempFile( 0 )
{ {
qDebug() << Q_FUNC_INFO;
// init // init
m_playbackStatus = "Stopped"; m_playbackStatus = "Stopped";
@@ -54,8 +52,8 @@ MprisPlugin::MprisPlugin()
new MprisPluginRootAdaptor( this ); new MprisPluginRootAdaptor( this );
new MprisPluginPlayerAdaptor( this ); new MprisPluginPlayerAdaptor( this );
QDBusConnection dbus = QDBusConnection::sessionBus(); QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.registerObject("/org/mpris/MediaPlayer2", this); dbus.registerObject( "/org/mpris/MediaPlayer2", this );
dbus.registerService("org.mpris.MediaPlayer2.tomahawk"); dbus.registerService( "org.mpris.MediaPlayer2.tomahawk" );
// Listen to volume changes // Listen to volume changes
connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ), connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ),
@@ -67,9 +65,11 @@ MprisPlugin::MprisPlugin()
// When a track is added or removed, CanGoNext updated signal is sent // When a track is added or removed, CanGoNext updated signal is sent
Tomahawk::playlistinterface_ptr playlist = AudioEngine::instance()->playlist(); Tomahawk::playlistinterface_ptr playlist = AudioEngine::instance()->playlist();
if( !playlist.isNull() ) if ( !playlist.isNull() )
{
connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ), connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) ); SLOT( onTrackCountChanged( unsigned int ) ) );
}
// Connect to AudioEngine's seeked signal // Connect to AudioEngine's seeked signal
connect( AudioEngine::instance(), SIGNAL( seeked( qint64 ) ), connect( AudioEngine::instance(), SIGNAL( seeked( qint64 ) ),
@@ -81,52 +81,55 @@ MprisPlugin::MprisPlugin()
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( finished( QString ) ),
SLOT( infoSystemFinished( QString ) ) );
} }
MprisPlugin::~MprisPlugin() MprisPlugin::~MprisPlugin()
{ {
qDebug() << Q_FUNC_INFO;
delete m_coverTempFile; delete m_coverTempFile;
} }
// org.mpris.MediaPlayer2 // org.mpris.MediaPlayer2
bool bool
MprisPlugin::canQuit() const MprisPlugin::canQuit() const
{ {
qDebug() << Q_FUNC_INFO;
return true; return true;
} }
bool bool
MprisPlugin::canRaise() const MprisPlugin::canRaise() const
{ {
qDebug() << Q_FUNC_INFO;
return false; return false;
} }
bool bool
MprisPlugin::hasTrackList() const MprisPlugin::hasTrackList() const
{ {
qDebug() << Q_FUNC_INFO;
return false; return false;
} }
QString QString
MprisPlugin::identity() const MprisPlugin::identity() const
{ {
return QString("Tomahawk"); return QString( "Tomahawk" );
} }
QString QString
MprisPlugin::desktopEntry() const MprisPlugin::desktopEntry() const
{ {
return QString("tomahawk"); return QString( "tomahawk" );
} }
QStringList QStringList
MprisPlugin::supportedUriSchemes() const MprisPlugin::supportedUriSchemes() const
{ {
@@ -135,23 +138,27 @@ MprisPlugin::supportedUriSchemes() const
return uriSchemes; return uriSchemes;
} }
QStringList QStringList
MprisPlugin::supportedMimeTypes() const MprisPlugin::supportedMimeTypes() const
{ {
return QStringList(); return QStringList();
} }
void void
MprisPlugin::Raise() MprisPlugin::Raise()
{ {
} }
void void
MprisPlugin::Quit() MprisPlugin::Quit()
{ {
QApplication::quit(); QApplication::quit();
} }
// org.mpris.MediaPlayer2.Player // org.mpris.MediaPlayer2.Player
bool bool
@@ -160,24 +167,28 @@ MprisPlugin::canControl() const
return true; return true;
} }
bool bool
MprisPlugin::canGoNext() const MprisPlugin::canGoNext() const
{ {
return AudioEngine::instance()->canGoNext(); return AudioEngine::instance()->canGoNext();
} }
bool bool
MprisPlugin::canGoPrevious() const MprisPlugin::canGoPrevious() const
{ {
return AudioEngine::instance()->canGoPrevious(); return AudioEngine::instance()->canGoPrevious();
} }
bool bool
MprisPlugin::canPause() const MprisPlugin::canPause() const
{ {
return AudioEngine::instance()->currentTrack(); return AudioEngine::instance()->currentTrack();
} }
bool bool
MprisPlugin::canPlay() const MprisPlugin::canPlay() const
{ {
@@ -186,6 +197,7 @@ MprisPlugin::canPlay() const
return AudioEngine::instance()->currentTrack() || ( !p.isNull() && p->trackCount() ); return AudioEngine::instance()->currentTrack() || ( !p.isNull() && p->trackCount() );
} }
bool bool
MprisPlugin::canSeek() const MprisPlugin::canSeek() const
{ {
@@ -196,6 +208,7 @@ MprisPlugin::canSeek() const
} }
QString QString
MprisPlugin::loopStatus() const MprisPlugin::loopStatus() const
{ {
@@ -215,39 +228,42 @@ MprisPlugin::loopStatus() const
return "None"; return "None";
break; break;
default: default:
return QString("None"); return "None";
break; break;
} }
return QString("None"); return QString( "None" );
} }
void void
MprisPlugin::setLoopStatus( const QString &value ) MprisPlugin::setLoopStatus( const QString& value )
{ {
Tomahawk::playlistinterface_ptr p = AudioEngine::instance()->playlist(); Tomahawk::playlistinterface_ptr p = AudioEngine::instance()->playlist();
if ( p.isNull() ) if ( p.isNull() )
return; return;
if( value == "Track") if ( value == "Track" )
p->setRepeatMode( PlaylistInterface::RepeatOne ); p->setRepeatMode( PlaylistInterface::RepeatOne );
else if( value == "Playlist" ) else if ( value == "Playlist" )
p->setRepeatMode( PlaylistInterface::RepeatAll ); p->setRepeatMode( PlaylistInterface::RepeatAll );
else if( value == "None" ) else if ( value == "None" )
p->setRepeatMode( PlaylistInterface::NoRepeat ); p->setRepeatMode( PlaylistInterface::NoRepeat );
} }
double double
MprisPlugin::maximumRate() const MprisPlugin::maximumRate() const
{ {
return 1.0; return 1.0;
} }
QVariantMap QVariantMap
MprisPlugin::metadata() const MprisPlugin::metadata() const
{ {
QVariantMap metadataMap; QVariantMap metadataMap;
Tomahawk::result_ptr track = AudioEngine::instance()->currentTrack(); Tomahawk::result_ptr track = AudioEngine::instance()->currentTrack();
if( track ) if ( track )
{ {
metadataMap.insert( "mpris:trackid", QString( "/track/" ) + track->id().replace( "-", "" ) ); metadataMap.insert( "mpris:trackid", QString( "/track/" ) + track->id().replace( "-", "" ) );
metadataMap.insert( "mpris:length", track->duration() ); metadataMap.insert( "mpris:length", track->duration() );
@@ -256,9 +272,11 @@ MprisPlugin::metadata() const
metadataMap.insert( "xesam:title", track->track() ); metadataMap.insert( "xesam:title", track->track() );
// Only return art if tempfile exists, and if its name contains the same "artist_album_tomahawk_cover.png" // Only return art if tempfile exists, and if its name contains the same "artist_album_tomahawk_cover.png"
if( m_coverTempFile && m_coverTempFile->exists() && if ( m_coverTempFile && m_coverTempFile->exists() &&
m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) ) m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
{
metadataMap.insert( "mpris:artUrl", QString( QUrl::fromLocalFile( QFileInfo( *m_coverTempFile ).absoluteFilePath() ).toEncoded() ) ); metadataMap.insert( "mpris:artUrl", QString( QUrl::fromLocalFile( QFileInfo( *m_coverTempFile ).absoluteFilePath() ).toEncoded() ) );
}
else else
{ {
// Need to fetch the album cover // Need to fetch the album cover
@@ -280,18 +298,21 @@ MprisPlugin::metadata() const
return metadataMap; return metadataMap;
} }
double double
MprisPlugin::minimumRate() const MprisPlugin::minimumRate() const
{ {
return 1.0; return 1.0;
} }
QString QString
MprisPlugin::playbackStatus() const MprisPlugin::playbackStatus() const
{ {
return m_playbackStatus; return m_playbackStatus;
} }
qlonglong qlonglong
MprisPlugin::position() const MprisPlugin::position() const
{ {
@@ -299,18 +320,21 @@ MprisPlugin::position() const
return (qlonglong) ( AudioEngine::instance()->currentTime() * 1000 ); return (qlonglong) ( AudioEngine::instance()->currentTime() * 1000 );
} }
double double
MprisPlugin::rate() const MprisPlugin::rate() const
{ {
return 1.0; return 1.0;
} }
void void
MprisPlugin::setRate( double value ) MprisPlugin::setRate( double value )
{ {
Q_UNUSED( value ); Q_UNUSED( value );
} }
bool bool
MprisPlugin::shuffle() const MprisPlugin::shuffle() const
{ {
@@ -320,6 +344,7 @@ MprisPlugin::shuffle() const
return p->shuffled(); return p->shuffled();
} }
void void
MprisPlugin::setShuffle( bool value ) MprisPlugin::setShuffle( bool value )
{ {
@@ -329,70 +354,76 @@ MprisPlugin::setShuffle( bool value )
return p->setShuffled( value ); return p->setShuffled( value );
} }
double double
MprisPlugin::volume() const MprisPlugin::volume() const
{ {
return AudioEngine::instance()->volume(); return AudioEngine::instance()->volume();
} }
void void
MprisPlugin::setVolume( double value ) MprisPlugin::setVolume( double value )
{ {
AudioEngine::instance()->setVolume( value ); AudioEngine::instance()->setVolume( value );
} }
void void
MprisPlugin::Next() MprisPlugin::Next()
{ {
AudioEngine::instance()->next(); AudioEngine::instance()->next();
} }
void void
MprisPlugin::OpenUri( const QString &Uri ) MprisPlugin::OpenUri( const QString& Uri )
{ {
if( Uri.contains( "tomahawk://" ) ) if ( Uri.contains( "tomahawk://" ) )
GlobalActionManager::instance()->parseTomahawkLink( Uri ); GlobalActionManager::instance()->parseTomahawkLink( Uri );
else if( Uri.contains( "spotify:" ) ) else if ( Uri.contains( "spotify:" ) )
GlobalActionManager::instance()->openSpotifyLink( Uri ); GlobalActionManager::instance()->openSpotifyLink( Uri );
} }
void void
MprisPlugin::Pause() MprisPlugin::Pause()
{ {
AudioEngine::instance()->pause(); AudioEngine::instance()->pause();
} }
void void
MprisPlugin::Play() MprisPlugin::Play()
{ {
AudioEngine::instance()->play(); AudioEngine::instance()->play();
} }
void void
MprisPlugin::PlayPause() MprisPlugin::PlayPause()
{ {
AudioEngine::instance()->playPause(); AudioEngine::instance()->playPause();
} }
void void
MprisPlugin::Previous() MprisPlugin::Previous()
{ {
AudioEngine::instance()->previous(); AudioEngine::instance()->previous();
} }
void void
MprisPlugin::Seek( qlonglong Offset ) MprisPlugin::Seek( qlonglong Offset )
{ {
qDebug() << Q_FUNC_INFO; if ( !canSeek() )
if( !canSeek() )
return; return;
qlonglong seekTime = position() + Offset; qlonglong seekTime = position() + Offset;
qDebug() << "seekTime: " << seekTime; if ( seekTime < 0 )
if( seekTime < 0 )
AudioEngine::instance()->seek( 0 ); AudioEngine::instance()->seek( 0 );
else if( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 ) else if ( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
Next(); Next();
// seekTime is in microseconds, but we work internally in milliseconds // seekTime is in microseconds, but we work internally in milliseconds
else else
@@ -400,50 +431,46 @@ MprisPlugin::Seek( qlonglong Offset )
} }
void void
MprisPlugin::SetPosition( const QDBusObjectPath &TrackId, qlonglong Position ) MprisPlugin::SetPosition( const QDBusObjectPath& TrackId, qlonglong Position )
{ {
qDebug() << Q_FUNC_INFO; if ( !canSeek() )
if( !canSeek() )
return; return;
qDebug() << "path: " << TrackId.path(); if ( TrackId.path() != QString( "/track/" ) + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
qDebug() << "position: " << Position;
if( TrackId.path() != QString("/track/") + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
return; return;
if( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 ) ) if ( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 ) )
return; return;
qDebug() << "seeking to: " << Position/1000 << "ms";
AudioEngine::instance()->seek( (qint64) (Position / 1000 ) ); AudioEngine::instance()->seek( (qint64) (Position / 1000 ) );
} }
void void
MprisPlugin::Stop() MprisPlugin::Stop()
{ {
AudioEngine::instance()->stop(); AudioEngine::instance()->stop();
} }
// InfoPlugin Methods // InfoPlugin Methods
void void
MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
{ {
Q_UNUSED( requestData ); Q_UNUSED( requestData );
qDebug() << Q_FUNC_INFO;
return; return;
} }
void void
MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ) MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input )
{ {
Q_UNUSED( caller ); Q_UNUSED( caller );
qDebug() << Q_FUNC_INFO;
bool isPlayingInfo = false; bool isPlayingInfo = false;
switch ( type ) switch ( type )
@@ -469,11 +496,11 @@ MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVar
break; break;
} }
if( isPlayingInfo ) if ( isPlayingInfo )
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus"); notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus" );
} }
void void
MprisPlugin::stateChanged( AudioState newState, AudioState oldState ) MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
{ {
@@ -481,12 +508,11 @@ MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
Q_UNUSED( oldState ); Q_UNUSED( oldState );
} }
/** Audio state slots */ /** Audio state slots */
void void
MprisPlugin::audioStarted( const QVariant &input ) MprisPlugin::audioStarted( const QVariant& input )
{ {
qDebug() << Q_FUNC_INFO;
if ( !input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) if ( !input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
return; return;
@@ -495,64 +521,55 @@ MprisPlugin::audioStarted( const QVariant &input )
return; return;
m_playbackStatus = "Playing"; m_playbackStatus = "Playing";
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata"); notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
//hash["artist"];
//hash["title"];
//QString nowPlaying = "";
//qDebug() << "nowPlaying: " << nowPlaying;
} }
void void
MprisPlugin::audioFinished( const QVariant &input ) MprisPlugin::audioFinished( const QVariant& input )
{ {
Q_UNUSED( input ); Q_UNUSED( input );
//qDebug() << Q_FUNC_INFO;
} }
void void
MprisPlugin::audioStopped() MprisPlugin::audioStopped()
{ {
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Stopped"; m_playbackStatus = "Stopped";
} }
void void
MprisPlugin::audioPaused() MprisPlugin::audioPaused()
{ {
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Paused"; m_playbackStatus = "Paused";
} }
void void
MprisPlugin::audioResumed( const QVariant &input ) MprisPlugin::audioResumed( const QVariant& input )
{ {
qDebug() << Q_FUNC_INFO;
audioStarted( input ); audioStarted( input );
} }
void void
MprisPlugin::onVolumeChanged( int volume ) MprisPlugin::onVolumeChanged( int volume )
{ {
Q_UNUSED( volume ); Q_UNUSED( volume );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume"); notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume" );
} }
void void
MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist ) MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
{ {
qDebug() << Q_FUNC_INFO;
disconnect( this, SLOT( onTrackCountChanged( unsigned int ) ) ); disconnect( this, SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "disconnected";
if( !playlist.isNull() )
qDebug() << "playlist not null";
if( !playlist.isNull() ) if ( !playlist.isNull() )
connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ), connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) ); SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "connected new playlist";
// Notify relevant changes // Notify relevant changes
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "LoopStatus" ); notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "LoopStatus" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Shuffle" ); notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Shuffle" );
@@ -560,6 +577,7 @@ MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
onTrackCountChanged( 0 ); onTrackCountChanged( 0 );
} }
void void
MprisPlugin::onTrackCountChanged( unsigned int tracks ) MprisPlugin::onTrackCountChanged( unsigned int tracks )
{ {
@@ -569,12 +587,14 @@ MprisPlugin::onTrackCountChanged( unsigned int tracks )
} }
void
MprisPlugin::onSeeked( qint64 ms ) void
{ MprisPlugin::onSeeked( qint64 ms )
{
qlonglong us = (qlonglong) ( ms*1000 ); qlonglong us = (qlonglong) ( ms*1000 );
emit Seeked( us ); emit Seeked( us );
} }
void void
MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
@@ -603,7 +623,7 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
image.loadFromData( ba ); image.loadFromData( ba );
// Pull out request data for album+artist // Pull out request data for album+artist
if( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
{ {
qDebug() << "Cannot convert metadata input to album cover retrieval"; qDebug() << "Cannot convert metadata input to album cover retrieval";
return; return;
@@ -612,41 +632,33 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
Tomahawk::InfoSystem::InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
// delete the old tempfile and make new one, to avoid caching of filename by mpris clients // delete the old tempfile and make new one, to avoid caching of filename by mpris clients
if( m_coverTempFile ) if ( m_coverTempFile )
{
delete m_coverTempFile; delete m_coverTempFile;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( m_coverTempFile = 0;
QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) ); }
if( !m_coverTempFile->open() )
if ( image.isNull() )
return;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
if ( !m_coverTempFile->open() )
{ {
qDebug() << "WARNING: could not write temporary file for cover art!"; qDebug() << "WARNING: could not write temporary file for cover art!";
} }
// Finally, save the image to the new temp file // Finally, save the image to the new temp file
//if( image.save( QFileInfo( *m_coverTempFile ).absoluteFilePath(), "PNG" ) ) if ( image.save( m_coverTempFile, "PNG" ) )
if( image.save( m_coverTempFile, "PNG") )
{ {
qDebug() << Q_FUNC_INFO << "Image saving successful, notifying"; qDebug() << "Saving cover image to:" << QFileInfo( *m_coverTempFile ).absoluteFilePath();
qDebug() << "Saving to: " << QFileInfo( *m_coverTempFile ).absoluteFilePath();
m_coverTempFile->close(); m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" ); notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
} }
else else
{ {
qDebug() << Q_FUNC_INFO << " failed to save image!"; tDebug() << Q_FUNC_INFO << "failed to save cover image!";
m_coverTempFile->close(); m_coverTempFile->close();
} }
/*
if( m_coverTempFile->open() )
{
QTextStream out( m_coverTempFile );
out << ba;
m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
*/
} }
} }
@@ -657,14 +669,14 @@ MprisPlugin::infoSystemFinished( QString target )
Q_UNUSED( target ); Q_UNUSED( target );
} }
void void
MprisPlugin::notifyPropertyChanged( const QString& interface, MprisPlugin::notifyPropertyChanged( const QString& interface, const QString& propertyName )
const QString& propertyName )
{ {
QDBusMessage signal = QDBusMessage::createSignal( QDBusMessage signal = QDBusMessage::createSignal(
"/org/mpris/MediaPlayer2", "/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties", "org.freedesktop.DBus.Properties",
"PropertiesChanged"); "PropertiesChanged" );
signal << interface; signal << interface;
QVariantMap changedProps; QVariantMap changedProps;
changedProps.insert(propertyName, property(propertyName.toAscii())); changedProps.insert(propertyName, property(propertyName.toAscii()));

View File

@@ -87,7 +87,7 @@ public:
Q_PROPERTY( QString LoopStatus READ loopStatus WRITE setLoopStatus ) Q_PROPERTY( QString LoopStatus READ loopStatus WRITE setLoopStatus )
QString loopStatus() const; QString loopStatus() const;
void setLoopStatus( const QString &value ); void setLoopStatus( const QString& value );
Q_PROPERTY( double MaximumRate READ maximumRate ) Q_PROPERTY( double MaximumRate READ maximumRate )
double maximumRate() const; double maximumRate() const;
@@ -129,16 +129,15 @@ public slots:
// org.mpris.MediaPlayer2.Player // org.mpris.MediaPlayer2.Player
void Next(); void Next();
void OpenUri( const QString &Uri ); void OpenUri( const QString& Uri );
void Pause(); void Pause();
void Play(); void Play();
void PlayPause(); void PlayPause();
void Previous(); void Previous();
void Seek( qlonglong Offset ); void Seek( qlonglong Offset );
void SetPosition( const QDBusObjectPath &TrackId, qlonglong Position ); void SetPosition( const QDBusObjectPath& TrackId, qlonglong Position );
void Stop(); void Stop();
protected slots: protected slots:
void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
@@ -160,16 +159,16 @@ private:
// Get Info // Get Info
// Push Info // Push Info
void audioStarted( const QVariant &input ); void audioStarted( const QVariant& input );
void audioFinished( const QVariant &input ); void audioFinished( const QVariant& input );
void audioStopped(); void audioStopped();
void audioPaused(); void audioPaused();
void audioResumed( const QVariant &input ); void audioResumed( const QVariant& input );
// DBus // DBus
void notifyPropertyChanged( const QString& interface, const QString& propertyName ); void notifyPropertyChanged( const QString& interface, const QString& propertyName );
QString m_playbackStatus; QString m_playbackStatus;
QTemporaryFile *m_coverTempFile; QTemporaryFile* m_coverTempFile;
}; };
}; };

View File

@@ -0,0 +1,56 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ErrorStatusMessage.h"
#include "utils/tomahawkutils.h"
#include <QTimer>
QPixmap* ErrorStatusMessage::s_pixmap = 0;
ErrorStatusMessage::ErrorStatusMessage( const QString& message, int timeoutSecs )
: JobStatusItem()
, m_message( message )
{
m_timer = new QTimer( this );
m_timer->setInterval( timeoutSecs * 1000 );
m_timer->setSingleShot( true );
connect( m_timer, SIGNAL( timeout() ), this, SIGNAL( finished() ) );
if ( !s_pixmap )
s_pixmap = new QPixmap( RESPATH "images/process-stop.png" );
m_timer->start();
}
QPixmap
ErrorStatusMessage::icon() const
{
Q_ASSERT( s_pixmap );
return *s_pixmap;
}
QString
ErrorStatusMessage::mainText() const
{
return m_message;
}

View File

@@ -0,0 +1,49 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ERRORSTATUSMESSAGE_H
#define ERRORSTATUSMESSAGE_H
#include "JobStatusItem.h"
#include "dllmacro.h"
#include <QPixmap>
class QTimer;
class DLLEXPORT ErrorStatusMessage : public JobStatusItem
{
Q_OBJECT
public:
explicit ErrorStatusMessage( const QString& errorMessage, int defaultTimeoutSecs = 8 );
QString type() const { return "errormessage"; }
QString rightColumnText() const { return QString(); }
QPixmap icon() const;
QString mainText() const;
bool allowMultiLine() const { return true; }
private:
QString m_message;
QTimer* m_timer;
static QPixmap* s_pixmap;
};
#endif // ERRORSTATUSMESSAGE_H

View File

@@ -0,0 +1,49 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "IndexingJobItem.h"
#include "utils/tomahawkutils.h"
#include <QPixmap>
static QPixmap* s_indexIcon = 0;
QString
IndexingJobItem::mainText() const
{
return tr( "Indexing database" );
}
QPixmap
IndexingJobItem::icon() const
{
if ( s_indexIcon == 0 )
s_indexIcon = new QPixmap( RESPATH "images/view-refresh.png" );
return *s_indexIcon;
}
void IndexingJobItem::done()
{
emit finished();
}

View File

@@ -0,0 +1,39 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INDEXINGJOBITEM_H
#define INDEXINGJOBITEM_H
#include <jobview/JobStatusItem.h>
class IndexingJobItem : public JobStatusItem
{
Q_OBJECT
public:
explicit IndexingJobItem() {}
void done();
virtual QString rightColumnText() const { return QString(); }
virtual QString mainText() const;
virtual QPixmap icon() const;
virtual QString type() const { return "indexerjob"; }
};
#endif // INDEXINGJOBITEM_H

View File

@@ -23,14 +23,16 @@
#include <QPainter> #include <QPainter>
#include <QApplication> #include <QApplication>
#include <QListView>
#define ROW_HEIGHT 20 #define ROW_HEIGHT 20
#define ICON_PADDING 1 #define ICON_PADDING 1
#define PADDING 2 #define PADDING 2
JobStatusDelegate::JobStatusDelegate( QObject* parent ) JobStatusDelegate::JobStatusDelegate( QObject* parent )
: QStyledItemDelegate ( parent ) : QStyledItemDelegate ( parent )
, m_parentView( qobject_cast< QListView* >( parent ) )
{ {
Q_ASSERT( m_parentView );
} }
JobStatusDelegate::~JobStatusDelegate() JobStatusDelegate::~JobStatusDelegate()
@@ -45,6 +47,7 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
QStyleOptionViewItemV4 opt = option; QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index ); initStyleOption( &opt, index );
QFontMetrics fm( opt.font ); QFontMetrics fm( opt.font );
const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
opt.state &= ~QStyle::State_MouseOver; opt.state &= ~QStyle::State_MouseOver;
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget ); QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );
@@ -52,7 +55,9 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
// painter->drawLine( opt.rect.topLeft(), opt.rect.topRight() ); // painter->drawLine( opt.rect.topLeft(), opt.rect.topRight() );
painter->setRenderHint( QPainter::Antialiasing ); painter->setRenderHint( QPainter::Antialiasing );
const QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING ); QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING );
if ( allowMultiLine )
iconRect.moveTop( opt.rect.top() + opt.rect.height() / 2 - iconRect.height() / 2);
QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >(); QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ); p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation );
painter->drawPixmap( iconRect, p ); painter->drawPixmap( iconRect, p );
@@ -71,15 +76,34 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
const int mainW = rightEdge - 3*PADDING - iconRect.right(); const int mainW = rightEdge - 3*PADDING - iconRect.right();
QString mainText = index.data( Qt::DisplayRole ).toString(); QString mainText = index.data( Qt::DisplayRole ).toString();
QTextOption to( Qt::AlignLeft | Qt::AlignVCenter );
if ( !allowMultiLine )
mainText = fm.elidedText( mainText, Qt::ElideRight, mainW ); mainText = fm.elidedText( mainText, Qt::ElideRight, mainW );
painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), Qt::AlignLeft | Qt::AlignVCenter, mainText ); else
to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere );
painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), mainText, to );
} }
QSize QSize
JobStatusDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const JobStatusDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{ {
// return QStyledItemDelegate::sizeHint( option, index ); const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
const int w = QStyledItemDelegate::sizeHint ( option, index ).width();
return QSize( w, ROW_HEIGHT ); if ( !allowMultiLine )
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT );
else if ( m_cachedMultiLineHeights.contains( index ) )
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), m_cachedMultiLineHeights[ index ] );
// Don't elide, but stretch across as many rows as required
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
const QString text = index.data( Qt::DisplayRole ).toString();
const int leftEdge = ICON_PADDING + ROW_HEIGHT + 2*PADDING;
const QRect rect = opt.fontMetrics.boundingRect( leftEdge, opt.rect.top(), m_parentView->width() - leftEdge, 200, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text );
m_cachedMultiLineHeights.insert( index, rect.height() + 4*PADDING );
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), rect.height() + 4*PADDING );
} }

View File

@@ -22,6 +22,7 @@
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
class QPainter; class QPainter;
class QListView;
class JobStatusDelegate : public QStyledItemDelegate class JobStatusDelegate : public QStyledItemDelegate
{ {
@@ -33,6 +34,10 @@ public:
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
private:
mutable QHash< QPersistentModelIndex, int > m_cachedMultiLineHeights;
QListView* m_parentView;
}; };
#endif // JOBSTATUSDELEGATE_H #endif // JOBSTATUSDELEGATE_H

View File

@@ -0,0 +1,42 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "JobStatusItem.h"
JobStatusItem::JobStatusItem()
: QObject()
{
}
JobStatusItem::~JobStatusItem()
{
}
bool JobStatusItem::allowMultiLine() const
{
return false;
}
bool JobStatusItem::collapseItem() const
{
return false;
}

View File

@@ -20,7 +20,8 @@
#define JOB_STATUS_ITEM #define JOB_STATUS_ITEM
#include <QObject> #include <QObject>
#include <QPixmap>
class QPixmap;
/** /**
* Implement your own JobStatusItem if you want to add some sort of job status entry in the JobStatusView. * Implement your own JobStatusItem if you want to add some sort of job status entry in the JobStatusView.
@@ -37,8 +38,8 @@ class JobStatusItem : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit JobStatusItem() : QObject() {} explicit JobStatusItem();
virtual ~JobStatusItem() {} virtual ~JobStatusItem();
virtual QString type() const = 0; virtual QString type() const = 0;
@@ -52,7 +53,8 @@ public:
* instead of showing each individually. In this case, the right column from the item will be ignored * instead of showing each individually. In this case, the right column from the item will be ignored
* and a count will be shown instead. * and a count will be shown instead.
*/ */
virtual bool collapseItem() const { return false; } virtual bool collapseItem() const;
virtual bool allowMultiLine() const;
signals: signals:
/// Ask for an update /// Ask for an update

View File

@@ -21,6 +21,7 @@
#include "JobStatusItem.h" #include "JobStatusItem.h"
#include "utils/logger.h" #include "utils/logger.h"
#include <QPixmap>
JobStatusModel::JobStatusModel( QObject* parent ) JobStatusModel::JobStatusModel( QObject* parent )
: QAbstractListModel ( parent ) : QAbstractListModel ( parent )
@@ -100,6 +101,8 @@ JobStatusModel::data( const QModelIndex& index, int role ) const
else else
return item->rightColumnText(); return item->rightColumnText();
} }
case AllowMultiLineRole:
return item->allowMultiLine();
} }
return QVariant(); return QVariant();

View File

@@ -31,7 +31,8 @@ public:
enum JobRoles { enum JobRoles {
// DecorationRole is icon // DecorationRole is icon
// DisplayRole is main col // DisplayRole is main col
RightColumnRole = Qt::UserRole + 1 RightColumnRole = Qt::UserRole + 1,
AllowMultiLineRole = Qt::UserRole + 2
}; };
explicit JobStatusModel( QObject* parent = 0 ); explicit JobStatusModel( QObject* parent = 0 );

View File

@@ -40,6 +40,7 @@ JobStatusView* JobStatusView::s_instance = 0;
JobStatusView::JobStatusView( AnimatedSplitter* parent ) JobStatusView::JobStatusView( AnimatedSplitter* parent )
: AnimatedWidget( parent ) : AnimatedWidget( parent )
, m_parent( parent ) , m_parent( parent )
, m_cachedHeight( -1 )
{ {
s_instance = this; s_instance = this;
@@ -56,9 +57,7 @@ JobStatusView::JobStatusView( AnimatedSplitter* parent )
m_view->setFrameShape( QFrame::NoFrame ); m_view->setFrameShape( QFrame::NoFrame );
m_view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); m_view->setAttribute( Qt::WA_MacShowFocusRect, 0 );
m_view->setUniformItemSizes( false );
// new QTreeWidgetItem( m_tree );
m_view->setUniformItemSizes( true );
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
QFont f = font(); QFont f = font();
@@ -86,12 +85,14 @@ JobStatusView::setModel( JobStatusModel* m )
connect( m_view->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( checkCount() ) ); connect( m_view->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
connect( m_view->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( checkCount() ) ); connect( m_view->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
connect( m_view->model(), SIGNAL( modelReset() ), this, SLOT( checkCount() ) );
} }
void void
JobStatusView::checkCount() JobStatusView::checkCount()
{ {
m_cachedHeight = -1;
if ( m_view->model()->rowCount() == 0 && !isHidden() ) if ( m_view->model()->rowCount() == 0 && !isHidden() )
emit hideWidget(); emit hideWidget();
else else
@@ -102,15 +103,21 @@ JobStatusView::checkCount()
QSize QSize
JobStatusView::sizeHint() const JobStatusView::sizeHint() const
{ {
if ( m_cachedHeight >= 0 )
return QSize( 0, m_cachedHeight );
unsigned int y = 0; unsigned int y = 0;
// y += m_tree->header()->height();
y += m_view->contentsMargins().top() + m_view->contentsMargins().bottom(); y += m_view->contentsMargins().top() + m_view->contentsMargins().bottom();
if ( m_view->model()->rowCount() ) if ( m_view->model()->rowCount() )
{ {
unsigned int rowheight = m_view->sizeHintForRow( 0 ); for ( int i = 0; i < m_view->model()->rowCount(); i++ )
y += rowheight * m_view->model()->rowCount() + 2; {
y += m_view->sizeHintForRow( i );
}
y += 2; // some padding
} }
m_cachedHeight = y;
return QSize( 0, y ); return QSize( 0, y );
} }

View File

@@ -56,6 +56,7 @@ private:
QListView* m_view; QListView* m_view;
JobStatusModel* m_model; JobStatusModel* m_model;
AnimatedSplitter* m_parent; AnimatedSplitter* m_parent;
mutable int m_cachedHeight;
static JobStatusView* s_instance; static JobStatusView* s_instance;
}; };

View File

@@ -21,7 +21,9 @@
#include "JobStatusItem.h" #include "JobStatusItem.h"
#include "typedefs.h" #include "typedefs.h"
#include <QHash> #include <QHash>
#include <QPixmap>
class LatchedStatusManager; class LatchedStatusManager;

View File

@@ -22,6 +22,8 @@
#include "jobview/JobStatusItem.h" #include "jobview/JobStatusItem.h"
#include "query.h" #include "query.h"
#include <QPixmap>
class PipelineStatusItem : public JobStatusItem class PipelineStatusItem : public JobStatusItem
{ {
Q_OBJECT Q_OBJECT

View File

@@ -21,6 +21,8 @@
#include "JobStatusItem.h" #include "JobStatusItem.h"
#include <QPixmap>
class StreamConnection; class StreamConnection;
class TransferStatusManager : public QObject class TransferStatusManager : public QObject

View File

@@ -43,7 +43,7 @@ PortFwdThread::~PortFwdThread()
{ {
qDebug() << Q_FUNC_INFO << "waiting for event loop to finish..."; qDebug() << Q_FUNC_INFO << "waiting for event loop to finish...";
quit(); quit();
wait( 1000 ); wait( 6000 );
delete m_portfwd; delete m_portfwd;
} }

View File

@@ -28,6 +28,8 @@
#include "utils/logger.h" #include "utils/logger.h"
#include "boost/bind.hpp"
#define DEFAULT_CONCURRENT_QUERIES 4 #define DEFAULT_CONCURRENT_QUERIES 4
#define MAX_CONCURRENT_QUERIES 16 #define MAX_CONCURRENT_QUERIES 16
#define CLEANUP_TIMEOUT 5 * 60 * 1000 #define CLEANUP_TIMEOUT 5 * 60 * 1000
@@ -61,6 +63,7 @@ Pipeline::Pipeline( QObject* parent )
Pipeline::~Pipeline() Pipeline::~Pipeline()
{ {
tDebug() << Q_FUNC_INFO;
m_running = false; m_running = false;
// stop script resolvers // stop script resolvers
@@ -418,11 +421,12 @@ Pipeline::shunt( const query_ptr& q )
r->resolve( q ); r->resolve( q );
emit resolving( q ); emit resolving( q );
m_qidsTimeout.insert( q->id(), true );
if ( r->timeout() > 0 ) if ( r->timeout() > 0 )
{
m_qidsTimeout.insert( q->id(), true );
new FuncTimeout( r->timeout(), boost::bind( &Pipeline::timeoutShunt, this, q ), this ); new FuncTimeout( r->timeout(), boost::bind( &Pipeline::timeoutShunt, this, q ), this );
} }
}
else else
{ {
// we get here if we disable a resolver while a query is resolving // we get here if we disable a resolver while a query is resolving

View File

@@ -48,6 +48,8 @@ public:
explicit Pipeline( QObject* parent = 0 ); explicit Pipeline( QObject* parent = 0 );
virtual ~Pipeline(); virtual ~Pipeline();
bool isRunning() const { return m_running; }
unsigned int pendingQueryCount() const { return m_queries_pending.count(); } unsigned int pendingQueryCount() const { return m_queries_pending.count(); }
unsigned int activeQueryCount() const { return m_qidsState.count(); } unsigned int activeQueryCount() const { return m_qidsState.count(); }

View File

@@ -71,7 +71,11 @@ XspfUpdater::playlistLoaded()
foreach ( const plentry_ptr ple, playlist()->entries() ) foreach ( const plentry_ptr ple, playlist()->entries() )
tracks << ple->query(); tracks << ple->query();
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries() ); bool changed = false;
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries(), changed );
if ( !changed )
return;
QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true ); QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true );
playlist()->createNewRevision( uuid(), playlist()->currentrevision(), el ); playlist()->createNewRevision( uuid(), playlist()->currentrevision(), el );

View File

@@ -40,7 +40,6 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel
, m_view( parent ) , m_view( parent )
, m_model( proxy ) , m_model( proxy )
{ {
m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
} }
@@ -89,21 +88,19 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) ); painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) );
} }
QRect r = option.rect.adjusted( 6, 5, -6, -41 );
QPixmap cover; QPixmap cover;
if ( !item->album().isNull() ) if ( !item->album().isNull() )
{ {
cover.loadFromData( item->album()->cover() ); cover = item->album()->cover( r.size() );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::CoverInCase, r.size() );
} }
else if ( !item->artist().isNull() ) else if ( !item->artist().isNull() )
{ {
cover.loadFromData( item->artist()->cover() ); cover = item->artist()->cover( r.size() );
} }
if ( cover.isNull() )
cover = m_defaultCover;
QRect r = option.rect.adjusted( 6, 5, -6, -41 );
if ( option.state & QStyle::State_Selected ) if ( option.state & QStyle::State_Selected )
{ {
#if defined(Q_WS_MAC) || defined(Q_WS_WIN) #if defined(Q_WS_MAC) || defined(Q_WS_WIN)
@@ -123,17 +120,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
#endif #endif
} }
QPixmap scover; painter->drawPixmap( r, cover );
if ( m_cache.contains( cover.cacheKey() ) )
{
scover = m_cache.value( cover.cacheKey() );
}
else
{
scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_cache.insert( cover.cacheKey(), scover );
}
painter->drawPixmap( r, scover );
painter->setPen( opt.palette.color( QPalette::Text ) ); painter->setPen( opt.palette.color( QPalette::Text ) );
QTextOption to; QTextOption to;

View File

@@ -49,12 +49,10 @@ private:
QAbstractItemView* m_view; QAbstractItemView* m_view;
AlbumProxyModel* m_model; AlbumProxyModel* m_model;
mutable QHash< qint64, QPixmap > m_cache;
mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects; mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
QPersistentModelIndex m_hoveringOver; QPersistentModelIndex m_hoveringOver;
QPixmap m_shadowPixmap; QPixmap m_shadowPixmap;
QPixmap m_defaultCover;
}; };
#endif // ALBUMITEMDELEGATE_H #endif // ALBUMITEMDELEGATE_H

View File

@@ -304,7 +304,18 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
if ( m_overwriteOnAdd ) if ( m_overwriteOnAdd )
clear(); clear();
if ( !albums.count() ) QList<Tomahawk::album_ptr> trimmedAlbums;
foreach ( const album_ptr& album, albums )
{
if ( !album.isNull() && album->name().length() )
{
if ( findItem( album ) || trimmedAlbums.contains( album ) )
continue;
trimmedAlbums << album;
}
}
if ( !trimmedAlbums.count() )
{ {
emit itemCountChanged( rowCount( QModelIndex() ) ); emit itemCountChanged( rowCount( QModelIndex() ) );
return; return;
@@ -313,12 +324,12 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
int c = rowCount( QModelIndex() ); int c = rowCount( QModelIndex() );
QPair< int, int > crows; QPair< int, int > crows;
crows.first = c; crows.first = c;
crows.second = c + albums.count() - 1; crows.second = c + trimmedAlbums.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second ); emit beginInsertRows( QModelIndex(), crows.first, crows.second );
AlbumItem* albumitem; AlbumItem* albumitem;
foreach( const album_ptr& album, albums ) foreach( const album_ptr& album, trimmedAlbums )
{ {
albumitem = new AlbumItem( album, m_rootItem ); albumitem = new AlbumItem( album, m_rootItem );
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem ); albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -339,7 +350,18 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
if ( m_overwriteOnAdd ) if ( m_overwriteOnAdd )
clear(); clear();
if ( !artists.count() ) QList<Tomahawk::artist_ptr> trimmedArtists;
foreach ( const artist_ptr& artist, artists )
{
if ( !artist.isNull() && artist->name().length() )
{
if ( findItem( artist ) || trimmedArtists.contains( artist ) )
continue;
trimmedArtists << artist;
}
}
if ( !trimmedArtists.count() )
{ {
emit itemCountChanged( rowCount( QModelIndex() ) ); emit itemCountChanged( rowCount( QModelIndex() ) );
return; return;
@@ -348,12 +370,12 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
int c = rowCount( QModelIndex() ); int c = rowCount( QModelIndex() );
QPair< int, int > crows; QPair< int, int > crows;
crows.first = c; crows.first = c;
crows.second = c + artists.count() - 1; crows.second = c + trimmedArtists.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second ); emit beginInsertRows( QModelIndex(), crows.first, crows.second );
AlbumItem* albumitem; AlbumItem* albumitem;
foreach( const artist_ptr& artist, artists ) foreach ( const artist_ptr& artist, trimmedArtists )
{ {
albumitem = new AlbumItem( artist, m_rootItem ); albumitem = new AlbumItem( artist, m_rootItem );
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem ); albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -396,3 +418,35 @@ AlbumModel::onDataChanged()
AlbumItem* p = (AlbumItem*)sender(); AlbumItem* p = (AlbumItem*)sender();
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) ); emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) );
} }
AlbumItem*
AlbumModel::findItem( const artist_ptr& artist ) const
{
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
{
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
if ( !item->artist().isNull() && item->artist() == artist )
{
return item;
}
}
return 0;
}
AlbumItem*
AlbumModel::findItem( const album_ptr& album ) const
{
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
{
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
if ( !item->album().isNull() && item->album() == album )
{
return item;
}
}
return 0;
}

View File

@@ -71,6 +71,9 @@ public:
virtual void setTitle( const QString& title ) { m_title = title; } virtual void setTitle( const QString& title ) { m_title = title; }
virtual void setDescription( const QString& description ) { m_description = description; } virtual void setDescription( const QString& description ) { m_description = description; }
AlbumItem* findItem( const Tomahawk::artist_ptr& artist ) const;
AlbumItem* findItem( const Tomahawk::album_ptr& album ) const;
AlbumItem* itemFromIndex( const QModelIndex& index ) const AlbumItem* itemFromIndex( const QModelIndex& index ) const
{ {
if ( index.isValid() ) if ( index.isValid() )

View File

@@ -25,6 +25,7 @@
#include <qmath.h> #include <qmath.h>
#include "audio/audioengine.h" #include "audio/audioengine.h"
#include "context/ContextWidget.h"
#include "tomahawksettings.h" #include "tomahawksettings.h"
#include "artist.h" #include "artist.h"
#include "albumitem.h" #include "albumitem.h"
@@ -113,6 +114,20 @@ AlbumView::setAlbumModel( AlbumModel* model )
} }
void
AlbumView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
QListView::currentChanged( current, previous );
AlbumItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) );
if ( item )
{
if ( !item->album().isNull() )
ViewManager::instance()->context()->setAlbum( item->album() );
}
}
void void
AlbumView::onItemActivated( const QModelIndex& index ) AlbumView::onItemActivated( const QModelIndex& index )
{ {

View File

@@ -71,6 +71,9 @@ protected:
void paintEvent( QPaintEvent* event ); void paintEvent( QPaintEvent* event );
void resizeEvent( QResizeEvent* event ); void resizeEvent( QResizeEvent* event );
protected slots:
virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous );
private slots: private slots:
void onItemCountChanged( unsigned int items ); void onItemCountChanged( unsigned int items );

View File

@@ -80,6 +80,11 @@ ArtistView::ArtistView( QWidget* parent )
setFont( f ); setFont( f );
#endif #endif
m_timer.setInterval( SCROLL_TIMEOUT );
connect( verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ), SLOT( onViewChanged() ) );
connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), SLOT( onViewChanged() ) );
connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) );
connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) );
connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) );
@@ -128,7 +133,8 @@ ArtistView::setTreeModel( TreeModel* model )
connect( m_proxyModel, SIGNAL( filteringFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); connect( m_proxyModel, SIGNAL( filteringFinished() ), m_loadingSpinner, SLOT( fadeOut() ) );
connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) ); connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) );
connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) ); connect( m_proxyModel, SIGNAL( filteringFinished() ), SLOT( onFilterChangeFinished() ) );
connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) );
guid(); // this will set the guid on the header guid(); // this will set the guid on the header
@@ -145,6 +151,44 @@ ArtistView::setTreeModel( TreeModel* model )
} }
void
ArtistView::onViewChanged()
{
if ( m_timer.isActive() )
m_timer.stop();
m_timer.start();
}
void
ArtistView::onScrollTimeout()
{
if ( m_timer.isActive() )
m_timer.stop();
QModelIndex left = indexAt( viewport()->rect().topLeft() );
while ( left.isValid() && left.parent().isValid() )
left = left.parent();
QModelIndex right = indexAt( viewport()->rect().bottomLeft() );
while ( right.isValid() && right.parent().isValid() )
right = right.parent();
int max = m_proxyModel->playlistInterface()->trackCount();
if ( right.isValid() )
max = right.row() + 1;
if ( !max )
return;
for ( int i = left.row(); i < max; i++ )
{
m_model->getCover( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) );
}
}
void void
ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previous ) ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{ {
@@ -236,7 +280,7 @@ ArtistView::onItemCountChanged( unsigned int items )
void void
ArtistView::onFilterChanged( const QString& ) ArtistView::onFilterChangeFinished()
{ {
if ( selectedIndexes().count() ) if ( selectedIndexes().count() )
scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter ); scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter );

View File

@@ -93,8 +93,10 @@ protected slots:
private slots: private slots:
void onItemCountChanged( unsigned int items ); void onItemCountChanged( unsigned int items );
void onFilterChanged( const QString& filter ); void onFilterChangeFinished();
void onFilteringStarted(); void onFilteringStarted();
void onViewChanged();
void onScrollTimeout();
void onCustomContextMenu( const QPoint& pos ); void onCustomContextMenu( const QPoint& pos );
void onMenuTriggered( int action ); void onMenuTriggered( int action );
@@ -113,6 +115,7 @@ private:
Tomahawk::ContextMenu* m_contextMenu; Tomahawk::ContextMenu* m_contextMenu;
bool m_showModes; bool m_showModes;
QTimer m_timer;
mutable QString m_guid; mutable QString m_guid;
}; };

View File

@@ -42,7 +42,7 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c
if ( m_type == SourceLovedTracks ) if ( m_type == SourceLovedTracks )
connect( m_source.data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) ); connect( m_source.data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
else if ( m_type == AllLovedTracks ) else if ( m_type == TopLovedTracks )
{ {
connect( SourceList::instance()->getLocal().data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) ); connect( SourceList::instance()->getLocal().data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
foreach ( const source_ptr& s, SourceList::instance()->sources( true ) ) foreach ( const source_ptr& s, SourceList::instance()->sources( true ) )
@@ -86,12 +86,12 @@ CustomPlaylistView::generateTracks()
"GROUP BY track.id " "GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) ); "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) );
break; break;
case AllLovedTracks: case TopLovedTracks:
sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter " sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter "
"FROM social_attributes, track, artist " "FROM social_attributes, track, artist "
"WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true'" "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' "
"GROUP BY track.id " "GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC " ); "ORDER BY counter DESC, social_attributes.timestamp DESC LIMIT 0, 50" );
break; break;
} }
@@ -104,7 +104,11 @@ CustomPlaylistView::generateTracks()
void void
CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks ) CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks )
{ {
QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks ); bool changed = false;
QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks, changed);
if ( !changed )
return;
m_model->clear(); m_model->clear();
m_model->append( newTracks ); m_model->append( newTracks );

View File

@@ -33,7 +33,7 @@ class DLLEXPORT CustomPlaylistView : public PlaylistView
public: public:
enum PlaylistType { enum PlaylistType {
SourceLovedTracks, SourceLovedTracks,
AllLovedTracks TopLovedTracks
}; };
explicit CustomPlaylistView( PlaylistType type, const source_ptr& s, QWidget* parent = 0 ); explicit CustomPlaylistView( PlaylistType type, const source_ptr& s, QWidget* parent = 0 );

View File

@@ -54,19 +54,22 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
{ {
Q_UNUSED( loadEntries ); Q_UNUSED( loadEntries );
if( !m_playlist.isNull() ) { if ( !m_playlist.isNull() )
{
disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) ); disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
} }
const int oldCount = rowCount( QModelIndex() );
m_playlist = playlist; m_playlist = playlist;
m_deduper.clear(); m_deduper.clear();
if( m_playlist->mode() == OnDemand ) if ( m_playlist->mode() == OnDemand )
setFilterUnresolvable( true ); setFilterUnresolvable( true );
connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) ); connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static ); PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static );
if( m_playlist->mode() == OnDemand ) if ( m_playlist->mode() == OnDemand && oldCount != rowCount( QModelIndex() ) )
emit trackCountChanged( rowCount( QModelIndex() ) ); emit trackCountChanged( rowCount( QModelIndex() ) );
} }
@@ -74,7 +77,7 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
QString QString
DynamicModel::description() const DynamicModel::description() const
{ {
if( !m_playlist.isNull() && !m_playlist->generator().isNull() ) if ( !m_playlist.isNull() && !m_playlist->generator().isNull() )
return m_playlist->generator()->sentenceSummary(); return m_playlist->generator()->sentenceSummary();
else else
return QString(); return QString();
@@ -95,7 +98,8 @@ DynamicModel::startOnDemand()
void void
DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query ) DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query )
{ {
if( m_onDemandRunning ) { if ( m_onDemandRunning )
{
bool isDuplicate = false; bool isDuplicate = false;
for ( int i = 0; i < m_deduper.size(); i++ ) for ( int i = 0; i < m_deduper.size(); i++ )
{ {
@@ -125,7 +129,7 @@ void
DynamicModel::stopOnDemand( bool stopPlaying ) DynamicModel::stopOnDemand( bool stopPlaying )
{ {
m_onDemandRunning = false; m_onDemandRunning = false;
if( stopPlaying ) if ( stopPlaying )
AudioEngine::instance()->stop(); AudioEngine::instance()->stop();
disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) ); disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
@@ -135,7 +139,7 @@ DynamicModel::stopOnDemand( bool stopPlaying )
void void
DynamicModel::changeStation() DynamicModel::changeStation()
{ {
if( m_onDemandRunning ) if ( m_onDemandRunning )
m_changeOnNext = true; m_changeOnNext = true;
else // if we're not running, just start else // if we're not running, just start
m_playlist->generator()->startOnDemand(); m_playlist->generator()->startOnDemand();
@@ -171,7 +175,7 @@ DynamicModel::trackResolveFinished( bool success )
{ {
qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts; qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
if( m_currentAttempts > 0 ) { if ( m_currentAttempts > 0 ) {
qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts; qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;
emit collapseFromTo( m_lastResolvedRow, m_currentAttempts ); emit collapseFromTo( m_lastResolvedRow, m_currentAttempts );
} }
@@ -188,11 +192,14 @@ void
DynamicModel::newTrackLoading() DynamicModel::newTrackLoading()
{ {
qDebug() << "Got NEW TRACK LOADING signal"; qDebug() << "Got NEW TRACK LOADING signal";
if( m_changeOnNext ) { // reset instead of getting the next one if ( m_changeOnNext )
{ // reset instead of getting the next one
m_lastResolvedRow = rowCount( QModelIndex() ); m_lastResolvedRow = rowCount( QModelIndex() );
m_searchingForNext = true; m_searchingForNext = true;
m_playlist->generator()->startOnDemand(); m_playlist->generator()->startOnDemand();
} else if( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext ) { // if we're in dynamic mode and we're also currently idle }
else if ( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext )
{ // if we're in dynamic mode and we're also currently idle
m_lastResolvedRow = rowCount( QModelIndex() ); m_lastResolvedRow = rowCount( QModelIndex() );
m_searchingForNext = true; m_searchingForNext = true;
qDebug() << "IDLE fetching new track!"; qDebug() << "IDLE fetching new track!";
@@ -204,13 +211,17 @@ DynamicModel::newTrackLoading()
void void
DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo ) DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo )
{ {
if( m_filterUnresolvable && m_playlist->mode() == OnDemand ) { // wait till we get them resolved (for previewing stations) if ( m_filterUnresolvable && m_playlist->mode() == OnDemand )
{ // wait till we get them resolved (for previewing stations)
m_limitResolvedTo = limitResolvedTo; m_limitResolvedTo = limitResolvedTo;
filterUnresolved( entries ); filterUnresolved( entries );
} else { }
else
{
addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old
if( m_playlist->mode() == OnDemand ) { if ( m_playlist->mode() == OnDemand )
{
m_lastResolvedRow = rowCount( QModelIndex() ); m_lastResolvedRow = rowCount( QModelIndex() );
} }
} }
@@ -224,9 +235,9 @@ DynamicModel::filterUnresolved( const QList< query_ptr >& entries )
{ {
m_toResolveList = entries; m_toResolveList = entries;
foreach( const query_ptr& q, entries ) { foreach ( const query_ptr& q, entries )
connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) ); connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) );
}
Pipeline::instance()->resolve( entries, true ); Pipeline::instance()->resolve( entries, true );
} }
@@ -240,7 +251,8 @@ DynamicModel::filteringTrackResolved( bool successful )
// if meantime the user began the station, abort // if meantime the user began the station, abort
qDebug() << "Got filtering resolved finished for track, was it successful?:" << q->track() << q->artist() << successful << q->playable(); qDebug() << "Got filtering resolved finished for track, was it successful?:" << q->track() << q->artist() << successful << q->playable();
if( m_onDemandRunning ) { if ( m_onDemandRunning )
{
m_toResolveList.clear(); m_toResolveList.clear();
m_resolvedList.clear(); m_resolvedList.clear();
@@ -248,8 +260,10 @@ DynamicModel::filteringTrackResolved( bool successful )
} }
query_ptr realptr; query_ptr realptr;
foreach( const query_ptr& qptr, m_toResolveList ) { foreach ( const query_ptr& qptr, m_toResolveList )
if( qptr.data() == q ) { {
if ( qptr.data() == q )
{
realptr = qptr; realptr = qptr;
break; break;
} }
@@ -259,25 +273,30 @@ DynamicModel::filteringTrackResolved( bool successful )
m_toResolveList.removeAll( realptr ); m_toResolveList.removeAll( realptr );
if( realptr->playable() ) { if ( realptr->playable() )
{
m_resolvedList << realptr; m_resolvedList << realptr;
// append and update internal lastResolvedRow // append and update internal lastResolvedRow
addToPlaylist( QList< query_ptr >() << realptr, false ); addToPlaylist( QList< query_ptr >() << realptr, false );
if( m_playlist->mode() == OnDemand ) { if ( m_playlist->mode() == OnDemand )
{
m_lastResolvedRow = rowCount( QModelIndex() ); m_lastResolvedRow = rowCount( QModelIndex() );
} }
if( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo ) { // done if ( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo )
{ // done
m_toResolveList.clear(); m_toResolveList.clear();
m_resolvedList.clear(); m_resolvedList.clear();
} }
} else { }
else
{
qDebug() << "Got unsuccessful resolve request for this track" << realptr->track() << realptr->artist(); qDebug() << "Got unsuccessful resolve request for this track" << realptr->track() << realptr->artist();
} }
if( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed if ( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) ); emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
} }
@@ -285,16 +304,20 @@ DynamicModel::filteringTrackResolved( bool successful )
void void
DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst ) DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst )
{ {
if( clearFirst ) if ( clearFirst )
clear(); clear();
foreach ( const query_ptr& q, entries ) foreach ( const query_ptr& q, entries )
m_deduper.append( QPair< QString, QString >( q->track(), q->artist() ) ); m_deduper.append( QPair< QString, QString >( q->track(), q->artist() ) );
if( m_playlist->author()->isLocal() && m_playlist->mode() == Static ) { if ( m_playlist->author()->isLocal() && m_playlist->mode() == Static )
{
m_playlist->addEntries( entries, m_playlist->currentrevision() ); m_playlist->addEntries( entries, m_playlist->currentrevision() );
} else { // read-only, so add tracks only in the GUI, not to the playlist itself }
foreach( const query_ptr& query, entries ) { else
{ // read-only, so add tracks only in the GUI, not to the playlist itself
foreach ( const query_ptr& query, entries )
{
append( query ); append( query );
} }
} }
@@ -310,12 +333,15 @@ DynamicModel::remove(const QModelIndex& idx, bool moreToCome)
return; return;
qDebug() << Q_FUNC_INFO << "DYNAMIC MODEL REMOVIN!" << moreToCome << ( idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) ); qDebug() << Q_FUNC_INFO << "DYNAMIC MODEL REMOVIN!" << moreToCome << ( idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) );
if( m_playlist->mode() == OnDemand ) { if ( m_playlist->mode() == OnDemand )
if( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) ) { // if the user is manually removing the last one, re-add as we're a station {
if ( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) )
{ // if the user is manually removing the last one, re-add as we're a station
newTrackLoading(); newTrackLoading();
} }
TrackModel::remove( idx ); TrackModel::remove( idx );
} else }
else
PlaylistModel::remove( idx, moreToCome ); PlaylistModel::remove( idx, moreToCome );
// don't call onPlaylistChanged. // don't call onPlaylistChanged.

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
* *
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
* *
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -181,6 +181,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide(); input->hide();
m_match = QWeakPointer< QWidget >( match ); m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input ); m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Artist Description" ) { } else if( selectedType() == "Artist Description" ) {
m_currentType = Echonest::DynamicPlaylist::Description; m_currentType = Echonest::DynamicPlaylist::Description;
@@ -199,6 +200,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide(); input->hide();
m_match = QWeakPointer< QWidget >( match ); m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input ); m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "User Radio" ) { } else if( selectedType() == "User Radio" ) {
m_currentType = Echonest::DynamicPlaylist::SourceCatalog; m_currentType = Echonest::DynamicPlaylist::SourceCatalog;
@@ -246,6 +248,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide(); input->hide();
m_match = QWeakPointer< QWidget >( match ); m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input ); m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Variety" ) { } else if( selectedType() == "Variety" ) {
m_currentType = Echonest::DynamicPlaylist::Variety; m_currentType = Echonest::DynamicPlaylist::Variety;
@@ -266,6 +269,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide(); input->hide();
m_match = QWeakPointer< QWidget >( match ); m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input ); m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Adventurousness" ) { } else if( selectedType() == "Adventurousness" ) {
m_currentType = Echonest::DynamicPlaylist::Adventurousness; m_currentType = Echonest::DynamicPlaylist::Adventurousness;
@@ -287,6 +291,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide(); input->hide();
m_match = QWeakPointer< QWidget >( match ); m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input ); m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Tempo" ) { } else if( selectedType() == "Tempo" ) {
m_currentType = Echonest::DynamicPlaylist::MinTempo; m_currentType = Echonest::DynamicPlaylist::MinTempo;

View File

@@ -118,32 +118,34 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
{ {
// special case: if we have launched multiple setRevision calls, and the number of controls is different, it means that we're getting an intermediate setRevision // special case: if we have launched multiple setRevision calls, and the number of controls is different, it means that we're getting an intermediate setRevision
// called after the user has already created more revisions. ignore in that case. // called after the user has already created more revisions. ignore in that case.
if( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0 if ( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
&& m_controls->controls().size() != playlist->generator()->controls().size() // different number of controls && m_controls->controls().size() != playlist->generator()->controls().size() // different number of controls
&& qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched ) { // difference in controls has to be less than how many revisions we launched && qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched )
{ // difference in controls has to be less than how many revisions we launched
return; return;
} }
m_seqRevLaunched = 0; m_seqRevLaunched = 0;
// if we're being told to load the same dynamic playlist over again, only do it if the controls have a different number // if we're being told to load the same dynamic playlist over again, only do it if the controls have a different number
if( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer if ( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
&& m_playlist->generator()->controls().size() == playlist->generator()->controls().size() ) { && m_playlist->generator()->controls().size() == playlist->generator()->controls().size() )
{
// we can skip our work. just let the dynamiccontrollist show the difference // we can skip our work. just let the dynamiccontrollist show the difference
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() ); m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
m_playlist = playlist; m_playlist = playlist;
if( !m_runningOnDemand ) { if ( !m_runningOnDemand )
m_model->loadPlaylist( m_playlist ); m_model->loadPlaylist( m_playlist );
} else if( !m_controlsChanged ) { // if the controls changed, we already dealt with that and don't want to change station yet else if ( !m_controlsChanged ) // if the controls changed, we already dealt with that and don't want to change station yet
m_model->changeStation(); m_model->changeStation();
}
m_controlsChanged = false; m_controlsChanged = false;
return; return;
} }
if( !m_playlist.isNull() ) { if ( !m_playlist.isNull() )
{
disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) ); disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) ); disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) );
disconnect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) ); disconnect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
@@ -160,23 +162,22 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
m_setup->setPlaylist( m_playlist ); m_setup->setPlaylist( m_playlist );
if( !m_playlist->author()->isLocal() ) { // hide controls, as we show the description in the summary if ( !m_playlist->author()->isLocal() ) // hide controls, as we show the description in the summary
m_layout->removeWidget( m_controls ); m_layout->removeWidget( m_controls );
} else if( m_layout->indexOf( m_controls ) == -1 ) { else if ( m_layout->indexOf( m_controls ) == -1 )
m_layout->insertWidget( 0, m_controls ); m_layout->insertWidget( 0, m_controls );
}
if( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
showPreview();
if( !m_playlist.isNull() )
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) ); connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) ); connect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
connect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) ); connect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) );
connect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onChanged() ) ); connect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onChanged() ) );
if ( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
showPreview();
if ( !m_playlist.isNull() )
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
} }
@@ -226,12 +227,15 @@ DynamicWidget::resizeEvent(QResizeEvent* )
void void
DynamicWidget::layoutFloatingWidgets() DynamicWidget::layoutFloatingWidgets()
{ {
if( !m_runningOnDemand ) { if ( !m_runningOnDemand )
{
int x = ( width() / 2 ) - ( m_setup->size().width() / 2 ); int x = ( width() / 2 ) - ( m_setup->size().width() / 2 );
int y = height() - m_setup->size().height() - 40; // padding int y = height() - m_setup->size().height() - 40; // padding
m_setup->move( x, y ); m_setup->move( x, y );
} else if( m_runningOnDemand && m_steering ) { }
else if( m_runningOnDemand && m_steering )
{
int x = ( width() / 2 ) - ( m_steering->size().width() / 2 ); int x = ( width() / 2 ) - ( m_steering->size().width() / 2 );
int y = height() - m_steering->size().height() - 40; // padding int y = height() - m_steering->size().height() - 40; // padding
@@ -243,13 +247,15 @@ DynamicWidget::layoutFloatingWidgets()
void void
DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl ) DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
{ {
if( pl == m_view->proxyModel()->playlistInterface() ) { // same playlist if ( pl == m_view->proxyModel()->playlistInterface() ) // same playlist
m_activePlaylist = true; m_activePlaylist = true;
} else { else
{
m_activePlaylist = false; m_activePlaylist = false;
// user started playing something somewhere else, so give it a rest // user started playing something somewhere else, so give it a rest
if( m_runningOnDemand ) { if ( m_runningOnDemand )
{
stopStation( false ); stopStation( false );
} }
} }
@@ -259,9 +265,8 @@ DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
void void
DynamicWidget::showEvent(QShowEvent* ) DynamicWidget::showEvent(QShowEvent* )
{ {
if( !m_playlist.isNull() && !m_runningOnDemand ) { if ( !m_playlist.isNull() && !m_runningOnDemand )
m_setup->fadeIn(); m_setup->fadeIn();
}
} }
@@ -289,8 +294,9 @@ DynamicWidget::stationFailed( const QString& msg )
void void
DynamicWidget::trackStarted() DynamicWidget::trackStarted()
{ {
if( m_activePlaylist && !m_playlist.isNull() && if ( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand ) { m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
startStation(); startStation();
} }
@@ -300,7 +306,7 @@ DynamicWidget::trackStarted()
void void
DynamicWidget::tracksAdded() DynamicWidget::tracksAdded()
{ {
if( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() ) if ( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
m_setup->fadeOut(); m_setup->fadeOut();
} }
@@ -325,7 +331,8 @@ DynamicWidget::startStation()
m_setup->fadeOut(); m_setup->fadeOut();
// show the steering controls // show the steering controls
if( m_playlist->generator()->onDemandSteerable() ) { if ( m_playlist->generator()->onDemandSteerable() )
{
// position it horizontally centered, above the botton. // position it horizontally centered, above the botton.
m_steering = m_playlist->generator()->steeringWidget(); m_steering = m_playlist->generator()->steeringWidget();
Q_ASSERT( m_steering ); Q_ASSERT( m_steering );
@@ -361,7 +368,7 @@ DynamicWidget::tracksGenerated( const QList< query_ptr >& queries )
{ {
m_resolveOnNextLoad = true; m_resolveOnNextLoad = true;
} }
else if( m_playlist->mode() == OnDemand ) else if ( m_playlist->mode() == OnDemand )
{ {
limit = 5; limit = 5;
} }
@@ -380,7 +387,7 @@ DynamicWidget::controlsChanged( bool added )
// when playing a station just ignore it till we're ready and get a controlChanged() // when playing a station just ignore it till we're ready and get a controlChanged()
m_controlsChanged = true; m_controlsChanged = true;
if( !m_playlist->author()->isLocal() ) if ( !m_playlist->author()->isLocal() )
return; return;
m_playlist->createNewRevision(); m_playlist->createNewRevision();
m_seqRevLaunched++; m_seqRevLaunched++;
@@ -396,7 +403,7 @@ void
DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control ) DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
{ {
Q_UNUSED( control ); Q_UNUSED( control );
if( !m_playlist->author()->isLocal() ) if ( !m_playlist->author()->isLocal() )
return; return;
m_playlist->createNewRevision(); m_playlist->createNewRevision();
m_seqRevLaunched++; m_seqRevLaunched++;
@@ -443,8 +450,7 @@ DynamicWidget::steeringChanged()
void void
DynamicWidget::showPreview() DynamicWidget::showPreview()
{ {
if ( m_playlist->mode() == OnDemand && if ( m_playlist->mode() == OnDemand && !m_runningOnDemand )
!m_runningOnDemand )
{ {
// if this is a not running station, preview matching tracks // if this is a not running station, preview matching tracks
m_model->clear(); m_model->clear();
@@ -456,11 +462,15 @@ DynamicWidget::showPreview()
void void
DynamicWidget::generatorError( const QString& title, const QString& content ) DynamicWidget::generatorError( const QString& title, const QString& content )
{ {
if( m_runningOnDemand ) {
stopStation( false );
}
m_view->setDynamicWorking( false ); m_view->setDynamicWorking( false );
m_loading->fadeOut(); m_loading->fadeOut();
if ( m_runningOnDemand )
{
stopStation( false );
m_view->showMessage( tr( "Station ran out of tracks!\n\nTry tweaking the filters for a new set of songs to play." ) );
}
else
m_view->showMessageTimeout( title, content ); m_view->showMessageTimeout( title, content );
} }
@@ -528,7 +538,7 @@ DynamicWidget::onDeleted()
void void
DynamicWidget::onChanged() DynamicWidget::onChanged()
{ {
if( !m_playlist.isNull() && if ( !m_playlist.isNull() &&
ViewManager::instance()->currentPage() == this ) ViewManager::instance()->currentPage() == this )
emit nameChanged( m_playlist->title() ); emit nameChanged( m_playlist->title() );
} }

View File

@@ -155,32 +155,18 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
QPixmap cover; QPixmap cover;
if ( !item->album().isNull() ) if ( !item->album().isNull() )
{ {
cover.loadFromData( item->album()->cover() ); cover = item->album()->cover( r.size(), false );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, r.size() );
} }
else if ( !item->artist().isNull() ) else if ( !item->artist().isNull() )
{ {
cover.loadFromData( item->artist()->cover() ); cover = item->artist()->cover( r.size(), false );
}
QPixmap scover;
if ( cover.isNull() ) if ( cover.isNull() )
{ cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::ScaledCover, r.size() );
if ( !item->artist().isNull() )
cover = m_defaultArtistImage;
else
cover = m_defaultAlbumCover;
} }
if ( m_cache.contains( cover.cacheKey() ) ) painter->drawPixmap( r, cover );
{
scover = m_cache.value( cover.cacheKey() );
}
else
{
scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_cache.insert( cover.cacheKey(), scover );
}
painter->drawPixmap( r, scover );
QTextOption to; QTextOption to;
to.setAlignment( Qt::AlignVCenter ); to.setAlignment( Qt::AlignVCenter );

View File

@@ -43,8 +43,6 @@ private:
ArtistView* m_view; ArtistView* m_view;
TreeProxyModel* m_model; TreeProxyModel* m_model;
mutable QHash< qint64, QPixmap > m_cache;
QPixmap m_nowPlayingIcon; QPixmap m_nowPlayingIcon;
QPixmap m_defaultAlbumCover; QPixmap m_defaultAlbumCover;
QPixmap m_defaultArtistImage; QPixmap m_defaultArtistImage;

View File

@@ -20,6 +20,7 @@
#include <QMimeData> #include <QMimeData>
#include "pipeline.h"
#include "source.h" #include "source.h"
#include "sourcelist.h" #include "sourcelist.h"
#include "audio/audioengine.h" #include "audio/audioengine.h"
@@ -87,6 +88,18 @@ TreeModel::collection() const
} }
void
TreeModel::getCover( const QModelIndex& index )
{
TreeModelItem* item = itemFromIndex( index );
if ( !item->artist().isNull() && !item->artist()->infoLoaded() )
item->artist()->cover( QSize( 0, 0 ) );
else if ( !item->album().isNull() && !item->album()->infoLoaded() )
item->album()->cover( QSize( 0, 0 ) );
}
void void
TreeModel::setCurrentItem( const QModelIndex& index ) TreeModel::setCurrentItem( const QModelIndex& index )
{ {
@@ -783,6 +796,8 @@ TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QModel
albumitem = new TreeModelItem( album, parentItem ); albumitem = new TreeModelItem( album, parentItem );
albumitem->index = createIndex( parentItem->children.count() - 1, 0, albumitem ); albumitem->index = createIndex( parentItem->children.count() - 1, 0, albumitem );
connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
getCover( albumitem->index );
} }
emit endInsertRows(); emit endInsertRows();
@@ -915,10 +930,11 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
foreach ( const QString& trackName, tracks ) foreach ( const QString& trackName, tracks )
{ {
query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ], uuid() ); query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ] );
query->setAlbumPos( trackNo++ ); query->setAlbumPos( trackNo++ );
ql << query; ql << query;
} }
Pipeline::instance()->resolve( ql );
onTracksAdded( ql, idx ); onTracksAdded( ql, idx );
} }

View File

@@ -98,6 +98,8 @@ public:
void addAlbums( const Tomahawk::artist_ptr& artist, const QModelIndex& parent, bool autoRefetch = false ); void addAlbums( const Tomahawk::artist_ptr& artist, const QModelIndex& parent, bool autoRefetch = false );
void addTracks( const Tomahawk::album_ptr& album, const QModelIndex& parent, bool autoRefetch = false ); void addTracks( const Tomahawk::album_ptr& album, const QModelIndex& parent, bool autoRefetch = false );
void getCover( const QModelIndex& index );
ColumnStyle columnStyle() const { return m_columnStyle; } ColumnStyle columnStyle() const { return m_columnStyle; }
void setColumnStyle( ColumnStyle style ); void setColumnStyle( ColumnStyle style );

View File

@@ -104,7 +104,9 @@ Query::Query( const QString& query, const QID& qid )
Query::~Query() Query::~Query()
{ {
QMutexLocker lock( &m_mutex );
m_ownRef.clear(); m_ownRef.clear();
m_results.clear();
} }
@@ -459,7 +461,15 @@ Query::howSimilar( const Tomahawk::result_ptr& r )
if ( isFullTextQuery() ) if ( isFullTextQuery() )
{ {
const QString artistTrackname = DatabaseImpl::sortname( fullTextQuery() );
const QString rArtistTrackname = DatabaseImpl::sortname( r->artist()->name() + " " + r->track() );
int atrdist = levenshtein( artistTrackname, rArtistTrackname );
int mlatr = qMax( artistTrackname.length(), rArtistTrackname.length() );
float dcatr = (float)( mlatr - atrdist ) / mlatr;
float res = qMax( dcart, dcalb ); float res = qMax( dcart, dcalb );
res = qMax( res, dcatr );
return qMax( res, dctrk ); return qMax( res, dctrk );
} }
else else
@@ -489,7 +499,7 @@ Query::loadSocialActions()
query_ptr q = m_ownRef.toStrongRef(); query_ptr q = m_ownRef.toStrongRef();
DatabaseCommand_LoadSocialActions* cmd = new DatabaseCommand_LoadSocialActions( q ); DatabaseCommand_LoadSocialActions* cmd = new DatabaseCommand_LoadSocialActions( q );
connect( cmd, SIGNAL( finished() ), SLOT( onSocialActionsLoaded() )); connect( cmd, SIGNAL( finished() ), SLOT( onSocialActionsLoaded() ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) ); Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
} }

View File

@@ -0,0 +1,19 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "resolver.h"

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
* *
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -55,7 +56,6 @@ ScriptResolver::ScriptResolver( const QString& exe )
// set the name to the binary, if we launch properly we'll get the name the resolver reports // set the name to the binary, if we launch properly we'll get the name the resolver reports
m_name = QFileInfo( filePath() ).baseName(); m_name = QFileInfo( filePath() ).baseName();
m_account = new Tomahawk::Accounts::SpotifyResolverAccount();
} }
@@ -283,9 +283,6 @@ ScriptResolver::handleMsg( const QByteArray& msg )
Tomahawk::query_ptr q = Tomahawk::Query::get( m.value( "artist" ).toString() , m.value( "track" ).toString() , QString(), uuid(), false ); Tomahawk::query_ptr q = Tomahawk::Query::get( m.value( "artist" ).toString() , m.value( "track" ).toString() , QString(), uuid(), false );
tracks << q; tracks << q;
} }
if(m_account)
m_account->addPlaylist( qid, title, tracks);
} }
} }
} }
@@ -309,7 +306,7 @@ ScriptResolver::cmdExited( int code, QProcess::ExitStatus status )
return; return;
} }
if ( m_num_restarts < 10 ) if ( m_num_restarts < 0 )
{ {
m_num_restarts++; m_num_restarts++;
tLog() << "*** Restart num" << m_num_restarts; tLog() << "*** Restart num" << m_num_restarts;

Some files were not shown because too many files have changed in this diff Show More