Added support for buttons in Rich Presence

This commit is contained in:
Phin 2022-08-26 02:07:50 +05:30
parent f6ca9fc7e7
commit 510db5f0dc
6 changed files with 73 additions and 6 deletions

View file

@ -4,7 +4,7 @@
A Python script that displays your [Plex](https://www.plex.tv) status on [Discord](https://discord.com) using [Rich Presence](https://discord.com/developers/docs/rich-presence/how-to). A Python script that displays your [Plex](https://www.plex.tv) status on [Discord](https://discord.com) using [Rich Presence](https://discord.com/developers/docs/rich-presence/how-to).
Current Version: 2.2.5 Current Version: 2.3.0
## Getting Started ## Getting Started
@ -30,9 +30,12 @@ The script must be running on the same machine as your Discord client.
* `useRemainingTime` (boolean, default: `false`) - Displays your media's remaining time instead of elapsed time in your Rich Presence if enabled. * `useRemainingTime` (boolean, default: `false`) - Displays your media's remaining time instead of elapsed time in your Rich Presence if enabled.
* `posters` * `posters`
* `enabled` (boolean, default: `false`) - Displays media posters in Rich Presence if enabled. Requires `imgurClientID`. * `enabled` (boolean, default: `false`) - Displays media posters in Rich Presence if enabled. Requires `imgurClientID`.
* `imgurClientID` (string, default: `""`) - [Instructions](#obtaining-an-imgur-client-id) * `imgurClientID` (string, default: `""`) - [Obtention Instructions](#obtaining-an-imgur-client-id)
* `buttons` (list) - [Information](#buttons)
* `label` (string) - The label to be displayed on the button.
* `url` (string) - A web address or a [dynamic URL placeholder](#dynamic-button-urls).
* `users` (list) * `users` (list)
* `token` (string) - An access token associated with your Plex account. ([X-Plex-Token](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token), [Authenticating with Plex](https://forums.plex.tv/t/authenticating-with-plex/609370)) * `token` (string) - An access token associated with your Plex account. ([X-Plex-Token](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/), [Authenticating with Plex](https://forums.plex.tv/t/authenticating-with-plex/609370))
* `servers` (list) * `servers` (list)
* `name` (string) - Name of the Plex Media Server you wish to connect to. * `name` (string) - Name of the Plex Media Server you wish to connect to.
* `listenForUser` (string, optional) - The script will respond to alerts originating only from this username. Defaults to the parent user's username if not set. * `listenForUser` (string, optional) - The script will respond to alerts originating only from this username. Defaults to the parent user's username if not set.
@ -45,6 +48,18 @@ The script must be running on the same machine as your Discord client.
2. Enter any name for the application and pick OAuth2 without a callback URL as the authorisation type 2. Enter any name for the application and pick OAuth2 without a callback URL as the authorisation type
3. Submit the form to obtain your application's client ID 3. Submit the form to obtain your application's client ID
### Buttons
A maximum of 2 buttons can be displayed in your Rich Presence.
Due to a strange Discord bug, buttons displayed in your Rich Presence are unresponsive to your own clicks, but other users are able to click on them to open their corresponding URLs.
#### Dynamic Button URLs
During runtime, the following dynamic URL placeholders will get replaced with real URLs based on the media being played:
* `dynamic:imdb`
* `dynamic:tmdb`
### Example ### Example
```json ```json
@ -58,7 +73,17 @@ The script must be running on the same machine as your Discord client.
"posters": { "posters": {
"enabled": true, "enabled": true,
"imgurClientID": "9e9sf637S8bRp4z" "imgurClientID": "9e9sf637S8bRp4z"
},
"buttons": [
{
"label": "IMDb Link",
"url": "dynamic:imdb"
},
{
"label": "My YouTube Channel",
"url": "https://www.youtube.com/"
} }
]
}, },
"users": [ "users": [
{ {

View file

@ -8,9 +8,14 @@ class Posters(TypedDict):
enabled: bool enabled: bool
imgurClientID: str imgurClientID: str
class Button(TypedDict):
label: str
url: str
class Display(TypedDict): class Display(TypedDict):
useRemainingTime: bool useRemainingTime: bool
posters: Posters posters: Posters
buttons: list[Button]
class Server(TypedDict, total = False): class Server(TypedDict, total = False):
name: str name: str

View file

@ -10,8 +10,13 @@ class ActivityTimestamps(TypedDict, total = False):
start: int start: int
end: int end: int
class ActivityButton(TypedDict):
label: str
url: str
class Activity(TypedDict, total = False): class Activity(TypedDict, total = False):
details: str details: str
state: str state: str
assets: ActivityAssets assets: ActivityAssets
timestamps: ActivityTimestamps timestamps: ActivityTimestamps
buttons: list[ActivityButton]

View file

@ -6,6 +6,7 @@ from .config import config
from .imgur import uploadImage from .imgur import uploadImage
from plexapi.alert import AlertListener from plexapi.alert import AlertListener
from plexapi.base import Playable, PlexPartialObject from plexapi.base import Playable, PlexPartialObject
from plexapi.media import Genre, GuidTag
from plexapi.myplex import MyPlexAccount, PlexServer from plexapi.myplex import MyPlexAccount, PlexServer
from typing import Optional from typing import Optional
from utils.logging import LoggerWithPrefix from utils.logging import LoggerWithPrefix
@ -183,7 +184,8 @@ class PlexAlertListener(threading.Thread):
stateStrings: list[str] = [formatSeconds(item.duration / 1000)] stateStrings: list[str] = [formatSeconds(item.duration / 1000)]
if mediaType == "movie": if mediaType == "movie":
title = f"{item.title} ({item.year})" title = f"{item.title} ({item.year})"
stateStrings.append(f"{', '.join(genre.tag for genre in item.genres[:3])}") genres: list[Genre] = item.genres[:3]
stateStrings.append(f"{', '.join(genre.tag for genre in genres)}")
largeText = "Watching a movie" largeText = "Watching a movie"
thumb = item.thumb thumb = item.thumb
else: else:
@ -220,6 +222,35 @@ class PlexAlertListener(threading.Thread):
"small_image": state, "small_image": state,
}, },
} }
if config["display"]["buttons"]:
guidTags: list[GuidTag] = []
if mediaType == "movie":
guidTags = item.guids
elif mediaType == "episode":
guidTags = self.server.fetchItem(item.grandparentRatingKey).guids
guids: list[str] = [guid.id for guid in guidTags]
buttons: list[models.discord.ActivityButton] = []
for button in config["display"]["buttons"]:
if button["url"].startswith("dynamic:"):
if guids:
newUrl = button["url"]
if button["url"] == "dynamic:imdb":
for guid in guids:
if guid.startswith("imdb://"):
newUrl = guid.replace("imdb://", "https://www.imdb.com/title/")
break
elif button["url"] == "dynamic:tmdb":
for guid in guids:
if guid.startswith("tmdb://"):
tmdbPathSegment = "movie" if mediaType == "movie" else "tv"
newUrl = guid.replace("tmdb://", f"https://www.themoviedb.org/{tmdbPathSegment}/")
break
if newUrl:
buttons.append({ "label": button["label"], "url": newUrl })
else:
buttons.append(button)
if buttons:
activity["buttons"] = buttons[:2]
if state == "playing": if state == "playing":
currentTimestamp = int(time.time()) currentTimestamp = int(time.time())
if config["display"]["useRemainingTime"]: if config["display"]["useRemainingTime"]:

View file

@ -17,6 +17,7 @@ config: models.config.Config = {
"enabled": False, "enabled": False,
"imgurClientID": "", "imgurClientID": "",
}, },
"buttons": [],
}, },
"users": [], "users": [],
} }

View file

@ -2,7 +2,7 @@ import os
import sys import sys
name = "Discord Rich Presence for Plex" name = "Discord Rich Presence for Plex"
version = "2.2.5" version = "2.3.0"
plexClientID = "discord-rich-presence-plex" plexClientID = "discord-rich-presence-plex"
discordClientID = "413407336082833418" discordClientID = "413407336082833418"