diff --git a/backend/src/api/endpoints/hls_api.rs b/backend/src/api/endpoints/hls_api.rs index 3d5dab20b..de49bf755 100644 --- a/backend/src/api/endpoints/hls_api.rs +++ b/backend/src/api/endpoints/hls_api.rs @@ -1,9 +1,9 @@ -use crate::api::api_utils::try_option_bad_request; +use crate::api::api_utils::{get_headers_from_request, try_option_bad_request}; use crate::api::api_utils::try_unwrap_body; use crate::api::api_utils::{ force_provider_stream_response, get_stream_alternative_url, is_seek_request, }; -use crate::api::model::AppState; +use crate::api::model::{AppState}; use crate::api::model::UserSession; use crate::api::model::{create_custom_video_stream_response, CustomVideoStreamType}; use crate::auth::Fingerprint; @@ -19,6 +19,7 @@ use serde::Deserialize; use shared::model::{PlaylistItemType, UserConnectionPermission, XtreamCluster}; use shared::utils::{is_hls_url, replace_url_extension, sanitize_sensitive_info, HLS_EXT}; use std::sync::Arc; +use axum::http::HeaderMap; #[derive(Debug, Deserialize)] struct HlsApiPathParams { @@ -46,6 +47,7 @@ pub(in crate::api) async fn handle_hls_stream_request( hls_url: &str, virtual_id: u32, input: &ConfigInput, + req_headers: &HeaderMap, connection_permission: UserConnectionPermission, ) -> impl IntoResponse + Send { let url = replace_url_extension(hls_url, HLS_EXT); @@ -91,10 +93,15 @@ pub(in crate::api) async fn handle_hls_stream_request( } }; + + let filter_header = None; + let req_headers = get_headers_from_request(req_headers, &filter_header); + let headers = request::get_request_headers(None, Some(&req_headers)); let input_source = InputSource::from(input).with_url(request_url); match request::download_text_content( Arc::clone(&app_state.http_client.load()), &input_source, + Some(&headers), None, ) .await @@ -232,6 +239,7 @@ async fn hls_api_stream( &session.stream_url, virtual_id, &input, + &req_headers, connection_permission, ) .await diff --git a/backend/src/api/endpoints/m3u_api.rs b/backend/src/api/endpoints/m3u_api.rs index 01809be61..9581aaa70 100644 --- a/backend/src/api/endpoints/m3u_api.rs +++ b/backend/src/api/endpoints/m3u_api.rs @@ -217,6 +217,7 @@ async fn m3u_api_stream( &pli.url, pli.virtual_id, &input, + &req_headers, connection_permission, ) .await diff --git a/backend/src/api/endpoints/v1_api_config.rs b/backend/src/api/endpoints/v1_api_config.rs index f16968756..c71e6dc9e 100644 --- a/backend/src/api/endpoints/v1_api_config.rs +++ b/backend/src/api/endpoints/v1_api_config.rs @@ -118,7 +118,7 @@ async fn config_batch_content( // The url is changed at this point, we need the raw url for the batch file if let Some(batch_url) = config_input.t_batch_url.as_ref() { let input_source = InputSource::from(&*config_input).with_url(batch_url.to_owned()); - return match download_text_content(Arc::clone(&app_state.http_client.load()), &input_source, None).await { + return match download_text_content(Arc::clone(&app_state.http_client.load()), &input_source, None, None).await { Ok((content, _path)) => { // Return CSV with explicit content-type try_unwrap_body!(axum::response::Response::builder() diff --git a/backend/src/api/endpoints/xtream_api.rs b/backend/src/api/endpoints/xtream_api.rs index eea038f91..25f3bd012 100644 --- a/backend/src/api/endpoints/xtream_api.rs +++ b/backend/src/api/endpoints/xtream_api.rs @@ -381,6 +381,7 @@ async fn xtream_player_api_stream( &stream_url, pli.virtual_id, &input, + &req_headers, connection_permission, ) .await @@ -483,6 +484,7 @@ async fn xtream_player_api_stream_with_token( &pli.url, pli.virtual_id, &input, + &req_headers, UserConnectionPermission::Allowed, ) .await @@ -1083,6 +1085,7 @@ async fn xtream_get_short_epg( Arc::clone(&app_state.http_client.load()), &input_source, None, + None, ) .await { diff --git a/backend/src/utils/network/request.rs b/backend/src/utils/network/request.rs index 2dea82e7a..26b4588d7 100644 --- a/backend/src/utils/network/request.rs +++ b/backend/src/utils/network/request.rs @@ -104,7 +104,7 @@ pub async fn get_input_text_content(client: Arc, input: &InputS debug_if_enabled!("getting input text content working_dir: {}, url: {}", working_dir, sanitize_sensitive_info(&input.url)); if input.url.parse::().is_ok() { - match download_text_content(client, input, persist_filepath).await { + match download_text_content(client, input, None, persist_filepath).await { Ok((content, _response_url)) => Ok(content), Err(e) => { error!("cant download input url: {} => {}", sanitize_sensitive_info(&input.url), sanitize_sensitive_info(e.to_string().as_str())); @@ -275,7 +275,6 @@ pub async fn get_remote_content_as_stream( let response_url = response.url().to_string(); let headers = response.headers(); - debug!("{headers:?}"); let header_value = headers.get(CONTENT_ENCODING); let mut encoding = header_value.and_then(|encoding_header| encoding_header.to_str().map_or(None, |value| Some(value.to_string()))); let stream_reader = StreamReader::new( @@ -303,9 +302,13 @@ pub async fn get_remote_content_as_stream( Ok((reader, response_url)) } -async fn get_remote_content(client: Arc, input: &InputSource, url: &Url) -> Result<(String, String), Error> { +async fn get_remote_content(client: Arc, input: &InputSource, headers: Option<&HeaderMap>, url: &Url) -> Result<(String, String), Error> { let start_time = Instant::now(); - let (mut stream, response_url) = get_remote_content_as_stream(client.clone(), url, input.method, Some(&input.headers)).await.map_err(|e| str_to_io_error(&format!("Failed to read content: {e}")))?; + let custom_headers: Option>> = headers.map(|h| h.iter().map(|(k, v)| (k.as_str().to_string(), v.as_bytes().to_vec())).collect()); + let headers = get_request_headers(Some(&input.headers), custom_headers.as_ref()); + let headers: HashMap = headers.iter().map(|(k, v)| (k.as_str().to_string(), String::from_utf8_lossy(&*v.as_bytes().to_vec()).to_string())).collect(); + + let (mut stream, response_url) = get_remote_content_as_stream(client.clone(), url, input.method, Some(&headers)).await.map_err(|e| str_to_io_error(&format!("Failed to read content: {e}")))?; let mut content = String::new(); stream.read_to_string(&mut content).await.map_err(|e| str_to_io_error(&format!("Failed to read content: {e}")))?; debug_if_enabled!("Request took:{} {}", format_elapsed_time(start_time.elapsed().as_secs()), sanitize_sensitive_info(url.as_str())); @@ -337,14 +340,14 @@ async fn download_epg_content_as_file(client: Arc, input: &Conf } } -pub async fn download_text_content(client: Arc, input: &InputSource, persist_filepath: Option) -> Result<(String, String), Error> { +pub async fn download_text_content(client: Arc, input: &InputSource, headers: Option<&HeaderMap>, persist_filepath: Option) -> Result<(String, String), Error> { if let Ok(url) = input.url.parse::() { let result = if url.scheme() == "file" { url.to_file_path().map_or_else(|()| Err(str_to_io_error(&format!("Unknown file {}", sanitize_sensitive_info(&input.url)))), |file_path| get_local_file_content(&file_path).map(|c| (c, url.to_string())), ) } else { - get_remote_content(client, input, &url).await + get_remote_content(client, input, headers, &url).await }; match result { Ok((content, response_url)) => { @@ -360,9 +363,9 @@ pub async fn download_text_content(client: Arc, input: &InputSo } } -async fn download_json_content(client: Arc, input: &InputSource,persist_filepath: Option) -> Result { +async fn download_json_content(client: Arc, input: &InputSource, persist_filepath: Option) -> Result { debug_if_enabled!("downloading json content from {}", sanitize_sensitive_info(&input.url)); - match download_text_content(client, input, persist_filepath).await { + match download_text_content(client, input, None, persist_filepath).await { Ok((content, _response_url)) => { match serde_json::from_str::(&content) { Ok(value) => Ok(value), diff --git a/backend/src/utils/network/xtream.rs b/backend/src/utils/network/xtream.rs index dd709e0fd..cf4c5b48d 100644 --- a/backend/src/utils/network/xtream.rs +++ b/backend/src/utils/network/xtream.rs @@ -46,7 +46,7 @@ pub fn get_xtream_player_api_info_url(input: &ConfigInput, cluster: XtreamCluste pub async fn get_xtream_stream_info_content(client: Arc, input: &InputSource) -> Result { - match request::download_text_content(client, input, None).await { + match request::download_text_content(client, input, None, None).await { Ok((content, _response_url)) => Ok(content), Err(err) => Err(err) } diff --git a/frontend/src/app/components/userlist/proxy_user_credentials_form.rs b/frontend/src/app/components/userlist/proxy_user_credentials_form.rs index f3ca1cfa0..9dd455840 100644 --- a/frontend/src/app/components/userlist/proxy_user_credentials_form.rs +++ b/frontend/src/app/components/userlist/proxy_user_credentials_form.rs @@ -157,9 +157,9 @@ pub fn ProxyUserCredentialsForm(props: &ProxyUserCredentialsFormProps) -> Html { html! {
- { if *update { + { /*if *update { config_field_custom!(translate.t("LABEL.PLAYLIST"), (*set_selected_target).as_ref().map_or_else(String::new, |t| t.clone())) - } else { config_field_child!(translate.t("LABEL.PLAYLIST"), { + } else {*/ config_field_child!(translate.t("LABEL.PLAYLIST"), { html! {