// ==UserScript==
// @name TheMovieDB Advanced Scripts Client
// @namespace https://github.com/Tetrax-10
// @version 1.0
// @description Communicates with TheMovieDB Advanced Scripts server
// @author Tetrax-10
// @icon https://www.themoviedb.org/favicon.ico
// @match *://*.themoviedb.org/*
// @run-at document-start
// @grant none
// ==/UserScript==
;(() => {
/*
* WebSocket URL for the local server.
* Change this if your server is running on a different host or port.
*/
const WS_URL = "ws://localhost:8765"
// Client's version
const CLIENT_VERSION = 1
// Create a namespace, which will be exposed to window object after version checking
const TmdbAdvScp = {
toast: null,
version: CLIENT_VERSION,
socketState: false,
socket: null,
}
// Track whether the WebSocket is currently open
TmdbAdvScp.socketState = false
// Check if the browser supports WebSocket
if (!("WebSocket" in window)) {
console.error("❌ [Client] WebSocket is not supported by this browser. TheMovieDB Advanced Scripts Client will not work.")
return
}
// Initialize the WebSocket connection
try {
TmdbAdvScp.socket = new WebSocket(WS_URL)
} catch (e) {
console.error(`❌ [Client] Failed to create WebSocket connection to ${WS_URL}:`, e)
return
}
/*
* Helper function to show a toast message if the toast handler is defined.
* @param {string} message - The text to display in the toast.
*/
async function toast(message) {
while (!document.getElementById("toast-container")) {
await new Promise((resolve) => setTimeout(resolve, 100))
}
// No matter the version checking result, make toast function available
TmdbAdvScp.toast = TmdbAdvScp.toast || window.TmdbAdvScp?.toast
try {
if (typeof TmdbAdvScp.toast === "function") {
TmdbAdvScp.toast(message)
}
} catch (e) {
console.error("❌ [Client] Error calling toast function:", e)
}
}
/*
* Handle any errors that occur during the connection phase.
* This will be called if the connection cannot be made.
*/
TmdbAdvScp.socket.onerror = (error) => {
console.error("❌ [Client] Can't connect to server. Please restart server and refresh", error)
}
/*
* When the connection is successfully opened, update the state.
*/
TmdbAdvScp.socket.addEventListener("open", () => {
TmdbAdvScp.socketState = true
})
/*
* When the connection closes, warn the user if it was open before.
* Reset the state so we don't display multiple warnings.
*/
TmdbAdvScp.socket.addEventListener("close", () => {
if (TmdbAdvScp.socketState) {
console.warn("⚠️ [Client] Lost connection to server! Please restart server and refresh")
TmdbAdvScp.socketState = false
}
})
/*
* Listen for incoming messages from the server.
* Messages can be JSON with an "action" field (e.g., "toast") or raw strings like "reload" or "connected".
*/
TmdbAdvScp.socket.addEventListener("message", (event) => {
const rawData = event.data
try {
// Attempt to parse the incoming data as JSON
const response = JSON.parse(rawData)
// Handle known JSON actions
if (response.action === "toast") {
// Display a toast message in the page if a toast function is provided
try {
toast(response.data)
} catch (e) {
console.error("❌ [Client] Error executing toast function:", e)
}
} else if (response.action === "version_result") {
// Version checking
if (CLIENT_VERSION === parseInt(response.data)) {
// Expose the TmdbAdvScp namespace to the window object
window.TmdbAdvScp = window.TmdbAdvScp || {}
window.TmdbAdvScp = { ...TmdbAdvScp, ...window.TmdbAdvScp } // don't change the merging order
} else if (CLIENT_VERSION > parseInt(response.data)) {
toast(
"⚠️ Server update available. Click me to download update."
)
} else if (CLIENT_VERSION < parseInt(response.data)) {
toast(
"⚠️ Userscripts update available. Click me to open the update guide."
)
} else {
toast(
"❌ Couldn't determine the server's version. Please open an issue on GitHub."
)
}
} else {
// If the action is unrecognized, log a warning
console.warn("⚠️ [Client] Unrecognized action received:", response.action)
}
} catch (e) {
// If parsing fails, handle raw string commands
switch (rawData) {
case "reload":
// Instruct the client to reload the page
window.location.reload()
break
case "connected":
// Initial handshake from server
console.log("✅ [Client] Connected to server")
// Request server to send its version
TmdbAdvScp.socket.send("version_request")
break
default:
// Log unexpected non-JSON messages
console.warn("⚠️ [Client] Received unexpected message:", rawData)
}
}
})
})()