Compare commits

...

6 commits

Author SHA1 Message Date
44edd7aa8b Update README.md 2025-06-09 13:22:06 +00:00
e79cac13cc ref 2025-06-09 00:56:26 +03:00
d1d03155e3 ref 2025-06-09 00:56:17 +03:00
e808886083 Update README.md 2025-06-07 07:28:58 +00:00
20f73be4c1 added tray | removed console at start 2025-03-21 19:29:43 +03:00
ccc6603f88 check changelog 2025-03-21 16:50:12 +03:00
19 changed files with 234 additions and 25 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
# custom
.env
app/data.json
# ---> Rust
# Generated by Cargo

View file

@ -1,3 +1,12 @@
# hdrezka-discord-rpc
# HDRezka Discord RPC | Chrome Extension
HDRezka Discord RPC | Chrome Extension
## How to build app
```
git clone https://git.amok.dev/AmokDev/HDRezcord.git
```
```
cargo run --release
```
## How to build extension
- SOON

View file

@ -1,11 +1,21 @@
[package]
name = "DiscordRPC-HDRezcord-APP"
version = "0.1.0"
version = "0.1.3"
authors = ["AmokDev <amokdevv@gmail.com>"]
edition = "2021"
[dependencies]
actix-web = "4"
rust-discord-activity = { git = "https://github.com/AmokDev/rust-discord-activity", version = "0.3.1" }
image = "0.25.5"
open = "5.3.2"
dioxus = { version = "0.6.3", features = ["desktop"] }
dioxus-desktop = "0.6.3"
rust-discord-rpc = { git = "https://git.amok.dev/AmokDev/rust-discord-rpc", version = "1.0.0" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tray-icon = "0.20.0"
winit = "0.30.9"
[target.'cfg(windows)'.build-dependencies]
winresource = "0.1.20"

11
app/build.rs Normal file
View file

@ -0,0 +1,11 @@
#[cfg(windows)]
fn main() {
let mut res = winresource::WindowsResource::new();
res.set_icon("resources/icon.ico");
res.compile().unwrap();
}
#[cfg(unix)]
fn main() {
}

View file

@ -1 +1 @@
{"episode":6,"season":1,"timestamps":[18000,2597000],"name":"Элементарно","image_url":"https://statichdrezka.ac/i/2020/12/17/xe6ea25038d76rc83x59z.jpeg"}
{"episode":0,"season":0,"timestamps":[1,1],"name":"Полетта","image_url":"https://statichdrezka.ac/i/2014/1/25/ze3dffce6f572bx77h96o.jpg"}

BIN
app/resources/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

View file

@ -1,4 +1,4 @@
use rust_discord_activity::{
use rust_discord_rpc::{
DiscordClient,
Asset,
Timestamp,
@ -17,8 +17,13 @@ pub async fn start_presence() {
let _ = client.connect();
println!(":: Rich Presence started!");
loop {
if client.is_connected == false {
println!(":: Reconnecting...");
tokio::time::sleep(Duration::from_secs(5)).await;
let _ = client.connect();
continue;
}
let post_data = load_from_json();
let mut episode: u8 = 0;
let mut season: u8 = 0;
@ -33,29 +38,41 @@ pub async fn start_presence() {
image_url = data.image_url;
}
let asset = Asset::new(Some(image_url), Some(String::from("#noWar")), None, None);
let mut asset = Asset::new(Some(image_url.clone()), None, None, None);
let now_in_millis = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis();
let timestamp = Timestamp::new(Some(now_in_millis - timestamps[0]), Some(now_in_millis + (timestamps[1] - timestamps[0])));
let mut activity = Activity::new();
let mut activity_type = ActivityType::WATCHING;
let mut subtitle = format!("Сезон {season} Серия {episode}");
if season == 0 && episode == 0 {
subtitle = format!("Фильм");
}
if episode == 0 && timestamps[0] == 0 && timestamps[1] == 0 {
tokio::time::sleep(Duration::from_secs(1)).await;
client.is_connected = false;
continue;
}
if timestamps[0] == 1 && timestamps[1] == 1 {
activity_type = ActivityType::COMPETING;
asset = Asset::new(Some(image_url), Some(String::from("Paused")), None, None);
}
if timestamps[0] == 2 && timestamps[1] == 2 {
activity_type = ActivityType::COMPETING;
asset = Asset::new(Some(String::from("hdrezcord-logo")), None, None, None);
name = String::from("Ничего не смотрит");
subtitle = String::from("...");
}
// сорян за гкод, потом пофикшу
activity
.set_state(Some(subtitle).into())
.set_activity_type(Some(ActivityType::WATCHING))
.set_activity_type(Some(activity_type))
.set_details(Some(name))
.set_timestamps(Some(timestamp))
.set_assets(Some(asset))
.set_instance(Some(true));
let payload = Payload::new(EventName::Activity, EventData::Activity(activity));
if episode == 0 && timestamps[0] == 0 && timestamps[1] == 0 {
continue;
}
let _ = client.send_payload(payload);
tokio::time::sleep(Duration::from_secs(1)).await;
}

BIN
app/src/icons/32.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

View file

@ -1,12 +1,21 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
mod network;
mod discord;
mod utils;
use network::api::start_api;
use discord::rpc::start_presence;
use utils::tray::init_tray;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::thread::spawn(|| {
init_tray();
});
let _ = tokio::join!(start_api(), start_presence());
Ok(())
}

View file

@ -3,7 +3,7 @@ use crate::utils::json_database::{save_to_json, PostData};
#[post("/set_presence")]
async fn set_presence(data: web::Json<PostData>) -> HttpResponse {
// println!("{:?}", data);
println!("{:?}", data);
let _ = save_to_json(&data);
HttpResponse::Ok().finish()
}

View file

@ -1 +1,2 @@
pub mod json_database;
pub mod json_database;
pub mod tray;

113
app/src/utils/tray.rs Normal file
View file

@ -0,0 +1,113 @@
use tray_icon::{
menu::{Menu, MenuEvent, MenuId, MenuItem},
TrayIcon, TrayIconBuilder, TrayIconEvent,
};
use winit::{
application::ApplicationHandler,
event_loop::EventLoop, platform::windows::EventLoopBuilderExtWindows,
};
use std::process;
#[derive(Debug)]
enum UserEvent {
MenuEvent(tray_icon::menu::MenuEvent),
}
struct Application {
tray_icon: Option<TrayIcon>,
}
impl Application {
fn new() -> Application {
Application { tray_icon: None }
}
fn new_tray_icon() -> TrayIcon {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/src/icons/32.ico");
let icon = load_icon(std::path::Path::new(path));
TrayIconBuilder::new()
.with_menu(Box::new(Self::new_tray_menu()))
.with_tooltip("HDRezcord")
.with_icon(icon)
.build()
.unwrap()
}
fn new_tray_menu() -> Menu {
let menu = Menu::new();
let _ = menu.append(&MenuItem::new("Sources", true, None));
// let _ = menu.append(&MenuItem::new("Stop Presence", true, None));
let _ = menu.append(&MenuItem::new("Exit", true, None));
menu
}
}
impl ApplicationHandler<UserEvent> for Application {
fn resumed(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {}
fn window_event(
&mut self,
_event_loop: &winit::event_loop::ActiveEventLoop,
_window_id: winit::window::WindowId,
_event: winit::event::WindowEvent,
) {
}
fn new_events(
&mut self,
_event_loop: &winit::event_loop::ActiveEventLoop,
cause: winit::event::StartCause,
) {
if winit::event::StartCause::Init == cause {
#[cfg(not(target_os = "linux"))]
{
self.tray_icon = Some(Self::new_tray_icon());
}
}
}
fn user_event(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop, event: UserEvent) {
// println!("{event:?}");
match event {
UserEvent::MenuEvent(menu_event) => {
// println!("{:?}", menu_event.id);
if menu_event.id == MenuId("1001".to_string()) {
let _ = open::that("https://git.amok.dev/AmokDev/HDRezcord");
} else if menu_event.id == MenuId("1002".to_string()) {
process::exit(0);
}
}
}
}
}
pub fn init_tray() {
let event_loop = EventLoop::<UserEvent>::with_user_event().with_any_thread(true).build().unwrap();
let proxy = event_loop.create_proxy();
MenuEvent::set_event_handler(Some(move |event| {
let _ = proxy.send_event(UserEvent::MenuEvent(event));
}));
let mut app = Application::new();
let _menu_channel = MenuEvent::receiver();
let _tray_channel = TrayIconEvent::receiver();
if let Err(err) = event_loop.run_app(&mut app) {
println!("Error: {:?}", err);
}
}
fn load_icon(path: &std::path::Path) -> tray_icon::Icon {
let (icon_rgba, icon_width, icon_height) = {
let image = image::open(path)
.expect("Failed to open icon path")
.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
(rgba, width, height)
};
tray_icon::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}

View file

@ -56,12 +56,32 @@ function get_timestamps() {
return [0, 0];
}
if (!get_timestamps.previousTimes) {
get_timestamps.previousTimes = [];
}
get_timestamps.previousTimes.push(currentTime);
if (get_timestamps.previousTimes.length > 3) {
get_timestamps.previousTimes.shift();
}
if (get_timestamps.previousTimes.length === 3 &&
get_timestamps.previousTimes.every(val => val === currentTime)) {
console.warn("currentTime repeated too many times");
return [1, 1];
}
return [timeToMilliseconds(currentTime), timeToMilliseconds(totalTime)];
}
function get_name() {
let element = document.querySelector('.b-post__title h1[itemprop="name"]');
return element.textContent;
try {
return element.textContent;
} catch (error) {
return null;
}
}
function get_image_url() {
@ -73,7 +93,7 @@ if (window.location.href.includes("flymaterez.net")) {
setInterval(async () => {
try {
chrome.storage.local.get(['pluginEnabled'], function(result) {
if (result.pluginEnabled === true) {
if (result.pluginEnabled === true && get_name() !== null) {
chrome.runtime.sendMessage({
type: "send_rpc_request",
title: document.title,
@ -84,12 +104,22 @@ if (window.location.href.includes("flymaterez.net")) {
name: get_name(),
image_url: get_image_url(),
});
} else if (result.pluginEnabled === true && get_name() === null) {
chrome.runtime.sendMessage({
type: "send_rpc_request",
title: document.title,
url: window.location.href,
episode: 0,
season: 0,
timestamps: [2, 2],
name: "name",
image_url: "image_url",
});
}
console.log(result.pluginEnabled);
});
} catch (error) {
console.warn(error);
// console.log(error);
;
}
}, 1000);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 B

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -9,11 +9,21 @@ function edit_status() {
if (result.pluginEnabled === true) {
chrome.storage.local.set({ pluginEnabled: false });
plugin_status.innerHTML = "Plugin is disabled";
chrome.runtime.sendMessage({
type: "send_rpc_request",
title: document.title,
url: window.location.href,
episode: 0,
season: 0,
timestamps: [0, 0],
name: "name",
image_url: "image_url",
});
} else if (result.pluginEnabled === false) {
chrome.storage.local.set({ pluginEnabled: true });
plugin_status.innerHTML = "Plugin is enabled";
}
console.log(result.pluginEnabled);
// console.log(result.pluginEnabled);
});
}
@ -30,7 +40,7 @@ function get_status() {
} else if (result.pluginEnabled === false) {
plugin_status.innerHTML = "Plugin is disabled";
}
console.log(result.pluginEnabled);
// console.log(result.pluginEnabled);
});
}

View file

@ -11,7 +11,6 @@ async function send_rpc_request(data) {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
console.log(response.type)
} catch (error) {
console.error('Error:', error);
}
@ -27,7 +26,6 @@ chrome.runtime.onMessage.addListener(
name: request.name,
image_url: request.image_url
})
console.log(request.episode, request.season, request.timestamps)
}
}
);