Using ctypes instead popen, Fix pairing issue, add thumbnails to game list.
This commit is contained in:
parent
e878e935dc
commit
984fb7e7b9
4 changed files with 180 additions and 80 deletions
|
@ -1,91 +1,80 @@
|
|||
import os
|
||||
import sys
|
||||
import urllib
|
||||
import urlparse
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
import xbmcaddon
|
||||
import re
|
||||
import subprocess
|
||||
import xbmcgui
|
||||
import xbmc
|
||||
|
||||
my_addon = xbmcaddon.Addon()
|
||||
my_env = os.environ.copy()
|
||||
my_env["LD_LIBRARY_PATH"] = my_addon.getAddonInfo("path")+"/libs:" + my_env["LD_LIBRARY_PATH"]
|
||||
import random
|
||||
import urllib
|
||||
import urlparse
|
||||
import sys
|
||||
import os
|
||||
|
||||
from resources.moonlight import LibGameStream
|
||||
|
||||
base_url = sys.argv[0]
|
||||
addon_handle = int(sys.argv[1])
|
||||
args = urlparse.parse_qs(sys.argv[2][1:])
|
||||
|
||||
xbmcplugin.setContent(addon_handle, 'files')
|
||||
addon = xbmcaddon.Addon()
|
||||
xbmcplugin.setContent(addon_handle, "files")
|
||||
|
||||
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:
|
||||
xbmcplugin.addDirectoryItem(handle=addon_handle,
|
||||
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)
|
||||
index()
|
||||
elif mode[0] == "stream":
|
||||
app = args.get('app', None)
|
||||
with open(my_addon.getAddonInfo("path") + '/start_moonlight.tmp', 'w') as f:
|
||||
if app is None:
|
||||
f.write("")
|
||||
else:
|
||||
f.write(app[0])
|
||||
app = args.get("app", None)
|
||||
stream(app)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?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>
|
||||
<import addon="xbmc.python" version="2.14.0"/>
|
||||
</requires>
|
||||
|
@ -16,4 +16,4 @@
|
|||
<language></language>
|
||||
<email>gustavobenn@hotmail.com</email>
|
||||
</extension>
|
||||
</addon>
|
||||
</addon>
|
0
script.moonlight/resources/__init__.py
Normal file
0
script.moonlight/resources/__init__.py
Normal file
111
script.moonlight/resources/moonlight.py
Normal file
111
script.moonlight/resources/moonlight.py
Normal 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
|
||||
|
Loading…
Reference in a new issue