You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
555 lines
16 KiB
555 lines
16 KiB
/*
|
|
nowlisteningplugin.cpp
|
|
|
|
Kopete Now Listening To plugin
|
|
|
|
Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
|
|
Copyright (c) 2005-2006 by Michaël Larouche <michael.larouche@kdemail.net>
|
|
|
|
Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
|
|
|
|
*************************************************************************
|
|
* *
|
|
* 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 <tqtimer.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <kgenericfactory.h>
|
|
#include <kapplication.h>
|
|
#include <dcopclient.h>
|
|
#include <kaction.h>
|
|
|
|
#include "config.h"
|
|
#include "kopetechatsessionmanager.h"
|
|
#include "kopetemetacontact.h"
|
|
#include "kopetecontact.h"
|
|
#include "kopetecommandhandler.h"
|
|
#include "kopeteaccount.h"
|
|
#include "kopeteprotocol.h"
|
|
#include "kopeteaccountmanager.h"
|
|
|
|
#include "nowlisteningconfig.h"
|
|
#include "nowlisteningplugin.h"
|
|
#include "nlmediaplayer.h"
|
|
#include "nlkscd.h"
|
|
#include "nlnoatun.h"
|
|
#include "nljuk.h"
|
|
#include "nlamarok.h"
|
|
#include "nlkaffeine.h"
|
|
#include "nowlisteningguiclient.h"
|
|
|
|
#if defined TQ_WS_X11 && !defined K_WS_TQTONLY && defined HAVE_XMMS
|
|
#include "nlxmms.h"
|
|
#endif
|
|
|
|
class NowListeningPlugin::Private
|
|
{
|
|
public:
|
|
Private() : m_currentMediaPlayer(0L), m_client(0L), m_currentChatSession(0L), m_currentMetaContact(0L),
|
|
advertTimer(0L)
|
|
{}
|
|
|
|
// abstracted media player interfaces
|
|
TQPtrList<NLMediaPlayer> m_mediaPlayerList;
|
|
NLMediaPlayer *m_currentMediaPlayer;
|
|
|
|
// Needed for DCOP interprocess communication
|
|
DCOPClient *m_client;
|
|
Kopete::ChatSession *m_currentChatSession;
|
|
Kopete::MetaContact *m_currentMetaContact;
|
|
|
|
// Used when using automatic advertising to know who has already gotten
|
|
// the music information
|
|
TQStringList m_musicSentTo;
|
|
|
|
// Used when advertising to status message.
|
|
TQTimer *advertTimer;
|
|
};
|
|
|
|
typedef KGenericFactory<NowListeningPlugin> NowListeningPluginFactory;
|
|
K_EXPORT_COMPONENT_FACTORY( kopete_nowlistening, NowListeningPluginFactory( "kopete_nowlistening" ) )
|
|
|
|
NowListeningPlugin::NowListeningPlugin( TQObject *tqparent, const char* name, const TQStringList& /*args*/ )
|
|
: Kopete::Plugin( NowListeningPluginFactory::instance(), tqparent, name )
|
|
{
|
|
if ( pluginStatic_ )
|
|
kdDebug( 14307 )<<"####"<<"Now Listening already initialized"<<endl;
|
|
else
|
|
pluginStatic_ = this;
|
|
|
|
d = new Private;
|
|
|
|
kdDebug(14307) << k_funcinfo << endl;
|
|
|
|
// Connection for the "/media" command (always needed)
|
|
connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL(
|
|
chatSessionCreated( Kopete::ChatSession * )) , TQT_SLOT( slotNewKMM(
|
|
Kopete::ChatSession * ) ) );
|
|
|
|
// If autoadvertising is on...
|
|
connect(Kopete::ChatSessionManager::self(),
|
|
TQT_SIGNAL(aboutToSend(Kopete::Message&)),
|
|
this,
|
|
TQT_SLOT(slotOutgoingMessage(Kopete::Message&)));
|
|
|
|
TQValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
|
|
for (TQValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it)
|
|
slotNewKMM( *it );
|
|
|
|
// get a pointer to the dcop client
|
|
d->m_client = kapp->dcopClient(); //new DCOPClient();
|
|
|
|
// set up known media players
|
|
d->m_mediaPlayerList.setAutoDelete( true );
|
|
d->m_mediaPlayerList.append( new NLKscd( d->m_client ) );
|
|
d->m_mediaPlayerList.append( new NLNoatun( d->m_client ) );
|
|
d->m_mediaPlayerList.append( new NLJuk( d->m_client ) );
|
|
d->m_mediaPlayerList.append( new NLamaroK( d->m_client ) );
|
|
d->m_mediaPlayerList.append( new NLKaffeine( d->m_client ) );
|
|
|
|
#if defined TQ_WS_X11 && !defined K_WS_TQTONLY && HAVE_XMMS
|
|
d->m_mediaPlayerList.append( new NLXmms() );
|
|
#endif
|
|
|
|
// User has selected a specific mediaPlayer so update the currentMediaPlayer pointer.
|
|
if( NowListeningConfig::self()->useSpecifiedMediaPlayer() )
|
|
{
|
|
updateCurrentMediaPlayer();
|
|
}
|
|
|
|
// watch for '/media' getting typed
|
|
Kopete::CommandHandler::commandHandler()->registerCommand(
|
|
this,
|
|
"media",
|
|
TQT_SLOT( slotMediaCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n("USAGE: /media - Displays information on current song"),
|
|
0
|
|
);
|
|
|
|
connect ( this , TQT_SIGNAL( settingsChanged() ) , this , TQT_SLOT( slotSettingsChanged() ) );
|
|
|
|
// Advert the accounts with the current listened track.
|
|
d->advertTimer = new TQTimer(this);
|
|
connect(d->advertTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAdvertCurrentMusic() ) );
|
|
d->advertTimer->start(5000); // Update every 5 seconds
|
|
}
|
|
|
|
NowListeningPlugin::~NowListeningPlugin()
|
|
{
|
|
//kdDebug( 14307 ) << k_funcinfo << endl;
|
|
|
|
delete d;
|
|
|
|
pluginStatic_ = 0L;
|
|
}
|
|
|
|
void NowListeningPlugin::slotNewKMM(Kopete::ChatSession *KMM)
|
|
{
|
|
new NowListeningGUIClient( KMM, this );
|
|
}
|
|
|
|
NowListeningPlugin* NowListeningPlugin::plugin()
|
|
{
|
|
return pluginStatic_ ;
|
|
}
|
|
|
|
void NowListeningPlugin::slotMediaCommand( const TQString &args, Kopete::ChatSession *theChat )
|
|
{
|
|
TQString advert = mediaPlayerAdvert();
|
|
if ( advert.isEmpty() )
|
|
{
|
|
// Catch no players/no track playing message case:
|
|
// Since we can't stop a message send in a plugin, add some message text to
|
|
// prevent us sending an empty message
|
|
advert = i18n("Message from Kopete user to another user; used when sending media information even though there are no songs playing or no media players running", "Now Listening for Kopete - it would tell you what I am listening to, if I was listening to something on a supported media player.");
|
|
}
|
|
|
|
Kopete::Message msg( theChat->myself(),
|
|
theChat->members(),
|
|
advert + " " + args,
|
|
Kopete::Message::Outbound,
|
|
Kopete::Message::RichText
|
|
);
|
|
|
|
theChat->sendMessage( msg );
|
|
}
|
|
|
|
void NowListeningPlugin::slotOutgoingMessage(Kopete::Message& msg)
|
|
{
|
|
// Only do stuff if autoadvertising is on
|
|
if(!NowListeningConfig::self()->chatAdvertising())
|
|
return;
|
|
|
|
TQString originalBody = msg.plainBody();
|
|
|
|
// If it is a /media message, don't process it
|
|
if(originalBody.startsWith(NowListeningConfig::self()->header()))
|
|
return;
|
|
|
|
// What will be sent
|
|
TQString newBody;
|
|
|
|
// Getting the list of contacts the message will be sent to to determine if at least
|
|
// one of them has never gotten the current music information.
|
|
Kopete::ContactPtrList dest = msg.to();
|
|
bool mustSendAnyway = false;
|
|
for( Kopete::Contact *c = dest.first() ; c ; c = dest.next() )
|
|
{
|
|
const TQString& cId = c->contactId();
|
|
if( 0 == d->m_musicSentTo.tqcontains( cId ) )
|
|
{
|
|
mustSendAnyway = true;
|
|
|
|
// The contact will get the music information so we put it in the list.
|
|
d->m_musicSentTo.push_back( cId );
|
|
}
|
|
}
|
|
|
|
bool newTrack = newTrackPlaying();
|
|
|
|
// We must send the music information if someone has never gotten it or the track(s)
|
|
// has changed since it was last sent.
|
|
if ( mustSendAnyway || newTrack )
|
|
{
|
|
TQString advert = mediaPlayerAdvert(false); // false since newTrackPlaying() did the update
|
|
if( !advert.isEmpty() )
|
|
newBody = originalBody + "<br>" + advert;
|
|
|
|
// If we send because the information has changed since it was last sent, we must
|
|
// rebuild the list of contacts the latest information was sent to.
|
|
if( newTrack )
|
|
{
|
|
d->m_musicSentTo.clear();
|
|
for( Kopete::Contact *c = dest.first() ; c ; c = dest.next() )
|
|
{
|
|
d->m_musicSentTo.push_back( c->contactId() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the body has been modified, change the message
|
|
if( !newBody.isEmpty() )
|
|
{
|
|
msg.setBody( newBody, Kopete::Message::RichText );
|
|
}
|
|
}
|
|
|
|
void NowListeningPlugin::slotAdvertCurrentMusic()
|
|
{
|
|
// Do anything when statusAdvertising is off.
|
|
if( !NowListeningConfig::self()->statusAdvertising() && !NowListeningConfig::self()->appendStatusAdvertising() )
|
|
return;
|
|
|
|
// This slot is called every 5 seconds, so we check if we have a new track playing.
|
|
if( newTrackPlaying() )
|
|
{
|
|
TQString advert;
|
|
|
|
TQPtrList<Kopete::Account> accountsList = Kopete::AccountManager::self()->accounts();
|
|
for( Kopete::Account* a = accountsList.first(); a; a = accountsList.next() )
|
|
{
|
|
/*
|
|
NOTE:
|
|
MSN status message(personal message) use a special tag to advert the current music playing.
|
|
So, we don't send the all formatted string, send a special string seperated by ";".
|
|
|
|
Also, do not use MSN hack in appending mode.
|
|
*/
|
|
if( a->protocol()->pluginId() == "MSNProtocol" && !NowListeningConfig::self()->appendStatusAdvertising() )
|
|
{
|
|
TQString track, artist, album, mediaList;
|
|
bool isPlaying=false;
|
|
|
|
if( NowListeningConfig::self()->useSpecifiedMediaPlayer() && d->m_currentMediaPlayer )
|
|
{
|
|
if( d->m_currentMediaPlayer->playing() )
|
|
{
|
|
track = d->m_currentMediaPlayer->track();
|
|
artist = d->m_currentMediaPlayer->artist();
|
|
album = d->m_currentMediaPlayer->album();
|
|
mediaList = track + ";" + artist + ";" + album;
|
|
isPlaying = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( NLMediaPlayer* i = d->m_mediaPlayerList.first(); i; i = d->m_mediaPlayerList.next() )
|
|
{
|
|
if( i->playing() )
|
|
{
|
|
track = i->track();
|
|
artist = i->artist();
|
|
album = i->album();
|
|
mediaList = track + ";" + artist + ";" + album;
|
|
isPlaying = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// KDE4 TODO: Use the new status message framework, and remove this "hack".
|
|
if( isPlaying )
|
|
{
|
|
advert = TQString("[Music]%1").tqarg(mediaList);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if( NowListeningConfig::self()->appendStatusAdvertising() )
|
|
{
|
|
// Check for the now listening message in parenthesis,
|
|
// include the header to not override other messages in parenthesis.
|
|
TQRegExp statusSong( TQString(" \\(%1.*\\)$").tqarg( NowListeningConfig::header()) );
|
|
|
|
// HACK: Don't keep appending the now listened song. Replace it in the status message.
|
|
advert = a->myself()->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
|
|
// Remove the braces when they are no listened song.
|
|
TQString mediaAdvert = mediaPlayerAdvert(false);
|
|
if(!mediaAdvert.isEmpty())
|
|
{
|
|
if(statusSong.search(advert) != -1)
|
|
{
|
|
advert = advert.tqreplace(statusSong, TQString(" (%1)").tqarg(mediaPlayerAdvert(false)) );
|
|
}
|
|
else
|
|
{
|
|
advert += TQString(" (%1)").tqarg( mediaPlayerAdvert(false) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
advert = advert.tqreplace(statusSong, "");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
advert = mediaPlayerAdvert(false); // newTrackPlaying has done the update.
|
|
}
|
|
}
|
|
|
|
a->setOnlineStatus(a->myself()->onlinetqStatus(), advert);
|
|
}
|
|
}
|
|
}
|
|
|
|
TQString NowListeningPlugin::mediaPlayerAdvert(bool update)
|
|
{
|
|
// generate message for all players
|
|
TQString message;
|
|
|
|
if( NowListeningConfig::self()->useSpecifiedMediaPlayer() && d->m_currentMediaPlayer != 0L )
|
|
{
|
|
buildTrackMessage(message, d->m_currentMediaPlayer, update);
|
|
}
|
|
else
|
|
{
|
|
for ( NLMediaPlayer* i = d->m_mediaPlayerList.first(); i; i = d->m_mediaPlayerList.next() )
|
|
{
|
|
buildTrackMessage(message, i, update);
|
|
}
|
|
}
|
|
|
|
kdDebug( 14307 ) << k_funcinfo << message << endl;
|
|
|
|
return message;
|
|
}
|
|
|
|
void NowListeningPlugin::buildTrackMessage(TQString &message, NLMediaPlayer *player, bool update)
|
|
{
|
|
TQString perTrack = NowListeningConfig::self()->perTrack();
|
|
|
|
if(update)
|
|
player->update();
|
|
if ( player->playing() )
|
|
{
|
|
kdDebug( 14307 ) << k_funcinfo << player->name() << " is playing" << endl;
|
|
if ( message.isEmpty() )
|
|
message = NowListeningConfig::self()->header();
|
|
|
|
if ( message != NowListeningConfig::self()->header() ) // > 1 track playing!
|
|
message = message + NowListeningConfig::self()->conjunction();
|
|
message = message + substDepthFirst( player, perTrack, false );
|
|
}
|
|
}
|
|
|
|
bool NowListeningPlugin::newTrackPlaying(void) const
|
|
{
|
|
if( NowListeningConfig::self()->useSpecifiedMediaPlayer() && d->m_currentMediaPlayer != 0L )
|
|
{
|
|
d->m_currentMediaPlayer->update();
|
|
if( d->m_currentMediaPlayer->newTrack() )
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
for ( NLMediaPlayer* i = d->m_mediaPlayerList.first(); i; i = d->m_mediaPlayerList.next() )
|
|
{
|
|
i->update();
|
|
if( i->newTrack() )
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TQString NowListeningPlugin::substDepthFirst( NLMediaPlayer *player,
|
|
TQString in, bool inBrackets ) const
|
|
{
|
|
TQString track = player->track();
|
|
TQString artist = player->artist();
|
|
TQString album = player->album();
|
|
TQString playerName = player->name();
|
|
|
|
for ( unsigned int i = 0; i < in.length(); i++ )
|
|
{
|
|
TQChar c = in.at( i );
|
|
//kdDebug(14307) << "Now working on:" << in << " char is: " << c << endl;
|
|
if ( c == '(' )
|
|
{
|
|
// find matching bracket
|
|
int depth = 0;
|
|
//kdDebug(14307) << "Looking for ')'" << endl;
|
|
for ( unsigned int j = i + 1; j < in.length(); j++ )
|
|
{
|
|
TQChar d = in.at( j );
|
|
//kdDebug(14307) << "Got " << d << endl;
|
|
if ( d == '(' )
|
|
depth++;
|
|
if ( d == ')' )
|
|
{
|
|
// have we found the match?
|
|
if ( depth == 0 )
|
|
{
|
|
// recursively replace contents of matching ()
|
|
TQString substitution = substDepthFirst( player,
|
|
in.mid( i + 1, j - i - 1), true ) ;
|
|
in.tqreplace ( i, j - i + 1, substitution );
|
|
// perform substitution and return the result
|
|
i = i + substitution.length() - 1;
|
|
break;
|
|
}
|
|
else
|
|
depth--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// no () found, perform substitution!
|
|
// get each string (to) to substitute for (from)
|
|
bool done = false;
|
|
if ( in.tqcontains ( "%track" ) )
|
|
{
|
|
if ( track.isEmpty() )
|
|
track = i18n("Unknown track");
|
|
|
|
in.tqreplace( "%track", track );
|
|
done = true;
|
|
}
|
|
|
|
if ( in.tqcontains ( "%artist" ) && !artist.isEmpty() )
|
|
{
|
|
if ( artist.isEmpty() )
|
|
artist = i18n("Unknown artist");
|
|
in.tqreplace( "%artist", artist );
|
|
done = true;
|
|
}
|
|
if ( in.tqcontains ( "%album" ) && !album.isEmpty() )
|
|
{
|
|
if ( album.isEmpty() )
|
|
album = i18n("Unknown album");
|
|
in.tqreplace( "%album", album );
|
|
done = true;
|
|
}
|
|
if ( in.tqcontains ( "%player" ) && !playerName.isEmpty() )
|
|
{
|
|
if ( playerName.isEmpty() )
|
|
playerName = i18n("Unknown player");
|
|
in.tqreplace( "%player", playerName );
|
|
done = true;
|
|
}
|
|
// make whether we return anything dependent on whether we
|
|
// were in brackets and if we were, if a substitution was made.
|
|
if ( inBrackets && !done )
|
|
return "";
|
|
|
|
return in;
|
|
}
|
|
|
|
void NowListeningPlugin::advertiseToChat( Kopete::ChatSession *theChat, TQString message )
|
|
{
|
|
Kopete::ContactPtrList pl = theChat->members();
|
|
|
|
// get on with it
|
|
kdDebug(14307) << k_funcinfo <<
|
|
( pl.isEmpty() ? "has no " : "has " ) << "interested recipients: " << endl;
|
|
/* for ( pl.first(); pl.current(); pl.next() )
|
|
kdDebug(14307) << "NowListeningPlugin::advertiseNewTracks() " << pl.current()->displayName() << endl; */
|
|
// if no-one in this KMM wants to be advertised to, don't send
|
|
// any message
|
|
if ( pl.isEmpty() )
|
|
return;
|
|
Kopete::Message msg( theChat->myself(),
|
|
pl,
|
|
message,
|
|
Kopete::Message::Outbound,
|
|
Kopete::Message::RichText );
|
|
theChat->sendMessage( msg );
|
|
}
|
|
|
|
void NowListeningPlugin::updateCurrentMediaPlayer()
|
|
{
|
|
kdDebug(14307) << k_funcinfo << "Update current media player (single mode)" << endl;
|
|
|
|
d->m_currentMediaPlayer = d->m_mediaPlayerList.at( NowListeningConfig::self()->selectedMediaPlayer() );
|
|
}
|
|
|
|
void NowListeningPlugin::slotSettingsChanged()
|
|
{
|
|
// Force reading config
|
|
NowListeningConfig::self()->readConfig();
|
|
|
|
// Update the currentMediaPlayer, because config has changed.
|
|
if( NowListeningConfig::useSpecifiedMediaPlayer() )
|
|
updateCurrentMediaPlayer();
|
|
|
|
disconnect(Kopete::ChatSessionManager::self(),
|
|
TQT_SIGNAL(aboutToSend(Kopete::Message&)),
|
|
this,
|
|
TQT_SLOT(slotOutgoingMessage(Kopete::Message&)));
|
|
|
|
d->advertTimer->stop();
|
|
disconnect(d->advertTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotAdvertCurrentMusic()));
|
|
|
|
if( NowListeningConfig::self()->chatAdvertising() )
|
|
{
|
|
kdDebug(14307) << k_funcinfo << "Now using chat window advertising." << endl;
|
|
|
|
connect(Kopete::ChatSessionManager::self(),
|
|
TQT_SIGNAL(aboutToSend(Kopete::Message&)),
|
|
this,
|
|
TQT_SLOT(slotOutgoingMessage(Kopete::Message&)));
|
|
}
|
|
else if( NowListeningConfig::self()->statusAdvertising() || NowListeningConfig::self()->appendStatusAdvertising() )
|
|
{
|
|
kdDebug(14307) << k_funcinfo << "Now using status message advertising." << endl;
|
|
|
|
connect(d->advertTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotAdvertCurrentMusic()));
|
|
d->advertTimer->start(5000);
|
|
}
|
|
}
|
|
|
|
NowListeningPlugin* NowListeningPlugin::pluginStatic_ = 0L;
|
|
|
|
#include "nowlisteningplugin.moc"
|
|
|
|
// vim: set noet ts=4 sts=4 sw=4:
|