Logo Search packages:      
Sourcecode: kmess version File versions  Download package

nowlisteningclient.cpp

/***************************************************************************
                          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"

Generated by  Doxygen 1.6.0   Back to index