Index: amarok-2.3.1/src/App.cpp =================================================================== --- amarok-2.3.1.orig/src/App.cpp 2010-08-25 09:54:59.000000000 +0200 +++ amarok-2.3.1/src/App.cpp 2010-08-26 15:48:20.000000000 +0200 @@ -36,6 +36,7 @@ #include "core/meta/support/MetaConstants.h" #include "core/meta/Meta.h" #include "core/meta/support/MetaUtility.h" +#include "Mpris2DBusHandler.h" #include "Osd.h" #include "PlaybackConfig.h" #include "PlayerDBusHandler.h" @@ -293,6 +294,7 @@ if (QDBusConnection::sessionBus().isConnected() && (dbusService = QDBusConnection::sessionBus().interface())) { dbusService->unregisterService("org.mpris.amarok"); + dbusService->unregisterService("org.mpris.MediaPlayer2.amarok"); } #endif } @@ -675,7 +677,9 @@ new Amarok::PlayerDBusHandler(); new Amarok::TracklistDBusHandler(); new CollectionDBusHandler( this ); + new Amarok::Mpris2DBusHandler(); QDBusConnection::sessionBus().registerService("org.mpris.amarok"); + QDBusConnection::sessionBus().registerService("org.mpris.MediaPlayer2.amarok"); PERF_LOG( "Done creating DBus handlers" ) #ifdef HAVE_INDICATEQT Index: amarok-2.3.1/src/CMakeLists.txt =================================================================== --- amarok-2.3.1.orig/src/CMakeLists.txt 2010-08-25 09:54:59.000000000 +0200 +++ amarok-2.3.1/src/CMakeLists.txt 2010-08-26 15:47:49.000000000 +0200 @@ -483,6 +483,7 @@ dbus/TracklistDBusHandler.cpp dbus/CollectionDBusHandler.cpp dbus/DBusQueryHelper.cpp + dbus/Mpris2DBusHandler.cpp ) @@ -749,6 +750,8 @@ qt4_add_dbus_adaptor( amaroklib_LIB_SRCS dbus/org.freedesktop.MediaPlayer.player.xml PlayerDBusHandler.h Amarok::PlayerDBusHandler PlayerAdaptor PlayerAdaptor) qt4_add_dbus_adaptor( amaroklib_LIB_SRCS dbus/org.freedesktop.MediaPlayer.tracklist.xml TracklistDBusHandler.h Amarok::TracklistDBusHandler TracklistAdaptor TracklistAdaptor) qt4_add_dbus_adaptor( amaroklib_LIB_SRCS dbus/org.kde.amarok.Collection.xml CollectionDBusHandler.h CollectionDBusHandler CollectionAdaptor CollectionAdaptor) +qt4_add_dbus_adaptor( amaroklib_LIB_SRCS dbus/org.mpris.MediaPlayer2.root.xml Mpris2DBusHandler.h Amarok::Mpris2DBusHandler Mpris2RootAdaptor Mpris2RootAdaptor) +qt4_add_dbus_adaptor( amaroklib_LIB_SRCS dbus/org.mpris.MediaPlayer2.Player.xml Mpris2DBusHandler.h Amarok::Mpris2DBusHandler Mpris2PlayerAdaptor Mpris2PlayerAdaptor) set( amaroklib_DEPENDS "amarokpud" ) set( amaroklib_DEPENDS "amarokcore" ) Index: amarok-2.3.1/src/core/meta/support/MetaUtility.cpp =================================================================== --- amarok-2.3.1.orig/src/core/meta/support/MetaUtility.cpp 2010-05-27 20:37:21.000000000 +0200 +++ amarok-2.3.1/src/core/meta/support/MetaUtility.cpp 2010-08-26 15:47:49.000000000 +0200 @@ -150,6 +150,74 @@ return map; } +QVariantMap +Meta::Field::mpris20MapFromTrack( const Meta::TrackPtr track ) +{ + QVariantMap map; + if( track ) + { + // We do not set mpris::trackid here because it depends on the position + // of the track in the playlist + map["mpris:length"] = track->length() * 1000; // microseconds + + if( track->album() ) + map["mpris:artUrl"] = track->album()->imageLocation().url(); + + if( track->album() ) { + map["xesam:album"] = track->album()->name(); + if ( track->album()->hasAlbumArtist() ) + map["xesam:albumArtist"] = QStringList() << track->album()->albumArtist()->name(); + } + + if( track->artist() ) + map["xesam:artist"] = QStringList() << track->artist()->name(); + + const QString lyrics = track->cachedLyrics(); + if( !lyrics.isEmpty() ) + map["xesam:asText"] = lyrics; + + if( track->bpm() > 0 ) + map["xesam:audioBPM"] = int(track->bpm()); + + map["xesam:autoRating"] = track->score(); + + map["xseam:comment"] = QStringList() << track->comment(); + + if( track->composer() ) + map["xesam:composer"] = QStringList() << track->composer()->name(); + + if( track->year() ) { + bool ok; + int year = track->year()->name().toInt(&ok); + if (ok) + map["xesam:contentCreated"] = QDateTime(QDate(year, 1, 1)).toString(Qt::ISODate); + } + + if( track->discNumber() ) + map["xesam:discNumber"] = track->discNumber(); + + if( track->firstPlayed() > 0 ) + map["xesam:firstUsed"] = QDateTime::fromTime_t(track->firstPlayed()).toString(Qt::ISODate); + + if( track->genre() ) + map["xesam:genre"] = QStringList() << track->genre()->name(); + + if( track->lastPlayed() > 0 ) + map["xesam:lastUsed"] = QDateTime::fromTime_t(track->lastPlayed()).toString(Qt::ISODate); + + map["xesam:title"] = track->prettyName(); + + map["xesam:trackNumber"] = track->trackNumber(); + + map["xesam:url"] = track->playableUrl().url(); + + map["xesam:useCount"] = track->playCount(); + + map["xesam:userRating"] = track->rating() / 10.; // xesam:userRating is a float + } + return map; +} + void Meta::Field::updateTrack( Meta::TrackPtr track, const QVariantMap &metadata ) Index: amarok-2.3.1/src/core/meta/support/MetaUtility.h =================================================================== --- amarok-2.3.1.orig/src/core/meta/support/MetaUtility.h 2010-05-27 20:37:21.000000000 +0200 +++ amarok-2.3.1/src/core/meta/support/MetaUtility.h 2010-08-26 15:47:49.000000000 +0200 @@ -62,8 +62,10 @@ //deprecated AMAROK_CORE_EXPORT QVariantMap mapFromTrack( const Meta::TrackPtr track ); - //this method will return a map with keys that are compatible to the fdo MPRIS specification + //this method will return a map with keys that are compatible to the fdo MPRIS 1.0 specification AMAROK_CORE_EXPORT QVariantMap mprisMapFromTrack( const Meta::TrackPtr track ); + //this method will return a map with keys that are compatible to the fdo MPRIS 2.0 specification + AMAROK_CORE_EXPORT QVariantMap mpris20MapFromTrack( const Meta::TrackPtr track ); AMAROK_CORE_EXPORT void updateTrack( Meta::TrackPtr track, const QVariantMap &metadata ); AMAROK_CORE_EXPORT QString xesamPrettyToFullFieldName( const QString &name ); AMAROK_CORE_EXPORT QString xesamFullToPrettyFieldName( const QString &name ); Index: amarok-2.3.1/src/dbus/Mpris2DBusHandler.cpp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ amarok-2.3.1/src/dbus/Mpris2DBusHandler.cpp 2010-08-26 15:47:49.000000000 +0200 @@ -0,0 +1,429 @@ +/**************************************************************************************** + * Copyright (c) 2008 Ian Monroe * + * Copyright (c) 2008 Peter ZHOU * + * * + * 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. * + * * + * This program 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 * + * this program. If not, see . * + ****************************************************************************************/ + +#include "Mpris2DBusHandler.h" + +#include "amarokconfig.h" +#include "App.h" +#include "core/meta/Meta.h" +#include "core/meta/support/MetaUtility.h" +#include "core/support/Debug.h" +#include "EngineController.h" +#include "Mpris2PlayerAdaptor.h" +#include "Mpris2RootAdaptor.h" +#include "Osd.h" +#include "playlist/PlaylistActions.h" +#include "playlist/PlaylistController.h" +#include "playlist/PlaylistModelStack.h" +#include "SvgHandler.h" + +#include +#include + +#include +#include +#include + +static const char *MPRIS2_OBJECT_PATH = "/org/mpris/MediaPlayer2"; +static const char *FDO_PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties"; + +static QString activeMprisTrackId() +{ + quint64 id = The::playlist()->activeId(); + if( id > 0 ) + return QString("%1/Track/%2").arg(MPRIS2_OBJECT_PATH).arg(id); + else + return QString(); +} + +enum Status { Playing, Paused, Stopped }; + +static Status getStatus( Phonon::State state) +{ + Status status; + switch( state ) + { + case Phonon::PlayingState: + case Phonon::BufferingState: + status = Playing; + break; + case Phonon::PausedState: + status = Paused; + break; + case Phonon::LoadingState: + case Phonon::StoppedState: + case Phonon::ErrorState: + status = Stopped; + break; + }; + return status; +} + +static Status getStatus() +{ + return getStatus( The::engineController()->state() ); +} + +namespace Amarok +{ + Mpris2DBusHandler::Mpris2DBusHandler() + : QObject( kapp ), + EngineObserver( The::engineController() ) + { + new Mpris2RootAdaptor( this ); + new Mpris2PlayerAdaptor( this ); + QDBusConnection::sessionBus().registerObject( MPRIS2_OBJECT_PATH, this ); + + updateTrackProgressionProperties(); + updatePlaybackStatusProperty(); + updateTrackProperties(); + + connect( The::playlistActions(), SIGNAL( navigatorChanged() ), + SLOT( updateTrackProgressionProperties() ) ); + } + + void Mpris2DBusHandler::setProperty( const char *name, const QVariant &value ) + { + if( qstrcmp( name, "LoopStatus" ) ) + SetLoopStatus( value.toString() ); + else if( qstrcmp( name, "Shuffle" ) ) + SetShuffle( value.toBool() ); + else + QObject::setProperty( name, value ); + } + + // + void Mpris2DBusHandler::Raise() + { + MainWindow *window = App::instance()->mainWindow(); + if( !window ) + { + warning() << "No window!"; + return; + } + KWindowSystem::forceActiveWindow( window->winId() ); + } + + void Mpris2DBusHandler::Quit() + { + // Same as KStandardAction::Quit + kapp->quit(); + } + + QString Mpris2DBusHandler::Identity() const + { + const KAboutData *about = KCmdLineArgs::aboutData(); + return about->programName(); + } + + QString Mpris2DBusHandler::DesktopEntry() const + { + return "amarok"; + } + + QStringList Mpris2DBusHandler::SupportedUriSchemes() const + { + // FIXME: Find a way to list supported schemes + static QStringList lst = QStringList() << "file" << "http"; + return lst; + } + + QStringList Mpris2DBusHandler::SupportedMimeTypes() const + { + return The::engineController()->supportedMimeTypes(); + } + // + + // + void Mpris2DBusHandler::Pause() + { + The::engineController()->playPause(); + } + + void Mpris2DBusHandler::Play() + { + The::engineController()->play(); + } + + void Mpris2DBusHandler::PlayPause() + { + if( The::engineController()->state() == Phonon::PlayingState ) + The::engineController()->pause(); + else + The::engineController()->play(); + } + + void Mpris2DBusHandler::Next() + { + The::playlistActions()->next(); + } + + void Mpris2DBusHandler::Previous() + { + The::playlistActions()->back(); + } + + void Mpris2DBusHandler::Stop() + { + The::engineController()->stop(); + } + + void Mpris2DBusHandler::StopAfterCurrent() + { + The::playlistActions()->setStopAfterMode( Playlist::StopAfterCurrent ); + } + + void Mpris2DBusHandler::VolumeUp( int step ) const + { + The::engineController()->increaseVolume( step ); + } + + void Mpris2DBusHandler::VolumeDown( int step ) const + { + The::engineController()->decreaseVolume( step ); + } + + void Mpris2DBusHandler::Mute() const + { + The::engineController()->toggleMute(); + } + + void Mpris2DBusHandler::ShowOSD() const + { + Amarok::OSD::instance()->forceToggleOSD(); + } + + void Mpris2DBusHandler::LoadThemeFile( const QString &path ) const + { + The::svgHandler()->setThemeFile( path ); + } + + void Mpris2DBusHandler::Seek( qlonglong time ) + { + // convert time from microseconds to milliseconds + time /= 1000; + int position = The::engineController()->trackPositionMs() + time; + if( position < 0 ) + Previous(); + else if( position > The::engineController()->trackLength() ) + Next(); + else + The::engineController()->seek( position ); + } + + void Mpris2DBusHandler::SetPosition( const QDBusObjectPath &trackId, qlonglong position ) + { + QString activeTrackId = activeMprisTrackId(); + if( trackId.path() == activeTrackId ) + The::engineController()->seek( position / 1000 ); + else + warning() << "SetPosition() called with a trackId (" << trackId.path() << ") which is not for the active track (" << activeTrackId << ")"; + } + + void Mpris2DBusHandler::OpenUri( const QString &uri ) + { + KUrl url( uri ); + The::playlistController()->insertOptioned( KUrl::List() << url, Playlist::AppendAndPlayImmediately ); + } + + void Mpris2DBusHandler::SetLoopStatus( const QString &status ) + { + AmarokConfig::EnumTrackProgression::type progression; + if( status == "None" ) + progression = AmarokConfig::EnumTrackProgression::Normal; + else if( status == "Track" ) + progression = AmarokConfig::EnumTrackProgression::RepeatTrack; + else if( status == "Playlist" ) + progression = AmarokConfig::EnumTrackProgression::RepeatPlaylist; + else + { + warning() << "Unknown loop status:" << status; + return; + } + + AmarokConfig::setTrackProgression( progression ); + The::playlistActions()->playlistModeChanged(); + } + + void Mpris2DBusHandler::SetShuffle( bool on ) + { + AmarokConfig::EnumTrackProgression::type progression; + if( on ) + progression = AmarokConfig::EnumTrackProgression::RandomTrack; + else + progression = AmarokConfig::EnumTrackProgression::Normal; + + AmarokConfig::setTrackProgression( progression ); + The::playlistActions()->playlistModeChanged(); + } + + int Mpris2DBusHandler::Volume() const + { + return The::engineController()->volume(); + } + + void Mpris2DBusHandler::SetVolume( int vol ) + { + The::engineController()->setVolume( vol ); + } + + //position is specified in microseconds + qlonglong Mpris2DBusHandler::Position() const + { + return The::engineController()->trackPositionMs() * 1000; + } + // + + void Mpris2DBusHandler::schedulePropertiesChangedEmission() + { + // For now we don't need to use a timer to avoid multiple calls to + // emitPropertiesChanged() because it won't do anything if all changed + // properties have been emitted + QMetaObject::invokeMethod(this, "emitPropertiesChanged", Qt::QueuedConnection); + } + + void Mpris2DBusHandler::emitPropertiesChanged() + { + if( m_changedProperties.isEmpty() ) + return; + + QVariantMap map; + Q_FOREACH( const QByteArray &name, m_changedProperties ) + map.insert( name, property( name.data() ) ); + m_changedProperties.clear(); + + QDBusMessage msg = QDBusMessage::createSignal( MPRIS2_OBJECT_PATH, FDO_PROPERTIES_INTERFACE, "PropertiesChanged" ); + msg.setArguments( QVariantList() << map ); + QDBusConnection::sessionBus().send( msg ); + } + + void Mpris2DBusHandler::setPropertyInternal( const char *name, const QVariant &value ) + { + // We use QObject::setProperty here because setPropertyInternal() is + // called to update our property map from Amarok internal state: as + // such we do not want to trigger any setter such as SetShuffle() or + // SetLoopStatus(), other we may end up in an infinite recursion + // (Amarok state change => setPropertyInternal => setProperty => Amarok + // state change =>...) + QVariant oldValue = property( name ); + if( oldValue.isValid() && oldValue != value ) + { + // Updating existing property + QObject::setProperty( name, value ); + m_changedProperties << name; + } + else + { + // New property + QObject::setProperty( name, value ); + } + } + + void Mpris2DBusHandler::updateTrackProgressionProperties() + { + QString status; + // Trying to match with AmarokConfig::EnumTrack + switch( AmarokConfig::trackProgression() ) + { + case AmarokConfig::EnumTrackProgression::Normal: + case AmarokConfig::EnumTrackProgression::RandomTrack: + case AmarokConfig::EnumTrackProgression::RandomAlbum: + status = "None"; + break; + case AmarokConfig::EnumTrackProgression::RepeatTrack: + status = "Track"; + break; + case AmarokConfig::EnumTrackProgression::RepeatAlbum: + case AmarokConfig::EnumTrackProgression::RepeatPlaylist: + status = "Playlist"; + break; + } + setPropertyInternal( "LoopStatus", status ); + + bool shuffle; + switch( AmarokConfig::trackProgression() ) + { + case AmarokConfig::EnumTrackProgression::RandomAlbum: + case AmarokConfig::EnumTrackProgression::RandomTrack: + shuffle = true; + break; + default: + shuffle = false; + break; + } + setPropertyInternal( "Shuffle", shuffle ); + + schedulePropertiesChangedEmission(); + } + + void Mpris2DBusHandler::updatePlaybackStatusProperty() + { + QString status; + switch( getStatus() ) + { + case Playing: + status = "Playing"; + break; + case Paused: + status = "Paused"; + break; + case Stopped: + status = "Stopped"; + break; + } + setPropertyInternal( "PlaybackStatus", status ); + + schedulePropertiesChangedEmission(); + } + + void Mpris2DBusHandler::updateTrackProperties() + { + int activeRow = The::playlist()->activeRow(); + Status status = getStatus(); + QVariantMap metaData = Meta::Field::mpris20MapFromTrack( The::engineController()->currentTrack() ); + metaData["mpris:trackid"] = activeMprisTrackId(); + + setPropertyInternal( "CanGoNext", activeRow < The::playlist()->qaim()->rowCount() - 1 ); + setPropertyInternal( "CanGoPrevious", activeRow > 0 ); + setPropertyInternal( "CanPlay", status != Playing ); + setPropertyInternal( "CanPause", status == Playing ); + setPropertyInternal( "CanSeek", status != Stopped ); + setPropertyInternal( "MetaData", metaData ); + + schedulePropertiesChangedEmission(); + } + + // + void Mpris2DBusHandler::engineStateChanged( Phonon::State /*currentState*/, Phonon::State /*oldState*/ ) + { + QMetaObject::invokeMethod(this, "updatePlaybackStatusProperty", Qt::QueuedConnection); + } + + void Mpris2DBusHandler::engineTrackChanged( Meta::TrackPtr ) + { + QMetaObject::invokeMethod(this, "updateTrackProperties", Qt::QueuedConnection); + } + + void Mpris2DBusHandler::engineTrackPositionChanged( qint64 position, bool userSeek ) + { + if( userSeek ) + Seeked( position * 1000 ); + } + // +} + +#include "Mpris2DBusHandler.moc" Index: amarok-2.3.1/src/dbus/Mpris2DBusHandler.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ amarok-2.3.1/src/dbus/Mpris2DBusHandler.h 2010-08-26 15:47:49.000000000 +0200 @@ -0,0 +1,170 @@ +/**************************************************************************************** + * Copyright (c) 2010 Canonical Ltd * + * Author: Aurelien Gateau * + * * + * 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. * + * * + * This program 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 * + * this program. If not, see . * + ****************************************************************************************/ + +#ifndef MPRIS2_DBUS_HANDLER_H +#define MPRIS2_DBUS_HANDLER_H + +#include "core/engine/EngineObserver.h" + +#include +#include + +class QDBusObjectPath; + +namespace Amarok +{ + class Mpris2DBusHandler : public QObject, public Engine::EngineObserver + { + Q_OBJECT + /** + * @defgroup org.mpris.MediaPlayer2 MPRIS 2.0 Root interface + * @{ + */ + Q_PROPERTY( bool CanQuit READ CanQuit ) + Q_PROPERTY( bool CanRaise READ CanRaise ) + Q_PROPERTY( bool HasTrackList READ HasTrackList ) + Q_PROPERTY( QString Identity READ Identity ) + Q_PROPERTY( QString DesktopEntry READ DesktopEntry ) + Q_PROPERTY( QStringList SupportedUriSchemes READ SupportedUriSchemes ) + Q_PROPERTY( QStringList SupportedMimeTypes READ SupportedMimeTypes ) + /* @} */ + + /** + * @defgroup org.mpris.MediaPlayer2.Player MPRIS 2.0 Player interface + * @{ + */ + Q_PROPERTY( int Rate READ Rate WRITE SetRate ) + Q_PROPERTY( int Volume READ Volume WRITE SetVolume ) + Q_PROPERTY( qlonglong Position READ Position ) + Q_PROPERTY( int MinimumRate READ MinimumRate ) + Q_PROPERTY( int MaximumRate READ MaximumRate ) + Q_PROPERTY( bool CanControl READ CanControl ) + /* @} */ + + public: + Mpris2DBusHandler(); + + void setProperty( const char *name, const QVariant &value ); + + /** + * @addtogroup org.mpris.MediaPlayer2 + * @{ + */ + void Raise(); + void Quit(); + + bool CanQuit() const + { + return true; + } + + bool CanRaise() const + { + return true; + } + + bool HasTrackList() const + { + return false; + } + + QString Identity() const; + QString DesktopEntry() const; + QStringList SupportedUriSchemes() const; + QStringList SupportedMimeTypes() const; + /* @} */ + + /** + * @addtogroup org.mpris.MediaPlayer2.Player + * @{ + */ + void Next(); + void Previous(); + void Pause(); + void PlayPause(); + void Stop(); + void Play(); + void Seek( qlonglong offset ); + void SetPosition( const QDBusObjectPath &trackId, qlonglong offset ); + void OpenUri( const QString &uri ); + + // NB: Amarok extensions, not part of the mpris spec + void StopAfterCurrent(); + void VolumeUp( int step ) const; + void VolumeDown( int step ) const; + void Mute() const; + void ShowOSD() const; + + void LoadThemeFile( const QString &path ) const; + + // Properties + void SetLoopStatus( const QString & ); + + int Rate() const + { + return 1; + } + + void SetRate( int ) {} + + void SetShuffle( bool ); + + int Volume() const; + void SetVolume( int ); + + qlonglong Position() const; + + int MinimumRate() const + { + return 1; + } + + int MaximumRate() const + { + return 1; + } + + bool CanControl() const + { + return true; + } + + Q_SIGNALS: + void Seeked( qlonglong ); + /* @} */ + + protected: + virtual void engineStateChanged( Phonon::State currentState, Phonon::State oldState ); + virtual void engineTrackChanged( Meta::TrackPtr ); + virtual void engineTrackPositionChanged( qint64 position, bool userSeek ); + + private: + QList m_changedProperties; + + void setPropertyInternal( const char *name, const QVariant &value ); + + void schedulePropertiesChangedEmission(); + + private Q_SLOTS: + void updateTrackProgressionProperties(); + void updatePlaybackStatusProperty(); + void updateTrackProperties(); + void emitPropertiesChanged(); + }; +} + +#endif Index: amarok-2.3.1/src/dbus/org.mpris.MediaPlayer2.Player.xml =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ amarok-2.3.1/src/dbus/org.mpris.MediaPlayer2.Player.xml 2010-08-26 15:47:49.000000000 +0200 @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: amarok-2.3.1/src/dbus/org.mpris.MediaPlayer2.root.xml =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ amarok-2.3.1/src/dbus/org.mpris.MediaPlayer2.root.xml 2010-08-26 15:47:49.000000000 +0200 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + Index: amarok-2.3.1/tests/mpristest.py =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ amarok-2.3.1/tests/mpristest.py 2010-08-26 15:47:49.000000000 +0200 @@ -0,0 +1,65 @@ +import subprocess +import time +import unittest + +import dbus + +APPNAME = "amarok" +MPRIS_OBJECT_PATH = "/org/mpris/MediaPlayer2" +MPRIS_PREFIX = "org.mpris.MediaPlayer2" + +ROOT_IFACE = MPRIS_PREFIX +PLAYER_IFACE = MPRIS_PREFIX + ".Player" + +def get_mpris_object(): + bus = dbus.SessionBus() + return bus.get_object(MPRIS_PREFIX + "." + APPNAME, MPRIS_OBJECT_PATH) + +def start_player(): + devnull = file("/dev/null", "a") + player = subprocess.Popen(["amarok", "--nofork"], stderr=devnull, stdout=devnull) + time.sleep(10) + return player + +def stop_player(player): + player.terminate() + player.wait() + +class TestMediaPlayer2(unittest.TestCase): + def check_property(self, props, key, expected_value): + value = props[key] + self.assertEquals(value, expected_value) + del props[key] + + def check_has_property(self, props, key): + self.assert_(key in props) + del props[key] + + def test_properties(self): + player = start_player() + try: + mpris = get_mpris_object() + props = mpris.GetAll(ROOT_IFACE) + self.check_property(props, "CanQuit", True) + self.check_property(props, "CanRaise", True) + self.check_property(props, "HasTrackList", False) + self.check_property(props, "Identity", "Amarok") + self.check_property(props, "DesktopEntry", "amarok") + self.check_has_property(props, "SupportedUriSchemes") + self.check_has_property(props, "SupportedMimeTypes") + self.assertEquals(len(props), 0) + finally: + stop_player(player) + + def test_quit(self): + player = start_player() + mpris = get_mpris_object() + mpris.Quit() + start = time.time() + while time.time() < start + 60 * 5 and player.poll() is None: + time.sleep(.5) + if player.poll() is None: + stop_player(player) + self.fail("Player not stopped") + +unittest.main()