Using ctypes instead popen, Fix pairing issue, add thumbnails to game list.

This commit is contained in:
Gustavo 2016-02-17 05:05:53 -02:00
parent e878e935dc
commit 984fb7e7b9
4 changed files with 180 additions and 80 deletions

View file

@ -1,91 +1,80 @@
import os
import sys
import urllib
import urlparse
import xbmcgui
import xbmcplugin import xbmcplugin
import xbmcaddon import xbmcaddon
import re import xbmcgui
import subprocess import xbmc
my_addon = xbmcaddon.Addon() import random
my_env = os.environ.copy() import urllib
my_env["LD_LIBRARY_PATH"] = my_addon.getAddonInfo("path")+"/libs:" + my_env["LD_LIBRARY_PATH"] import urlparse
import sys
import os
from resources.moonlight import LibGameStream
base_url = sys.argv[0] base_url = sys.argv[0]
addon_handle = int(sys.argv[1]) addon_handle = int(sys.argv[1])
args = urlparse.parse_qs(sys.argv[2][1:]) args = urlparse.parse_qs(sys.argv[2][1:])
addon = xbmcaddon.Addon()
xbmcplugin.setContent(addon_handle, 'files') xbmcplugin.setContent(addon_handle, "files")
def build_url(query): def build_url(query):
return base_url + '?' + urllib.urlencode(query) return base_url + "?" + urllib.urlencode(query)
mode = args.get('mode', None) def index():
gs = LibGameStream(addon.getAddonInfo("path") + "/libs")
address = addon.getSetting("MOON_SERVER_IP")
if address == "0.0.0.0":
address = gs.discover_server()
if not gs.connect_server(address):
dialog = xbmcgui.Dialog()
dialog.ok("Error", "Failed connect to server (%s)" % (address))
return
if not gs.isPaired():
pin = "%d%d%d%d" % (random.randint(0,9), random.randint(0,9), random.randint(0,9), random.randint(0,9))
dialog = xbmcgui.Dialog()
dialog.notification("PIN code", "Insert the pin code in server: %s" % pin, xbmcgui.NOTIFICATION_INFO, 10000)
if gs.pair(pin):
dialog = xbmcgui.Dialog()
dialog.ok("Paired", "Succesfully paired")
else:
dialog = xbmcgui.Dialog()
dialog.ok("Error", "Failed to pair to server, try again")
return
for appId, name in gs.applist():
base_path = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8') + "/images"
if not os.path.exists(base_path):
os.makedirs(base_path)
poster_path = base_path + "/" + str(appId) + ".png"
if not os.path.isfile(poster_path):
gs.poster(appId, base_path)
xbmc.sleep(100)
xbmcplugin.addDirectoryItem(handle=addon_handle,
url=build_url({"mode": "stream", "app": name}),
listitem=xbmcgui.ListItem(label=name, thumbnailImage=poster_path),
isFolder=False)
xbmcplugin.endOfDirectory(addon_handle)
def stream(app = None):
with open(addon.getAddonInfo("path") + "/start_moonlight.tmp", "w") as f:
if app is None:
f.write("")
else:
f.write(app[0])
mode = args.get("mode", None)
if mode is None: if mode is None:
xbmcplugin.addDirectoryItem(handle=addon_handle, index()
url=build_url({'mode': 'pair'}),
listitem=xbmcgui.ListItem('Pair'),
isFolder=False)
xbmcplugin.addDirectoryItem(handle=addon_handle,
url=build_url({'mode': 'list'}),
listitem=xbmcgui.ListItem('List Games'),
isFolder=True)
xbmcplugin.addDirectoryItem(handle=addon_handle,
url=build_url({'mode': 'stream', 'app': ''}),
listitem=xbmcgui.ListItem('Stream Default'),
isFolder=False)
xbmcplugin.endOfDirectory(addon_handle)
elif mode[0] == "pair":
pair_args = ["pair"]
if my_addon.getSetting("MOON_SERVER_IP") != "0.0.0.0":
pair_args += [my_addon.getSetting("MOON_SERVER_IP")]
p = subprocess.Popen([my_addon.getAddonInfo("path") + "/bin/moonlight"] + pair_args, stdout=subprocess.PIPE, env=my_env)
pin = ""
while True:
l = p.stdout.readline()
if "following PIN" in l:
m = re.search(':\s(\d+)', l)
pin = m.group(0)
break
dialog = xbmcgui.Dialog()
dialog.ok("PIN code", "Insert the pin code in server: %s" % pin)
ret = ''.join(p.stdout.readlines())
if "Succesfully paired" in ret:
dialog = xbmcgui.Dialog()
dialog.ok("Paired", "Succesfully paired")
else:
dialog = xbmcgui.Dialog()
dialog.ok("Error", "Failed to pair to server")
elif mode[0] == "list":
list_args = ["list"]
if my_addon.getSetting("MOON_SERVER_IP") != "0.0.0.0":
list_args += [my_addon.getSetting("MOON_SERVER_IP")]
p = subprocess.Popen([my_addon.getAddonInfo("path") + "/bin/moonlight"] + list_args, stdout=subprocess.PIPE, env=my_env)
ret = ''.join(p.stdout.readlines()).strip()
m = re.findall(r"\d.\s(.*?)\n|$", ret)
for game in list(m):
name = game.strip()
if name != "":
xbmcplugin.addDirectoryItem(handle=addon_handle,
url=build_url({'mode': 'stream', 'app': name}),
listitem=xbmcgui.ListItem(name),
isFolder=False)
xbmcplugin.endOfDirectory(addon_handle)
elif mode[0] == "stream": elif mode[0] == "stream":
app = args.get('app', None) app = args.get("app", None)
with open(my_addon.getAddonInfo("path") + '/start_moonlight.tmp', 'w') as f: stream(app)
if app is None:
f.write("")
else:
f.write(app[0])

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.moonlight" name="Moonlight" version="1.0.1" provider-name="dead"> <addon id="script.moonlight" name="Moonlight" version="1.0.2" provider-name="dead">
<requires> <requires>
<import addon="xbmc.python" version="2.14.0"/> <import addon="xbmc.python" version="2.14.0"/>
</requires> </requires>

View file

View file

@ -0,0 +1,111 @@
import ctypes
import os
from uuid import uuid4
GS_OK = 0
GS_FAILED = -1
GS_OUT_OF_MEMORY = -2
GS_INVALID = -3
GS_WRONG_STATE = -4
GS_IO_ERROR = -5
GS_NOT_SUPPORTED_4K = -6
class SERVER_DATA(ctypes.Structure):
_fields_ = [("address", ctypes.c_char_p),
("paired", ctypes.c_bool),
("supports4K", ctypes.c_bool),
("currentGame", ctypes.c_int),
("serverMajorVersion", ctypes.c_int)]
class APP_LIST(ctypes.Structure):
pass
APP_LIST._fields_ = [("name", ctypes.c_char_p),
("id", ctypes.c_int),
("next", ctypes.POINTER(APP_LIST))]
class _HTTP_DATA(ctypes.Structure):
_fields_ = [("memory", ctypes.POINTER(ctypes.c_ubyte)),
("size", ctypes.c_size_t)]
class LibGameStream:
def __init__(self, libpath = ""):
self.commomlib = ctypes.cdll.LoadLibrary(os.path.join(libpath, "libmoonlight-common.so.0"))
self.gslib = ctypes.cdll.LoadLibrary(os.path.join(libpath, "libgamestream.so.0"))
self.connected = False
self.address = ""
self.key_dir = ""
def discover_server(self):
addr = ctypes.create_string_buffer('\000' * 40)
self.gslib.gs_discover_server(addr)
return addr.value
def connect_server(self, address, key_dir = ""):
self.server = ctypes.pointer(SERVER_DATA(address, False, False, 0, 0))
self.address = address
if key_dir == "":
if "XDG_CONFIG_DIR" in os.environ:
key_dir = os.path.join(os.environ["XDG_CONFIG_DIR"], "moonlight")
else:
key_dir = os.path.join(os.environ["HOME"], ".cache", "moonlight")
self.key_dir = key_dir
ret = self.gslib.gs_init(self.server, ctypes.c_char_p(key_dir))
if ret == GS_OK:
self.connected = True
return True
return False
def isPaired(self):
if not self.connected:
return False
return self.server[0].paired
def applist(self):
if not self.connected:
return None
lst = []
applst_ptr = ctypes.POINTER(APP_LIST)
applst = applst_ptr()
ret = self.gslib.gs_applist(self.server, ctypes.byref(applst))
if ret != GS_OK:
return None
while applst:
lst.append((applst[0].id, applst[0].name))
applst = applst[0].next
return lst
def poster(self, appId, toFolder):
unique_id = ""
with open(os.path.join(self.key_dir, "uniqueid.dat"), "r") as f:
unique_id = f.read()
uid = uuid4()
url = "https://%s:47984/appasset?uniqueid=%s&uuid=%s&appid=%d&AssetType=2&AssetIdx=0" % (self.address, unique_id, str(uid), appId)
self.gslib.http_create_data.restype = ctypes.POINTER(_HTTP_DATA)
data = self.gslib.http_create_data();
self.gslib.http_request(ctypes.c_char_p(url), data)
barray = bytearray(data[0].memory[0:data[0].size])
with open(os.path.join(toFolder, str(appId) + ".png"), "wb") as f:
f.write(barray)
self.gslib.http_free_data(data);
def pair(self, pin):
if not self.connected:
return None
ret = self.gslib.gs_pair(self.server, ctypes.c_char_p(pin))
return ret == GS_OK