1
# -*- coding: utf-8 -*-
4
## Copyright (C) 2009 Lucky <lucky1.data@gmail.com>
5
## Copyright (C) 2009 Philip Withnall <philip@tecnocode.co.uk>
7
## This program is free software; you can redistribute it and/or modify
8
## it under the terms of the GNU General Public License as published by
9
## the Free Software Foundation; either version 2 of the License, or
10
## (at your option) any later version.
12
## This program is distributed in the hope that it will be useful,
13
## but WITHOUT ANY WARRANTY; without even the implied warranty of
14
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
## GNU General Public License for more details.
17
## You should have received a copy of the GNU General Public License
18
## along with this program; if not, write to the Free Software
19
## Foundation, Inc., 51 Franklin St, Fifth Floor,
20
## Boston, MA 02110-1301 USA.
22
## Sunday 13th May 2007: Bastien Nocera: Add exception clause.
23
## See license_change file for details.
26
from gi.repository import GObject, Peas, Totem # pylint: disable-msg=E0611
27
import dbus, dbus.service
28
from dbus.mainloop.glib import DBusGMainLoop
30
gettext.textdomain ("totem")
33
class DbusService (GObject.Object, Peas.Activatable):
34
__gtype_name__ = 'DbusService'
36
object = GObject.property (type = GObject.Object)
39
GObject.Object.__init__ (self)
43
def do_activate (self):
44
DBusGMainLoop (set_as_default = True)
46
name = dbus.service.BusName ('org.mpris.MediaPlayer2.totem',
47
bus = dbus.SessionBus ())
48
self.root = Root (name, self.object)
50
def do_deactivate (self):
51
# Ensure we don't leak our paths on the bus
52
self.root.disconnect ()
54
class Root (dbus.service.Object): # pylint: disable-msg=R0923,R0904
55
def __init__ (self, name, totem):
56
dbus.service.Object.__init__ (self, name, '/org/mpris/MediaPlayer2')
59
self.null_metadata = {
60
'year' : '', 'tracknumber' : '', 'location' : '',
61
'title' : '', 'album' : '', 'time' : '', 'genre' : '',
64
self.current_metadata = self.null_metadata.copy ()
65
self.current_position = 0
67
totem.connect ('metadata-updated', self.__do_update_metadata)
68
totem.connect ('notify::playing', self.__do_notify_playing)
69
totem.connect ('notify::seekable', self.__do_notify_seekable)
70
totem.connect ('notify::current-mrl', self.__do_notify_current_mrl)
71
totem.connect ('notify::current-time', self.__do_notify_current_time)
73
def disconnect (self):
74
self.totem.disconnect_by_func (self.__do_notify_current_time)
75
self.totem.disconnect_by_func (self.__do_notify_current_mrl)
76
self.totem.disconnect_by_func (self.__do_notify_seekable)
77
self.totem.disconnect_by_func (self.__do_notify_playing)
78
self.totem.disconnect_by_func (self.__do_update_metadata)
80
self.__do_update_metadata (self.totem, '', '', '', 0)
82
self.remove_from_connection (None, None)
84
def __calculate_playback_status (self):
85
if self.totem.is_playing ():
87
elif self.totem.is_paused ():
92
def __calculate_metadata (self):
94
'mpris:trackid': dbus.String (self.totem.props.current_mrl,
96
'mpris:length': dbus.Int64 (
97
self.totem.props.stream_length * 1000L,
101
if self.current_metadata['title'] != '':
102
metadata['xesam:title'] = dbus.String (
103
self.current_metadata['title'], variant_level = 1)
105
if self.current_metadata['artist'] != '':
106
metadata['xesam:artist'] = dbus.Array (
107
[ self.current_metadata['artist'] ], variant_level = 1)
109
if self.current_metadata['album'] != '':
110
metadata['xesam:album'] = dbus.String (
111
self.current_metadata['album'], variant_level = 1)
113
if self.current_metadata['tracknumber'] != '':
114
metadata['xesam:trackNumber'] = dbus.Int32 (
115
self.current_metadata['tracknumber'], variant_level = 1)
119
def __do_update_metadata (self, totem, artist, # pylint: disable-msg=R0913
121
self.current_metadata = self.null_metadata.copy ()
123
self.current_metadata['title'] = title
125
self.current_metadata['artist'] = artist
127
self.current_metadata['album'] = album
129
self.current_metadata['tracknumber'] = num
131
self.PropertiesChanged ('org.mpris.MediaPlayer2.Player',
132
{ 'Metadata': self.__calculate_metadata () }, [])
134
def __do_notify_playing (self, totem, prop):
135
self.PropertiesChanged ('org.mpris.MediaPlayer2.Player',
136
{ 'PlaybackStatus': self.__calculate_playback_status () }, [])
138
def __do_notify_current_mrl (self, totem, prop):
139
self.PropertiesChanged ('org.mpris.MediaPlayer2.Player', {
140
'CanPlay': (self.totem.props.current_mrl != None),
141
'CanPause': (self.totem.props.current_mrl != None),
142
'CanSeek': (self.totem.props.current_mrl != None and
143
self.totem.props.seekable),
146
def __do_notify_seekable (self, totem, prop):
147
self.PropertiesChanged ('org.mpris.MediaPlayer2.Player', {
148
'CanSeek': (self.totem.props.current_mrl != None and
149
self.totem.props.seekable),
152
def __do_notify_current_time (self, totem, prop):
153
# Only notify of seeks if we've skipped more than 3 seconds
154
if abs (totem.props.current_time - self.current_position) > 3:
155
self.Seeked (totem.props.current_time * 1000L)
157
self.current_position = totem.props.current_time
159
# org.freedesktop.DBus.Properties interface
160
@dbus.service.method (dbus_interface = dbus.PROPERTIES_IFACE,
161
in_signature = 'ss', # pylint: disable-msg=C0103
163
def Get (self, interface_name, property_name):
164
return self.GetAll (interface_name)[property_name]
166
@dbus.service.method (dbus_interface = dbus.PROPERTIES_IFACE,
167
in_signature = 's', # pylint: disable-msg=C0103
168
out_signature = 'a{sv}')
169
def GetAll (self, interface_name):
170
if interface_name == 'org.mpris.MediaPlayer2':
174
'HasTrackList': False,
175
'Identity': self.totem.get_version (),
176
'DesktopEntry': 'totem',
177
'SupportedUriSchemes': self.totem.get_supported_uri_schemes (),
178
'SupportedMimeTypes': self.totem.get_supported_content_types (),
180
elif interface_name == 'org.mpris.MediaPlayer2.Player':
181
# Loop status (we don't support Track)
182
if self.totem.action_remote_get_setting (
183
Totem.RemoteSetting.REPEAT):
184
loop_status = 'Playlist'
189
shuffle = self.totem.action_remote_get_setting (
190
Totem.RemoteSetting.SHUFFLE)
193
'PlaybackStatus': self.__calculate_playback_status (),
194
'LoopStatus': loop_status, # TODO: Notifications
198
'Shuffle': shuffle, # TODO: Notifications
199
'Metadata': self.__calculate_metadata (),
200
'Volume': self.totem.get_volume (), # TODO: Notifications
201
'Position': self.totem.props.current_time * 1000L,
202
'CanGoNext': True, # TODO
203
'CanGoPrevious': True, # TODO
204
'CanPlay': (self.totem.props.current_mrl != None),
205
'CanPause': (self.totem.props.current_mrl != None),
206
'CanSeek': (self.totem.props.current_mrl != None and
207
self.totem.props.seekable),
211
raise dbus.exceptions.DBusException (
212
'org.mpris.MediaPlayer2.UnknownInterface',
213
_(u'The MediaPlayer2 object does not implement the ‘%s’ interface')
216
@dbus.service.method (dbus_interface = dbus.PROPERTIES_IFACE,
217
in_signature = 'ssv') # pylint: disable-msg=C0103
218
def Set (self, interface_name, property_name, new_value):
219
if interface_name == 'org.mpris.MediaPlayer2':
220
raise dbus.exceptions.DBusException (
221
'org.mpris.MediaPlayer2.ReadOnlyProperty',
222
_(u'The property ‘%s’ is not writeable.'))
223
elif interface_name == 'org.mpris.MediaPlayer2.Player':
224
if property_name == 'LoopStatus':
225
self.totem.action_remote_set_setting (
226
Totem.RemoteSetting.REPEAT, (new_value == 'Playlist'))
227
elif property_name == 'Rate':
228
# Ignore, since we don't support setting the rate
230
elif property_name == 'Shuffle':
231
self.totem.action_remote_set_setting (
232
Totem.RemoteSetting.SHUFFLE, new_value)
233
elif property_name == 'Volume':
234
self.totem.action_volume (new_value)
236
raise dbus.exceptions.DBusException (
237
'org.mpris.MediaPlayer2.ReadOnlyProperty',
238
_(u'Unknown property ‘%s’ requested of a MediaPlayer 2 object')
241
raise dbus.exceptions.DBusException (
242
'org.mpris.MediaPlayer2.UnknownInterface',
243
_(u'The MediaPlayer2 object does not implement the ‘%s’ interface')
246
@dbus.service.signal (dbus_interface = dbus.PROPERTIES_IFACE,
247
signature = 'sa{sv}as') # pylint: disable-msg=C0103
248
def PropertiesChanged (self, interface_name, changed_properties,
249
invalidated_properties):
252
# org.mpris.MediaPlayer2 interface
253
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2',
254
in_signature = '', # pylint: disable-msg=C0103
257
main_window = self.totem.get_main_window ()
258
main_window.present ()
260
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2',
261
in_signature = '', # pylint: disable-msg=C0103
264
self.totem.action_exit ()
266
# org.mpris.MediaPlayer2.Player interface
267
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
268
in_signature = '', # pylint: disable-msg=C0103
271
if self.totem.is_playing () or self.totem.is_paused ():
274
self.totem.action_next ()
276
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
277
in_signature = '', # pylint: disable-msg=C0103
280
if self.totem.is_playing () or self.totem.is_paused ():
283
self.totem.action_previous ()
285
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
286
in_signature = '', # pylint: disable-msg=C0103
289
self.totem.action_pause ()
291
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
292
in_signature = '', # pylint: disable-msg=C0103
294
def PlayPause (self):
295
self.totem.action_play_pause ()
297
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
298
in_signature = '', # pylint: disable-msg=C0103
301
self.totem.action_stop ()
303
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
304
in_signature = '', # pylint: disable-msg=C0103
307
# If playing or no track loaded: do nothing,
308
# else: start playing.
309
if self.totem.is_playing () or self.totem.props.current_mrl == None:
312
self.totem.action_play ()
314
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
315
in_signature = 'x', # pylint: disable-msg=C0103
317
def Seek (self, offset):
318
self.totem.action_seek_relative (offset / 1000L, False)
320
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
321
in_signature = 'ox', # pylint: disable-msg=C0103
323
def SetPosition (self, track_id, position):
324
position = position / 1000L
326
# Bail if the position is not in the permitted range
327
if position < 0 or position > self.totem.props.stream_length:
330
self.totem.action_seek_time (position, False)
332
@dbus.service.method (dbus_interface = 'org.mpris.MediaPlayer2.Player',
333
in_signature = 's', # pylint: disable-msg=C0103
335
def OpenUri (self, uri):
336
if self.totem.action_set_mrl (uri):
337
self.totem.action_play ()
339
raise dbus.exceptions.DBusException (
340
'org.mpris.MediaPlayer2.InvalidUri',
341
_(u'The URI ‘%s’ is not supported.') % uri)
343
@dbus.service.signal (dbus_interface = 'org.mpris.MediaPlayer2.Player',
344
signature = 'x') # pylint: disable-msg=C0103
345
def Seeked (self, position):