mirror of
https://github.com/phin05/discord-rich-presence-plex
synced 2024-11-25 19:10:18 +00:00
Update discordRichPresencePlex.py
This commit is contained in:
parent
f3e6ab32f6
commit
a56c8694d0
1 changed files with 112 additions and 74 deletions
|
@ -1,4 +1,5 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import plexapi.myplex
|
import plexapi.myplex
|
||||||
|
@ -11,16 +12,22 @@ import time
|
||||||
|
|
||||||
class plexConfig:
|
class plexConfig:
|
||||||
|
|
||||||
plexServerName = ""
|
extraLogging = True
|
||||||
plexUsername = ""
|
|
||||||
plexPasswordOrToken = ""
|
def __init__(self, serverName = "", username = "", password = "", token = "", listenForUser = ""):
|
||||||
usingToken = False
|
self.serverName = serverName
|
||||||
listenForUser = ""
|
self.username = username
|
||||||
extraLogging = False
|
self.password = password
|
||||||
|
self.token = token
|
||||||
|
self.listenForUser = (username if listenForUser == "" else listenForUser).lower()
|
||||||
|
|
||||||
|
plexConfigs = [
|
||||||
|
# plexConfig(serverName = "", username = "", password = "", token = "", listenForUser = "")
|
||||||
|
]
|
||||||
|
|
||||||
class discordRichPresence:
|
class discordRichPresence:
|
||||||
|
|
||||||
def __init__(self, clientID):
|
def __init__(self, clientID, child):
|
||||||
self.IPCPipe = ((os.environ.get("XDG_RUNTIME_DIR", None) or os.environ.get("TMPDIR", None) or os.environ.get("TMP", None) or os.environ.get("TEMP", None) or "/tmp") + "/discord-ipc-0") if isLinux else "\\\\?\\pipe\\discord-ipc-0"
|
self.IPCPipe = ((os.environ.get("XDG_RUNTIME_DIR", None) or os.environ.get("TMPDIR", None) or os.environ.get("TMP", None) or os.environ.get("TEMP", None) or "/tmp") + "/discord-ipc-0") if isLinux else "\\\\?\\pipe\\discord-ipc-0"
|
||||||
self.clientID = clientID
|
self.clientID = clientID
|
||||||
self.pipeReader = None
|
self.pipeReader = None
|
||||||
|
@ -28,20 +35,19 @@ class discordRichPresence:
|
||||||
self.process = None
|
self.process = None
|
||||||
self.running = False
|
self.running = False
|
||||||
self.resetNext = False
|
self.resetNext = False
|
||||||
|
self.child = child
|
||||||
|
|
||||||
async def read(self):
|
async def read(self):
|
||||||
try:
|
try:
|
||||||
print("\nReading:")
|
|
||||||
data = await self.pipeReader.read(1024)
|
data = await self.pipeReader.read(1024)
|
||||||
print(json.loads(data[8:].decode("utf-8")))
|
self.child.log("[READ] " + str(json.loads(data[8:].decode("utf-8"))))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error: " + str(e))
|
self.child.log("Error: " + str(e))
|
||||||
self.resetNext = True
|
self.resetNext = True
|
||||||
|
|
||||||
def write(self, op, payload):
|
def write(self, op, payload):
|
||||||
print("\nWriting:")
|
|
||||||
payload = json.dumps(payload)
|
payload = json.dumps(payload)
|
||||||
print(payload)
|
self.child.log("[WRITE] " + str(payload))
|
||||||
data = self.pipeWriter.write(struct.pack("<ii", op, len(payload)) + payload.encode("utf-8"))
|
data = self.pipeWriter.write(struct.pack("<ii", op, len(payload)) + payload.encode("utf-8"))
|
||||||
|
|
||||||
async def handshake(self):
|
async def handshake(self):
|
||||||
|
@ -55,17 +61,17 @@ class discordRichPresence:
|
||||||
self.running = True
|
self.running = True
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
print("\nOpening Discord IPC Pipe")
|
self.child.log("Opening Discord IPC Pipe")
|
||||||
emptyProcessFilePath = tempfile.gettempdir() + "\\discordRichPresencePlex-emptyProcess.py"
|
emptyProcessFilePath = tempfile.gettempdir() + "\\discordRichPresencePlex-emptyProcess.py"
|
||||||
if (not os.path.exists(emptyProcessFilePath)):
|
if (not os.path.exists(emptyProcessFilePath)):
|
||||||
with open(emptyProcessFilePath, "w") as emptyProcessFile:
|
with open(emptyProcessFilePath, "w") as emptyProcessFile:
|
||||||
emptyProcessFile.write("import time\n\ntry:\n\twhile (True):\n\t\ttime.sleep(60)\nexcept:\n\tpass")
|
emptyProcessFile.write("import timetry:\twhile (True):\t\ttime.sleep(60)except:\tpass")
|
||||||
self.process = subprocess.Popen(["python3" if isLinux else "pythonw", emptyProcessFilePath])
|
self.process = subprocess.Popen(["python3" if isLinux else "pythonw", emptyProcessFilePath])
|
||||||
self.loop = asyncio.get_event_loop() if isLinux else asyncio.ProactorEventLoop()
|
self.loop = asyncio.get_event_loop() if isLinux else asyncio.ProactorEventLoop()
|
||||||
self.loop.run_until_complete(self.handshake())
|
self.loop.run_until_complete(self.handshake())
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
print("\nClosing Discord IPC Pipe")
|
self.child.log("Closing Discord IPC Pipe")
|
||||||
self.process.kill()
|
self.process.kill()
|
||||||
self.pipeWriter.close()
|
self.pipeWriter.close()
|
||||||
self.loop.close()
|
self.loop.close()
|
||||||
|
@ -85,13 +91,6 @@ class discordRichPresence:
|
||||||
|
|
||||||
class discordRichPresencePlex(discordRichPresence):
|
class discordRichPresencePlex(discordRichPresence):
|
||||||
|
|
||||||
plexServerName = plexConfig.plexServerName
|
|
||||||
plexUsername = plexConfig.plexUsername
|
|
||||||
plexPasswordOrToken = plexConfig.plexPasswordOrToken
|
|
||||||
usingToken = plexConfig.usingToken
|
|
||||||
listenForUser = plexConfig.listenForUser
|
|
||||||
listenForUser = plexUsername if listenForUser == "" else listenForUser
|
|
||||||
|
|
||||||
productName = "Plex Media Server"
|
productName = "Plex Media Server"
|
||||||
lastState = None
|
lastState = None
|
||||||
lastSessionKey = None
|
lastSessionKey = None
|
||||||
|
@ -100,27 +99,65 @@ class discordRichPresencePlex(discordRichPresence):
|
||||||
stopTimerInterval = 5
|
stopTimerInterval = 5
|
||||||
stopTimer2 = None
|
stopTimer2 = None
|
||||||
stopTimer2Interval = 35
|
stopTimer2Interval = 35
|
||||||
|
checkConnectionTimer = None
|
||||||
|
checkConnectionTimerInterval = 60
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, plexConfig):
|
||||||
super().__init__("413407336082833418")
|
self.plexConfig = plexConfig
|
||||||
|
self.instanceID = hashlib.md5(str(id(self)).encode("UTF-8")).hexdigest()[:5]
|
||||||
|
super().__init__("413407336082833418", self)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if (self.usingToken):
|
try:
|
||||||
self.plexAccount = plexapi.myplex.MyPlexAccount(self.plexUsername, token = self.plexPasswordOrToken)
|
if (self.plexConfig.token):
|
||||||
|
self.plexAccount = plexapi.myplex.MyPlexAccount(self.plexConfig.username, token = self.plexConfig.token)
|
||||||
else:
|
else:
|
||||||
self.plexAccount = plexapi.myplex.MyPlexAccount(self.plexUsername, self.plexPasswordOrToken)
|
self.plexAccount = plexapi.myplex.MyPlexAccount(self.plexConfig.username, self.plexConfig.password)
|
||||||
print("Logged in as Plex User \"" + self.plexAccount.username + "\"")
|
self.log("Logged in as Plex User \"" + self.plexAccount.username + "\"")
|
||||||
self.plexServer = None
|
self.plexServer = None
|
||||||
for resource in self.plexAccount.resources():
|
for resource in self.plexAccount.resources():
|
||||||
if (resource.product == self.productName and resource.name == self.plexServerName):
|
if (resource.product == self.productName and resource.name == self.plexConfig.serverName):
|
||||||
self.plexServer = resource.connect()
|
self.plexServer = resource.connect()
|
||||||
self.plexServer.startAlertListener(self.onPlexServerAlert)
|
self.plexServer.startAlertListener(self.onPlexServerAlert)
|
||||||
print("Connected to " + self.productName + " \"" + self.plexServerName + "\"")
|
self.log("Connected to " + self.productName + " \"" + self.plexConfig.serverName + "\"")
|
||||||
print("Listening for PlaySessionStateNotification alerts from user \"" + self.listenForUser + "\"")
|
self.log("Listening for PlaySessionStateNotification alerts from user \"" + self.plexConfig.listenForUser + "\"")
|
||||||
|
if (self.checkConnectionTimer):
|
||||||
|
self.checkConnectionTimer.cancel()
|
||||||
|
self.checkConnectionTimer = threading.Timer(self.checkConnectionTimerInterval, self.checkConnection)
|
||||||
|
self.checkConnectionTimer.start()
|
||||||
break
|
break
|
||||||
if (not self.plexServer):
|
if (not self.plexServer):
|
||||||
print(self.productName + " \"" + self.plexServerName + "\" not found")
|
self.log(self.productName + " \"" + self.plexConfig.serverName + "\" not found")
|
||||||
sys.exit(0)
|
except Exception as e:
|
||||||
|
self.log("Failed to connect to Plex: " + str(e))
|
||||||
|
self.log("Reconnecting in 5 seconds")
|
||||||
|
time.sleep(5)
|
||||||
|
self.run()
|
||||||
|
|
||||||
|
def checkConnection(self):
|
||||||
|
try:
|
||||||
|
self.plexAccount.users()
|
||||||
|
self.checkConnectionTimer = threading.Timer(self.checkConnectionTimerInterval, self.checkConnection)
|
||||||
|
self.checkConnectionTimer.start()
|
||||||
|
except:
|
||||||
|
if (self.stopTimer):
|
||||||
|
self.stopTimer.cancel()
|
||||||
|
if (self.stopTimer2):
|
||||||
|
self.stopTimer2.cancel()
|
||||||
|
if (self.running):
|
||||||
|
self.stop()
|
||||||
|
self.log("Connection to Plex lost, reconnecting")
|
||||||
|
self.run()
|
||||||
|
|
||||||
|
def log(self, text, colour = "", extra = False):
|
||||||
|
prefix = "[" + self.plexConfig.serverName + "/" + self.instanceID + "] "
|
||||||
|
lock.acquire()
|
||||||
|
if (extra):
|
||||||
|
if (plexConfig.extraLogging):
|
||||||
|
print(prefix + colourText(str(text), colour))
|
||||||
|
else:
|
||||||
|
print(prefix + colourText(str(text), colour))
|
||||||
|
lock.release()
|
||||||
|
|
||||||
def onPlexServerAlert(self, data):
|
def onPlexServerAlert(self, data):
|
||||||
try:
|
try:
|
||||||
|
@ -130,13 +167,13 @@ class discordRichPresencePlex(discordRichPresence):
|
||||||
sessionKey = int(sessionData["sessionKey"])
|
sessionKey = int(sessionData["sessionKey"])
|
||||||
ratingKey = int(sessionData["ratingKey"])
|
ratingKey = int(sessionData["ratingKey"])
|
||||||
viewOffset = int(sessionData["viewOffset"])
|
viewOffset = int(sessionData["viewOffset"])
|
||||||
printExtraLog("\nReceived Update: " + colourText(sessionData, "yellow").replace("'", "\""))
|
self.log("Received Update: " + colourText(sessionData, "yellow").replace("'", "\""), extra = True)
|
||||||
if (self.lastSessionKey == sessionKey and self.lastRatingKey == ratingKey):
|
if (self.lastSessionKey == sessionKey and self.lastRatingKey == ratingKey):
|
||||||
if (self.stopTimer2):
|
if (self.stopTimer2):
|
||||||
self.stopTimer2.cancel()
|
self.stopTimer2.cancel()
|
||||||
self.stopTimer2 = None
|
self.stopTimer2 = None
|
||||||
if (self.lastState == state):
|
if (self.lastState == state):
|
||||||
printExtraLog("Nothing changed, ignoring", "yellow")
|
self.log("Nothing changed, ignoring", "yellow", extra = True)
|
||||||
self.stopTimer2 = threading.Timer(self.stopTimer2Interval, self.stopOnNoUpdate)
|
self.stopTimer2 = threading.Timer(self.stopTimer2Interval, self.stopOnNoUpdate)
|
||||||
self.stopTimer2.start()
|
self.stopTimer2.start()
|
||||||
return
|
return
|
||||||
|
@ -144,38 +181,38 @@ class discordRichPresencePlex(discordRichPresence):
|
||||||
self.lastState, self.lastSessionKey, self.lastRatingKey = None, None, None
|
self.lastState, self.lastSessionKey, self.lastRatingKey = None, None, None
|
||||||
self.stopTimer = threading.Timer(self.stopTimerInterval, self.stop)
|
self.stopTimer = threading.Timer(self.stopTimerInterval, self.stop)
|
||||||
self.stopTimer.start()
|
self.stopTimer.start()
|
||||||
printExtraLog("Started stopTimer", "green")
|
self.log("Started stopTimer", "yellow", True)
|
||||||
return
|
return
|
||||||
elif (state == "stopped"):
|
elif (state == "stopped"):
|
||||||
printExtraLog("\"stopped\" state update from unknown session key, ignoring", "yellow")
|
self.log("\"stopped\" state update from unknown session key, ignoring", "yellow", True)
|
||||||
return
|
return
|
||||||
printExtraLog("Checking Sessions for Session Key " + colourText(sessionKey, "yellow") + ":")
|
self.log("Checking Sessions for Session Key " + colourText(sessionKey, "yellow"), extra = True)
|
||||||
plexServerSessions = self.plexServer.sessions()
|
plexServerSessions = self.plexServer.sessions()
|
||||||
if (len(plexServerSessions) < 1):
|
if (len(plexServerSessions) < 1):
|
||||||
printExtraLog("Empty session list, ignoring", "red")
|
self.log("Empty session list, ignoring", "red", True)
|
||||||
return
|
return
|
||||||
for session in plexServerSessions:
|
for session in plexServerSessions:
|
||||||
printExtraLog(str(session) + ", Session Key: " + colourText(session.sessionKey, "yellow") + ", Users: " + colourText(session.usernames, "yellow").replace("'", "\""))
|
self.log(str(session) + ", Session Key: " + colourText(session.sessionKey, "yellow") + ", Users: " + colourText(session.usernames, "yellow").replace("'", "\""), extra = True)
|
||||||
sessionFound = False
|
sessionFound = False
|
||||||
if (session.sessionKey == sessionKey):
|
if (session.sessionKey == sessionKey):
|
||||||
sessionFound = True
|
sessionFound = True
|
||||||
printExtraLog("Session found", "green")
|
self.log("Session found", "green", True)
|
||||||
if (session.usernames[0].lower() == self.listenForUser.lower()):
|
if (session.usernames[0].lower() == self.plexConfig.listenForUser):
|
||||||
printExtraLog("Username \"" + session.usernames[0].lower() + "\" matches \"" + self.listenForUser.lower() + "\", continuing", "green")
|
self.log("Username \"" + session.usernames[0].lower() + "\" matches \"" + self.plexConfig.listenForUser + "\", continuing", "green", True)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
printExtraLog("Username \"" + session.usernames[0].lower() + "\" doesn't match \"" + self.listenForUser.lower() + "\", ignoring", "red")
|
self.log("Username \"" + session.usernames[0].lower() + "\" doesn't match \"" + self.plexConfig.listenForUser + "\", ignoring", "red", True)
|
||||||
return
|
return
|
||||||
if (not sessionFound):
|
if (not sessionFound):
|
||||||
printExtraLog("No matching session found", "red")
|
self.log("No matching session found", "red", True)
|
||||||
return
|
return
|
||||||
|
if (self.stopTimer):
|
||||||
|
self.stopTimer.cancel()
|
||||||
|
self.stopTimer = None
|
||||||
if (self.stopTimer2):
|
if (self.stopTimer2):
|
||||||
self.stopTimer2.cancel()
|
self.stopTimer2.cancel()
|
||||||
self.stopTimer2 = threading.Timer(self.stopTimer2Interval, self.stopOnNoUpdate)
|
self.stopTimer2 = threading.Timer(self.stopTimer2Interval, self.stopOnNoUpdate)
|
||||||
self.stopTimer2.start()
|
self.stopTimer2.start()
|
||||||
if (self.stopTimer):
|
|
||||||
self.stopTimer.cancel()
|
|
||||||
self.stopTimer = None
|
|
||||||
self.lastState, self.lastSessionKey, self.lastRatingKey = state, sessionKey, ratingKey
|
self.lastState, self.lastSessionKey, self.lastRatingKey = state, sessionKey, ratingKey
|
||||||
metadata = self.plexServer.fetchItem(ratingKey)
|
metadata = self.plexServer.fetchItem(ratingKey)
|
||||||
mediaType = metadata.type
|
mediaType = metadata.type
|
||||||
|
@ -199,7 +236,7 @@ class discordRichPresencePlex(discordRichPresence):
|
||||||
extra = artist + " · " + metadata.parentTitle
|
extra = artist + " · " + metadata.parentTitle
|
||||||
largeText = "Listening to Music"
|
largeText = "Listening to Music"
|
||||||
else:
|
else:
|
||||||
printExtraLog("Unsupported media type \"" + mediaType + "\", ignoring", "red")
|
self.log("Unsupported media type \"" + mediaType + "\", ignoring", "red", True)
|
||||||
return
|
return
|
||||||
activity = {
|
activity = {
|
||||||
"details": title,
|
"details": title,
|
||||||
|
@ -221,16 +258,23 @@ class discordRichPresencePlex(discordRichPresence):
|
||||||
self.start()
|
self.start()
|
||||||
self.send(activity)
|
self.send(activity)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error: " + str(e))
|
self.log("Error: " + str(e))
|
||||||
if (self.process):
|
if (self.process):
|
||||||
self.process.kill()
|
self.process.kill()
|
||||||
|
|
||||||
def stopOnNoUpdate(self):
|
def stopOnNoUpdate(self):
|
||||||
printExtraLog("\nNo updates from session key " + str(self.lastSessionKey) + ", stopping", "red")
|
self.log("No updates from session key " + str(self.lastSessionKey) + ", stopping", "red", True)
|
||||||
self.lastState, self.lastSessionKey, self.lastRatingKey = None, None, None
|
self.lastState, self.lastSessionKey, self.lastRatingKey = None, None, None
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
isLinux = sys.platform in ["linux", "darwin"]
|
isLinux = sys.platform in ["linux", "darwin"]
|
||||||
|
lock = threading.Semaphore(value = 1)
|
||||||
|
|
||||||
|
os.system("clear" if isLinux else "cls")
|
||||||
|
|
||||||
|
if (len(plexConfigs) == 0):
|
||||||
|
print("Error: plexConfigs list is empty")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
colours = {
|
colours = {
|
||||||
"red": "91",
|
"red": "91",
|
||||||
|
@ -250,25 +294,19 @@ def colourText(text, colour = ""):
|
||||||
suffix = "\033[0m"
|
suffix = "\033[0m"
|
||||||
return prefix + str(text) + suffix
|
return prefix + str(text) + suffix
|
||||||
|
|
||||||
def colourPrint(text, colour = ""):
|
discordRichPresencePlexInstances = []
|
||||||
if (colour):
|
for config in plexConfigs:
|
||||||
print(colourText(text, colour))
|
discordRichPresencePlexInstances.append(discordRichPresencePlex(config))
|
||||||
else:
|
|
||||||
print(text)
|
|
||||||
|
|
||||||
def printExtraLog(text, colour = ""):
|
|
||||||
if (plexConfig.extraLogging):
|
|
||||||
colourPrint(text, colour)
|
|
||||||
|
|
||||||
os.system("clear" if isLinux else "cls")
|
|
||||||
|
|
||||||
discordRichPresencePlexInstance = discordRichPresencePlex()
|
|
||||||
try:
|
try:
|
||||||
|
for discordRichPresencePlexInstance in discordRichPresencePlexInstances:
|
||||||
discordRichPresencePlexInstance.run()
|
discordRichPresencePlexInstance.run()
|
||||||
while True:
|
while True:
|
||||||
time.sleep(60)
|
time.sleep(3600)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
for discordRichPresencePlexInstance in discordRichPresencePlexInstances:
|
||||||
if (discordRichPresencePlexInstance.running):
|
if (discordRichPresencePlexInstance.running):
|
||||||
discordRichPresencePlexInstance.stop()
|
discordRichPresencePlexInstance.stop()
|
||||||
|
if (discordRichPresencePlexInstance.checkConnectionTimer):
|
||||||
|
discordRichPresencePlexInstance.checkConnectionTimer.cancel()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error: " + str(e))
|
print("Error: " + str(e))
|
||||||
|
|
Loading…
Reference in a new issue