1
# Copyright (C) 2009 Abhishek Mukherjee
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 3, or (at your option)
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
"""/Player object for MPRIS specification interface to Exaile
18
http://wiki.xmms2.xmms.se/wiki/MPRIS#.2FPlayer_object_methods
22
from __future__ import division
24
from xl import settings
31
import mpris_tag_converter
33
INTERFACE_NAME = 'org.freedesktop.MediaPlayer'
35
class MprisCaps(object):
37
Specification for the capabilities field in MPRIS
45
CAN_PROVIDE_METADATA = 1 << 5
46
CAN_HAS_TRACKLIST = 1 << 6
48
EXAILE_CAPS = (MprisCaps.CAN_GO_NEXT
49
| MprisCaps.CAN_GO_PREV
53
| MprisCaps.CAN_PROVIDE_METADATA
54
| MprisCaps.CAN_HAS_TRACKLIST)
56
class ExaileMprisPlayer(dbus.service.Object):
59
/Player (Root) object methods
62
def __init__(self, exaile, bus):
63
dbus.service.Object.__init__(self, bus, '/Player')
65
self._tag_converter = mpris_tag_converter.ExaileTagConverter(exaile)
66
xl.event.add_callback(self.track_change_cb, 'playback_track_start')
67
# FIXME: Does not watch for shuffle, repeat
68
# TODO: playback_start does not distinguish if play button was pressed
69
# or we simply moved to a new track
70
for event in ('playback_player_end', 'playback_player_start',
71
'playback_toggle_pause'):
72
xl.event.add_callback(self.status_change_cb, event)
75
@dbus.service.method(INTERFACE_NAME)
78
Goes to the next element
80
self.exaile.queue.next()
82
@dbus.service.method(INTERFACE_NAME)
85
Goes to the previous element
87
self.exaile.queue.prev()
89
@dbus.service.method(INTERFACE_NAME)
92
If playing, pause. If paused, unpause.
94
self.exaile.player.toggle_pause()
96
@dbus.service.method(INTERFACE_NAME)
101
self.exaile.player.stop()
103
@dbus.service.method(INTERFACE_NAME)
106
If Playing, rewind to the beginning of the current track, else.
109
if self.exaile.player.is_playing():
110
self.exaile.player.play(self.exaile.player.current)
112
self.exaile.queue.play()
114
@dbus.service.method(INTERFACE_NAME, in_signature="b")
115
def Repeat(self, repeat):
117
Toggle the current track repeat
121
@dbus.service.method(INTERFACE_NAME, out_signature="(iiii)")
124
Return the status of "Media Player" as a struct of 4 ints:
125
* First integer: 0 = Playing, 1 = Paused, 2 = Stopped.
126
* Second interger: 0 = Playing linearly , 1 = Playing randomly.
127
* Third integer: 0 = Go to the next element once the current has
128
finished playing , 1 = Repeat the current element
129
* Fourth integer: 0 = Stop playing once the last element has been
130
played, 1 = Never give up playing
132
if self.exaile.player.is_playing():
134
elif self.exaile.player.is_paused():
139
if not self.exaile.queue.current_playlist.random_enabled:
144
go_to_next = 0 # Do not have ability to repeat single track
146
if not self.exaile.queue.current_playlist.repeat_enabled:
151
return (playing, random, go_to_next, repeat)
153
@dbus.service.method(INTERFACE_NAME, out_signature="a{sv}")
154
def GetMetadata(self):
156
Gives all meta data available for the currently played element.
158
if self.exaile.player.current is None:
160
return self._tag_converter.get_metadata(self.exaile.player.current)
162
@dbus.service.method(INTERFACE_NAME, out_signature="i")
165
Returns the "Media player"'s current capabilities, see MprisCaps
169
@dbus.service.method(INTERFACE_NAME, in_signature="i")
170
def VolumeSet(self, volume):
172
Sets the volume, arument in the range [0, 100]
174
if volume < 0 or volume > 100:
177
settings.set_option('player/volume', volume / 100)
179
@dbus.service.method(INTERFACE_NAME, out_signature="i")
182
Returns the current volume (must be in [0;100])
184
return settings.get_option('player/volume', 0) * 100
186
@dbus.service.method(INTERFACE_NAME, in_signature="i")
187
def PositionSet(self, millisec):
189
Sets the playing position (argument must be in [0, <track_length>]
192
if millisec > self.exaile.player.current.tags['__length'] * 1000 \
195
self.exaile.player.seek(millisec / 1000)
197
@dbus.service.method(INTERFACE_NAME, out_signature="i")
198
def PositionGet(self):
200
Returns the playing position (will be [0, track_length] in
203
return int(self.exaile.player.get_position() / 1000000)
205
def track_change_cb(self, type, object, data):
207
Callback will emit the dbus signal TrackChange with the current
210
metadata = self.GetMetadata()
211
self.TrackChange(metadata)
213
def status_change_cb(self, type, object, data):
215
Callback will emit the dbus signal StatusChange with the current
218
struct = self.GetStatus()
219
self.StatusChange(struct)
221
def caps_change_cb(self, type, object, data):
223
Callback will emit the dbus signal CapsChange with the current Caps
225
caps = self.GetCaps()
226
self.CapsChange(caps)
228
@dbus.service.signal(INTERFACE_NAME, signature="a{sv}")
229
def TrackChange(self, metadata):
231
Signal is emitted when the "Media Player" plays another "Track".
232
Argument of the signal is the metadata attached to the new "Track"
236
@dbus.service.signal(INTERFACE_NAME, signature="(iiii)")
237
def StatusChange(self, struct):
239
Signal is emitted when the status of the "Media Player" change. The
240
argument has the same meaning as the value returned by GetStatus.
244
@dbus.service.signal(INTERFACE_NAME)
245
def CapsChange(self):
247
Signal is emitted when the "Media Player" changes capabilities, see