diff --git a/plugins/discordPresence/README.md b/plugins/discordPresence/README.md
deleted file mode 100644
index 687cc9d6..00000000
--- a/plugins/discordPresence/README.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Discord Presence
-
-https://discourse.stashapp.cc/t/discord-presence/1374
-
-A plugin which shows the metadata of the currently playing Stash scene as your Discord presence
-
-## Setup
-### Prerequisites to get the plugin working
-- Download and run [Discord Presence Server](https://github.com/NotForMyCV/discord-presence-server/releases). You **do not** need any browser extensions.
-- Ensure you have CommunityScriptsUILibrary installed in your Stash plugins, if it isn't automatically installed
-
-#### Why the desktop app?
-
-This plugin relies on a separate desktop app (Discord RPC Server) running in the background. This is required because only a local app can talk to your Discord client to set a custom presence. The ability to do so from a website/browser is whitelisted by Discord (otherwise any website you visit could change your Discord client presence). Discord RPC Server is an open source application which exposes a websocket connection, so that other browser scripts and extensions (i.e. this plugin) can send presence updates to it, which it then forwards to your Discord client.
-
-
-## Configuration
-You can customize almost any part of the activity presence with the plugin options.
-
-| Presence element | Plugin setting name | Default value (if empty; reverts to: ) | Configuration |
-|-----------------------|-------------------------------|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Activity name | Custom Discord application ID | `1236860180407521341` (displays "Stash") | Create a new application under your [Discord developer portal](https://discord.com/developers/applications). The name of the application will be the name of the activity being shown as "Playing". Copy the `APPLICATION ID` (20 digit number) from the Developer Portal and set it in the plugin options. |
-| Details (first line) | Presence details text | `{title}` | Custom text and variables |
-| State (second line) | Presence state text | `from {studio_name}` | Custom text and variables |
-| Show activity image | Show presence image | Off | Toggle switch |
-| Custom activity image | Custom presence image key | `stashbox` | After creating a Discord app (see first config option) go to your application settings > Rich Presence > Art Assets. Upload your custom image, give it a key name, and put this in the plugin option (takes a short while for the asset to appear after uploading). |
-| Activity hover text | Custom image text | Empty | Custom text and variables |
-| Show URL button | Show scene URL button | Off | Toggle switch |
-| Custom button text | Custom button text | `Watch` | Custom text and variables |
-
-## String variables
-You can insert metadata from the currently playing scene into configurable elements, by enclosing variables in curly braces.
-For example, if you were watching a scene called "Kittens" and wanted to display "Watching Kittens" under the presence details, you would set the config option to `Watching {title}`.
-Below are a list of available variable names:
-- `{id}`
-- `{title}`
-- `{code}`
-- `{details}`
-- `{director}`
-- `{date}`
-- `{rating100}`
-- `{o_counter}`
-- `{organized}`
-- `{interactive}`
-- `{interactive_speed}`
-- `{created_at}`
-- `{updated_at}`
-- `{resume_time}`
-- `{last_played_at}`
-- `{play_duration}`
-- `{play_count}`
-- `{url}`
-- `{studio_name}`
-- `{file_duration}`
-- `{performers}`
diff --git a/plugins/discordPresence/discordPresence.js b/plugins/discordPresence/discordPresence.js
deleted file mode 100644
index 07073448..00000000
--- a/plugins/discordPresence/discordPresence.js
+++ /dev/null
@@ -1,287 +0,0 @@
-(async function () {
- /**
- * @typedef {{
- * discordClientId?: string;
- * discordDetailsText?: string;
- * discordStateText?: string;
- * discordShowImage?: boolean;
- * discordLargeImageKey?: string;
- * discordLargeImageText?: string;
- * discordShowUrlButton?: boolean;
- * discordUrlButtonText?: string;
- * }} PluginConfig
- */
-
- /**
- * @typedef {{
- * id, title, code, details, director, urls?: string[], date, rating100, o_counter,
- * organized, interactive, interactive_speed, created_at, updated_at, resume_time,
- * last_played_at, play_duration, play_count, files: {duration:number}[],
- * studio?: {id, name}, performers: {name, gender}[]
- * }} SceneData
- */
-
- /**
- * @typedef {{ studio_name: string, url: string, file_duration: string, performers: string }
- * & Omit
- * } FlattenedSceneData
- */
-
- const SCENE_GQL_QUERY = `
- query FindScene($id: ID!) {
- findScene(id: $id) {
- ...SceneData
- }
- }
-
- fragment SceneData on Scene {
- id
- title
- code
- details
- director
- urls
- date
- rating100
- o_counter
- organized
- interactive
- interactive_speed
- created_at
- updated_at
- resume_time
- last_played_at
- play_duration
- play_count
- files { duration }
- studio { name }
- performers { name, gender }
- }
- `;
-
- const PLUGIN_ID = "discordPresence";
-
- const userConfig = await csLib.getConfiguration(PLUGIN_ID, {});
- console.debug("Discord Presence Plugin: user config", userConfig);
-
- /** @type {Required} */
- const CONFIG = {
- // DEFAULTS
- discordClientId: "1236860180407521341",
- discordDetailsText: "{title}",
- discordStateText: "from {studio_name}",
- discordShowImage: false,
- discordLargeImageKey: "stashbox",
- discordLargeImageText: "Stashapp",
- discordShowUrlButton: false,
- discordUrlButtonText: "Watch",
- ...userConfig,
- };
-
- console.debug("Discord Presence Plugin: loaded config", CONFIG);
-
- function throttle(mainFunction, delay) {
- let timerFlag = null;
-
- return (...args) => {
- if (timerFlag === null) {
- mainFunction(...args);
- timerFlag = setTimeout(() => {
- timerFlag = null;
- }, delay);
- }
- };
- }
-
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
- const player = () => document.querySelector("#VideoJsPlayer video");
-
- let WAITING_FOR_REFRESH = true;
- let SCENE_ID = null;
- /** @type {FlattenedSceneData?} */ let cachedSceneData;
-
- /** @type {WebSocket} */ let ws;
- const wsAlive = () => ws && ws.readyState === 1;
-
- // Start ws connection to RPC server and add video listener
- // Will retry on disconnection/error after 10s
- async function start() {
- if (ws && ws.readyState <= 1) {
- return;
- }
-
- // https://github.com/NotForMyCV/discord-presence-server/releases
- ws = new WebSocket("ws://localhost:6969");
-
- ws.addEventListener("open", () => {
- csLib.PathElementListener("/scenes/", "video", videoListener);
- });
-
- window.addEventListener("beforeunload", () => {
- clearDiscordActivity();
- });
-
- // If failed during video playback, remove the listeners
- ws.addEventListener("close", async () => {
- if (player()) {
- unbindVideoListener(player());
- }
-
- await sleep(10000);
- start();
- });
-
- ws.addEventListener("error", async () => {
- if (player()) {
- unbindVideoListener(player());
- }
-
- console.error(
- `Discord Presence Plugin: Could not connect to Discord Rich Presence Server.
- Consult the README on how to setup the Rich Presence Server:
- https://github.com/stashapp/CommunityScripts/tree/main/plugins/discordPresence`
- );
- await sleep(10000);
- start();
- });
- }
-
- start();
-
- /** @return {Promise} */
- async function getSceneData(sceneId) {
- if (!sceneId) return null;
-
- if (Number(sceneId).toString() === Number(cachedSceneData?.id).toString()) {
- return cachedSceneData;
- }
-
- const reqData = {
- variables: { id: sceneId },
- query: SCENE_GQL_QUERY,
- };
-
- /** @type {SceneData} */
- const sceneData = await csLib
- .callGQL(reqData)
- .then((data) => data.findScene);
-
- if (!sceneData) return null;
-
- const newProps = {
- studio_name: sceneData.studio?.name ?? "Unknown Studio",
- url: sceneData.urls?.length ? sceneData.urls[0] : "",
- file_duration: sceneData.files?.length ? sceneData.files[0].duration : 0,
- performers: sceneData.performers.length
- ? sceneData.performers.map((performer) => performer.name).join(", ")
- : "Unlisted Performer(s)",
- };
-
- delete sceneData.urls;
- delete sceneData.studio;
- delete sceneData.files;
- delete sceneData.performers;
-
- cachedSceneData = { ...sceneData, ...newProps };
- return cachedSceneData;
- }
-
- const clearDiscordActivity = () => {
- if (!!SCENE_ID === false || !wsAlive()) {
- return;
- }
-
- SCENE_ID = null;
- ws.send(
- JSON.stringify({
- clientId: CONFIG.discordClientId,
- clearActivity: true,
- })
- );
- };
-
- const setDiscordActivity = throttle(async (event) => {
- if (event?.type === "timeupdate") {
- if (!WAITING_FOR_REFRESH) {
- return;
- }
-
- WAITING_FOR_REFRESH = false;
- setTimeout(() => (WAITING_FOR_REFRESH = true), 5000);
- }
-
- const sceneData = await getSceneData(SCENE_ID);
- if (!sceneData) return;
-
- const currentTime = player()?.currentTime ?? 0;
- const endTimestamp =
- Date.now() + (sceneData.file_duration - currentTime) * 1000;
-
- let body = {
- details: replaceVars(CONFIG.discordDetailsText, sceneData),
- state: replaceVars(CONFIG.discordStateText, sceneData),
- largeImageKey: CONFIG.discordShowImage
- ? CONFIG.discordLargeImageKey
- : undefined,
- largeImageText: replaceVars(CONFIG.discordLargeImageText, sceneData),
- endTimestamp: sceneData.file_duration > 0 ? endTimestamp : undefined,
- buttons:
- CONFIG.discordShowUrlButton && URL.canParse(sceneData.url)
- ? [
- {
- label: replaceVars(CONFIG.discordUrlButtonText, sceneData),
- url: sceneData.url,
- },
- ]
- : undefined,
- instance: true,
- };
-
- if (!wsAlive()) {
- return;
- }
-
- ws.send(
- JSON.stringify({
- clientId: CONFIG.discordClientId,
- presence: body,
- })
- );
- }, 1000);
-
- /**
- * Performs string replacement on templated config vars with scene data
- * @param {string} templateStr
- * @param {FlattenedSceneData} sceneData
- */
- function replaceVars(templateStr, sceneData) {
- const pattern = /{\s*(\w+?)\s*}/g;
-
- const replacedStr = templateStr
- .replace(pattern, (_, token) => sceneData[token] ?? "")
- .trim();
-
- if (replacedStr.length <= 128) {
- return replacedStr;
- }
-
- return replacedStr.substring(0, 125) + "...";
- }
-
- const videoListener = (video) => {
- SCENE_ID = parseInt(location.pathname.split("/")[2]);
- video.addEventListener("playing", setDiscordActivity);
- video.addEventListener("play", setDiscordActivity);
- video.addEventListener("timeupdate", setDiscordActivity);
- video.addEventListener("seeked", setDiscordActivity);
- video.addEventListener("ended", clearDiscordActivity);
- };
-
- const unbindVideoListener = (video) => {
- video.removeEventListener("playing", setDiscordActivity);
- video.removeEventListener("play", setDiscordActivity);
- video.removeEventListener("timeupdate", setDiscordActivity);
- video.removeEventListener("seeked", setDiscordActivity);
- video.removeEventListener("ended", clearDiscordActivity);
- };
-})();
diff --git a/plugins/discordPresence/discordPresence.yml b/plugins/discordPresence/discordPresence.yml
deleted file mode 100644
index 65cad628..00000000
--- a/plugins/discordPresence/discordPresence.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-name: Discord Presence
-description: Sets currently playing scene data as your Discord status. See README for prerequisites and config options (blue hyperlink next to enable/disable button)
-url: https://github.com/stashapp/CommunityScripts/tree/main/plugins/discordPresence
-# requires: CommunityScriptsUILibrary
-version: 1.3
-settings:
- discordClientId:
- displayName: Custom Discord application ID
- description: Set a custom client ID
- type: STRING
- discordDetailsText:
- displayName: Presence details text
- description: Format the first line of your presence text
- type: STRING
- discordStateText:
- displayName: Presence state text
- description: Format the second line of your presence text
- type: STRING
- discordShowImage:
- displayName: Show presence image
- description: Show the large presence activity image
- type: BOOLEAN
- discordLargeImageKey:
- displayName: Custom presence image key
- description: Set a presence image key (requires custom application ID and art asset, see README)
- type: STRING
- discordLargeImageText:
- displayName: Custom image text
- description: Format the hover text for the activity image
- type: STRING
- discordShowUrlButton:
- displayName: Show scene URL button
- description: Show a presence button which links to the first scene URL
- type: BOOLEAN
- discordUrlButtonText:
- displayName: Custom button text
- description: Format the text for the presence button
- type: STRING
-ui:
- requires:
- - CommunityScriptsUILibrary
- javascript:
- - discordPresence.js
- csp:
- connect-src:
- - ws://localhost:6969