mirror of
https://github.com/Stremio/stremio-shell-ng.git
synced 2026-01-11 22:40:32 +00:00
feat: add discord rich presence
This commit is contained in:
parent
b8a0345b0d
commit
bb49a88355
6 changed files with 230 additions and 0 deletions
36
Cargo.lock
generated
36
Cargo.lock
generated
|
|
@ -406,6 +406,21 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "discord-rich-presence"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ead3c5edc7e048c317c6fc4a7e24aff0c7e4c136918e2ba38106a385b2cc53a5"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.34"
|
||||
|
|
@ -1513,6 +1528,17 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_test"
|
||||
version = "1.0.176"
|
||||
|
|
@ -1604,6 +1630,7 @@ dependencies = [
|
|||
"bitflags 2.4.2",
|
||||
"chrono",
|
||||
"clap",
|
||||
"discord-rich-presence",
|
||||
"flume",
|
||||
"libmpv2",
|
||||
"libmpv2-sys",
|
||||
|
|
@ -1964,6 +1991,15 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ version = "5.0.15"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
discord-rich-presence = "1"
|
||||
once_cell = "1.19"
|
||||
native-windows-gui = { git = "https://github.com/Stremio/native-windows-gui", features = [
|
||||
"high-dpi",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use crate::stremio_app::{
|
|||
PipeServer,
|
||||
};
|
||||
|
||||
use super::discord::DiscordRpc;
|
||||
use super::stremio_server::StremioServer;
|
||||
|
||||
#[derive(Default, NwgUi)]
|
||||
|
|
@ -247,6 +248,10 @@ impl MainWindow {
|
|||
let hide_splash_sender = self.hide_splash_notice.sender();
|
||||
let focus_sender = self.focus_notice.sender();
|
||||
let autoupdater_setup_mutex = self.autoupdater_setup_file.clone();
|
||||
|
||||
let discord_rpc: Arc<Mutex<Option<DiscordRpc>>> = Arc::new(Mutex::new(None));
|
||||
let discord_rpc_clone = discord_rpc.clone();
|
||||
|
||||
thread::spawn(move || loop {
|
||||
if let Some(msg) = web_rx
|
||||
.recv()
|
||||
|
|
@ -336,6 +341,58 @@ impl MainWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
Some("discord-connect") => {
|
||||
let mut discord_guard = discord_rpc_clone.lock().unwrap();
|
||||
if discord_guard.is_none() {
|
||||
let discord = DiscordRpc::new();
|
||||
match discord.connect() {
|
||||
Ok(()) => {
|
||||
*discord_guard = Some(discord);
|
||||
web_tx_web.send(RPCResponse::discord_status(true)).ok();
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Discord connect error: {}", e);
|
||||
web_tx_web.send(RPCResponse::discord_status(false)).ok();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Already connected
|
||||
web_tx_web.send(RPCResponse::discord_status(true)).ok();
|
||||
}
|
||||
}
|
||||
Some("discord-disconnect") => {
|
||||
let mut discord_guard = discord_rpc_clone.lock().unwrap();
|
||||
if let Some(ref discord) = *discord_guard {
|
||||
if let Err(e) = discord.disconnect() {
|
||||
eprintln!("Discord disconnect error: {}", e);
|
||||
}
|
||||
}
|
||||
*discord_guard = None;
|
||||
web_tx_web.send(RPCResponse::discord_status(false)).ok();
|
||||
}
|
||||
Some("discord-set-activity") => {
|
||||
if let Some(params) = msg.get_params() {
|
||||
let state = params.get("state").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let details = params.get("details").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let image = params.get("image").and_then(|v| v.as_str());
|
||||
let start_timestamp = params.get("startTimestamp").and_then(|v| v.as_i64());
|
||||
|
||||
let discord_guard = discord_rpc_clone.lock().unwrap();
|
||||
if let Some(ref discord) = *discord_guard {
|
||||
if let Err(e) = discord.set_activity(state, details, image, start_timestamp) {
|
||||
eprintln!("Discord set activity error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some("discord-clear-activity") => {
|
||||
let discord_guard = discord_rpc_clone.lock().unwrap();
|
||||
if let Some(ref discord) = *discord_guard {
|
||||
if let Err(e) = discord.clear_activity() {
|
||||
eprintln!("Discord clear activity error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(player_command) if player_command.starts_with("mpv-") => {
|
||||
let resp_json = serde_json::to_string(
|
||||
&msg.args.expect("Cannot have method without args"),
|
||||
|
|
|
|||
130
src/stremio_app/discord.rs
Normal file
130
src/stremio_app/discord.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use discord_rich_presence::{activity, DiscordIpc, DiscordIpcClient};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
const DISCORD_APP_ID: &str = "1452620752263319665";
|
||||
|
||||
/// Discord Rich Presence for Stremio
|
||||
/// Handles connection to Discord and activity updates
|
||||
pub struct DiscordRpc {
|
||||
client: Arc<Mutex<Option<DiscordIpcClient>>>,
|
||||
}
|
||||
|
||||
impl Default for DiscordRpc {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DiscordRpc {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
client: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(&self) -> Result<(), String> {
|
||||
let mut client_guard = self.client.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
if client_guard.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut client = DiscordIpcClient::new(DISCORD_APP_ID)
|
||||
.map_err(|e| format!("Failed to create Discord client: {}", e))?;
|
||||
|
||||
client
|
||||
.connect()
|
||||
.map_err(|e| format!("Failed to connect to Discord: {}", e))?;
|
||||
|
||||
*client_guard = Some(client);
|
||||
|
||||
println!("Discord RPC connected");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disconnect(&self) -> Result<(), String> {
|
||||
let mut client_guard = self.client.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(mut client) = client_guard.take() {
|
||||
client
|
||||
.close()
|
||||
.map_err(|e| format!("Failed to close Discord connection: {}", e))?;
|
||||
}
|
||||
|
||||
println!("Discord RPC disconnected");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.client
|
||||
.lock()
|
||||
.map(|guard| guard.is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn set_activity(
|
||||
&self,
|
||||
state: &str,
|
||||
details: &str,
|
||||
large_image: Option<&str>,
|
||||
start_timestamp: Option<i64>,
|
||||
) -> Result<(), String> {
|
||||
let mut client_guard = self.client.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(ref mut client) = *client_guard {
|
||||
let mut payload = activity::Activity::new().state(state).details(details);
|
||||
|
||||
// add assets
|
||||
if let Some(image) = large_image {
|
||||
payload = payload.assets(
|
||||
activity::Assets::new()
|
||||
.large_image(image)
|
||||
.large_text("Stremio"),
|
||||
);
|
||||
} else {
|
||||
// Default Stremio logo
|
||||
payload = payload.assets(
|
||||
activity::Assets::new()
|
||||
.large_image("stremio_logo")
|
||||
.large_text("Stremio"),
|
||||
);
|
||||
}
|
||||
|
||||
// add timestamps
|
||||
if let Some(start) = start_timestamp {
|
||||
payload = payload.timestamps(activity::Timestamps::new().start(start));
|
||||
}
|
||||
|
||||
client
|
||||
.set_activity(payload)
|
||||
.map_err(|e| format!("Failed to set activity: {}", e))?;
|
||||
|
||||
println!("Discord activity set: {} - {}", state, details);
|
||||
} else {
|
||||
return Err("Discord not connected".to_string());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear activity
|
||||
pub fn clear_activity(&self) -> Result<(), String> {
|
||||
let mut client_guard = self.client.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(ref mut client) = *client_guard {
|
||||
client
|
||||
.clear_activity()
|
||||
.map_err(|e| format!("Failed to clear activity: {}", e))?;
|
||||
|
||||
println!("Discord activity cleared");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DiscordRpc {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.disconnect();
|
||||
}
|
||||
}
|
||||
|
|
@ -105,4 +105,9 @@ impl RPCResponse {
|
|||
pub fn update_available() -> String {
|
||||
Self::response_message(Some(json!(["autoupdater-show-notif"])))
|
||||
}
|
||||
pub fn discord_status(connected: bool) -> String {
|
||||
Self::response_message(Some(json!(["discord-status", {
|
||||
"connected": connected,
|
||||
}])))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod app;
|
||||
pub use app::MainWindow;
|
||||
pub mod ipc;
|
||||
pub mod discord;
|
||||
pub mod stremio_player;
|
||||
pub mod stremio_server;
|
||||
pub mod stremio_wevbiew;
|
||||
|
|
|
|||
Loading…
Reference in a new issue