backend: handle app already running logic + refactor response
This commit is contained in:
Generated
+1
@@ -671,6 +671,7 @@ dependencies = [
|
|||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac-sha256",
|
"hmac-sha256",
|
||||||
|
"http",
|
||||||
"libc",
|
"libc",
|
||||||
"moonlight-common-c-sys",
|
"moonlight-common-c-sys",
|
||||||
"openssl",
|
"openssl",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ directories = "6.0.0"
|
|||||||
getrandom = { version = "0.3.3", features = ["std"] }
|
getrandom = { version = "0.3.3", features = ["std"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
hmac-sha256 = "1.1.12"
|
hmac-sha256 = "1.1.12"
|
||||||
|
http = "1.3.1"
|
||||||
libc = "0.2.174"
|
libc = "0.2.174"
|
||||||
moonlight-common-c-sys = { path = "../moonlight-common-c-sys" }
|
moonlight-common-c-sys = { path = "../moonlight-common-c-sys" }
|
||||||
openssl = "0.10.73"
|
openssl = "0.10.73"
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ use salvo::prelude::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::{common::AppResult, state::StateReader};
|
use crate::{
|
||||||
|
common,
|
||||||
|
common::{AppError, AppResult},
|
||||||
|
responses,
|
||||||
|
state::StateReader,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct AppListRespApp {
|
struct AppListRespApp {
|
||||||
@@ -29,6 +34,7 @@ struct App {
|
|||||||
title: String,
|
title: String,
|
||||||
id: u64,
|
id: u64,
|
||||||
hdr_supported: bool,
|
hdr_supported: bool,
|
||||||
|
active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, ToSchema)]
|
#[derive(Debug, Serialize, ToSchema)]
|
||||||
@@ -40,7 +46,7 @@ struct GetAppsResponse {
|
|||||||
impl crate::backend::Backend {
|
impl crate::backend::Backend {
|
||||||
#[craft(endpoint(status_codes(StatusCode::OK, StatusCode::INTERNAL_SERVER_ERROR)))]
|
#[craft(endpoint(status_codes(StatusCode::OK, StatusCode::INTERNAL_SERVER_ERROR)))]
|
||||||
pub async fn get_apps(self: ::std::sync::Arc<Self>) -> AppResult<Json<GetAppsResponse>> {
|
pub async fn get_apps(self: ::std::sync::Arc<Self>) -> AppResult<Json<GetAppsResponse>> {
|
||||||
let standard_error = Err(crate::common::AppError {
|
let standard_error = Err(AppError {
|
||||||
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
description: "failed to get available apps".to_string(),
|
description: "failed to get available apps".to_string(),
|
||||||
});
|
});
|
||||||
@@ -67,7 +73,29 @@ impl crate::backend::Backend {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (_, server) in servers.into_iter() {
|
for (_, server) in servers.into_iter() {
|
||||||
let mut base_url = crate::common::base_url(
|
let mut server_info_base_url = common::base_url(
|
||||||
|
"https",
|
||||||
|
&server.host,
|
||||||
|
server.https_port(),
|
||||||
|
&unique_id,
|
||||||
|
"serverinfo",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let server_info = common::get_url(&mut server_info_base_url, true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
debug!("server_info: {server_info}");
|
||||||
|
let server_info: responses::ServerInfoResponse =
|
||||||
|
match serde_xml_rs::from_str(&server_info) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not parse serverinfo response: {e}");
|
||||||
|
return standard_error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut base_url = common::base_url(
|
||||||
"https",
|
"https",
|
||||||
&server.host,
|
&server.host,
|
||||||
server.https_port(),
|
server.https_port(),
|
||||||
@@ -76,7 +104,7 @@ impl crate::backend::Backend {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let resp = match crate::common::get_url(&mut base_url, true).await {
|
let resp = match common::get_url(&mut base_url, true).await {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("could not get applist from server {}: {}", server.name, e);
|
error!("could not get applist from server {}: {}", server.name, e);
|
||||||
@@ -103,6 +131,7 @@ impl crate::backend::Backend {
|
|||||||
title: a.app_title,
|
title: a.app_title,
|
||||||
hdr_supported: a.is_hdr_supported,
|
hdr_supported: a.is_hdr_supported,
|
||||||
id: a.id,
|
id: a.id,
|
||||||
|
active: a.id == server_info.currentgame,
|
||||||
})
|
})
|
||||||
.rev()
|
.rev()
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ mod common;
|
|||||||
mod gamestream;
|
mod gamestream;
|
||||||
mod pair;
|
mod pair;
|
||||||
mod proxy;
|
mod proxy;
|
||||||
|
mod responses;
|
||||||
mod state;
|
mod state;
|
||||||
mod stream;
|
mod stream;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ServerInfoResponse {
|
||||||
|
pub hostname: String,
|
||||||
|
pub appversion: String,
|
||||||
|
pub GfeVersion: String,
|
||||||
|
pub uniqueid: uuid::Uuid,
|
||||||
|
pub HttpsPort: u16,
|
||||||
|
pub ExternalPort: u16,
|
||||||
|
pub MaxLumaPixelsHEVC: u64,
|
||||||
|
pub mac: String,
|
||||||
|
//pub ServerCommand: String,
|
||||||
|
//pub Permission: u64,
|
||||||
|
//pub VirtualDisplayCapable: bool,
|
||||||
|
//pub VirtualDisplayDriverReady: bool,
|
||||||
|
pub LocalIP: String,
|
||||||
|
pub ServerCodecModeSupport: i32,
|
||||||
|
pub PairStatus: u64,
|
||||||
|
pub currentgame: u64,
|
||||||
|
pub state: String,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use moonlight_common_c_sys::SCM_H264;
|
use moonlight_common_c_sys::SCM_H264;
|
||||||
use salvo::prelude::*;
|
use salvo::prelude::*;
|
||||||
@@ -6,7 +8,7 @@ use tracing::{debug, error, info};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{AppError, AppResult, get_url},
|
common::{AppError, AppResult, get_url},
|
||||||
proxy,
|
proxy, responses,
|
||||||
state::{GamestreamServer, StateReadAccess, StateReader},
|
state::{GamestreamServer, StateReadAccess, StateReader},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,33 +27,16 @@ struct PostStreamStartResponse {
|
|||||||
//cert_hash: String,
|
//cert_hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ServerInfoResponse {
|
|
||||||
hostname: String,
|
|
||||||
appversion: String,
|
|
||||||
GfeVersion: String,
|
|
||||||
uniqueid: uuid::Uuid,
|
|
||||||
HttpsPort: u16,
|
|
||||||
ExternalPort: u16,
|
|
||||||
MaxLumaPixelsHEVC: u64,
|
|
||||||
mac: String,
|
|
||||||
//ServerCommand: String,
|
|
||||||
//Permission: u64,
|
|
||||||
//VirtualDisplayCapable: bool,
|
|
||||||
//VirtualDisplayDriverReady: bool,
|
|
||||||
LocalIP: String,
|
|
||||||
ServerCodecModeSupport: i32,
|
|
||||||
PairStatus: u64,
|
|
||||||
currentgame: u64,
|
|
||||||
state: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct LaunchResponse {
|
struct LaunchResponse {
|
||||||
|
#[serde(rename = "@status_code")]
|
||||||
|
status_code: String,
|
||||||
|
#[serde(rename = "@status_message")]
|
||||||
|
status_message: Option<String>,
|
||||||
#[serde(rename = "sessionUrl0")]
|
#[serde(rename = "sessionUrl0")]
|
||||||
session_url_0: url::Url,
|
session_url_0: Option<url::Url>,
|
||||||
#[serde(rename = "gamesession")]
|
#[serde(rename = "gamesession")]
|
||||||
game_session: u64,
|
game_session: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_server(reader: &StateReadAccess, server: &String) -> Result<Option<GamestreamServer>> {
|
fn get_server(reader: &StateReadAccess, server: &String) -> Result<Option<GamestreamServer>> {
|
||||||
@@ -108,7 +93,7 @@ impl crate::backend::Backend {
|
|||||||
Ok(s) => match s {
|
Ok(s) => match s {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => {
|
None => {
|
||||||
info!("No server with name: {}", body.server);
|
error!("No server with name: {}", body.server);
|
||||||
return Err(AppError {
|
return Err(AppError {
|
||||||
status_code: StatusCode::BAD_REQUEST,
|
status_code: StatusCode::BAD_REQUEST,
|
||||||
description: "No server configured with that name.".to_string(),
|
description: "No server configured with that name.".to_string(),
|
||||||
@@ -139,8 +124,9 @@ impl crate::backend::Backend {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let server_info = crate::common::get_url(&mut base_url, true).await.unwrap();
|
let server_info = crate::common::get_url(&mut base_url, true).await.unwrap();
|
||||||
debug!("app_info: {server_info}");
|
debug!("server_info: {server_info}");
|
||||||
let server_info: ServerInfoResponse = match serde_xml_rs::from_str(&server_info) {
|
let server_info: responses::ServerInfoResponse = match serde_xml_rs::from_str(&server_info)
|
||||||
|
{
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Could not parse serverinfo response: {e}");
|
error!("Could not parse serverinfo response: {e}");
|
||||||
@@ -227,6 +213,36 @@ impl crate::backend::Backend {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let Ok(launch_resp_status) = http::StatusCode::from_str(&launch_response.status_code)
|
||||||
|
else {
|
||||||
|
error!(
|
||||||
|
"Server returned invalid status code: {}",
|
||||||
|
launch_response.status_code
|
||||||
|
);
|
||||||
|
return standard_error;
|
||||||
|
};
|
||||||
|
if !launch_resp_status.is_success() {
|
||||||
|
let status_message = launch_response
|
||||||
|
.status_message
|
||||||
|
.unwrap_or("Server did not provide a status message".to_string());
|
||||||
|
|
||||||
|
info!("Could not launch game: {}", status_message,);
|
||||||
|
return Err(AppError {
|
||||||
|
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
description: format!("Could not launch game: {}", status_message),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(url) = launch_response.session_url_0 else {
|
||||||
|
error!("Server response was missing session_url_0");
|
||||||
|
return standard_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(game_session) = launch_response.game_session else {
|
||||||
|
error!("Server response was missing game_session");
|
||||||
|
return standard_error;
|
||||||
|
};
|
||||||
|
|
||||||
let stream_id = uuid::Uuid::new_v4();
|
let stream_id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
let server_codec_mode_support = if server_info.ServerCodecModeSupport == 0 {
|
let server_codec_mode_support = if server_info.ServerCodecModeSupport == 0 {
|
||||||
@@ -237,8 +253,8 @@ impl crate::backend::Backend {
|
|||||||
|
|
||||||
let stream = crate::backend::Stream {
|
let stream = crate::backend::Stream {
|
||||||
id: stream_id,
|
id: stream_id,
|
||||||
url: launch_response.session_url_0,
|
url,
|
||||||
game_session: launch_response.game_session,
|
game_session,
|
||||||
server_address: server.host.clone(),
|
server_address: server.host.clone(),
|
||||||
stream_config: body.stream_config.clone(),
|
stream_config: body.stream_config.clone(),
|
||||||
app_version: server_info.appversion,
|
app_version: server_info.appversion,
|
||||||
|
|||||||
Reference in New Issue
Block a user