some xtream refactorings

This commit is contained in:
euzu
2023-11-22 21:06:58 +01:00
parent fce81e2cb7
commit fd69a835b6
13 changed files with 403 additions and 54 deletions
+4
View File
@@ -149,6 +149,10 @@ pub(crate) struct UserApiRequest {
pub token: String,
#[serde(default = "default_as_empty_str")]
pub action: String,
#[serde(default = "default_as_empty_str")]
pub series_id: String,
#[serde(default = "default_as_empty_str")]
pub vod_id: String,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
+9 -4
View File
@@ -19,11 +19,9 @@ pub(crate) async fn serve_file(file_path: &Path, req: &HttpRequest) -> HttpRespo
}
}
pub(crate) fn get_user_target<'a>(api_req: &'a web::Query<UserApiRequest>, app_state: &'a web::Data<AppState>) -> Option<(UserCredentials, &'a ConfigTarget)> {
let username = api_req.username.as_str().trim();
let password = api_req.password.as_str().trim();
pub(crate) fn get_user_target_by_credentials<'a>(username: &str, password: &str, api_req: &'a web::Query<UserApiRequest>, app_state: &'a web::Data<AppState>) -> Option<(UserCredentials, &'a ConfigTarget)> {
if !username.is_empty() && !password.is_empty() {
app_state.config.get_target_for_user(username, password)
app_state.config.get_target_for_user(username, password)
} else {
let token = api_req.token.as_str().trim();
if !token.is_empty() {
@@ -32,4 +30,11 @@ pub(crate) fn get_user_target<'a>(api_req: &'a web::Query<UserApiRequest>, app_s
None
}
}
}
pub(crate) fn get_user_target<'a>(api_req: &'a web::Query<UserApiRequest>, app_state: &'a web::Data<AppState>) -> Option<(UserCredentials, &'a ConfigTarget)> {
let username = api_req.username.as_str().trim();
let password = api_req.password.as_str().trim();
get_user_target_by_credentials(username, password, api_req, app_state)
}
+4 -3
View File
@@ -32,8 +32,9 @@ pub(crate) async fn start_server(cfg: Arc<Config>, targets: Arc<ProcessTargets>)
let port = cfg.api.port;
let web_dir = cfg.api.web_root.to_string();
let web_dir_path = PathBuf::from(&web_dir);
if !web_dir_path.exists() || !web_dir_path.is_dir() {
return Err(std::io::Error::new(ErrorKind::NotFound, format!("web_root does not exists or is not an directory: {:?}", &web_dir_path)));
if !&web_dir_path.exists() || !&web_dir_path.is_dir() {
return Err(std::io::Error::new(ErrorKind::NotFound,
format!("web_root does not exists or is not an directory: {:?}", &web_dir_path)));
}
let schedule = cfg.schedule.clone();
@@ -72,7 +73,7 @@ pub(crate) async fn start_server(cfg: Arc<Config>, targets: Arc<ProcessTargets>)
.service(m3u_api_register())
.service(xmltv_api_register())
.service(index)
.service(actix_files::Files::new("/", web_dir.to_string()))
.service(actix_files::Files::new("/", &web_dir_path))
)
.bind(format!("{}:{}", host, port))?
.run().await
+116 -17
View File
@@ -1,15 +1,18 @@
// https://github.com/tellytv/go.xtream-codes/blob/master/structs.go
use std::io::Error;
use std::str::FromStr;
use actix_web::{HttpRequest, HttpResponse, web, Resource};
use chrono::{Duration, Local};
use log::debug;
use crate::api::api_utils::{get_user_target, serve_file};
use crate::api::api_utils::{get_user_target, get_user_target_by_credentials, serve_file};
use crate::api::api_model::{AppState, UserApiRequest, XtreamAuthorizationResponse, XtreamServerInfo, XtreamUserInfo};
use crate::model::api_proxy::{UserCredentials};
use crate::model::config::Config;
use crate::model::config::{Config};
use crate::model::model_config::{TargetType};
use crate::repository::xtream_repository::{COL_CAT_LIVE, COL_CAT_SERIES, COL_CAT_VOD, COL_LIVE, COL_SERIES, COL_VOD, xtream_get_all};
use crate::repository::xtream_repository::{COL_CAT_LIVE, COL_CAT_SERIES, COL_CAT_VOD, COL_LIVE, COL_SERIES, COL_VOD, xtream_get_all, xtream_get_series_info, xtream_get_vod_info};
use crate::utils::get_client_request;
fn get_user_info(user: &UserCredentials, cfg: &Config) -> XtreamAuthorizationResponse {
let server = cfg._api_proxy.read().unwrap().as_ref().unwrap().server.clone();
@@ -41,6 +44,65 @@ fn get_user_info(user: &UserCredentials, cfg: &Config) -> XtreamAuthorizationRes
}
}
async fn xtream_player_api_stream(
api_req: &web::Query<UserApiRequest>,
_app_state: &web::Data<AppState>,
context: &str,
username: &str,
password: &str,
stream_id: &str,
) -> HttpResponse {
if let Some((_user, target)) = get_user_target_by_credentials(&username, &password, api_req, _app_state) {
let target_name = &target.name;
if target.has_output(&TargetType::Xtream) {
match _app_state.config.get_xtream_input_for_target(target_name) {
None => {}
Some(input) => {
let username = input.username.as_ref().unwrap().clone();
let password = input.password.as_ref().unwrap().clone();
let stream_url = format!("{}/{}/{}/{}/{}", input.url, context, username, password, stream_id);
let url = reqwest::Url::parse(&stream_url).unwrap();
let client = get_client_request(input, url);
if let Ok(response) = client.send().await {
if response.status().is_success() {
return HttpResponse::Ok().streaming(response.bytes_stream());
}
}
}
}
}
}
HttpResponse::BadRequest().finish()
}
async fn xtream_player_api_live_stream(
api_req: web::Query<UserApiRequest>,
path: web::Path<(String, String, String)>,
_app_state: web::Data<AppState>,
) -> HttpResponse {
let (username, password, stream_id) = path.into_inner();
xtream_player_api_stream(&api_req, &_app_state, "live", &username, &password, &stream_id).await
}
async fn xtream_player_api_series_stream(
api_req: web::Query<UserApiRequest>,
path: web::Path<(String, String, String)>,
_app_state: web::Data<AppState>,
) -> HttpResponse {
let (username, password, stream_id) = path.into_inner();
xtream_player_api_stream(&api_req, &_app_state, "series", &username, &password, &stream_id).await
}
async fn xtream_player_api_movie_stream(
api_req: web::Query<UserApiRequest>,
path: web::Path<(String, String, String)>,
_app_state: web::Data<AppState>,
) -> HttpResponse {
let (username, password, stream_id) = path.into_inner();
xtream_player_api_stream(&api_req, &_app_state, "movie", &username, &password, &stream_id).await
}
async fn xtream_player_api(
api_req: web::Query<UserApiRequest>,
req: HttpRequest,
@@ -54,21 +116,55 @@ async fn xtream_player_api(
if action.is_empty() {
return HttpResponse::Ok().json(get_user_info(&user, &_app_state.config));
}
match match action {
"get_live_categories" => xtream_get_all(&_app_state.config, target_name, COL_CAT_LIVE),
"get_vod_categories" => xtream_get_all(&_app_state.config, target_name, COL_CAT_VOD),
"get_series_categories" => xtream_get_all(&_app_state.config, target_name, COL_CAT_SERIES),
"get_live_streams" => xtream_get_all(&_app_state.config, target_name, COL_LIVE),
"get_vod_streams" => xtream_get_all(&_app_state.config, target_name, COL_VOD),
"get_series" => xtream_get_all(&_app_state.config, target_name, COL_SERIES),
_ => Err(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Cant find action: {}/{}", target_name, action))),
} {
Ok(file_path) => {
serve_file(&file_path, &req).await
match action {
"get_series_info" => {
match FromStr::from_str(api_req.series_id.trim()) {
Ok(stream_id) => {
match xtream_get_series_info(&_app_state.config, target_name, stream_id) {
Ok(content) => HttpResponse::Ok().content_type(mime::APPLICATION_JSON).body(content),
Err(_) => HttpResponse::NoContent().finish()
}
}
Err(_) => HttpResponse::BadRequest().finish()
}
}
Err(err) => {
debug!("Could not open file for xtream target: {} {}", target_name, err);
HttpResponse::NoContent().finish()
"get_vod_info" => {
match FromStr::from_str(api_req.vod_id.trim()) {
Ok(stream_id) => {
match xtream_get_vod_info(&_app_state.config, target_name, stream_id) {
Ok(content) => HttpResponse::Ok().content_type(mime::APPLICATION_JSON).body(content),
Err(_) => HttpResponse::NoContent().finish()
}
}
Err(_) => HttpResponse::BadRequest().finish()
}
}
_ => {
match match action {
"get_live_categories" => xtream_get_all(&_app_state.config, target_name, COL_CAT_LIVE),
"get_vod_categories" => xtream_get_all(&_app_state.config, target_name, COL_CAT_VOD),
"get_series_categories" => xtream_get_all(&_app_state.config, target_name, COL_CAT_SERIES),
"get_live_streams" => xtream_get_all(&_app_state.config, target_name, COL_LIVE),
"get_vod_streams" => xtream_get_all(&_app_state.config, target_name, COL_VOD),
"get_series" => xtream_get_all(&_app_state.config, target_name, COL_SERIES),
_ => Err(Error::new(std::io::ErrorKind::Unsupported, format!("Cant find action: {}/{}", target_name, action))),
} {
Ok(maybe_file_path_or_content) => {
let (path, content) = maybe_file_path_or_content;
if let Some(file_path) = path {
serve_file(&file_path, &req).await
} else if let Some(payload) = content {
HttpResponse::Ok().body(payload)
} else {
HttpResponse::NoContent().finish()
}
}
Err(err) => {
debug!("Could not open file for xtream target: {} {}", target_name, err);
HttpResponse::NoContent().finish()
}
}
}
}
} else {
@@ -92,5 +188,8 @@ pub(crate) fn xtream_api_register() -> Vec<Resource> {
vec![
web::resource("/player_api.php").route(web::get().to(xtream_player_api)),
web::resource("/xtream").route(web::get().to(xtream_player_api)),
web::resource("/live/{username}/{password}/{stream_id}").route(web::get().to(xtream_player_api_live_stream)),
web::resource("/movie/{username}/{password}/{stream_id}").route(web::get().to(xtream_player_api_movie_stream)),
web::resource("/series/{username}/{password}/{stream_id}").route(web::get().to(xtream_player_api_series_stream)),
]
}