/*************************************************************************** nowlisteningclient.cpp - description ------------------- begin : Sat Nov 4 2006 copyright : (C) 2006 by Diederik van der Boor email : "vdboor" --at-- "codingdomain.com" ***************************************************************************/ /*************************************************************************** * * * This program 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 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "nowlisteningclient.h" #include "kmessdebug.h" #include <dcopclient.h> #include <kapplication.h> #include <qcstring.h> #ifdef KMESSDEBUG_NOWLISTENINGCLIENT #define KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL #endif // This code is inspired by the "now listening" plugin of Kopete. // Therefore some parts are also // (c) 2002-2006 the by Kopete developers <kopete-devel@kde.org> /** * Constructor. */ 00040 NowListeningClient::NowListeningClient() : playing_(false) { // Create client. client_ = kapp->dcopClient(); // Connect timer event. connect( &timer_, SIGNAL(timeout()), this, SLOT(slotUpdate()) ); } /** * Destructor */ 00054 NowListeningClient::~NowListeningClient() { } /** * Make a call to a boolean function. */ 00064 bool NowListeningClient::callDcop( const QCString &app, const QCString &object, const QCString &method, const QByteArray &args, bool &returnValue ) const { QCString replyType; QByteArray replyData; // Make call if( ! client_->call( app, object, method, args, replyType, replyData ) ) { kdWarning() << "NowlisteningClient: DCOP call '" << app << " " << object << " " << method << "' failed!" << endl; return false; // failed } // Check reply type if( replyType != "bool" ) { kdWarning() << "NowlisteningClient: DCOP call '" << app << " " << object << " " << method << " returned unexpected data type: " << replyType << "!" << endl; return false; } // Extract data QDataStream reply( replyData, IO_ReadOnly ); reply >> returnValue; return true; } /** * Make a call to a integer function. */ 00097 bool NowListeningClient::callDcop( const QCString &app, const QCString &object, const QCString &method, const QByteArray &args, int &returnValue ) const { QCString replyType; QByteArray replyData; // Make call if( ! client_->call( app, object, method, args, replyType, replyData ) ) { kdWarning() << "NowlisteningClient: DCOP call '" << app << " " << object << " " << method << "' failed!" << endl; return false; // failed } // Check reply type if( replyType != "int" ) { kdWarning() << "NowlisteningClient: DCOP call '" << app << " " << object << " " << method << " returned unexpected data type: " << replyType << "!" << endl; return false; } // Extract data QDataStream reply( replyData, IO_ReadOnly ); reply >> returnValue; return true; } /** * Make a call to a QString function. */ 00130 bool NowListeningClient::callDcop( const QCString &app, const QCString &object, const QCString &method, const QByteArray &args, QString &returnValue ) const { QCString replyType; QByteArray replyData; // Make call if( ! client_->call( app, object, method, args, replyType, replyData ) ) { kdWarning() << "NowlisteningClient: DCOP call '" << app << " " << object << " " << method << "' failed!" << endl; return false; // failed } // Check reply type if( replyType != "QString" ) { kdWarning() << "NowlisteningClient: DCOP call '" << app << " " << object << " " << method << "'" << " returned unexpected data type: " << replyType << "!" << endl; return false; } // Extract data QDataStream reply( replyData, IO_ReadOnly ); reply >> returnValue; return true; } /** * Make a call to a QString function with QString argument. */ 00162 bool NowListeningClient::callDcop( const QCString &app, const QCString &object, const QCString &method, const QString &arg1, QString &returnValue ) const { // Fill parameters QByteArray args; QDataStream arg( args, IO_WriteOnly ); arg << arg1; // Call real QString method if( ! callDcop( app, object, method, args, returnValue ) ) { kdWarning() << "NowlisteningClient: DCOP call '" << app << " " << object << " " << method << "' failed!" << endl; return false; } return true; } /** * Find a DCOP application that starts with the given app name. */ 00185 QCString NowListeningClient::findDcopApplication( const QCString &appName ) const { // See if full name is registered. if( client_->isApplicationRegistered( appName ) ) { return appName; } // Find app which DCOP name starts with the appname. int nameLength = appName.length(); QCStringList appNames = client_->registeredApplications(); for( QCStringList::iterator it = appNames.begin(); it != appNames.end(); it++ ) { if( (*it).left( nameLength ) == appName ) // QString has startsWith(), QCString not. { return (*it); } } return QCString(); } /** * Enable or disable the update interval timer. */ 00212 void NowListeningClient::setEnabled( bool enable ) { if( enable ) { if( ! timer_.isActive() ) { // Query directly so the GUI is up to date slotUpdate(); // check every 5 seconds timer_.start( 5000, false ); } } else { //emit a null changedSong signal to clean the GUI emit changedSong( QString::null, QString::null, QString::null, false ); timer_.stop(); } } /** * Update the current song */ 00238 void NowListeningClient::slotUpdate() { // Detect changes to reduce signal calls. QString prevArtist = artist_; QString prevAlbum = album_; QString prevTrack = track_; bool prevPlaying = playing_; // Reset until proven otherwise. playing_ = false; // Query all apps. if( queryKsCD() || queryNoatun() || queryJuk() || queryAmarok() || queryKaffeine() ) { // Found active media player! #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: found song " << artist_ << " - " << track_ << " (album=" << album_ << " playing=" << playing_ << ")" << endl; #endif // App found and playing, detect change. if( playing_ ) { if( prevArtist != artist_ || prevAlbum != album_ || prevTrack != track_ || ! prevPlaying ) { #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: playing information changed, emitting changedSong()." << endl; #endif emit changedSong( artist_, album_, track_, playing_ ); } } } // Emit a signal when the player was stopped. if( prevPlaying && ! playing_ ) { #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: player was stopped, emitting changedSong()." << endl; #endif emit changedSong( QString::null, QString::null, QString::null, false ); } } /** * Query Amarok for track information. */ 00293 bool NowListeningClient::queryAmarok() { QByteArray args; // See if the application is registered. if( ! client_->isApplicationRegistered( "amarok" ) ) { return false; } #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: querying Amarok for now listening information..." << endl; #endif // See if Amarok is playing. // use status() call first, if not supported (amaroK 1.0 or earlier), use isPlaying int status = 0; if( ! callDcop( "amarok", "player", "status()", args, status ) ) { // Failed, try isPlaying() if( ! callDcop( "amarok", "player", "isPlaying()", args, playing_ ) ) { return false; // completely failed. } } else { playing_ = (status != 0); } // Get data if( playing_ && callDcop( "amarok", "player", "artist()", args, artist_ ) && callDcop( "amarok", "player", "album()", args, album_ ) && callDcop( "amarok", "player", "title()", args, track_ ) ) { return true; // got track } return false; // failed } /** * Query Juk for track information. */ 00340 bool NowListeningClient::queryJuk() { QByteArray args; // See if the application is registered. if( ! client_->isApplicationRegistered( "juk" ) ) { return false; } #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: querying Juk for now listening information..." << endl; #endif // Get data if( callDcop( "juk", "Player", "playing()", args, playing_ ) && playing_ && callDcop( "juk", "Player", "trackProperty(QString)", "Album", album_ ) && callDcop( "juk", "Player", "trackProperty(QString)", "Artist", artist_ ) && callDcop( "juk", "Player", "trackProperty(QString)", "Title", track_ ) ) { return true; // success! } return false; // failed } /** * Query Kaffeine for track information. */ 00373 bool NowListeningClient::queryKaffeine() { QByteArray args; // See if the application is registered. if( ! client_->isApplicationRegistered( "kaffeine" ) ) { return false; } #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: querying Kaffeine for now listening information..." << endl; #endif // Get data if( callDcop( "kaffeine", "KaffeineIface", "isPlaying()", args, playing_ ) && playing_ && callDcop( "kaffeine", "KaffeineIface", "artist()", args, artist_ ) && callDcop( "kaffeine", "KaffeineIface", "album()", args, album_ ) && callDcop( "kaffeine", "KaffeineIface", "title()", args, track_ ) ) { return true; // got track } return false; // failed } /** * Query KsCD for track information. */ 00405 bool NowListeningClient::queryKsCD() { QByteArray args; // See if the application is registered. if( ! client_->isApplicationRegistered( "kscd" ) ) { return false; } #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: querying KsCD for now listening information..." << endl; #endif // See if the player is active. if( ! callDcop( "kscd", "CDPlayer", "playing()", args, playing_ ) ) { // Call failed, playing() method not available. playing_ = true; } // Get data if( playing_ && callDcop( "kscd", "CDPlayer", "currentArtist()", args, artist_ ) && callDcop( "kscd", "CDPlayer", "currentAlbum()", args, album_ ) && callDcop( "kscd", "CDPlayer", "currentTrackTitle()", args, track_ ) ) { return true; // got track } return false; // failed } /** * Query Noatun for track information. */ 00443 bool NowListeningClient::queryNoatun() { QByteArray args; // Real appname may have a numeric suffix, because noatun may have multiple instances open. QCString appName = findDcopApplication( QCString("noatun") ); if( appName.isNull() ) { return false; } #ifdef KMESSDEBUG_NOWLISTENINGCLIENT_GENERAL kdDebug() << "NowListeningClient: querying Noatun for now listening information..." << endl; #endif // See if the player is active. int state = 0; if( ! callDcop(appName, "Noatun", "state()", args, state ) ) { return false; } playing_ = (state == 2); // Get data // Title can be empty (no ID3 tag), fallback to filename instead QString title; if( playing_ && callDcop( appName, "Noatun", "currentProperty(QString)", "author", artist_ ) && callDcop( appName, "Noatun", "currentProperty(QString)", "album", album_ ) && callDcop( appName, "Noatun", "currentProperty(QString)", "title", track_ ) && ( ! track_.isEmpty() || callDcop( appName, "Noatun", "title()", args, track_ ) ) ) { return true; } return true; } #include "nowlisteningclient.moc"