Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Server crash logs and boot availability #13

Merged
merged 2 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use clap::Parser;
use native_windows_gui::{self as nwg, NativeUi};
mod stremio_app;
use crate::stremio_app::{
constants::{DEV_ENDPOINT, IPC_PATH, STA_ENDPOINT, WEB_ENDPOINT},
stremio_server::StremioServer,
constants::{DEV_ENDPOINT, IPC_PATH, STA_ENDPOINT, STREMIO_SERVER_DEV_MODE, WEB_ENDPOINT},
MainWindow, PipeClient,
};

Expand Down Expand Up @@ -75,9 +74,10 @@ fn main() {
}
// END IPC

if !opt.development {
StremioServer::new();
}
std::env::set_var(
STREMIO_SERVER_DEV_MODE,
if opt.development { "true" } else { "false" },
);

let webui_url = if opt.development && opt.webui_url == WEB_ENDPOINT {
DEV_ENDPOINT.to_string()
Expand Down
8 changes: 6 additions & 2 deletions src/stremio_app/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ use crate::stremio_app::{
PipeServer,
};

use super::stremio_server::StremioServer;

#[derive(Default, NwgUi)]
pub struct MainWindow {
pub command: String,
Expand All @@ -50,11 +52,13 @@ pub struct MainWindow {
#[nwg_events((tray, MousePressLeftUp): [Self::on_show], (tray_exit, OnMenuItemSelected): [nwg::stop_thread_dispatch()], (tray_show_hide, OnMenuItemSelected): [Self::on_show_hide], (tray_topmost, OnMenuItemSelected): [Self::on_toggle_topmost]) ]
pub tray: SystemTray,
#[nwg_partial(parent: window)]
pub webview: WebView,
pub splash_screen: SplashImage,
#[nwg_partial(parent: window)]
pub server: StremioServer,
#[nwg_partial(parent: window)]
pub player: Player,
#[nwg_partial(parent: window)]
pub splash_screen: SplashImage,
pub webview: WebView,
#[nwg_control]
#[nwg_events(OnNotice: [Self::on_toggle_fullscreen_notice] )]
pub toggle_fullscreen_notice: nwg::Notice,
Expand Down
29 changes: 16 additions & 13 deletions src/stremio_app/constants.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
pub const APP_NAME: &str = "Stremio";
pub const IPC_PATH: &str = "//./pipe/com.stremio5.";
pub const DEV_ENDPOINT: &str = "http://127.0.0.1:11470";
pub const WEB_ENDPOINT: &str = "https://web.stremio.com/";
pub const STA_ENDPOINT: &str = "https://staging.strem.io/";
pub const WINDOW_MIN_WIDTH: i32 = 1000;
pub const WINDOW_MIN_HEIGHT: i32 = 600;
pub const UPDATE_INTERVAL: u64 = 12 * 60 * 60;
pub const UPDATE_ENDPOINT: [&str; 3] = [
"https://www.strem.io/updater/check?product=stremio-shell-ng",
"https://www.stremio.com/updater/check?product=stremio-shell-ng",
"https://www.stremio.net/updater/check?product=stremio-shell-ng",
];
pub const APP_NAME: &str = "Stremio";
pub const IPC_PATH: &str = "//./pipe/com.stremio5.";
pub const DEV_ENDPOINT: &str = "http://127.0.0.1:11470";
pub const WEB_ENDPOINT: &str = "https://web.stremio.com/";
pub const STA_ENDPOINT: &str = "https://staging.strem.io/";
pub const WINDOW_MIN_WIDTH: i32 = 1000;
pub const WINDOW_MIN_HEIGHT: i32 = 600;
pub const UPDATE_INTERVAL: u64 = 12 * 60 * 60;
pub const UPDATE_ENDPOINT: [&str; 3] = [
"https://www.strem.io/updater/check?product=stremio-shell-ng",
"https://www.stremio.com/updater/check?product=stremio-shell-ng",
"https://www.stremio.net/updater/check?product=stremio-shell-ng",
];
pub const STREMIO_SERVER_DEV_MODE: &str = "STREMIO_SERVER_DEV_MODE";
pub const SRV_BUFFER_SIZE: usize = 1024;
pub const SRV_LOG_SIZE: usize = 20;
197 changes: 169 additions & 28 deletions src/stremio_app/stremio_server/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
use native_windows_gui as nwg;
use crate::stremio_app::constants::{SRV_BUFFER_SIZE, SRV_LOG_SIZE, STREMIO_SERVER_DEV_MODE};
use native_windows_gui::{self as nwg, PartialUi};
use std::io::Write;
use std::{
env, fs, os::windows::process::CommandExt, path, process::Command, thread, time::Duration,
env, fs,
io::Read,
ops::Deref,
os::windows::process::CommandExt,
path,
process::{Command, Stdio},
sync::{Arc, Mutex},
thread,
};
use winapi::um::{
processthreadsapi::GetCurrentProcess,
Expand All @@ -12,10 +21,23 @@ use winapi::um::{
},
};

pub struct StremioServer {}
#[derive(Default)]
pub struct StremioServer {
development: bool,
parent: nwg::ControlHandle,
crash_notice: nwg::Notice,
logs: Arc<Mutex<String>>,
}

impl StremioServer {
pub fn new() -> StremioServer {
pub fn start(&self) {
if self.development {
return;
}
let (tx, rx) = flume::unbounded();
let logs = self.logs.clone();
let sender = self.crash_notice.sender();

thread::spawn(move || {
// Use Win32JobObject to kill the child process when the parent process is killed
// With the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK and JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flags
Expand Down Expand Up @@ -45,31 +67,150 @@ impl StremioServer {
.and_then(fs::canonicalize)
.expect("Cannot get the current executable path");
path.pop();
loop {
let runtime_path = path.clone().join(path::Path::new("stremio-runtime"));
let server_path = path.clone().join(path::Path::new("server.js"));
let child = Command::new(runtime_path)
.arg(server_path)
.creation_flags(CREATE_NO_WINDOW)
.spawn();
match child {
Ok(mut child) => {
// TODO: store somehow last few lines of the child's stdout/stderr instead of just waiting
child.wait().expect("Cannot wait for the server");
}
Err(err) => {
nwg::error_message(
"Stremio server",
format!("Cannot execute stremio-runtime: {}", &err).as_str(),
);
break;
}
};
// TODO: show error message with the child's stdout/stderr
thread::sleep(Duration::from_millis(500));
dbg!("Trying to restart the server...");
let lines = Arc::new(Mutex::new(String::new()));
let runtime_path = path.clone().join(path::Path::new("stremio-runtime"));
let server_path = path.clone().join(path::Path::new("server.js"));
let child = Command::new(runtime_path)
.arg(server_path)
.creation_flags(CREATE_NO_WINDOW)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn();
match child {
Ok(mut child) => {
let mut stdout = child.stdout.take().unwrap();
let out_lines = lines.clone();
let tx = tx.clone();
let out_thread = thread::spawn(move || {
let http_endpoint = String::new();
loop {
let mut buffer = [0; SRV_BUFFER_SIZE];
let on = stdout.read(&mut buffer[..]).unwrap_or(!0);
if on > buffer.len() {
continue;
}
std::io::stdout().write_all(&buffer).ok();
let string_data = String::from_utf8_lossy(&buffer[..on]);
{
let lines = &mut *out_lines.lock().unwrap();
*lines += string_data.deref();
if http_endpoint.is_empty() {
if let Some(http_endpoint) = string_data
.lines()
.find(|line| line.starts_with("EngineFS server started at"))
{
let http_endpoint =
http_endpoint.split_whitespace().last().unwrap();
println!("HTTP endpoint: {}", http_endpoint);
let endpoint = http_endpoint.to_string();
tx.send(endpoint.clone()).ok();
}
}
*lines = lines
.lines()
.rev()
.take(SRV_LOG_SIZE)
.collect::<Vec<&str>>()
.into_iter()
.rev()
.collect::<Vec<&str>>()
.join("\n");
};
if on == 0 {
// Server terminated
break;
}
}
});

let mut stderr = child.stderr.take().unwrap();
let err_lines = lines.clone();
let err_thread = thread::spawn(move || {
let mut buffer = [0; SRV_BUFFER_SIZE];
loop {
let en = stderr.read(&mut buffer[..]).unwrap_or(!0);
if en > buffer.len() {
continue;
}
std::io::stderr().write_all(&buffer).ok();
let string_data = String::from_utf8_lossy(&buffer[..en]);
// eprint!("{:?}", &buffer);
{
let lines = &mut *err_lines.lock().unwrap();
*lines += string_data.deref();
*lines = lines
.lines()
.rev()
.take(SRV_LOG_SIZE)
.collect::<Vec<&str>>()
.into_iter()
.rev()
.collect::<Vec<&str>>()
.join("\n");
};
if en == 0 {
// Server terminated
break;
}
}
});
out_thread.join().ok();
err_thread.join().ok();
}
Err(err) => {
nwg::error_message(
"Stremio server",
format!("Cannot execute stremio-runtime: {}", &err).as_str(),
);
}
};

{
let mut logs = logs.lock().unwrap();
*logs = lines.lock().unwrap().deref().to_string();
}
println!("Server terminated.");
sender.notice();
});
StremioServer {}

// Wait for the server to start
rx.recv().unwrap();
}
}

impl PartialUi for StremioServer {
fn build_partial<W: Into<nwg::ControlHandle>>(
data: &mut Self,
parent: Option<W>,
) -> Result<(), nwg::NwgError> {
if std::env::var(STREMIO_SERVER_DEV_MODE).unwrap_or("false".to_string()) == "true" {
data.development = true;
}

data.parent = parent.expect("No parent window").into();

nwg::Notice::builder()
.parent(data.parent)
.build(&mut data.crash_notice)
.ok();
data.start();
println!("Stremio server started");
Ok(())
}
fn process_event<'a>(
&self,
evt: nwg::Event,
_evt_data: &nwg::EventData,
handle: nwg::ControlHandle,
) {
use nwg::Event as E;
if evt == E::OnNotice && handle == self.crash_notice.handle {
nwg::modal_error_message(
self.parent,
"Stremio server crash log",
self.logs.lock().unwrap().deref(),
);
self.start();
}
}
}