Skip to content

Commit

Permalink
New MediaPlayer plugin and service
Browse files Browse the repository at this point in the history
  • Loading branch information
infirit committed Mar 3, 2016
1 parent 19d3908 commit e73fc1d
Show file tree
Hide file tree
Showing 8 changed files with 461 additions and 0 deletions.
40 changes: 40 additions & 0 deletions blueman/bluez/AVRemote.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# coding=utf-8
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from __future__ import unicode_literals

from blueman.bluez.PropertiesBase import PropertiesBase
from gi.repository import GObject


class AVRemote(PropertiesBase):
__gsignals__ = {
str('status-changed'): (GObject.SignalFlags.NO_HOOKS, None, (GObject.TYPE_PYOBJECT,))
}

_interface_name = 'org.bluez.MediaPlayer1'

def _init(self, obj_path):
super(AVRemote, self)._init(self._interface_name, obj_path)

def play(self):
self._call('Play')

def pause(self):
self._call('Pause')

def stop(self):
self._call('Stop')

def next(self):
self._call('Next')

def previous(self):
self._call('Previous')

def fast_forward(self):
self._call('FastForward')

def rewind(self):
self._call('Rewind')
1 change: 1 addition & 0 deletions blueman/bluez/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ blueman_PYTHON = \
Adapter.py \
Agent.py \
AgentManager.py \
AVRemote.py \
Base.py \
Device.py \
errors.py \
Expand Down
237 changes: 237 additions & 0 deletions blueman/plugins/applet/AVRemote.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# coding=utf-8
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from __future__ import unicode_literals

from blueman.Functions import *
from blueman.Constants import *
from blueman.Sdp import uuid128_to_uuid16, AV_REMOTE_TARGET_SVCLASS_ID
from blueman.plugins.AppletPlugin import AppletPlugin
from blueman.bluez.AVRemote import AVRemote
from gi.repository import GLib
import blueman.bluez as Bluez

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Pango, GLib
import gettext
import os
import time
from locale import bind_textdomain_codeset
import cgi


class PlayerWindow(Gtk.Window):
running = False

def __init__(self):
if not PlayerWindow.running:
PlayerWindow.running = True
else:
return

super(PlayerWindow, self).__init__(title="MediaPlayer")

builder = Gtk.Builder()
builder.add_from_file(UI_PATH + "/media-player.ui")
builder.set_translation_domain("blueman")
bind_textdomain_codeset("blueman", "UTF-8")

self.add(builder.get_object("media_player"))

self._control_buttons = {}
self._control_buttons['play'] = builder.get_object("media_play")
self._control_buttons['pause'] = builder.get_object("media_pause")
self._control_buttons['stop'] = builder.get_object("media_stop")
self._control_buttons['next'] = builder.get_object("media_next")
self._control_buttons['prev'] = builder.get_object("media_previous")

self._control_buttons['play'].connect("clicked", self._on_media_button_clicked, "play")
self._control_buttons['pause'].connect("clicked", self._on_media_button_clicked, "pause")
self._control_buttons['stop'].connect("clicked", self._on_media_button_clicked, "stop")
self._control_buttons['next'].connect("clicked", self._on_media_button_clicked, "next")
self._control_buttons['prev'].connect("clicked", self._on_media_button_clicked, "prev")

self._artist = builder.get_object("media_artist")
self._track = builder.get_object("media_track")
self._time = builder.get_object("media_time")

self.set_icon(get_icon("multimedia-player"))
self.show()
self.connect("delete-event", self._on_window_delete)

cr_name_address = builder.get_object("name_address")
cr_name_address.props.ellipsize = Pango.EllipsizeMode.END

cr_connected = builder.get_object("connected")
cr_connected.props.sensitive = False
cr_connected.props.style = Pango.Style.ITALIC

self.liststore = Gtk.ListStore(str, str, object)
self.device_combo = builder.get_object("device_combo")
self.device_combo.set_model(self.liststore)
self.device_combo.connect("changed", self._on_device_combo_changed)

self._manager = Bluez.Manager()

self._remote_control_devices = {}
self.active_remote = None

for adapter in self._manager.list_adapters():
for device in adapter.list_devices():
if self._has_remote_control(device['UUIDs']):
object_path = device.get_object_path()
player_path = os.path.join(object_path, 'player0')
remote_control = AVRemote(player_path)
sig = remote_control.connect_signal("property-changed", self._on_property_changed)
self._remote_control_devices[object_path] = (remote_control, sig)

self.add_to_list(device)
dprint("Added device: ", player_path)

GLib.timeout_add_seconds(1, self._update_time)

def add_to_list(self, device):
dev_info = "%s\n<small>%s</small>" % (device['Alias'], device['Address'])
if device['Connected']:
conn_info = "Connected"
else:
conn_info = "Not Connected"

titer = self.liststore.append([dev_info, conn_info, device.get_object_path()])

def _has_remote_control(self, uuids):
for uuid in uuids:
uuid16 = uuid128_to_uuid16(uuid)
if uuid16 == AV_REMOTE_TARGET_SVCLASS_ID:
return True
return False

def update_track_info(self, remote):
self._update_artist()
self._update_time()
self._update_track()

def _update_time(self):
if self.active_remote is None:
return True

pos = self.active_remote['Position']
track_lenght = float(self.active_remote['Track']['Duration'])
time_left = (track_lenght - pos) / 1000
left_string = time.strftime('%M:%S', time.gmtime(time_left))
self._time.set_markup("<b>%s</b>" % left_string)

return True

def _update_artist(self):
artist = self.active_remote['Track']['Artist']
artist_markup = "<b>%s</b>" % cgi.escape(artist)
self._artist.set_markup(artist_markup)

def _update_track(self):
track = self.active_remote['Track']['Title']
self._track.set_markup(cgi.escape(track))

def _on_device_combo_changed(self, combo):
titer = combo.get_active_iter()
object_path = self.liststore[titer][2]
self.active_remote = self._remote_control_devices[object_path][0]
self.update_track_info(self.active_remote)
dprint(object_path)

def _on_media_button_clicked(self, button, action):
dprint(action)
if self.active_remote is None:
return

if action == "play":
self.active_remote.play()
elif action == "pause":
self.active_remote.pause()
elif action == "next":
self.active_remote.next()
elif action == "prev":
self.active_remote.previous()
elif action == "stop":
self.active_remote.stop()

def _on_window_delete(self, win, event):
for path in list(self._remote_control_devices.keys()):
remote, sig = self._remote_control_devices.pop(path)
remote.disconnect_signal(sig)

self.active_remote = None
self._remote_control_devices = {}
PlayerWindow.running = False
self.destroy()

def _update_track_times(self, track_lenght, time_left):
position = self.active_remote['Position']
self.time_label.set_markup("<b>%s</b>")

def _on_device_created(self, object_path):
dprint(object_path)

def _on_device_removed(self, object_path):
dprint(object_path)

def _on_property_changed(self, remote, name, val, path):
if not path == self.active_remote.get_object_path():
return

dprint(name, val)
if name == "Track":
self._update_artist()
self._update_track()
elif name == "Position":
self._update_time()
elif name == "Status":
if val == "stopped":
self._control_buttons['play'].props.sensitive = True
self._control_buttons['stop'].props.sensitive = False
self._control_buttons['pause'].props.sensitive = False
elif val == "playing":
self._control_buttons['play'].props.sensitive = False
self._control_buttons['stop'].props.sensitive = True
self._control_buttons['pause'].props.sensitive = True
elif val == "paused":
self._control_buttons['play'].props.sensitive = True
self._control_buttons['stop'].props.sensitive = False
self._control_buttons['pause'].props.sensitive = False


class RemoteMediaPlayer(AppletPlugin):
__depends__ = ["Menu"]
__icon__ = "multimedia-player"
__description__ = _(
"Allows you to control remote A/V media devices.")
__author__ = "Sander Sweers (infirit)"
__autoload__ = False

def on_load(self, applet):
item = create_menuitem(_("Media _Player"), get_icon("multimedia-player", 16))
item.props.tooltip_text = _("Show a media player remote control")
item.connect("activate", self.activate_ui)

self.Applet.Plugins.Menu.Register(self, item, 85, True)

self.player_window = None

def on_unload(self):
self.Applet.Plugins.Menu.Unregister(self)
if self.player_window is not None:
self.player_window.destroy()

def activate_ui(self, item):
if not self.player_window:
self.player_window = PlayerWindow()
elif self.player_window.running:
self.player_window.present()

def on_device_created(self, device):
self.player_window._on_device_created(device)

def on_device_removed(self, device):
self.player_window._on_device_removed(device)
1 change: 1 addition & 0 deletions blueman/plugins/applet/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ bluemandir = $(pythondir)/blueman/plugins/applet
blueman_PYTHON = \
__init__.py \
AuthAgent.py \
AVRemote.py \
DBusService.py \
DhcpClient.py \
DiscvManager.py \
Expand Down
16 changes: 16 additions & 0 deletions blueman/services/AVRemote.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# coding=utf-8
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from __future__ import unicode_literals

from blueman.Service import Service
from blueman.Sdp import AV_REMOTE_TARGET_SVCLASS_ID


class AudioSink(Service):
__group__ = 'audio'
__svclass_id__ = AV_REMOTE_TARGET_SVCLASS_ID
__description__ = _("Allows to control media player device")
__icon__ = "multimedia-player"
__priority__ = 90
1 change: 1 addition & 0 deletions blueman/services/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ blueman_PYTHON = \
__init__.py \
AudioSink.py \
AudioSource.py \
AVRemote.py \
DialupNetwork.py \
Functions.py \
GroupNetwork.py \
Expand Down
1 change: 1 addition & 0 deletions data/ui/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ ui_DATA = \
adapters-tab.ui \
applet-passkey.ui \
manager-main.ui \
media-player.ui \
device-list-widget.ui \
services-network.ui \
services-transfer.ui \
Expand Down
Loading

0 comments on commit e73fc1d

Please sign in to comment.