mirror of
https://github.com/Stremio/stremio-shell-ng.git
synced 2026-08-20 03:15:54 +00:00
Merge pull request #33 from Stremio/feat/discord-rich-presence
Some checks are pending
Continuous integration / test (push) Waiting to run
Some checks are pending
Continuous integration / test (push) Waiting to run
Feat/discord rich presence
This commit is contained in:
commit
d98d21bfb9
6 changed files with 245 additions and 1 deletions
38
Cargo.lock
generated
38
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 0.8.2",
|
||||
]
|
||||
|
||||
[[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",
|
||||
|
|
@ -1621,7 +1648,7 @@ dependencies = [
|
|||
"sha2",
|
||||
"url",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"uuid 1.20.0",
|
||||
"webview2",
|
||||
"webview2-sys",
|
||||
"whoami",
|
||||
|
|
@ -1965,6 +1992,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 = "uuid"
|
||||
version = "1.20.0"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ version = "5.0.22"
|
|||
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",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ use crate::stremio_app::{
|
|||
PipeServer,
|
||||
};
|
||||
|
||||
use super::discord::DiscordRpc;
|
||||
use super::stremio_server::StremioServer;
|
||||
|
||||
#[derive(Default, NwgUi)]
|
||||
|
|
@ -256,7 +257,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 = DiscordRpc::new(web_tx.clone());
|
||||
let requested_fullscreen = self.requested_fullscreen.clone();
|
||||
|
||||
thread::spawn(move || loop {
|
||||
if let Some(msg) = web_rx
|
||||
.recv()
|
||||
|
|
@ -401,6 +405,44 @@ impl MainWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
Some("discord-connect") => {
|
||||
if let Err(e) = discord_rpc.connect() {
|
||||
eprintln!("Discord connect error: {}", e);
|
||||
web_tx_web.send(RPCResponse::discord_status(false)).ok();
|
||||
}
|
||||
}
|
||||
Some("discord-disconnect") => {
|
||||
if let Err(e) = discord_rpc.disconnect() {
|
||||
eprintln!("Discord disconnect error: {}", e);
|
||||
}
|
||||
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 end_timestamp = params.get("endTimestamp").and_then(|v| v.as_i64());
|
||||
|
||||
if let Err(e) = discord_rpc.set_activity(
|
||||
state,
|
||||
details,
|
||||
image,
|
||||
start_timestamp,
|
||||
end_timestamp,
|
||||
) {
|
||||
eprintln!("Discord set activity error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some("discord-clear-activity") => {
|
||||
if let Err(e) = discord_rpc.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"),
|
||||
|
|
|
|||
159
src/stremio_app/discord.rs
Normal file
159
src/stremio_app/discord.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use std::sync::mpsc::{self, Sender};
|
||||
|
||||
use discord_rich_presence::{activity, DiscordIpc, DiscordIpcClient};
|
||||
|
||||
use crate::stremio_app::ipc::RPCResponse;
|
||||
|
||||
const DISCORD_APP_ID: &str = "1452620752263319665";
|
||||
|
||||
enum DiscordCommand {
|
||||
Connect,
|
||||
Disconnect,
|
||||
SetActivity {
|
||||
state: String,
|
||||
details: String,
|
||||
large_image: Option<String>,
|
||||
start_timestamp: Option<i64>,
|
||||
end_timestamp: Option<i64>,
|
||||
},
|
||||
ClearActivity,
|
||||
}
|
||||
|
||||
pub struct DiscordRpc {
|
||||
commands: Sender<DiscordCommand>,
|
||||
}
|
||||
|
||||
impl DiscordRpc {
|
||||
pub fn new(status_tx: flume::Sender<String>) -> Self {
|
||||
let (commands, receiver) = mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut client: Option<DiscordIpcClient> = None;
|
||||
|
||||
for command in receiver {
|
||||
match command {
|
||||
DiscordCommand::Connect => {
|
||||
if client.is_some() {
|
||||
send_status(&status_tx, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut next_client = DiscordIpcClient::new(DISCORD_APP_ID);
|
||||
match next_client.connect() {
|
||||
Ok(()) => {
|
||||
client = Some(next_client);
|
||||
send_status(&status_tx, true);
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("Discord connect error: {error}");
|
||||
send_status(&status_tx, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
DiscordCommand::Disconnect => {
|
||||
if let Some(mut current_client) = client.take() {
|
||||
if let Err(error) = current_client.close() {
|
||||
eprintln!("Discord disconnect error: {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
DiscordCommand::SetActivity {
|
||||
state,
|
||||
details,
|
||||
large_image,
|
||||
start_timestamp,
|
||||
end_timestamp,
|
||||
} => {
|
||||
let Some(current_client) = client.as_mut() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut payload = activity::Activity::new()
|
||||
.activity_type(activity::ActivityType::Watching)
|
||||
.state(&state)
|
||||
.details(&details);
|
||||
payload = payload.assets(
|
||||
activity::Assets::new()
|
||||
.large_image(large_image.as_deref().unwrap_or("stremio_logo"))
|
||||
.large_text("Stremio"),
|
||||
);
|
||||
|
||||
let timestamps = match (start_timestamp, end_timestamp) {
|
||||
(Some(start), Some(end)) => {
|
||||
Some(activity::Timestamps::new().start(start).end(end))
|
||||
}
|
||||
(Some(start), None) => Some(activity::Timestamps::new().start(start)),
|
||||
(None, Some(end)) => Some(activity::Timestamps::new().end(end)),
|
||||
(None, None) => None,
|
||||
};
|
||||
if let Some(timestamps) = timestamps {
|
||||
payload = payload.timestamps(timestamps);
|
||||
}
|
||||
|
||||
if let Err(error) = current_client.set_activity(payload) {
|
||||
eprintln!("Discord set activity error: {error}");
|
||||
client = None;
|
||||
send_status(&status_tx, false);
|
||||
}
|
||||
}
|
||||
DiscordCommand::ClearActivity => {
|
||||
let Some(current_client) = client.as_mut() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Err(error) = current_client.clear_activity() {
|
||||
eprintln!("Discord clear activity error: {error}");
|
||||
client = None;
|
||||
send_status(&status_tx, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut current_client) = client {
|
||||
let _ = current_client.close();
|
||||
}
|
||||
});
|
||||
|
||||
Self { commands }
|
||||
}
|
||||
|
||||
pub fn connect(&self) -> Result<(), String> {
|
||||
self.send(DiscordCommand::Connect)
|
||||
}
|
||||
|
||||
pub fn disconnect(&self) -> Result<(), String> {
|
||||
self.send(DiscordCommand::Disconnect)
|
||||
}
|
||||
|
||||
pub fn set_activity(
|
||||
&self,
|
||||
state: &str,
|
||||
details: &str,
|
||||
large_image: Option<&str>,
|
||||
start_timestamp: Option<i64>,
|
||||
end_timestamp: Option<i64>,
|
||||
) -> Result<(), String> {
|
||||
self.send(DiscordCommand::SetActivity {
|
||||
state: state.to_string(),
|
||||
details: details.to_string(),
|
||||
large_image: large_image.map(ToString::to_string),
|
||||
start_timestamp,
|
||||
end_timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear_activity(&self) -> Result<(), String> {
|
||||
self.send(DiscordCommand::ClearActivity)
|
||||
}
|
||||
|
||||
fn send(&self, command: DiscordCommand) -> Result<(), String> {
|
||||
self.commands
|
||||
.send(command)
|
||||
.map_err(|e| format!("Failed to send Discord command: {e}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn send_status(status_tx: &flume::Sender<String>, connected: bool) {
|
||||
status_tx.send(RPCResponse::discord_status(connected)).ok();
|
||||
}
|
||||
|
|
@ -105,6 +105,11 @@ 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,
|
||||
}])))
|
||||
}
|
||||
pub fn media_key(action: &str) -> String {
|
||||
Self::response_message(Some(json!(["media-key", action])))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
pub mod app;
|
||||
pub use app::MainWindow;
|
||||
pub mod discord;
|
||||
pub mod ipc;
|
||||
pub mod stremio_player;
|
||||
pub mod stremio_server;
|
||||
|
|
|
|||
Loading…
Reference in a new issue