mediaserver/mediaserver/mediaserver.py
2021-05-04 18:14:29 +02:00

464 lines
16 KiB
Python
Executable File

# License for this source file
#
# Copyright (c) 2021 Miqra Engineering
#
import sys, os
import signal
from optparse import OptionParser
from collections import OrderedDict
from .resources import Resources
# perform gstreamer imports (python3 style)
import gi
gi.require_version('Gst', '1.0')
gi.require_version('Gtk', '3.0')
gi.require_version('GstGL', '1.0')
gi.require_version('GstVideo', '1.0')
gi.require_version('GdkPixbuf', '2.0')
from gi.repository import GObject
from gi.repository import Gst, GstGL, GstVideo
from gi.repository import GdkPixbuf
from gi.repository import GLib, Gio, Gtk, Gdk
from gi.repository import GdkX11
Gst.init(None)
# perform dbus imports
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
# set dbus to use GObject Mainloop
DBusGMainLoop(set_as_default=True)
# imports from module
from . monitoredplayer import MonitoredPlayer
class MediaService(dbus.service.Object):
def __init__(self):
bus_name = dbus.service.BusName('nl.miqra.MediaCore.Media', bus=dbus.SystemBus())
dbus.service.Object.__init__(self, bus_name, '/nl/miqra/MediaCore/Media')
self.window = Gtk.Window(title="MediaServer")
self.windowInit()
self.player = MonitoredPlayer()
self.player.connect('playback-ready',self.onPlayerReady) # parameters: source_file tag_dict
self.player.connect('playback-playing',self.onPlayerPlaying) # parameters: None
self.player.connect('playback-stopped',self.onPlayerStopped) # parameters: None
self.player.connect('playback-paused',self.onPlayerPaused) # parameters: None
self.player.connect('playback-finished',self.onPlayerFinished) # parameters: None
self.player.connect('playback-error',self.onPlayerError) # parameters: error debug
self.player.connect('volume-changed',self.onPlayerVolumeChanged) # parameters: volume
self.player.connect('playback-buffering',self.onPlayerBuffering) # parameters: volume
self.window.connect('show',self.onWindowShow)
self.loadcount = 0
self.loadmax = 1
self.quickplay = False
self.quickplayduration = 0
self.quickplayloop = False
def Start(self):
self.window.show()
self.tick()
# Keepalive timer to help react to Keyboard interrupts
def tick(self):
GObject.timeout_add(100, self.tick)
def windowInit(self):
self.window.set_name("mainwindow")
self.imagebox = Gtk.Box()
self.imagebox.set_name("imbox")
self.imagebox.set_hexpand(True)
self.imagebox.set_vexpand(True)
self.imagebox.set_halign(Gtk.Align.CENTER)
self.imagebox.set_valign(Gtk.Align.CENTER)
css = b"""
#mainwindow
{
background-color: #000;
}
"""
css_provider = Gtk.CssProvider()
css_provider.load_from_data(css)
context = Gtk.StyleContext()
screen = Gdk.Screen.get_default()
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
self.window.add(self.imagebox)
self.window.fullscreen()
pass
def loadImage(self,image):
# remove any existing children
for child in self.imagebox.get_children():
self.imagebox.remove(child)
self.imagebox.add(image)
self.imagebox.show_all()
def loadImageFile(self,imfile):
print("Loading image {0}".format(imfile))
image = Gtk.Image()
image.set_from_file(imfile)
self.loadImage(image)
def loadImageData(self,data):
print("Loading image from data")
loader = GdkPixbuf.PixbufLoader.new()
loader.write(data)
pixbuf = loader.get_pixbuf()
loader.close()
image = Gtk.Image()
image.set_from_pixbuf(pixbuf)
self.loadImage(image)
def onWindowShow(self,w):
xid = self.window.get_window().get_xid()
print("Retrieved XID for window: {0}".format(xid))
self.player.link_to_window(xid)
self.loadBaseImage()
def loadBaseImage(self):
self.loadImageFile(Resources.filename("images/background-base.svg"))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='')
def QuickPlay(self, file,):
""" Directly play back a local file
"""
if self.loadcount < self.loadmax:
print("Quickplaying file {0}".format(file))
self.quickplay = True
self.quickplayduration = 0
self.quickplayloop = False
self.player.load(file)
self.loadcount += 1
else:
print("Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='sd', out_signature='')
def QuickPlayFor(self, file, duration):
""" Directly play back a local file
"""
if self.loadcount < self.loadmax:
print("Quickplaying file {0} for {1} seconds".format(file,duration))
self.quickplay = True
self.quickplayduration = duration
self.quickplayloop = False
self.player.load(file)
self.loadcount += 1
else:
print("Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax))
# self.quickplayer.playfor(file,duration)
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='')
def QuickPlayUrl(self, url):
""" Directly play back a url
"""
if self.loadcount < self.loadmax:
print("Quickplaying url {0}".format(url))
self.quickplay = True
self.quickplayduration = 0
self.quickplayloop = False
self.player.load_uri(url)
self.loadcount += 1
else:
print("Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(url,self.loadmax))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='sd', out_signature='')
def QuickPlayUrlFor(self, url, duration):
""" Directly play back a url
"""
if self.loadcount < self.loadmax:
print("Quickplaying url {0} for {1} seconds".format(url,duration))
self.quickplay = True
self.quickplayduration = duration
self.quickplayloop = False
self.player.load_uri(url)
self.loadcount += 1
else:
print("Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(url,self.loadmax))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='')
def QuickLoop(self, file):
""" Directly play back a url
"""
if self.loadcount < self.loadmax:
print("Quickplaying file {0}".format(file))
self.quickplay = True
self.quickplayduration = 0
self.quickplayloop = True
self.player.load(file)
self.loadcount += 1
else:
print("Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='')
def QuickLoopUrl(self, url):
""" Directly play back a url
"""
if self.loadcount < self.loadmax:
print("Quickplaying url {0}".format(url))
self.quickplay = True
self.quickplayduration = 0
self.quickplayloop = True
self.player.load_uri(url)
self.loadcount += 1
else:
print("Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(url,self.loadmax))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='')
def LoadFile(self, file):
""" Load a local file for playback
"""
if self.loadcount < self.loadmax:
print("Loading file {0}".format(file))
self.quickplay = False
self.player.load(file)
self.loadcount += 1
self.OnLoading("file://{0}".format(file))
else:
print("Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='')
def LoadUrl(self, uri):
""" Load an url for playback
"""
if self.loadcount < self.loadmax:
print("Loading url {0}".format(uri))
self.quickplay = False
self.player.load_uri(uri)
self.loadcount += 1
self.OnLoading(uri)
else:
print("Skipping load of url {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(uri,self.loadmax))
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='d', out_signature='')
def PlayFor(self, duration):
""" Starts playback for [duration] seconds
"""
print("Starting playback for {0} seconds".format(duration))
self.player.playfor(duration)
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='')
def Play(self):
""" Starts/resumes playback
"""
pos = self.player.position()
print("Starting playback at timestamp {0}".format(pos))
self.player.play()
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='')
def Loop(self):
""" Starts/resumes playback
"""
pos = self.player.position()
print("Starting playback at timestamp {0}".format(pos))
self.player.loop()
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='')
def Pause(self):
""" Pauses playback
"""
print("Pausing")
self.player.pause()
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='')
def Stop(self):
""" Stops playback
"""
print("Stopping")
self.player.stop()
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='d')
def Length(self):
""" returns length of currently loaded source
"""
return self.player.duration()
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='d')
def Position(self):
""" returns current position in the source
"""
return self.player.position()
@dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='d', out_signature='b')
def Seek(self,position):
""" returns current position in the source
"""
self.player.seek(position)
### Callback functions
def onPlayerReady(self,player,filename,tags):
print("Loaded {0}".format(filename))
print("Tags:")
taglist = {}
for tag in tags:
try:
taglist[tag] = str(tags[tag])
print(" {0}: {1}".format(tag,taglist[tag]))
except Exception as x:
print("Error converting value for '{0}':\n {1}".format(tag,x))
# extract cover art from tags
if "image" in tags:
buffer = tags["image"].get_buffer() # Gst.Buffer
(result, mapinfo) = buffer.map(Gst.MapFlags.READ)
if result:
self.loadImageData(mapinfo.data)
# reset load counter
self.loadcount = 0
if self.quickplay:
if self.quickplayloop:
print("Direct looping")
self.player.loop()
elif self.quickplayduration > 0:
print("Direct playing for {0} s".format(self.quickplayduration))
self.player.playfor(self.quickplayduration)
else:
print("Direct playing")
self.player.play()
else:
# signal ready
self.OnReady(filename,taglist)
def onPlayerPlaying(self,player):
self.OnPlaying()
def onPlayerStopped(self,player):
self.loadBaseImage()
self.OnStopped()
def onPlayerPaused(self,player):
self.OnPaused()
def onPlayerFinished(self,player):
self.loadBaseImage()
self.OnFinished()
def onPlayerError(self,player,state,error,debug):
print("Player state: {0}".format(state))
if state == "LOADING":
print("Failure during LOAD: {0}".format(error))
self.OnLoadFail(error)
else:
print("Failure during RUN: {0}".format(error))
self.OnRunFail(error)
def onPlayerVolumeChanged(self,player,volume):
pass
def onPlayerBuffering(self,player,pct):
self.OnBuffering(pct)
pass
## Signalling functions (DBus)
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='s')
def OnLoading(self,url):
""" gets called when a source has been requested for playback
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='sa{ss}')
def OnReady(self,filename,tags):
""" gets called when a source is ready for playback
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='')
def OnPlaying(self):
""" gets called when a playback has started
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='')
def OnPaused(self):
""" gets called when a playback is paused
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='q')
def OnBuffering(self,percent):
""" gets called when a playback is paused
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='')
def OnStopped(self):
""" gets called when playback has stopped
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='')
def OnFinished(self):
""" gets called when playback has completed
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='')
def OnFinishing(self):
""" gets called when playback is nearly completed
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='s')
def OnLoadFail(self,reason):
""" gets called when loading a source for playback fails
"""
pass
@dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='s')
def OnRunFail(self,reason):
""" gets called when playback fails
"""
pass
def Run():
print("Initializing")
# quick bugfix - set XDG runtime dir if it has not been set
if not 'XDG_RUNTIME_DIR' in os.environ:
os.environ['XDG_RUNTIME_DIR'] = "/run/user/{0}".format(os.getuid())
mediaservice = MediaService()
mediaservice.Start()
loop = GObject.MainLoop()
def onsigint(signal,frame):
print("Quitting")
loop.quit()
signal.signal(signal.SIGINT, onsigint)
print("Starting..." )
try:
loop.run()
except KeyboardInterrupt:
print("Quitting")
loop.quit()