From d8002b27dd467a6b6871f87610e21985ea73a3af Mon Sep 17 00:00:00 2001 From: euzu Date: Mon, 3 Feb 2025 23:54:08 +0100 Subject: [PATCH] xtream_live_stream_use_prefix added --- CHANGELOG.md | 4 +- Cargo.lock | 120 ++++++++++++++++++++------------ README.md | 3 +- src/api/model/xtream.rs | 23 ++++-- src/api/v1_api.rs | 1 + src/api/xtream_api.rs | 17 ++++- src/model/config.rs | 3 +- src/processing/xtream_parser.rs | 13 ++-- 8 files changed, 124 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3635dc07e..7ca5aeb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,9 @@ update_on_boot: false web_ui_enabled: true ``` - Added new option to `input` `xtream_live_stream_without_extension`. Default is `false`. Some providers don't like `.ts` extension, some providers need it. -Now you can disable or enable it for a provider. + Now you can disable or enable it for a provider. +- Aded new option to `input` `xtream_live_stream_use_prefix`.. Default is `true`. Some providers don't like `/live/` prefix for streams, some providers need it. + Now you can disable or enable it for a provider. - Added `path` to `api-proxy.yml` server config for simpler front reverse-proxy configuration (like nginx) - added `hlsr` handling. - refactored strm generation, does not delete root directory, avoids unchanged file creations. diff --git a/Cargo.lock b/Cargo.lock index 042ea2f5b..1c6d02e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,7 +261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -622,9 +622,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -634,9 +634,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "bytestring" @@ -655,9 +655,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" dependencies = [ "jobserver", "libc", @@ -871,9 +871,9 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" dependencies = [ "convert_case", "proc-macro2", @@ -1189,10 +1189,22 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "gimli" version = "0.31.1" @@ -1320,9 +1332,9 @@ checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -1338,9 +1350,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -1858,7 +1870,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1930,9 +1942,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.69" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -1971,9 +1983,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", @@ -2086,18 +2098,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", @@ -2223,7 +2235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", - "getrandom", + "getrandom 0.2.15", "rand", "ring", "rustc-hash", @@ -2286,7 +2298,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -2393,7 +2405,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -2481,9 +2493,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.21" +version = "0.23.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" dependencies = [ "once_cell", "ring", @@ -2504,9 +2516,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" dependencies = [ "web-time", ] @@ -2539,9 +2551,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "schannel" @@ -2609,9 +2621,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -2755,9 +2767,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2807,13 +2819,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand 2.3.0", - "getrandom", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3058,9 +3070,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unidecode" @@ -3154,6 +3166,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -3260,9 +3281,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.7" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" dependencies = [ "rustls-pki-types", ] @@ -3478,13 +3499,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "write16" version = "1.0.0" diff --git a/README.md b/README.md index 99bbd169e..1cd4305c1 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,8 @@ Each input has the following attributes: + `xtream_skip_live` true or false, live section can be skipped. + `xtream_skip_vod` true or false, vod section can be skipped. + `xtream_skip_series` true or false, series section can be skipped. - + `xtream_live_stream_without_extension` default false, if set to true `.ts` extension is not added to he stream link. + + `xtream_live_stream_without_extension` default false, if set to true `.ts` extension is not added to the stream link. + + `xtream_live_stream_use_prefix` default true, if set to true `/live/` prefix is added to the stream link. `persist` should be different for `m3u` and `xtream` types. For `m3u` use full filename like `./playlist_{}.m3u`. diff --git a/src/api/model/xtream.rs b/src/api/model/xtream.rs index d204b8f61..a4bd01d06 100644 --- a/src/api/model/xtream.rs +++ b/src/api/model/xtream.rs @@ -26,8 +26,9 @@ pub struct XtreamUserInfo { pub struct XtreamServerInfo { pub url: String, pub port: String, - pub path: Option, - pub protocol: String, // http, https + pub https_port: String, + pub server_protocol: String, // http, https + pub rtmp_port: String, pub timezone: String, pub timestamp_now: i64, pub time_now: String, //"2021-06-28 17:07:37" @@ -39,6 +40,17 @@ pub struct XtreamAuthorizationResponse { pub server_info: XtreamServerInfo, } +#[derive(Serialize)] +pub struct XtreamServerInfoDto { + pub url: String, + pub port: String, + pub path: Option, + pub protocol: String, // http, https + pub timezone: String, + pub timestamp_now: i64, + pub time_now: String, //"2021-06-28 17:07:37" +} + impl XtreamAuthorizationResponse { pub fn new(server_info: &ApiProxyServerInfo, user: &ProxyUserCredentials) -> Self { let now = Local::now(); @@ -58,9 +70,10 @@ impl XtreamAuthorizationResponse { }, server_info: XtreamServerInfo { url: server_info.host.clone(), - port: server_info.port.clone(), - protocol: server_info.protocol.clone(), - path: server_info.path.clone(), + port: if server_info.protocol == "http" { server_info.port.clone() } else { String::from("80") }, + https_port: if server_info.protocol == "https" { server_info.port.clone() } else { String::from("443") }, + server_protocol: server_info.protocol.clone(), + rtmp_port: String::new(), timezone: server_info.timezone.to_string(), timestamp_now: now.timestamp(), time_now: now.format("%Y-%m-%d %H:%M:%S").to_string(), diff --git a/src/api/v1_api.rs b/src/api/v1_api.rs index 57605fee4..2d88a6494 100644 --- a/src/api/v1_api.rs +++ b/src/api/v1_api.rs @@ -125,6 +125,7 @@ fn create_config_input_for_url(name: &str, url: &str) -> ConfigInput { xtream_skip_vod: false, xtream_skip_series: false, xtream_live_stream_without_extension: false, + xtream_live_stream_use_prefix: true, }), ..Default::default() } diff --git a/src/api/xtream_api.rs b/src/api/xtream_api.rs index a86ef9b72..43cddb2bf 100644 --- a/src/api/xtream_api.rs +++ b/src/api/xtream_api.rs @@ -102,9 +102,19 @@ pub fn serve_query(file_path: &Path, filter: &HashMap<&str, &str>) -> HttpRespon HttpResponse::Ok().json(filtered) } -fn get_xtream_player_api_stream_url(input: &ConfigInput, context: &str, action_path: &str, fallback_url: &str) -> Option { - let ctx_path = if context.is_empty() { String::new() } else { format!("{context}/") }; +fn get_xtream_player_api_stream_url(input: &ConfigInput, context: &XtreamApiStreamContext, action_path: &str, fallback_url: &str) -> Option { if let Some(user_info) = input.get_user_info() { + let ctx = match context { + XtreamApiStreamContext::LiveAlt | + XtreamApiStreamContext::Live => { + let use_prefix = input.options.as_ref().map_or(true, |o| o.xtream_live_stream_use_prefix); + String::from( if use_prefix { "live" } else { "" }) + }, + XtreamApiStreamContext::Movie + | XtreamApiStreamContext::Series + | XtreamApiStreamContext::Timeshift => context.to_string() + }; + let ctx_path = if ctx.is_empty() { String::new() } else { format!("{ctx}/") }; Some(format!("{}/{}{}/{}/{}", &user_info.base_url, ctx_path, @@ -121,6 +131,7 @@ fn get_xtream_player_api_stream_url(input: &ConfigInput, context: &str, action_p fn get_user_info(user: &ProxyUserCredentials, cfg: &Config) -> XtreamAuthorizationResponse { let server_info = cfg.get_user_server_info(user); + XtreamAuthorizationResponse::new(&server_info, user) } @@ -178,7 +189,7 @@ async fn xtream_player_api_stream( }; let stream_url = try_option_bad_request!(get_xtream_player_api_stream_url(input, - stream_req.context.to_string().as_str(), &query_path, pli.url.as_str()), + &stream_req.context, &query_path, pli.url.as_str()), true, format!("Cant find stream url for target {target_name}, context {}, stream_id {virtual_id}", stream_req.context)); diff --git a/src/model/config.rs b/src/model/config.rs index 09d5ec0fe..14af00879 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -33,7 +33,6 @@ pub const MAPPER_ATTRIBUTE_FIELDS: &[&str] = &[ "time_shift", "rec", "url", "epg_channel_id", "epg_id" ]; - pub const AFFIX_FIELDS: &[&str] = &["name", "title", "group"]; pub const COUNTER_FIELDS: &[&str] = &["name", "title", "chno"]; @@ -555,6 +554,8 @@ pub struct ConfigInputOptions { pub xtream_skip_vod: bool, #[serde(default)] pub xtream_skip_series: bool, + #[serde(default = "default_as_true")] + pub xtream_live_stream_use_prefix: bool, #[serde(default)] pub xtream_live_stream_without_extension: bool, } diff --git a/src/processing/xtream_parser.rs b/src/processing/xtream_parser.rs index 50a3a4983..675284e79 100644 --- a/src/processing/xtream_parser.rs +++ b/src/processing/xtream_parser.rs @@ -80,10 +80,14 @@ pub fn parse_xtream_series_info(info: &Value, group_title: &str, series_name: &s } pub fn create_xtream_url(xtream_cluster: XtreamCluster, url: &str, username: &str, password: &str, - stream: &XtreamStream, live_stream_without_extension: bool) -> Rc { + stream: &XtreamStream, live_stream_use_prefix: bool, live_stream_without_extension: bool) -> Rc { if stream.direct_source.is_empty() { let stream_base_url = match xtream_cluster { - XtreamCluster::Live => format!("{url}/live/{username}/{password}/{}{}", &stream.get_stream_id(), if live_stream_without_extension { "" } else { ".ts" }), + XtreamCluster::Live => { + let ctx_path = if live_stream_use_prefix {"live/"} else { "" }; + let suffix = if live_stream_without_extension { "" } else { ".ts" }; + format!("{url}/{ctx_path}{username}/{password}/{}{suffix}", &stream.get_stream_id()) + }, XtreamCluster::Video => { let ext = stream.container_extension.as_ref().map_or("mp4", |e| e.as_str()); format!("{url}/movie/{username}/{password}/{}.{ext}", &stream.get_stream_id()) @@ -120,13 +124,14 @@ pub fn parse_xtream(input: &ConfigInput, channels: vec![], }); - let live_stream_without_extension = input.options.as_ref().is_some_and(|o| o.xtream_live_stream_without_extension); + let (live_stream_use_prefix, live_stream_without_extension) = input.options.as_ref() + .map_or((true, false), |o| (o.xtream_live_stream_use_prefix, o.xtream_live_stream_without_extension)); for stream in xtream_streams { let group = group_map.get(&stream.category_id).unwrap_or(&unknown_grp); let mut grp = group.borrow_mut(); let category_name = &grp.category_name; - let stream_url = create_xtream_url(xtream_cluster, url, username, password, &stream, live_stream_without_extension); + let stream_url = create_xtream_url(xtream_cluster, url, username, password, &stream, live_stream_use_prefix, live_stream_without_extension); let item = PlaylistItem { header: RefCell::new(PlaylistItemHeader { id: Rc::new(stream.get_stream_id().to_string()),