From 70123795397a140409be94ee1e999c2c4eed0c70 Mon Sep 17 00:00:00 2001 From: euzu Date: Sun, 26 Jan 2025 13:16:07 +0100 Subject: [PATCH] fixed series redirect --- CHANGELOG.md | 3 +- src/api/xtream_api.rs | 23 ++++++--- src/processing/xtream_parser.rs | 4 +- src/repository/xtream_repository.rs | 8 ++-- test/http-client.env.json | 14 +++++- test/rest-api.http | 74 +++++++++-------------------- 6 files changed, 60 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f27ff520a..f18d98ef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # Changelog -# 2.1.2 (2025-01-xx) +# 2.1.2 (2025-01-26) - `Strm` output has an additional option `strm_props`. These props are written to the strm file. You can add properties like `#KODIPROP:seekable=true|false`, `#KODIPROP:inputstream=inputstream.ffmpeg` or `"#KODIPROP:http-reconnect=true`. - Fixed xtream affix-processed output. - `log_sanitize_sensitive_info` added to `config.yml`. Default is `true`. - added `resource_rewrite_disabled` to `reverse_proxy` config to disable resource url rewrite. +- Fixed series redirect proxy mode. - Added `pushover.net` config to messaging. ```yaml messaging: diff --git a/src/api/xtream_api.rs b/src/api/xtream_api.rs index 9b582982d..87115c2e7 100644 --- a/src/api/xtream_api.rs +++ b/src/api/xtream_api.rs @@ -188,7 +188,7 @@ async fn xtream_player_api_stream( } let (action_stream_id, stream_ext) = xtream_api_request_separate_number_and_remainder(stream_req.stream_id); let virtual_id: u32 = try_result_bad_request!(action_stream_id.trim().parse()); - let pli = try_result_bad_request!(xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, None).await, true, format!("Failed to read xtream item for stream id {}", virtual_id)); + let (pli, mapping) = try_result_bad_request!(xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, None).await, true, format!("Failed to read xtream item for stream id {}", virtual_id)); let input = try_option_bad_request!(app_state.config.get_input_by_id(pli.input_id), true, format!("Cant find input for target {target_name}, context {}, stream_id {virtual_id}", stream_req.context)); if pli.item_type == PlaylistItemType::LiveHls { @@ -197,8 +197,17 @@ async fn xtream_player_api_stream( } if user.proxy == ProxyType::Redirect { - debug_if_enabled!("Redirecting stream request to {}", sanitize_sensitive_info(&pli.url)); - return HttpResponse::Found().insert_header(("Location", pli.url.as_str())).finish(); + if pli.item_type == PlaylistItemType::Series { + debug_if_enabled!("Redirecting stream request to {}", sanitize_sensitive_info(&pli.url)); + return HttpResponse::Found().insert_header(("Location", pli.url.as_str())).finish(); + } else { + let ext = stream_ext.unwrap_or_else(String::new); + let url = input.url.as_str(); + let username = input.username.as_ref().map_or("", |v| v); + let password = input.password.as_ref().map_or("", |v| v); + let stream_url = format!("{url}/series/{username}/{password}/{}{ext}", mapping.provider_id); + return HttpResponse::Found().insert_header(("Location", stream_url)).finish(); + } } let extension = stream_ext.unwrap_or_else( @@ -357,7 +366,7 @@ async fn xtream_player_api_resource( } let virtual_id: u32 = try_result_bad_request!(resource_req.stream_id.trim().parse()); let resource = resource_req.action_path.trim(); - let pli = try_result_bad_request!(xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, None).await, true, format!("Failed to read xtream item for stream id {}", virtual_id)); + let (pli, _) = try_result_bad_request!(xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, None).await, true, format!("Failed to read xtream item for stream id {}", virtual_id)); let stream_url = if resource.starts_with(INFO_RESOURCE_PREFIX) { try_result_bad_request!(xtream_get_info_resource_url(&app_state.config, &pli, target, resource).await) } else if resource.starts_with(SEASON_RESOURCE_PREFIX) { @@ -452,7 +461,7 @@ async fn xtream_get_stream_info_response(app_state: &AppState, user: &ProxyUserC Err(_) => return HttpResponse::BadRequest().finish() }; - if let Ok(pli) = xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, Some(cluster)).await { + if let Ok((pli, _)) = xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, Some(cluster)).await { let input_id = pli.input_id; if let Some(input) = app_state.config.get_input_by_id(input_id) { if let Some(info_url) = download::get_xtream_player_api_info_url(input, cluster, pli.provider_id) { @@ -480,7 +489,7 @@ async fn xtream_get_short_epg(app_state: &AppState, user: &ProxyUserCredentials, Err(_) => return HttpResponse::BadRequest().finish() }; - if let Ok(pli) = xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, None).await { + if let Ok((pli, _)) = xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, None).await { let input_id: u16 = pli.input_id; if let Some(input) = app_state.config.get_input_by_id(input_id) { if let Some(action_url) = download::get_xtream_player_api_action_url(input, ACTION_GET_SHORT_EPG) { @@ -530,7 +539,7 @@ async fn xtream_player_api_handle_content_action(config: &Config, target_name: & async fn xtream_get_catchup_response(app_state: &AppState, target: &ConfigTarget, stream_id: &str, start: &str, end: &str) -> HttpResponse { let virtual_id: u32 = try_result_bad_request!(FromStr::from_str(stream_id)); - let pli = try_result_bad_request!(xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, Some(XtreamCluster::Live)).await); + let (pli, _) = try_result_bad_request!(xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, Some(XtreamCluster::Live)).await); let input = try_option_bad_request!(app_state.config.get_input_by_id(pli.input_id)); let info_url = try_option_bad_request!(download::get_xtream_player_api_action_url(input, ACTION_GET_CATCHUP_TABLE).map(|action_url| format!("{action_url}&{TAG_STREAM_ID}={}&start={start}&end={end}", pli.provider_id))); let content = try_result_bad_request!(download::get_xtream_stream_info_content(Arc::clone(&app_state.http_client), info_url.as_str(), input).await); diff --git a/src/processing/xtream_parser.rs b/src/processing/xtream_parser.rs index 64f161242..d14d680dd 100644 --- a/src/processing/xtream_parser.rs +++ b/src/processing/xtream_parser.rs @@ -29,7 +29,7 @@ fn map_to_xtream_streams(xtream_cluster: XtreamCluster, streams: &Value) -> Resu } } -fn create_xtream_series_info_url(url: &str, username: &str, password: &str, episode: &XtreamSeriesInfoEpisode) -> Rc { +fn create_xtream_series_episode_url(url: &str, username: &str, password: &str, episode: &XtreamSeriesInfoEpisode) -> Rc { if episode.direct_source.is_empty() { let ext = episode.container_extension.clone(); let stream_base_url = format!("{url}/series/{username}/{password}/{}.{ext}", episode.id); @@ -48,7 +48,7 @@ pub fn parse_xtream_series_info(info: &Value, group_title: &str, series_name: &s Ok(series_info) => { if let Some(episodes) = &series_info.episodes { let result: Vec<(XtreamSeriesInfoEpisode, PlaylistItem)> = episodes.values().flatten().map(|episode| { - let episode_url = create_xtream_series_info_url(url, username, password, episode); + let episode_url = create_xtream_series_episode_url(url, username, password, episode); (episode.clone(), PlaylistItem { header: RefCell::new(PlaylistItemHeader { diff --git a/src/repository/xtream_repository.rs b/src/repository/xtream_repository.rs index dd89d2e46..983e4425e 100644 --- a/src/repository/xtream_repository.rs +++ b/src/repository/xtream_repository.rs @@ -378,7 +378,7 @@ pub async fn xtream_get_item_for_stream_id( config: &Config, target: &ConfigTarget, xtream_cluster: Option, -) -> Result { +) -> Result<(XtreamPlaylistItem, VirtualIdRecord), Error> { let target_path = get_target_storage_path(config, target.name.as_str()).ok_or_else(|| str_to_io_error(&format!("Could not find path for target {}", &target.name)))?; let storage_path = xtream_get_storage_path(config, target.name.as_str()).ok_or_else(|| str_to_io_error(&format!("Could not find path for target {} xtream output", &target.name)))?; { @@ -387,7 +387,7 @@ pub async fn xtream_get_item_for_stream_id( let mut target_id_mapping = BPlusTreeQuery::::try_new(&target_id_mapping_file).map_err(|err| str_to_io_error(&format!("Could not load id mapping for target {} err:{err}", target.name)))?; let mapping = target_id_mapping.query(&virtual_id).ok_or_else(|| str_to_io_error(&format!("Could not find mapping for target {} and id {}", target.name, virtual_id)))?; - match mapping.item_type { + let result = match mapping.item_type { PlaylistItemType::SeriesInfo => { xtream_read_series_item_for_stream_id(config, virtual_id, &storage_path).await } @@ -409,7 +409,9 @@ pub async fn xtream_get_item_for_stream_id( let cluster = try_cluster!(xtream_cluster, mapping.item_type, virtual_id)?; xtream_read_item_for_stream_id(config, virtual_id, &storage_path, cluster).await } - } + }; + + result.map(|xpli| (xpli, mapping)) } } diff --git a/test/http-client.env.json b/test/http-client.env.json index e419a2267..4b8d25afb 100644 --- a/test/http-client.env.json +++ b/test/http-client.env.json @@ -1,7 +1,17 @@ { - "dev": { + "dev-redirect": { "local": "http://localhost:8901", "silver": "http://10.41.41.41:8901", - "beta": "http://10.41.41.41:8902" + "beta": "http://10.41.41.41:8902", + "username": "xt", + "password": "xt" + }, + "dev-reverse": { + "local": "http://localhost:8901", + "silver": "http://10.41.41.41:8901", + "beta": "http://10.41.41.41:8902", + "username": "xtr", + "password": "xtr" } + } \ No newline at end of file diff --git a/test/rest-api.http b/test/rest-api.http index 52c0168ff..72d6b88ff 100644 --- a/test/rest-api.http +++ b/test/rest-api.http @@ -18,82 +18,54 @@ Content-Type: application/json {"username": "test", "password": "test"} ### m3u playlist -GET {{local}}/m3u?username=xt&password=xt +GET {{local}}/m3u?username={{username}}&password={{password}} ### m3u playlist - form auth POST {{local}}/m3u Content-Type: application/x-www-form-urlencoded -username=xt&password=xt +username={{username}}&password={{password}} ### m3u playlist reverse -GET {{local}}/m3u?username=xtr&password=xtr +GET {{local}}/m3u?username={{username}}&password={{password}} ### m3u playlist -GET {{local}}/m3u?username=xt&password=xt - -### m3u playlist -GET {{local}}/m3u?username=xt2&password=xt2 - +GET {{local}}/m3u?username={{username}}&password={{password}} ### xtream login -GET {{local}}/player_api.php?username=xt&password=xt +GET {{local}}/player_api.php?username={{username}}&password={{password}} ### xtream live_categories -GET {{local}}/player_api.php?username=xt&password=xt&action=get_live_categories +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_live_categories -### xtream live_streams reverse -GET {{local}}/player_api.php?username=xtr&password=xtr&action=get_live_streams&category_id=2 - -### xtream live_streams redirect -GET {{local}}/player_api.php?username=xt&password=xt&action=get_live_streams&category_id=102 +### xtream live_streams +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_live_streams&category_id=102 ### xtream vod_categories -GET {{local}}/player_api.php?username=xt&password=xt&action=get_vod_categories +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_vod_categories -### xtream vod streams reverse -GET {{local}}/player_api.php?username=xtr&password=xtr&action=get_vod_streams&category_id=29 +### xtream vod streams +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_vod_streams&category_id=29 -### xtream vod streams redirect -GET {{local}}/player_api.php?username=xt&password=xt&action=get_vod_streams&category_id=636 +### xtream vod info +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_vod_info&vod_id=38497 -### xtream vod info reverse -GET {{local}}/player_api.php?username=xtr&password=xtr&action=get_vod_info&vod_id=38497 +### xtream series_categories +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_series_categories -### xtream vod info redirect -GET {{local}}/player_api.php?username=xt&password=xt&action=get_vod_info&vod_id=38497 +### xtream series +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_series -### xtream series_categories reverse -GET {{local}}/player_api.php?username=xtr&password=xtr&action=get_series_categories +### xtream series for category +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_series&category_id=12 -### xtream series_categories redirect -GET {{local}}/player_api.php?username=xt&password=xt&action=get_series_categories -### xtream series reverse -GET {{local}}/player_api.php?username=xtr&password=xtr&action=get_series - -### xtream series redirect -GET {{local}}/player_api.php?username=xt&password=xt&action=get_series - -### xtream series for category redirect -GET {{local}}/player_api.php?username=xt&password=xt&action=get_series&category_id=13 - -### xtream series for category reverse -GET {{local}}/player_api.php?username=xtr&password=xtr&action=get_series&category_id=13 - -### xtream series info redirect -GET {{local}}/player_api.php?username=xt&password=xt&action=get_series_info&series_id=3 - -### xtream series info reverse -GET {{local}}/player_api.php?username=xtr&password=xtr&action=get_series_info&series_id=3 +### xtream series info +GET {{local}}/player_api.php?username={{username}}&password={{password}}&action=get_series_info&series_id=586 ### xtream series stream - GET {{local}}/series/xt/xt/18137 + GET {{local}}/series/{{username}}/{{password}}/201422 ### get stream -{{local}}/m3u-stream/live/xt/xt/667 - - -### xtream series_categories redirect -GET http://10.41.41.41:8902/player_api.php?username=xt&password=xt&action=get_vod_streams&category_id=29 +{{local}}/m3u-stream/live/{{username}}/{{password}}/667